// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved. // For licensing, see https://github.com/mudita/MuditaOS/blob/master/LICENSE.md #include #include "BluetoothWorker.hpp" #include "BtCommand.hpp" #include #include "interface/BluetoothDriverImpl.hpp" #include "audio/BluetoothAudioDevice.hpp" #include "BtKeysStorage.hpp" #if DEBUG_BLUETOOTH_HCI_COMS == 1 #define logHciComs(...) LOG_DEBUG(__VA_ARGS__) #else #define logHciComs(...) #endif #if DEBUG_BLUETOOTH_HCI_BYTES == 1 #include #define logHciBytes(...) LOG_DEBUG(__VA_ARGS__) #else #define logHciBytes(...) #endif namespace queues { constexpr auto io = "qBtIO"; constexpr auto cmd = "qBtCmds"; constexpr auto btstack = "qBtStack"; constexpr auto queueLength = 10; constexpr auto triggerQueueLength = 3; } // namespace queues namespace { constexpr auto BluetoothWorkerStackDepth = 1024 * 4; class DeviceRegistration { public: using OnLinkKeyAddedCallback = std::function; DeviceRegistration(std::shared_ptr settings, OnLinkKeyAddedCallback &&onLinkKeyAdded) : settings{std::move(settings)}, onLinkKeyAdded{std::move(onLinkKeyAdded)} {} [[nodiscard]] auto operator()() { bluetooth::GAP::registerScan(); auto settingsName = std::get(settings->getValue(bluetooth::Settings::DeviceName)); if (settingsName.empty()) { LOG_WARN("Settings name is empty!"); constexpr auto name = "Mudita Pure"; settings->setValue(bluetooth::Settings::DeviceName, name); settingsName = name; } bluetooth::set_name(settingsName); settings->onLinkKeyAdded = onLinkKeyAdded; return bluetooth::Result::Code::Success; } private: std::shared_ptr settings; OnLinkKeyAddedCallback onLinkKeyAdded; }; auto createStatefulController(sys::Service *service, std::shared_ptr driver, std::shared_ptr commandHandler, const std::shared_ptr &settings, std::shared_ptr profileManager, DeviceRegistration::OnLinkKeyAddedCallback &&onLinkKeyAddedCallback, std::shared_ptr> pairedDevices) { ; return std::make_unique( driver, commandHandler, DeviceRegistration{settings, std::move(onLinkKeyAddedCallback)}, settings, pairedDevices, profileManager); } } // namespace BluetoothWorker::BluetoothWorker(sys::Service *service) : Worker(service, BluetoothWorkerStackDepth), service(service), profileManager(std::make_shared(service)), settings(dynamic_cast(service)->settingsHolder), runLoop(std::make_unique()), driver(std::make_shared(runLoop->getRunLoopInstance(), service)), handler(std::make_shared(service, settings, profileManager, driver)), controller{createStatefulController( service, driver, handler, settings, profileManager, [this](const std::string &addr) { onLinkKeyAdded(addr); }, pairedDevices)} { init({ {queues::io, sizeof(bluetooth::Message), queues::queueLength}, {queues::cmd, 0, queues::queueLength}, {queues::btstack, sizeof(bool), queues::triggerQueueLength}, }); registerQueues(); } void BluetoothWorker::registerQueues() { workerQueue = std::make_shared>( Worker::getQueueHandleByName(queues::cmd)); dynamic_cast(service)->workerQueue = workerQueue; runLoop->setTriggerQueue(Worker::getQueueHandleByName(queues::btstack)); bsp::BlueKitchen::getInstance().qHandle = queues[queueIO_handle]->GetQueueHandle(); } void BluetoothWorker::onLinkKeyAdded(const std::string &deviceAddress) { auto devices = bluetooth::GAP::getDevicesList(); for (auto &device : devices) { if (bd_addr_to_str(device.address) == deviceAddress) { pairedDevices->emplace_back(device); settings->setValue(bluetooth::Settings::BondedDevices, SettingsSerializer::toString(*pairedDevices)); } } } BluetoothWorker::~BluetoothWorker() { controller->handle(bluetooth::event::ShutDown{}); } auto BluetoothWorker::run() -> bool { LOG_INFO("BluetoothWorker run request"); if (isRunning) { return true; } if (const auto status = Worker::run(); !status) { return false; } isRunning = true; return true; } auto BluetoothWorker::handleCommand(QueueHandle_t queue) -> bool { LOG_INFO("Handle bluetooth command(s)"); xQueueReceive(queue, nullptr, 0); while (not workerQueue->empty()) { auto command = workerQueue->peek(); if (command == std::nullopt) { LOG_ERROR("There was no data even with notification"); break; } if (command->event != nullptr) { controller->handle(*command->event); } } return true; } auto BluetoothWorker::handleBtStackTrigger(QueueHandle_t queue) -> bool { bool notification; if (xQueueReceive(queue, ¬ification, 0) != pdTRUE) { LOG_ERROR("Queue receive failure!"); return false; } if (notification) { cpp_freertos::LockGuard lock(loopMutex); runLoop->process(); return true; } return false; } auto BluetoothWorker::handleMessage(uint32_t queueID) -> bool { QueueHandle_t queue = queues[queueID]->GetQueueHandle(); if (queueID == queueService) { LOG_DEBUG("Ignoring message on 'queueService' queue"); return true; } if (queueID == queueCommands) { handleCommand(queue); return true; } if (queueID == queueRunloopTrigger) { handleBtStackTrigger(queue); return true; } if (queueID != queueIO_handle) { LOG_ERROR("Wrong queue! %" PRIu32, queueID); return false; } auto notification = bluetooth::Message::EvtErrorRec; if (xQueueReceive(queue, ¬ification, 0) != pdTRUE) { LOG_ERROR("Queue receive failure!"); return false; } auto &blueKitchen = bsp::BlueKitchen::getInstance(); switch (notification) { case bluetooth::Message::EvtSending: { logHciComs("[evt] sending"); } break; case bluetooth::Message::EvtSent: { logHciComs("[evt] sent"); if (blueKitchen.writeDoneCallback) { blueKitchen.writeDoneCallback(); } } break; case bluetooth::Message::EvtReceiving: { logHciComs("[evt] receiving"); } break; case bluetooth::Message::EvtReceived: { logHciBytes("[evt] BT DMA received <-- [%ld]>%s<", blueKitchen.readLength, [&]() -> std::string { std::stringstream ss; for (auto i = 0; i < blueKitchen.readLength; ++i) { ss << " 0x" << std::hex << static_cast(blueKitchen.readBuffer[i]); } return ss.str(); }() .c_str()); blueKitchen.readLength = 0; if (blueKitchen.readReadyCallback) { blueKitchen.readReadyCallback(); } } break; case bluetooth::Message::EvtSendingError: case bluetooth::Message::EvtReceivingError: case bluetooth::Message::EvtUartError: case bluetooth::Message::EvtRecUnwanted: LOG_ERROR("Uart error [%d]: %s", notification, bluetooth::MessageCstr(notification)); break; default: LOG_ERROR("Unknown message: %d", notification); break; } return true; } void BluetoothWorker::closeWorker() { controller->handle(bluetooth::event::PowerOff{}); this->close(); } void BluetoothWorker::setAudioDevice(std::shared_ptr device) { cpp_freertos::LockGuard lock(loopMutex); profileManager->setAudioDevice(std::move(device)); }