From 16ab3f6e0648b92c8e421b48ef6549c82712fbd8 Mon Sep 17 00:00:00 2001 From: h2zero Date: Fri, 24 Apr 2026 11:58:22 -0600 Subject: [PATCH] Replace task noitifcations with semaphores to avoid conflicts. This will prevent a case where applications may notify a task that is blocking on a BLE operation before it completes. This can occur when writing to a characteristic in the application and blocking with task notifications until a notification occurs that releases it and the notification arrives before the write response. --- Kconfig | 7 -- src/NimBLEClient.cpp | 22 +++--- src/NimBLEClient.h | 4 +- src/NimBLEDevice.cpp | 2 + src/NimBLEL2CAPChannel.cpp | 2 +- src/NimBLEL2CAPChannel.h | 5 +- src/NimBLERemoteCharacteristic.cpp | 8 +- src/NimBLERemoteService.cpp | 8 +- src/NimBLERemoteValueAttribute.cpp | 24 +++--- src/NimBLEScan.cpp | 2 +- src/NimBLEScan.h | 17 ++-- src/NimBLEUtils.cpp | 123 ++++++++++++++--------------- src/NimBLEUtils.h | 71 ++++++++++------- 13 files changed, 151 insertions(+), 144 deletions(-) diff --git a/Kconfig b/Kconfig index 590b37fe1..bfd47962a 100644 --- a/Kconfig +++ b/Kconfig @@ -197,11 +197,4 @@ config NIMBLE_CPP_DEBUG_ASSERT_ENABLED Enabling this option will add debug asserts to the NimBLE CPP library. This will use approximately 1kB of flash memory. -config NIMBLE_CPP_FREERTOS_TASK_BLOCK_BIT - int "FreeRTOS task block bit." - default 31 - help - Configure the bit to set in the task notification value when a task is blocked waiting for an event. - This should be set to a bit that is not used by other notifications in the system. - endmenu \ No newline at end of file diff --git a/src/NimBLEClient.cpp b/src/NimBLEClient.cpp index f17d0489c..2d219a73d 100644 --- a/src/NimBLEClient.cpp +++ b/src/NimBLEClient.cpp @@ -248,9 +248,9 @@ int NimBLEClient::startConnectionAttempt(const ble_addr_t* peerAddr) { */ bool NimBLEClient::connect(const NimBLEAddress& address, bool deleteAttributes, bool asyncConnect, bool exchangeMTU) { NIMBLE_LOGD(LOG_TAG, ">> connect(%s)", address.toString().c_str()); - NimBLETaskData taskData(this); - const ble_addr_t* peerAddr = address.getBase(); - int rc = 0; + NimBLEUtils::TaskData taskData(this); + const ble_addr_t* peerAddr = address.getBase(); + int rc = 0; if (!NimBLEDevice::m_synced) { NIMBLE_LOGE(LOG_TAG, "Host not synced with controller."); @@ -347,7 +347,7 @@ bool NimBLEClient::secureConnection(bool async) const { return true; } - NimBLETaskData taskData(const_cast(this), BLE_HS_ENOTCONN); + NimBLEUtils::TaskData taskData(const_cast(this), BLE_HS_ENOTCONN); m_pTaskData = &taskData; int retryCount = 1; do { @@ -769,8 +769,8 @@ bool NimBLEClient::retrieveServices(const NimBLEUUID* uuidFilter) { return false; } - int rc = 0; - NimBLETaskData taskData(this); + int rc = 0; + NimBLEUtils::TaskData taskData(this); if (uuidFilter == nullptr) { rc = ble_gattc_disc_all_svcs(m_connHandle, NimBLEClient::serviceDiscoveredCB, &taskData); @@ -809,8 +809,8 @@ int NimBLEClient::serviceDiscoveredCB(uint16_t connHandle, error->status, (error->status == 0) ? service->start_handle : -1); - NimBLETaskData* pTaskData = (NimBLETaskData*)arg; - NimBLEClient* pClient = (NimBLEClient*)pTaskData->m_pInstance; + NimBLEUtils::TaskData* pTaskData = (NimBLEUtils::TaskData*)arg; + NimBLEClient* pClient = (NimBLEClient*)pTaskData->m_pInstance; if (error->status == BLE_HS_ENOTCONN) { NIMBLE_LOGE(LOG_TAG, "<< Service Discovered; Disconnected"); @@ -1010,9 +1010,9 @@ void NimBLEClient::setConnectRetries(uint8_t numRetries) { * @param [in] arg A pointer to the client instance that registered for this callback. */ int NimBLEClient::handleGapEvent(struct ble_gap_event* event, void* arg) { - NimBLEClient* pClient = (NimBLEClient*)arg; - int rc = 0; - NimBLETaskData* pTaskData = pClient->m_pTaskData; // save a copy in case client is deleted + NimBLEClient* pClient = (NimBLEClient*)arg; + int rc = 0; + NimBLEUtils::TaskData* pTaskData = pClient->m_pTaskData; // save a copy in case client is deleted NIMBLE_LOGD(LOG_TAG, ">> handleGapEvent %s", NimBLEUtils::gapEventToString(event->type)); diff --git a/src/NimBLEClient.h b/src/NimBLEClient.h index 9b9b8538e..761f9e72d 100644 --- a/src/NimBLEClient.h +++ b/src/NimBLEClient.h @@ -28,6 +28,7 @@ # endif # include "NimBLEAddress.h" +# include "NimBLEUtils.h" # include # include @@ -41,7 +42,6 @@ class NimBLEAdvertisedDevice; class NimBLEAttValue; class NimBLEClientCallbacks; class NimBLEConnInfo; -struct NimBLETaskData; /** * @brief A model of a BLE client. @@ -155,7 +155,7 @@ class NimBLEClient { NimBLEAddress m_peerAddress; mutable int m_lastErr; int32_t m_connectTimeout; - mutable NimBLETaskData* m_pTaskData; + mutable NimBLEUtils::TaskData* m_pTaskData; std::vector m_svcVec; NimBLEClientCallbacks* m_pClientCallbacks; uint16_t m_connHandle; diff --git a/src/NimBLEDevice.cpp b/src/NimBLEDevice.cpp index 899242fc2..06cb78651 100644 --- a/src/NimBLEDevice.cpp +++ b/src/NimBLEDevice.cpp @@ -1071,6 +1071,8 @@ bool NimBLEDevice::deinit(bool clearAll) { deleteClient(clt); } # endif + + NimBLEUtils::deleteTaskSems(); } return rc == 0; diff --git a/src/NimBLEL2CAPChannel.cpp b/src/NimBLEL2CAPChannel.cpp index 2eff6a827..36b740ad3 100644 --- a/src/NimBLEL2CAPChannel.cpp +++ b/src/NimBLEL2CAPChannel.cpp @@ -88,7 +88,7 @@ int NimBLEL2CAPChannel::writeFragment(std::vector::const_iterator begin if (stalled) { NIMBLE_LOGD(LOG_TAG, "L2CAP Channel waiting for unstall..."); - NimBLETaskData taskData; + NimBLEUtils::TaskData taskData; m_pTaskData = &taskData; NimBLEUtils::taskWait(taskData, BLE_NPL_TIME_FOREVER); m_pTaskData = nullptr; diff --git a/src/NimBLEL2CAPChannel.h b/src/NimBLEL2CAPChannel.h index 3c1c95072..0e196bdbc 100644 --- a/src/NimBLEL2CAPChannel.h +++ b/src/NimBLEL2CAPChannel.h @@ -22,12 +22,13 @@ # undef max /**************************/ +# include "NimBLEUtils.h" + # include # include class NimBLEClient; class NimBLEL2CAPChannelCallbacks; -struct NimBLETaskData; /** * @brief Encapsulates a L2CAP channel. @@ -94,7 +95,7 @@ class NimBLEL2CAPChannel { // Runtime handling std::atomic stalled{false}; - NimBLETaskData* m_pTaskData{nullptr}; + NimBLEUtils::TaskData* m_pTaskData{nullptr}; // Allocate / deallocate NimBLE memory pool bool setupMemPool(); diff --git a/src/NimBLERemoteCharacteristic.cpp b/src/NimBLERemoteCharacteristic.cpp index 64a1f3083..a8d2801bc 100644 --- a/src/NimBLERemoteCharacteristic.cpp +++ b/src/NimBLERemoteCharacteristic.cpp @@ -66,7 +66,7 @@ int NimBLERemoteCharacteristic::descriptorDiscCB( uint16_t connHandle, const ble_gatt_error* error, uint16_t chrHandle, const ble_gatt_dsc* dsc, void* arg) { int rc = error->status; auto filter = (NimBLEDescriptorFilter*)arg; - auto pTaskData = (NimBLETaskData*)filter->taskData; + auto pTaskData = (NimBLEUtils::TaskData*)filter->taskData; const auto pChr = (NimBLERemoteCharacteristic*)pTaskData->m_pInstance; const auto uuid = filter->uuid; // UUID to filter for NIMBLE_LOGD(LOG_TAG, "Descriptor Discovery >> status: %d handle: %d", rc, (rc == 0) ? dsc->handle : -1); @@ -116,7 +116,7 @@ bool NimBLERemoteCharacteristic::retrieveDescriptors(NimBLEDescriptorFilter* pFi return true; } - NimBLETaskData taskData(const_cast(this)); + NimBLEUtils::TaskData taskData(const_cast(this)); NimBLEDescriptorFilter defaultFilter{nullptr, nullptr, &taskData}; if (pFilter == nullptr) { pFilter = &defaultFilter; @@ -134,7 +134,7 @@ bool NimBLERemoteCharacteristic::retrieveDescriptors(NimBLEDescriptorFilter* pFi auto prevDscCount = m_vDescriptors.size(); NimBLEUtils::taskWait(taskData, BLE_NPL_TIME_FOREVER); - rc = ((NimBLETaskData*)pFilter->taskData)->m_flags; + rc = ((NimBLEUtils::TaskData*)pFilter->taskData)->m_flags; if (rc != BLE_HS_EDONE) { NIMBLE_LOGE(LOG_TAG, "<< retrieveDescriptors(): failed: rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc)); return false; @@ -156,7 +156,7 @@ bool NimBLERemoteCharacteristic::retrieveDescriptors(NimBLEDescriptorFilter* pFi NimBLERemoteDescriptor* NimBLERemoteCharacteristic::getDescriptor(const NimBLEUUID& uuid) const { NIMBLE_LOGD(LOG_TAG, ">> getDescriptor: uuid: %s", uuid.toString().c_str()); NimBLEUUID uuidTmp{uuid}; - NimBLETaskData taskData(const_cast(this)); + NimBLEUtils::TaskData taskData(const_cast(this)); NimBLEDescriptorFilter filter{nullptr, &uuidTmp, &taskData}; for (const auto& dsc : m_vDescriptors) { diff --git a/src/NimBLERemoteService.cpp b/src/NimBLERemoteService.cpp index f15a9a31e..f5831a410 100644 --- a/src/NimBLERemoteService.cpp +++ b/src/NimBLERemoteService.cpp @@ -75,7 +75,7 @@ NimBLERemoteCharacteristic* NimBLERemoteService::getCharacteristic(const char* u */ NimBLERemoteCharacteristic* NimBLERemoteService::getCharacteristic(const NimBLEUUID& uuid) const { NIMBLE_LOGD(LOG_TAG, ">> getCharacteristic: uuid: %s", uuid.toString().c_str()); - NimBLERemoteCharacteristic* pChar = nullptr; + NimBLERemoteCharacteristic* pChar = nullptr; for (const auto& it : m_vChars) { if (it->getUUID() == uuid) { @@ -136,7 +136,7 @@ int NimBLERemoteService::characteristicDiscCB(uint16_t conn_handle, "Characteristic Discovery >> status: %d handle: %d", error->status, (error->status == 0) ? chr->def_handle : -1); - auto pTaskData = (NimBLETaskData*)arg; + auto pTaskData = (NimBLEUtils::TaskData*)arg; const auto pSvc = (NimBLERemoteService*)pTaskData->m_pInstance; if (error->status == BLE_HS_ENOTCONN) { @@ -178,8 +178,8 @@ int NimBLERemoteService::characteristicDiscCB(uint16_t conn_handle, */ bool NimBLERemoteService::retrieveCharacteristics(const NimBLEUUID* uuidFilter, NimBLERemoteCharacteristic** ppChar) const { NIMBLE_LOGD(LOG_TAG, ">> retrieveCharacteristics()"); - int rc = 0; - NimBLETaskData taskData(const_cast(this)); + int rc = 0; + NimBLEUtils::TaskData taskData(const_cast(this)); if (uuidFilter == nullptr) { rc = ble_gattc_disc_all_chrs(m_pClient->getConnHandle(), diff --git a/src/NimBLERemoteValueAttribute.cpp b/src/NimBLERemoteValueAttribute.cpp index c2ef17dd8..46e52b941 100644 --- a/src/NimBLERemoteValueAttribute.cpp +++ b/src/NimBLERemoteValueAttribute.cpp @@ -29,11 +29,11 @@ static const char* LOG_TAG = "NimBLERemoteValueAttribute"; bool NimBLERemoteValueAttribute::writeValue(const uint8_t* data, size_t length, bool response) const { NIMBLE_LOGD(LOG_TAG, ">> writeValue()"); - const NimBLEClient* pClient = getClient(); - int retryCount = 1; - int rc = 0; - uint16_t mtu = pClient->getMTU() - 3; - NimBLETaskData taskData(const_cast(this)); + const NimBLEClient* pClient = getClient(); + int retryCount = 1; + int rc = 0; + uint16_t mtu = pClient->getMTU() - 3; + NimBLEUtils::TaskData taskData(const_cast(this)); // Check if the data length is longer than we can write in one connection event. // If so we must do a long write which requires a response. @@ -98,7 +98,7 @@ bool NimBLERemoteValueAttribute::writeValue(const uint8_t* data, size_t length, * @return success == 0 or error code. */ int NimBLERemoteValueAttribute::onWriteCB(uint16_t conn_handle, const ble_gatt_error* error, ble_gatt_attr* attr, void* arg) { - auto pTaskData = static_cast(arg); + auto pTaskData = static_cast(arg); const auto pAtt = static_cast(pTaskData->m_pInstance); if (error->status == BLE_HS_ENOTCONN) { @@ -124,11 +124,11 @@ int NimBLERemoteValueAttribute::onWriteCB(uint16_t conn_handle, const ble_gatt_e NimBLEAttValue NimBLERemoteValueAttribute::readValue(time_t* timestamp) { NIMBLE_LOGD(LOG_TAG, ">> readValue()"); - NimBLEAttValue value{}; - const NimBLEClient* pClient = getClient(); - int rc = 0; - int retryCount = 1; - NimBLETaskData taskData(const_cast(this), 0, &value); + NimBLEAttValue value{}; + const NimBLEClient* pClient = getClient(); + int rc = 0; + int retryCount = 1; + NimBLEUtils::TaskData taskData(const_cast(this), 0, &value); do { rc = ble_gattc_read_long(pClient->getConnHandle(), getHandle(), 0, NimBLERemoteValueAttribute::onReadCB, &taskData); @@ -183,7 +183,7 @@ NimBLEAttValue NimBLERemoteValueAttribute::readValue(time_t* timestamp) { * @return success == 0 or error code. */ int NimBLERemoteValueAttribute::onReadCB(uint16_t conn_handle, const ble_gatt_error* error, ble_gatt_attr* attr, void* arg) { - auto pTaskData = static_cast(arg); + auto pTaskData = static_cast(arg); const auto pAtt = static_cast(pTaskData->m_pInstance); if (error->status == BLE_HS_ENOTCONN) { diff --git a/src/NimBLEScan.cpp b/src/NimBLEScan.cpp index 540280758..90a381aa2 100644 --- a/src/NimBLEScan.cpp +++ b/src/NimBLEScan.cpp @@ -689,7 +689,7 @@ NimBLEScanResults NimBLEScan::getResults(uint32_t duration, bool is_continue) { return m_scanResults; } - NimBLETaskData taskData; + NimBLEUtils::TaskData taskData; m_pTaskData = &taskData; if (start(duration, is_continue)) { diff --git a/src/NimBLEScan.h b/src/NimBLEScan.h index 2b8a24996..0c612851d 100644 --- a/src/NimBLEScan.h +++ b/src/NimBLEScan.h @@ -129,11 +129,16 @@ class NimBLEScan { snprintf(&out[0], out.size(), "Scan stats:\n" - " Devices seen : %" PRIu32 "\n" - " Duplicate advs : %" PRIu32 "\n" - " Scan responses : %" PRIu32 "\n" - " SR timing (ms) : min=%" PRIu32 ", max=%" PRIu32 ", avg=%" PRIu64 "\n" - " Orphaned SR : %" PRIu32 "\n" + " Devices seen : %" PRIu32 + "\n" + " Duplicate advs : %" PRIu32 + "\n" + " Scan responses : %" PRIu32 + "\n" + " SR timing (ms) : min=%" PRIu32 ", max=%" PRIu32 ", avg=%" PRIu64 + "\n" + " Orphaned SR : %" PRIu32 + "\n" " Missed SR : %" PRIu32 "\n", devCount, dupCount, @@ -187,7 +192,7 @@ class NimBLEScan { NimBLEScanCallbacks* m_pScanCallbacks; ble_gap_disc_params m_scanParams; NimBLEScanResults m_scanResults; - NimBLETaskData* m_pTaskData; + NimBLEUtils::TaskData* m_pTaskData; ble_npl_callout m_srTimer{}; ble_npl_time_t m_srTimeoutTicks{}; uint8_t m_maxResults; diff --git a/src/NimBLEUtils.cpp b/src/NimBLEUtils.cpp index 75969a59e..ec9767af0 100644 --- a/src/NimBLEUtils.cpp +++ b/src/NimBLEUtils.cpp @@ -21,9 +21,9 @@ # include "NimBLEAddress.h" # include "NimBLELog.h" -#ifdef USING_NIMBLE_ARDUINO_HEADERS +# ifdef USING_NIMBLE_ARDUINO_HEADERS # include "nimble/nimble/host/include/host/ble_hs.h" -#else +# else # include "host/ble_hs.h" # endif @@ -59,57 +59,18 @@ # endif # endif -# if defined INC_FREERTOS_H -# ifndef MYNEWT_VAL_NIMBLE_CPP_FREERTOS_TASK_BLOCK_BIT -# ifndef CONFIG_NIMBLE_CPP_FREERTOS_TASK_BLOCK_BIT -# define MYNEWT_VAL_NIMBLE_CPP_FREERTOS_TASK_BLOCK_BIT 31 -# else -# define MYNEWT_VAL_NIMBLE_CPP_FREERTOS_TASK_BLOCK_BIT CONFIG_NIMBLE_CPP_FREERTOS_TASK_BLOCK_BIT -# endif -# endif -# endif +static const char* LOG_TAG = "NimBLEUtils"; -constexpr uint32_t TASK_BLOCK_BIT = (1 << MYNEWT_VAL(NIMBLE_CPP_FREERTOS_TASK_BLOCK_BIT)); -static const char* LOG_TAG = "NimBLEUtils"; +std::vector NimBLEUtils::m_taskSemEntries{}; /** - * @brief Construct a NimBLETaskData instance. + * @brief Construct a NimBLEUtils::TaskData instance. * @param [in] pInstance An instance of the class that will be waiting. * @param [in] flags General purpose flags for the caller. * @param [in] buf A buffer for data. */ -NimBLETaskData::NimBLETaskData(void* pInstance, int flags, void* buf) - : m_pInstance{pInstance}, - m_flags{flags}, - m_pBuf{buf} -# ifdef INC_FREERTOS_H - , - m_pHandle{xTaskGetCurrentTaskHandle()} { -} -# else -{ - ble_npl_sem* sem = new ble_npl_sem; - if (ble_npl_sem_init(sem, 0) != BLE_NPL_OK) { - NIMBLE_LOGE(LOG_TAG, "Failed to init semaphore"); - delete sem; - m_pHandle = nullptr; - } else { - m_pHandle = sem; - } -} -# endif - -/** - * @brief Destructor. - */ -NimBLETaskData::~NimBLETaskData() { -# ifndef INC_FREERTOS_H - if (m_pHandle != nullptr) { - ble_npl_sem_deinit(static_cast(m_pHandle)); - delete static_cast(m_pHandle); - } -# endif -} +NimBLEUtils::TaskData::TaskData(void* pInstance, int flags, void* buf) + : m_pInstance{pInstance}, m_flags{flags}, m_pBuf{buf} {} /** * @brief Blocks the calling task until released or timeout. @@ -117,7 +78,7 @@ NimBLETaskData::~NimBLETaskData() { * @param [in] timeout The time to wait in milliseconds. * @return True if the task completed, false if the timeout was reached. */ -bool NimBLEUtils::taskWait(const NimBLETaskData& taskData, uint32_t timeout) { +bool NimBLEUtils::taskWait(const TaskData& taskData, uint32_t timeout) { ble_npl_time_t ticks; if (timeout == BLE_NPL_TIME_FOREVER) { ticks = BLE_NPL_TIME_FOREVER; @@ -125,18 +86,33 @@ bool NimBLEUtils::taskWait(const NimBLETaskData& taskData, uint32_t timeout) { ble_npl_time_ms_to_ticks(timeout, &ticks); } -# ifdef INC_FREERTOS_H - uint32_t notificationValue; - xTaskNotifyWait(0, TASK_BLOCK_BIT, ¬ificationValue, 0); - if (notificationValue & TASK_BLOCK_BIT) { - return true; + // find a semaphore that is not currently in use, or create a new one + for (auto& entry : m_taskSemEntries) { + if (!entry->inUse) { + taskData.m_pSem = entry; + break; + } } - return xTaskNotifyWait(0, TASK_BLOCK_BIT, nullptr, ticks) == pdTRUE; + if (taskData.m_pSem == nullptr) { + auto* sem = new ble_npl_sem; + if (ble_npl_sem_init(sem, 0) != BLE_NPL_OK) { + NIMBLE_LOGE(LOG_TAG, "Failed to initialize semaphore for taskWait"); + delete sem; + return false; + } -# else - return ble_npl_sem_pend(static_cast(taskData.m_pHandle), ticks) == BLE_NPL_OK; -# endif + TaskSemEntry* entry = new TaskSemEntry; + entry->sem = sem; + entry->inUse = true; + m_taskSemEntries.push_back(entry); + taskData.m_pSem = entry; + NIMBLE_LOGD(LOG_TAG, "Created new semaphore for taskWait, total semaphores: %d\n", (int)m_taskSemEntries.size()); + } + + NIMBLE_LOGD(LOG_TAG, "Task waiting with timeout %" PRIu32 "ms", timeout); + taskData.m_pSem->inUse = true; + return ble_npl_sem_pend(taskData.m_pSem->sem, ticks) == BLE_NPL_OK; } // taskWait /** @@ -144,17 +120,36 @@ bool NimBLEUtils::taskWait(const NimBLETaskData& taskData, uint32_t timeout) { * @param [in] taskData A pointer to the task data structure. * @param [in] flags A return value to set in the task data structure. */ -void NimBLEUtils::taskRelease(const NimBLETaskData& taskData, int flags) { +void NimBLEUtils::taskRelease(const TaskData& taskData, int flags) { taskData.m_flags = flags; - if (taskData.m_pHandle != nullptr) { -# ifdef INC_FREERTOS_H - xTaskNotify(static_cast(taskData.m_pHandle), TASK_BLOCK_BIT, eSetBits); -# else - ble_npl_sem_release(static_cast(taskData.m_pHandle)); -# endif + if (taskData.m_pSem == nullptr) { + NIMBLE_LOGE(LOG_TAG, "taskRelease called with null semaphore"); + return; + } + + taskData.m_pSem->inUse = false; + auto rc = ble_npl_sem_release(taskData.m_pSem->sem); + if (rc != BLE_NPL_OK) { + NIMBLE_LOGE(LOG_TAG, "Failed to release semaphore: rc=%d %s", rc, returnCodeToString(rc)); + return; } } // taskRelease +/** + * @brief Deletes all semaphores used for task waiting. + * @details This should be called when the NimBLE stack is deinitialized to clean up any resources used by waiting tasks. + */ +void NimBLEUtils::deleteTaskSems() { + for (auto& entry : m_taskSemEntries) { + ble_npl_sem_release(entry->sem); + ble_npl_time_delay(10); // give time for any pending tasks to be released + ble_npl_sem_deinit(entry->sem); + delete entry->sem; + delete entry; + } + m_taskSemEntries.clear(); +} + /** * @brief Converts a return code from the NimBLE stack to a text string. * @param [in] rc The return code to convert. @@ -563,7 +558,7 @@ const char* NimBLEUtils::gapEventToString(uint8_t eventType) { NIMBLE_LOGD(LOG_TAG, "Unknown event type %d 0x%.2x", eventType, eventType); return "Unknown event type"; } -# else // MYNEWT_VAL(NIMBLE_CPP_ENABLE_GAP_EVENT_CODE_TEXT) +# else // MYNEWT_VAL(NIMBLE_CPP_ENABLE_GAP_EVENT_CODE_TEXT) (void)eventType; return ""; # endif // MYNEWT_VAL(NIMBLE_CPP_ENABLE_GAP_EVENT_CODE_TEXT) diff --git a/src/NimBLEUtils.h b/src/NimBLEUtils.h index 63b7f1996..fafb02c19 100644 --- a/src/NimBLEUtils.h +++ b/src/NimBLEUtils.h @@ -29,51 +29,62 @@ # endif # endif -#if MYNEWT_VAL(NIMBLE_CPP_DEBUG_ASSERT_ENABLED) && !defined NDEBUG -void nimble_cpp_assert(const char *file, unsigned line) __attribute((weak, noreturn)); -# define NIMBLE_ATT_VAL_FILE (__builtin_strrchr(__FILE__, '/') ? \ - __builtin_strrchr (__FILE__, '/') + 1 : __FILE__) -# define NIMBLE_CPP_DEBUG_ASSERT(cond) \ - if (!(cond)) { \ - nimble_cpp_assert(NIMBLE_ATT_VAL_FILE, __LINE__); \ - } -#else -# define NIMBLE_CPP_DEBUG_ASSERT(cond) (void(0)) -#endif +# if MYNEWT_VAL(NIMBLE_CPP_DEBUG_ASSERT_ENABLED) && !defined NDEBUG +void nimble_cpp_assert(const char* file, unsigned line) __attribute((weak, noreturn)); +# define NIMBLE_ATT_VAL_FILE (__builtin_strrchr(__FILE__, '/') ? __builtin_strrchr(__FILE__, '/') + 1 : __FILE__) +# define NIMBLE_CPP_DEBUG_ASSERT(cond) \ + if (!(cond)) { \ + nimble_cpp_assert(NIMBLE_ATT_VAL_FILE, __LINE__); \ + } +# else +# define NIMBLE_CPP_DEBUG_ASSERT(cond) (void(0)) +# endif # include +# include class NimBLEAddress; - -/** - * @brief A structure to hold data for a task that is waiting for a response. - * @details This structure is used in conjunction with NimBLEUtils::taskWait() and NimBLEUtils::taskRelease(). - * All items are optional, the m_pHandle will be set in taskWait(). - */ -struct NimBLETaskData { - NimBLETaskData(void* pInstance = nullptr, int flags = 0, void* buf = nullptr); - ~NimBLETaskData(); - void* m_pInstance{nullptr}; - mutable int m_flags{0}; - void* m_pBuf{nullptr}; - - private: - mutable void* m_pHandle{nullptr}; // semaphore or task handle - friend class NimBLEUtils; -}; +struct ble_npl_sem; /** * @brief A BLE Utility class with methods for debugging and general purpose use. */ class NimBLEUtils { + struct TaskSemEntry { + ble_npl_sem* sem; + bool inUse{false}; + }; + public: + /** + * @brief A structure to hold data for a task that is waiting for a response. + * @details This structure is used in conjunction with NimBLEUtils::taskWait() and NimBLEUtils::taskRelease(). + * All items are optional; the internal semaphore entry is assigned in taskWait(). + */ + struct TaskData { + TaskData(void* pInstance = nullptr, int flags = 0, void* buf = nullptr); + ~TaskData() = default; + void* m_pInstance{nullptr}; + mutable int m_flags{0}; + void* m_pBuf{nullptr}; + + private: + friend class NimBLEUtils; + mutable TaskSemEntry* m_pSem{nullptr}; + }; + static const char* gapEventToString(uint8_t eventType); static std::string dataToHexString(const uint8_t* source, uint8_t length); static const char* advTypeToString(uint8_t advType); static const char* returnCodeToString(int rc); static NimBLEAddress generateAddr(bool nrpa); - static bool taskWait(const NimBLETaskData& taskData, uint32_t timeout); - static void taskRelease(const NimBLETaskData& taskData, int rc = 0); + static bool taskWait(const TaskData& taskData, uint32_t timeout); + static void taskRelease(const TaskData& taskData, int rc = 0); + + private: + friend class NimBLEDevice; + static void deleteTaskSems(); + static std::vector m_taskSemEntries; }; #endif // CONFIG_BT_NIMBLE_ENABLED