1 /* 2 * Copyright (C) 2017 BlueKitchen GmbH 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. Neither the name of the copyright holders nor the names of 14 * contributors may be used to endorse or promote products derived 15 * from this software without specific prior written permission. 16 * 4. Any redistribution, use, or modification is done solely for 17 * personal benefit and not for any commercial purpose or for 18 * monetary gain. 19 * 20 * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS 21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 23 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS 24 * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 26 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 27 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 28 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 29 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 30 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 * 33 * Please inquire about commercial licensing options at 34 * [email protected] 35 * 36 */ 37 38 /* 39 * btstack_run_loop_freertos.c 40 * 41 * Run loop on dedicated thread on FreeRTOS 42 * The run loop is triggered from other task/ISRs either via Event Groups 43 * or Task Notifications if HAVE_FREERTOS_TASK_NOTIFICATIONS is defined 44 */ 45 46 #define BTSTACK_FILE__ "btstack_run_loop_freertos.c" 47 48 #include <stddef.h> // NULL 49 50 #include "btstack_run_loop_freertos.h" 51 52 #include "btstack_linked_list.h" 53 #include "btstack_debug.h" 54 #include "btstack_util.h" 55 #include "hal_time_ms.h" 56 57 // some SDKs, e.g. esp-idf, place FreeRTOS headers into an 'freertos' folder to avoid name collisions (e.g. list.h, queue.h, ..) 58 // wih this flag, the headers are properly found 59 60 #ifdef HAVE_FREERTOS_INCLUDE_PREFIX 61 #include "freertos/FreeRTOS.h" 62 #include "freertos/task.h" 63 #include "freertos/queue.h" 64 #include "freertos/event_groups.h" 65 #else 66 #include "FreeRTOS.h" 67 #include "task.h" 68 #include "queue.h" 69 #include "event_groups.h" 70 #endif 71 72 typedef struct function_call { 73 void (*fn)(void * arg); 74 void * arg; 75 } function_call_t; 76 77 static const btstack_run_loop_t btstack_run_loop_freertos; 78 79 static QueueHandle_t btstack_run_loop_queue; 80 static TaskHandle_t btstack_run_loop_task; 81 82 #ifndef HAVE_FREERTOS_TASK_NOTIFICATIONS 83 static EventGroupHandle_t btstack_run_loop_event_group; 84 #endif 85 86 // bit 0 event group reserved to wakeup run loop 87 #define EVENT_GROUP_FLAG_RUN_LOOP 1 88 89 // the run loop 90 static btstack_linked_list_t timers; 91 static btstack_linked_list_t data_sources; 92 93 static uint32_t btstack_run_loop_freertos_get_time_ms(void){ 94 return hal_time_ms(); 95 } 96 97 // set timer 98 static void btstack_run_loop_freertos_set_timer(btstack_timer_source_t *ts, uint32_t timeout_in_ms){ 99 ts->timeout = btstack_run_loop_freertos_get_time_ms() + timeout_in_ms + 1; 100 } 101 102 /** 103 * Add timer to run_loop (keep list sorted) 104 */ 105 static void btstack_run_loop_freertos_add_timer(btstack_timer_source_t *ts){ 106 btstack_linked_item_t *it; 107 for (it = (btstack_linked_item_t *) &timers; it->next ; it = it->next){ 108 // don't add timer that's already in there 109 btstack_timer_source_t * next = (btstack_timer_source_t *) it->next; 110 if (next == ts){ 111 log_error( "btstack_run_loop_timer_add error: timer to add already in list!"); 112 return; 113 } 114 // exit if new timeout before list timeout 115 int32_t delta = btstack_time_delta(ts->timeout, next->timeout); 116 if (delta < 0) break; 117 } 118 ts->item.next = it->next; 119 it->next = (btstack_linked_item_t *) ts; 120 } 121 122 /** 123 * Remove timer from run loop 124 */ 125 static int btstack_run_loop_freertos_remove_timer(btstack_timer_source_t *ts){ 126 return btstack_linked_list_remove(&timers, (btstack_linked_item_t *) ts); 127 } 128 129 static void btstack_run_loop_freertos_dump_timer(void){ 130 #ifdef ENABLE_LOG_INFO 131 btstack_linked_item_t *it; 132 int i = 0; 133 for (it = (btstack_linked_item_t *) timers; it ; it = it->next){ 134 btstack_timer_source_t *ts = (btstack_timer_source_t*) it; 135 log_info("timer %u, timeout %u\n", i, (unsigned int) ts->timeout); 136 } 137 #endif 138 } 139 140 // schedules execution from regular thread 141 void btstack_run_loop_freertos_trigger(void){ 142 #ifdef HAVE_FREERTOS_TASK_NOTIFICATIONS 143 xTaskNotify(btstack_run_loop_task, EVENT_GROUP_FLAG_RUN_LOOP, eSetBits); 144 #else 145 xEventGroupSetBits(btstack_run_loop_event_group, EVENT_GROUP_FLAG_RUN_LOOP); 146 #endif 147 } 148 149 void btstack_run_loop_freertos_execute_code_on_main_thread(void (*fn)(void *arg), void * arg){ 150 151 // directly call function if already on btstack task 152 if (xTaskGetCurrentTaskHandle() == btstack_run_loop_task){ 153 (*fn)(arg); 154 return; 155 } 156 157 function_call_t message; 158 message.fn = fn; 159 message.arg = arg; 160 BaseType_t res = xQueueSendToBack(btstack_run_loop_queue, &message, 0); // portMAX_DELAY); 161 if (res != pdTRUE){ 162 log_error("Failed to post fn %p", fn); 163 } 164 btstack_run_loop_freertos_trigger(); 165 } 166 167 #if defined(HAVE_FREERTOS_TASK_NOTIFICATIONS) || (INCLUDE_xEventGroupSetBitFromISR == 1) 168 void btstack_run_loop_freertos_trigger_from_isr(void){ 169 BaseType_t xHigherPriorityTaskWoken; 170 #ifdef HAVE_FREERTOS_TASK_NOTIFICATIONS 171 xTaskNotifyFromISR(btstack_run_loop_task, EVENT_GROUP_FLAG_RUN_LOOP, eSetBits, &xHigherPriorityTaskWoken); 172 if (xHigherPriorityTaskWoken) { 173 #ifdef ESP_PLATFORM 174 portYIELD_FROM_ISR(); 175 #else 176 portYIELD_FROM_ISR( xHigherPriorityTaskWoken ); 177 #endif 178 } 179 #else 180 xEventGroupSetBitsFromISR(btstack_run_loop_event_group, EVENT_GROUP_FLAG_RUN_LOOP, &xHigherPriorityTaskWoken); 181 #endif 182 } 183 184 void btstack_run_loop_freertos_execute_code_on_main_thread_from_isr(void (*fn)(void *arg), void * arg){ 185 function_call_t message; 186 message.fn = fn; 187 message.arg = arg; 188 BaseType_t xHigherPriorityTaskWoken; 189 xQueueSendToBackFromISR(btstack_run_loop_queue, &message, &xHigherPriorityTaskWoken); 190 btstack_run_loop_freertos_trigger_from_isr(); 191 } 192 #endif 193 194 /** 195 * Execute run_loop 196 */ 197 static void btstack_run_loop_freertos_execute(void) { 198 log_debug("RL: execute"); 199 200 while (1) { 201 202 // process data sources 203 btstack_data_source_t *ds; 204 btstack_data_source_t *next; 205 for (ds = (btstack_data_source_t *) data_sources; ds != NULL ; ds = next){ 206 next = (btstack_data_source_t *) ds->item.next; // cache pointer to next data_source to allow data source to remove itself 207 if (ds->flags & DATA_SOURCE_CALLBACK_POLL){ 208 ds->process(ds, DATA_SOURCE_CALLBACK_POLL); 209 } 210 } 211 212 // process registered function calls on run loop thread 213 while (1){ 214 function_call_t message = { NULL, NULL }; 215 BaseType_t res = xQueueReceive( btstack_run_loop_queue, &message, 0); 216 if (res == pdFALSE) break; 217 if (message.fn){ 218 message.fn(message.arg); 219 } 220 } 221 222 // process timers and get next timeout 223 uint32_t timeout_ms = portMAX_DELAY; 224 log_debug("RL: portMAX_DELAY %u", portMAX_DELAY); 225 while (timers) { 226 btstack_timer_source_t * ts = (btstack_timer_source_t *) timers; 227 uint32_t now = btstack_run_loop_freertos_get_time_ms(); 228 int32_t delta_ms = btstack_time_delta(ts->timeout, now); 229 log_debug("RL: now %u, expires %u -> delta %d", now, ts->timeout, delta_ms); 230 if (delta_ms > 0){ 231 timeout_ms = delta_ms; 232 break; 233 } 234 // remove timer before processing it to allow handler to re-register with run loop 235 btstack_run_loop_freertos_remove_timer(ts); 236 log_debug("RL: first timer %p", ts->process); 237 ts->process(ts); 238 } 239 240 // wait for timeout or event group/task notification 241 log_debug("RL: wait with timeout %u", (int) timeout_ms); 242 #ifdef HAVE_FREERTOS_TASK_NOTIFICATIONS 243 xTaskNotifyWait(pdFALSE, 0xffffffff, NULL, pdMS_TO_TICKS(timeout_ms)); 244 #else 245 xEventGroupWaitBits(btstack_run_loop_event_group, EVENT_GROUP_FLAG_RUN_LOOP, 1, 0, pdMS_TO_TICKS(timeout_ms)); 246 #endif 247 } 248 } 249 250 static void btstack_run_loop_freertos_add_data_source(btstack_data_source_t *ds){ 251 btstack_linked_list_add(&data_sources, (btstack_linked_item_t *) ds); 252 } 253 254 static int btstack_run_loop_freertos_remove_data_source(btstack_data_source_t *ds){ 255 return btstack_linked_list_remove(&data_sources, (btstack_linked_item_t *) ds); 256 } 257 258 static void btstack_run_loop_freertos_enable_data_source_callbacks(btstack_data_source_t * ds, uint16_t callback_types){ 259 ds->flags |= callback_types; 260 } 261 262 static void btstack_run_loop_freertos_disable_data_source_callbacks(btstack_data_source_t * ds, uint16_t callback_types){ 263 ds->flags &= ~callback_types; 264 } 265 266 static void btstack_run_loop_freertos_init(void){ 267 timers = NULL; 268 269 // queue to receive events: up to 2 calls from transport, up to 3 for app 270 btstack_run_loop_queue = xQueueCreate(20, sizeof(function_call_t)); 271 272 #ifndef HAVE_FREERTOS_TASK_NOTIFICATIONS 273 // event group to wake run loop 274 btstack_run_loop_event_group = xEventGroupCreate(); 275 #endif 276 277 // task to handle to optimize 'run on main thread' 278 btstack_run_loop_task = xTaskGetCurrentTaskHandle(); 279 280 log_info("run loop init, task %p, queue item size %u", btstack_run_loop_task, (int) sizeof(function_call_t)); 281 } 282 283 /** 284 * @brief Provide btstack_run_loop_posix instance for use with btstack_run_loop_init 285 */ 286 const btstack_run_loop_t * btstack_run_loop_freertos_get_instance(void){ 287 return &btstack_run_loop_freertos; 288 } 289 290 static const btstack_run_loop_t btstack_run_loop_freertos = { 291 &btstack_run_loop_freertos_init, 292 &btstack_run_loop_freertos_add_data_source, 293 &btstack_run_loop_freertos_remove_data_source, 294 &btstack_run_loop_freertos_enable_data_source_callbacks, 295 &btstack_run_loop_freertos_disable_data_source_callbacks, 296 &btstack_run_loop_freertos_set_timer, 297 &btstack_run_loop_freertos_add_timer, 298 &btstack_run_loop_freertos_remove_timer, 299 &btstack_run_loop_freertos_execute, 300 &btstack_run_loop_freertos_dump_timer, 301 &btstack_run_loop_freertos_get_time_ms, 302 }; 303