// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved. // For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md #include "BluetoothRunLoop.hpp" #include "btstack_util.h" #include #include namespace bluetooth { QueueHandle_t RunLoop::btstackRunLoopQueue; TaskHandle_t RunLoop::btstackRunLoopTask; QueueHandle_t RunLoop::triggerQueue; TimerHandle_t RunLoop::btstackTimer = nullptr; btstack_linked_list_t RunLoop::timers; btstack_linked_list_t RunLoop::dataSources; bool RunLoop::runLoopExitRequested; bool RunLoop::removeTimer(btstack_timer_source_t *timerSource) { return btstack_linked_list_remove(&timers, reinterpret_cast(timerSource)); } void RunLoop::setTriggerQueue(QueueHandle_t queue) { assert(queue != nullptr); triggerQueue = queue; } void RunLoop::deinit() { vQueueDelete(btstackRunLoopQueue); xTimerDelete(btstackTimer, 0); } void RunLoop::init() { timers = nullptr; btstackRunLoopQueue = xQueueCreate(runLoopQueueLength, runLoopQueueItemSize); // Task to handle to optimize 'run on main thread' btstackRunLoopTask = xTaskGetCurrentTaskHandle(); LOG_INFO("Run loop init, task %p, queue item size %zu", btstackRunLoopTask, sizeof(FunctionCallType)); } void RunLoop::enableDataSourceCallbacks(btstack_data_source_t *dataSource, std::uint16_t callbackTypes) { dataSource->flags |= callbackTypes; } void RunLoop::disableDataSourceCallbacks(btstack_data_source_t *dataSource, std::uint16_t callbackTypes) { dataSource->flags &= ~callbackTypes; } void RunLoop::addDataSource(btstack_data_source_t *dataSource) { btstack_linked_list_add(&dataSources, reinterpret_cast(dataSource)); } auto RunLoop::removeDataSource(btstack_data_source_t *dataSource) -> bool { return btstack_linked_list_remove(&dataSources, reinterpret_cast(dataSource)); } void RunLoop::triggerExit() { runLoopExitRequested = true; } auto RunLoop::getTimeMs() -> TickType_t { return xTaskGetTickCount(); } void RunLoop::trigger() { bool trigger = true; if (triggerQueue != nullptr) { xQueueSend(triggerQueue, &trigger, 0); } else { LOG_FATAL("Trigger queue does not exist!"); } } void RunLoop::addTimer(btstack_timer_source_t *timerSource) { btstack_linked_item_t *it = nullptr; for (it = reinterpret_cast(&timers); it->next != nullptr; it = it->next) { // Don't add timer that's already in there auto next = reinterpret_cast(it->next); if (next == timerSource) { LOG_ERROR("Timer 'btstack_run_loop_timer' already in list!"); return; } // Exit if new timeout before list timeout const auto delta = btstack_time_delta(timerSource->timeout, next->timeout); if (delta < 0) { break; } } timerSource->item.next = it->next; it->next = reinterpret_cast(timerSource); trigger(); } void RunLoop::setTimer(btstack_timer_source_t *timerSource, std::uint32_t timeoutMs) { timerSource->timeout = getTimeMs() + timeoutMs + 1; trigger(); } void RunLoop::triggerCallback([[maybe_unused]] TimerHandle_t xTimer) { trigger(); } void RunLoop::start() { if (btstackTimer == nullptr) { btstackTimer = xTimerCreate("BTStackTimer", pdMS_TO_TICKS(defaultTimerPeriodMs), pdTRUE, nullptr, triggerCallback); xTimerStart(btstackTimer, 0); } } auto RunLoop::process() -> bool { // Process registered function calls on run loop thread while (true) { FunctionCallType message = {nullptr, nullptr}; const auto res = xQueueReceive(btstackRunLoopQueue, &message, 0); if (res == pdFALSE) { break; } if (message.fn != nullptr) { message.fn(message.arg); } } // Process timers and get next timeout std::uint32_t timeoutMs = defaultTimerPeriodMs; while (timers != nullptr) { auto timerSource = reinterpret_cast(timers); const auto now = getTimeMs(); const auto deltaMs = btstack_time_delta(timerSource->timeout, now); if (deltaMs > 0) { timeoutMs = deltaMs; break; } // Remove timer before processing it to allow handler to re-register with run loop removeTimer(timerSource); timerSource->process(timerSource); } // Exit triggered by btstack_run_loop_freertos_trigger_exit (from data source, timer, run on main thread) if (runLoopExitRequested) { return true; } xTimerChangePeriod(btstackTimer, pdMS_TO_TICKS(timeoutMs), 0); return false; } auto RunLoop::getRunLoopInstance() -> btstack_run_loop * { runLoop = btstack_run_loop{ &init, &addDataSource, &removeDataSource, &enableDataSourceCallbacks, &disableDataSourceCallbacks, &setTimer, &addTimer, &removeTimer, &start, nullptr, &getTimeMs, }; return &runLoop; } } // namespace bluetooth