From a12f3d478579e4e389be202dd02fbf8e47f5ea26 Mon Sep 17 00:00:00 2001 From: h2zero Date: Mon, 25 Nov 2024 18:41:10 -0700 Subject: [PATCH] Remove characteristic subscription tracking. Removes tracking of client characteristic subscription status from `NimBLEServer` and `NimBLECharacteristic` and instead uses the functions and tracking in the host stack. * `NimBLECharacteristic::notify` and `NimBLECharacteristic::indicate` now return a `bool`, true = success. --- src/NimBLECharacteristic.cpp | 205 +++++++++++------------------------ src/NimBLECharacteristic.h | 53 +++++---- src/NimBLEDescriptor.h | 4 +- src/NimBLEServer.cpp | 100 +++++++---------- src/NimBLEServer.h | 4 - 5 files changed, 131 insertions(+), 235 deletions(-) diff --git a/src/NimBLECharacteristic.cpp b/src/NimBLECharacteristic.cpp index e2380e3..a6e2fc1 100644 --- a/src/NimBLECharacteristic.cpp +++ b/src/NimBLECharacteristic.cpp @@ -18,9 +18,6 @@ # include "NimBLEDevice.h" # include "NimBLELog.h" -# define NIMBLE_SUB_NOTIFY 0x0001 -# define NIMBLE_SUB_INDICATE 0x0002 - static NimBLECharacteristicCallbacks defaultCallback; static const char* LOG_TAG = "NimBLECharacteristic"; @@ -28,21 +25,21 @@ static const char* LOG_TAG = "NimBLECharacteristic"; * @brief Construct a characteristic * @param [in] uuid - UUID (const char*) for the characteristic. * @param [in] properties - Properties for the characteristic. - * @param [in] max_len - The maximum length in bytes that the characteristic value can hold. (Default: 512 bytes for esp32, 20 for all others). + * @param [in] maxLen - The maximum length in bytes that the characteristic value can hold. (Default: 512 bytes for esp32, 20 for all others). * @param [in] pService - pointer to the service instance this characteristic belongs to. */ -NimBLECharacteristic::NimBLECharacteristic(const char* uuid, uint16_t properties, uint16_t max_len, NimBLEService* pService) - : NimBLECharacteristic(NimBLEUUID(uuid), properties, max_len, pService) {} +NimBLECharacteristic::NimBLECharacteristic(const char* uuid, uint16_t properties, uint16_t maxLen, NimBLEService* pService) + : NimBLECharacteristic(NimBLEUUID(uuid), properties, maxLen, pService) {} /** * @brief Construct a characteristic * @param [in] uuid - UUID for the characteristic. * @param [in] properties - Properties for the characteristic. - * @param [in] max_len - The maximum length in bytes that the characteristic value can hold. (Default: 512 bytes for esp32, 20 for all others). + * @param [in] maxLen - The maximum length in bytes that the characteristic value can hold. (Default: 512 bytes for esp32, 20 for all others). * @param [in] pService - pointer to the service instance this characteristic belongs to. */ -NimBLECharacteristic::NimBLECharacteristic(const NimBLEUUID& uuid, uint16_t properties, uint16_t max_len, NimBLEService* pService) - : NimBLELocalValueAttribute{uuid, 0, max_len}, m_pCallbacks{&defaultCallback}, m_pService{pService} { +NimBLECharacteristic::NimBLECharacteristic(const NimBLEUUID& uuid, uint16_t properties, uint16_t maxLen, NimBLEService* pService) + : NimBLELocalValueAttribute{uuid, 0, maxLen}, m_pCallbacks{&defaultCallback}, m_pService{pService} { setProperties(properties); } // NimBLECharacteristic @@ -59,26 +56,26 @@ NimBLECharacteristic::~NimBLECharacteristic() { * @brief Create a new BLE Descriptor associated with this characteristic. * @param [in] uuid - The UUID of the descriptor. * @param [in] properties - The properties of the descriptor. - * @param [in] max_len - The max length in bytes of the descriptor value. + * @param [in] maxLen - The max length in bytes of the descriptor value. * @return The new BLE descriptor. */ -NimBLEDescriptor* NimBLECharacteristic::createDescriptor(const char* uuid, uint32_t properties, uint16_t max_len) { - return createDescriptor(NimBLEUUID(uuid), properties, max_len); +NimBLEDescriptor* NimBLECharacteristic::createDescriptor(const char* uuid, uint32_t properties, uint16_t maxLen) { + return createDescriptor(NimBLEUUID(uuid), properties, maxLen); } /** * @brief Create a new BLE Descriptor associated with this characteristic. * @param [in] uuid - The UUID of the descriptor. * @param [in] properties - The properties of the descriptor. - * @param [in] max_len - The max length in bytes of the descriptor value. + * @param [in] maxLen - The max length in bytes of the descriptor value. * @return The new BLE descriptor. */ -NimBLEDescriptor* NimBLECharacteristic::createDescriptor(const NimBLEUUID& uuid, uint32_t properties, uint16_t max_len) { +NimBLEDescriptor* NimBLECharacteristic::createDescriptor(const NimBLEUUID& uuid, uint32_t properties, uint16_t maxLen) { NimBLEDescriptor* pDescriptor = nullptr; if (uuid == NimBLEUUID(uint16_t(0x2904))) { pDescriptor = new NimBLE2904(this); } else { - pDescriptor = new NimBLEDescriptor(uuid, properties, max_len, this); + pDescriptor = new NimBLEDescriptor(uuid, properties, maxLen, this); } addDescriptor(pDescriptor); @@ -199,182 +196,108 @@ void NimBLECharacteristic::setService(NimBLEService* pService) { m_pService = pService; } // setService -/** - * @brief Get the number of clients subscribed to the characteristic. - * @returns Number of clients subscribed to notifications / indications. - */ -size_t NimBLECharacteristic::getSubscribedCount() const { - return m_subscribedVec.size(); -} - -/** - * @brief Set the subscribe status for this characteristic.\n - * This will maintain a vector of subscribed clients and their indicate/notify status. - */ -void NimBLECharacteristic::setSubscribe(const ble_gap_event* event, NimBLEConnInfo& connInfo) { - uint16_t subVal = 0; - if (event->subscribe.cur_notify > 0 && (m_properties & NIMBLE_PROPERTY::NOTIFY)) { - subVal |= NIMBLE_SUB_NOTIFY; - } - if (event->subscribe.cur_indicate && (m_properties & NIMBLE_PROPERTY::INDICATE)) { - subVal |= NIMBLE_SUB_INDICATE; - } - - NIMBLE_LOGI(LOG_TAG, "New subscribe value for conn: %d val: %d", connInfo.getConnHandle(), subVal); - - if (!event->subscribe.cur_indicate && event->subscribe.prev_indicate) { - NimBLEDevice::getServer()->clearIndicateWait(connInfo.getConnHandle()); - } - - auto it = m_subscribedVec.begin(); - for (; it != m_subscribedVec.end(); ++it) { - if ((*it).first == connInfo.getConnHandle()) { - break; - } - } - - if (subVal > 0) { - if (it == m_subscribedVec.end()) { - m_subscribedVec.push_back({connInfo.getConnHandle(), subVal}); - } else { - (*it).second = subVal; - } - } else if (it != m_subscribedVec.end()) { - m_subscribedVec.erase(it); - } - - m_pCallbacks->onSubscribe(this, connInfo, subVal); -} - /** * @brief Send an indication. - * @param[in] conn_handle Connection handle to send an individual indication, or BLE_HS_CONN_HANDLE_NONE to send + * @param[in] connHandle Connection handle to send an individual indication, or BLE_HS_CONN_HANDLE_NONE to send * the indication to all subscribed clients. + * @return True if the indication was sent successfully, false otherwise. */ -void NimBLECharacteristic::indicate(uint16_t conn_handle) const { - sendValue(m_value.data(), m_value.size(), false, conn_handle); +bool NimBLECharacteristic::indicate(uint16_t connHandle) const { + return sendValue(nullptr, 0, false, connHandle); } // indicate /** * @brief Send an indication. * @param[in] value A pointer to the data to send. * @param[in] length The length of the data to send. - * @param[in] conn_handle Connection handle to send an individual indication, or BLE_HS_CONN_HANDLE_NONE to send + * @param[in] connHandle Connection handle to send an individual indication, or BLE_HS_CONN_HANDLE_NONE to send * the indication to all subscribed clients. + * @return True if the indication was sent successfully, false otherwise. */ -void NimBLECharacteristic::indicate(const uint8_t* value, size_t length, uint16_t conn_handle) const { - sendValue(value, length, false, conn_handle); +bool NimBLECharacteristic::indicate(const uint8_t* value, size_t length, uint16_t connHandle) const { + return sendValue(value, length, false, connHandle); } // indicate /** * @brief Send a notification. - * @param[in] conn_handle Connection handle to send an individual notification, or BLE_HS_CONN_HANDLE_NONE to send + * @param[in] connHandle Connection handle to send an individual notification, or BLE_HS_CONN_HANDLE_NONE to send * the notification to all subscribed clients. + * @return True if the notification was sent successfully, false otherwise. */ -void NimBLECharacteristic::notify(uint16_t conn_handle) const { - sendValue(m_value.data(), m_value.size(), true, conn_handle); +bool NimBLECharacteristic::notify(uint16_t connHandle) const { + return sendValue(nullptr, 0, true, connHandle); } // notify /** * @brief Send a notification. * @param[in] value A pointer to the data to send. * @param[in] length The length of the data to send. - * @param[in] conn_handle Connection handle to send an individual notification, or BLE_HS_CONN_HANDLE_NONE to send + * @param[in] connHandle Connection handle to send an individual notification, or BLE_HS_CONN_HANDLE_NONE to send * the notification to all subscribed clients. + * @return True if the notification was sent successfully, false otherwise. */ -void NimBLECharacteristic::notify(const uint8_t* value, size_t length, uint16_t conn_handle) const { - sendValue(value, length, true, conn_handle); +bool NimBLECharacteristic::notify(const uint8_t* value, size_t length, uint16_t connHandle) const { + return sendValue(value, length, true, connHandle); } // indicate /** * @brief Sends a notification or indication. * @param[in] value A pointer to the data to send. * @param[in] length The length of the data to send. - * @param[in] is_notification if true sends a notification, false sends an indication. - * @param[in] conn_handle Connection handle to send to a specific peer, or BLE_HS_CONN_HANDLE_NONE to send - * to all subscribed clients. + * @param[in] isNotification if true sends a notification, false sends an indication. + * @param[in] connHandle Connection handle to send to a specific peer. + * @return True if the value was sent successfully, false otherwise. */ -void NimBLECharacteristic::sendValue(const uint8_t* value, size_t length, bool is_notification, uint16_t conn_handle) const { - NIMBLE_LOGD(LOG_TAG, ">> sendValue"); +bool NimBLECharacteristic::sendValue(const uint8_t* value, size_t length, bool isNotification, uint16_t connHandle) const { + int rc = 0; - if (is_notification && !(getProperties() & NIMBLE_PROPERTY::NOTIFY)) { - NIMBLE_LOGE(LOG_TAG, "<< sendValue: notification not enabled for characteristic"); - return; - } + if (value != nullptr && length > 0) { // custom notification value + // Notify all connected peers unless a specific handle is provided + for (const auto& ch : NimBLEDevice::getServer()->getPeerDevices()) { + if (connHandle != BLE_HS_CONN_HANDLE_NONE && ch != connHandle) { + continue; // only send to the specific handle, minor inefficiency but saves code. + } - if (!is_notification && !(getProperties() & NIMBLE_PROPERTY::INDICATE)) { - NIMBLE_LOGE(LOG_TAG, "<< sendValue: indication not enabled for characteristic"); - return; - } + // Must re-create the data buffer on each iteration because it is freed by the calls bellow. + os_mbuf* om = ble_hs_mbuf_from_flat(value, length); + if (!om) { + NIMBLE_LOGE(LOG_TAG, "<< sendValue: failed to allocate mbuf"); + return false; + } - if (!m_subscribedVec.size()) { - NIMBLE_LOGD(LOG_TAG, "<< sendValue: No clients subscribed."); - return; - } + if (isNotification) { + rc = ble_gattc_notify_custom(ch, m_handle, om); + } else { + rc = ble_gattc_indicate_custom(ch, m_handle, om); + } - for (const auto& it : m_subscribedVec) { - // check if connected and subscribed - if (!it.second) { - continue; - } - - // sending to a specific client? - if ((conn_handle <= BLE_HCI_LE_CONN_HANDLE_MAX) && (it.first != conn_handle)) { - continue; - } - - if (is_notification && !(it.second & NIMBLE_SUB_NOTIFY)) { - continue; - } - - if (!is_notification && !(it.second & NIMBLE_SUB_INDICATE)) { - continue; - } - - // check if security requirements are satisfied - if ((getProperties() & BLE_GATT_CHR_F_READ_AUTHEN) || (getProperties() & BLE_GATT_CHR_F_READ_AUTHOR) || - (getProperties() & BLE_GATT_CHR_F_READ_ENC)) { - ble_gap_conn_desc desc; - if (ble_gap_conn_find(it.first, &desc) != 0 || !desc.sec_state.encrypted) { - continue; + if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "<< sendValue: failed to send value, rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc)); + break; } } - - // don't create the m_buf until we are sure to send the data or else - // we could be allocating a buffer that doesn't get released. - // We also must create it in each loop iteration because it is consumed with each host call. - os_mbuf* om = ble_hs_mbuf_from_flat(value, length); - if (!om) { - NIMBLE_LOGE(LOG_TAG, "<< sendValue: failed to allocate mbuf"); - return; - } - - if (is_notification) { - ble_gattc_notify_custom(it.first, getHandle(), om); + } else if (connHandle != BLE_HS_CONN_HANDLE_NONE) { // only sending to specific peer + // Null buffer will read the value from the characteristic + if (isNotification) { + rc = ble_gattc_notify_custom(connHandle, m_handle, NULL); } else { - if (!NimBLEDevice::getServer()->setIndicateWait(it.first)) { - NIMBLE_LOGE(LOG_TAG, "<< sendValue: waiting for previous indicate"); - os_mbuf_free_chain(om); - return; - } - - if (ble_gattc_indicate_custom(it.first, getHandle(), om) != 0) { - NimBLEDevice::getServer()->clearIndicateWait(it.first); - } + rc = ble_gattc_indicate_custom(connHandle, m_handle, NULL); } + } else { // Notify or indicate to all connected peers the characteristic value + ble_gatts_chr_updated(m_handle); } - NIMBLE_LOGD(LOG_TAG, "<< sendValue"); + return rc == 0; } // sendValue void NimBLECharacteristic::readEvent(NimBLEConnInfo& connInfo) { m_pCallbacks->onRead(this, connInfo); -} +} // readEvent void NimBLECharacteristic::writeEvent(const uint8_t* val, uint16_t len, NimBLEConnInfo& connInfo) { setValue(val, len); m_pCallbacks->onWrite(this, connInfo); -} +} // writeEvent /** * @brief Set the callback handlers for this characteristic. @@ -455,7 +378,7 @@ void NimBLECharacteristicCallbacks::onStatus(NimBLECharacteristic* pCharacterist * * 3 = Notifications and Indications */ void NimBLECharacteristicCallbacks::onSubscribe(NimBLECharacteristic* pCharacteristic, - NimBLEConnInfo& connInfo, + NimBLEConnInfo& connInfo, uint16_t subValue) { NIMBLE_LOGD("NimBLECharacteristicCallbacks", "onSubscribe: default"); } diff --git a/src/NimBLECharacteristic.h b/src/NimBLECharacteristic.h index a787ebe..46f97ac 100644 --- a/src/NimBLECharacteristic.h +++ b/src/NimBLECharacteristic.h @@ -39,32 +39,31 @@ class NimBLECharacteristic : public NimBLELocalValueAttribute { public: NimBLECharacteristic(const char* uuid, uint16_t properties = NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE, - uint16_t max_len = BLE_ATT_ATTR_MAX_LEN, + uint16_t maxLen = BLE_ATT_ATTR_MAX_LEN, NimBLEService* pService = nullptr); NimBLECharacteristic(const NimBLEUUID& uuid, uint16_t properties = NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE, - uint16_t max_len = BLE_ATT_ATTR_MAX_LEN, + uint16_t maxLen = BLE_ATT_ATTR_MAX_LEN, NimBLEService* pService = nullptr); ~NimBLECharacteristic(); std::string toString() const; - size_t getSubscribedCount() const; void addDescriptor(NimBLEDescriptor* pDescriptor); void removeDescriptor(NimBLEDescriptor* pDescriptor, bool deleteDsc = false); uint16_t getProperties() const; void setCallbacks(NimBLECharacteristicCallbacks* pCallbacks); - void indicate(uint16_t conn_handle = BLE_HS_CONN_HANDLE_NONE) const; - void indicate(const uint8_t* value, size_t length, uint16_t conn_handle = BLE_HS_CONN_HANDLE_NONE) const; - void notify(uint16_t conn_handle = BLE_HS_CONN_HANDLE_NONE) const; - void notify(const uint8_t* value, size_t length, uint16_t conn_handle = BLE_HS_CONN_HANDLE_NONE) const; + bool indicate(uint16_t connHandle = BLE_HS_CONN_HANDLE_NONE) const; + bool indicate(const uint8_t* value, size_t length, uint16_t connHandle = BLE_HS_CONN_HANDLE_NONE) const; + bool notify(uint16_t connHandle = BLE_HS_CONN_HANDLE_NONE) const; + bool notify(const uint8_t* value, size_t length, uint16_t connHandle = BLE_HS_CONN_HANDLE_NONE) const; NimBLEDescriptor* createDescriptor(const char* uuid, uint32_t properties = NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE, - uint16_t max_len = BLE_ATT_ATTR_MAX_LEN); + uint16_t maxLen = BLE_ATT_ATTR_MAX_LEN); NimBLEDescriptor* createDescriptor(const NimBLEUUID& uuid, uint32_t properties = NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE, - uint16_t max_len = BLE_ATT_ATTR_MAX_LEN); + uint16_t maxLen = BLE_ATT_ATTR_MAX_LEN); NimBLEDescriptor* getDescriptorByUUID(const char* uuid) const; NimBLEDescriptor* getDescriptorByUUID(const NimBLEUUID& uuid) const; NimBLEDescriptor* getDescriptorByHandle(uint16_t handle) const; @@ -81,18 +80,18 @@ class NimBLECharacteristic : public NimBLELocalValueAttribute { * pointer and getting the length of the array using sizeof. * @tparam T The a reference to a class containing the data to send. * @param[in] value The value to set. - * @param[in] conn_handle The connection handle to send the notification to. + * @param[in] connHandle The connection handle to send the notification to. * @note This function is only available if the type T is not a pointer. */ template - typename std::enable_if::value, void>::type - notify(const T& value, uint16_t conn_handle = BLE_HS_CONN_HANDLE_NONE) const { + typename std::enable_if::value, bool>::type notify(const T& value, + uint16_t connHandle = BLE_HS_CONN_HANDLE_NONE) const { if constexpr (Has_data_size::value) { - notify(reinterpret_cast(value.data()), value.size(), conn_handle); + return notify(reinterpret_cast(value.data()), value.size(), connHandle); } else if constexpr (Has_c_str_length::value) { - notify(reinterpret_cast(value.c_str()), value.length(), conn_handle); + return notify(reinterpret_cast(value.c_str()), value.length(), connHandle); } else { - notify(reinterpret_cast(&value), sizeof(value), conn_handle); + return notify(reinterpret_cast(&value), sizeof(value), connHandle); } } @@ -103,18 +102,18 @@ class NimBLECharacteristic : public NimBLELocalValueAttribute { * pointer and getting the length of the array using sizeof. * @tparam T The a reference to a class containing the data to send. * @param[in] value The value to set. - * @param[in] conn_handle The connection handle to send the indication to. + * @param[in] connHandle The connection handle to send the indication to. * @note This function is only available if the type T is not a pointer. */ template - typename std::enable_if::value, void>::type - indicate(const T& value, uint16_t conn_handle = BLE_HS_CONN_HANDLE_NONE) const { + typename std::enable_if::value, bool>::type indicate( + const T& value, uint16_t connHandle = BLE_HS_CONN_HANDLE_NONE) const { if constexpr (Has_data_size::value) { - indicate(reinterpret_cast(value.data()), value.size(), conn_handle); + return indicate(reinterpret_cast(value.data()), value.size(), connHandle); } else if constexpr (Has_c_str_length::value) { - indicate(reinterpret_cast(value.c_str()), value.length(), conn_handle); + return indicate(reinterpret_cast(value.c_str()), value.length(), connHandle); } else { - indicate(reinterpret_cast(&value), sizeof(value), conn_handle); + return indicate(reinterpret_cast(&value), sizeof(value), connHandle); } } @@ -123,18 +122,16 @@ class NimBLECharacteristic : public NimBLELocalValueAttribute { friend class NimBLEService; void setService(NimBLEService* pService); - void setSubscribe(const ble_gap_event* event, NimBLEConnInfo& connInfo); void readEvent(NimBLEConnInfo& connInfo) override; void writeEvent(const uint8_t* val, uint16_t len, NimBLEConnInfo& connInfo) override; - void sendValue(const uint8_t* value, + bool sendValue(const uint8_t* value, size_t length, bool is_notification = true, - uint16_t conn_handle = BLE_HS_CONN_HANDLE_NONE) const; + uint16_t connHandle = BLE_HS_CONN_HANDLE_NONE) const; - NimBLECharacteristicCallbacks* m_pCallbacks{nullptr}; - NimBLEService* m_pService{nullptr}; - std::vector m_vDescriptors{}; - std::vector> m_subscribedVec{}; + NimBLECharacteristicCallbacks* m_pCallbacks{nullptr}; + NimBLEService* m_pService{nullptr}; + std::vector m_vDescriptors{}; }; // NimBLECharacteristic /** diff --git a/src/NimBLEDescriptor.h b/src/NimBLEDescriptor.h index 435e103..df686d5 100644 --- a/src/NimBLEDescriptor.h +++ b/src/NimBLEDescriptor.h @@ -34,11 +34,11 @@ class NimBLEDescriptorCallbacks; */ class NimBLEDescriptor : public NimBLELocalValueAttribute { public: - NimBLEDescriptor(const char* uuid, uint16_t properties, uint16_t max_len, NimBLECharacteristic* pCharacteristic = nullptr); + NimBLEDescriptor(const char* uuid, uint16_t properties, uint16_t maxLen, NimBLECharacteristic* pCharacteristic = nullptr); NimBLEDescriptor(const NimBLEUUID& uuid, uint16_t properties, - uint16_t max_len, + uint16_t maxLen, NimBLECharacteristic* pCharacteristic = nullptr); ~NimBLEDescriptor() = default; diff --git a/src/NimBLEServer.cpp b/src/NimBLEServer.cpp index a808496..1a9baac 100644 --- a/src/NimBLEServer.cpp +++ b/src/NimBLEServer.cpp @@ -48,10 +48,8 @@ NimBLEServer::NimBLEServer() m_advertiseOnDisconnect{false}, # endif m_pServerCallbacks{&defaultCallbacks}, - m_svcVec{}, - m_notifyChrVec{} { + m_svcVec{} { m_connectedPeers.fill(BLE_HS_CONN_HANDLE_NONE); - m_indWait.fill(BLE_HS_CONN_HANDLE_NONE); } // NimBLEServer /** @@ -199,13 +197,8 @@ void NimBLEServer::start() { } } - for (auto& chr : svc->m_vChars) { - // if Notify / Indicate is enabled but we didn't create the descriptor - // we do it now. - if ((chr->m_properties & BLE_GATT_CHR_F_INDICATE) || (chr->m_properties & BLE_GATT_CHR_F_NOTIFY)) { - m_notifyChrVec.push_back(chr); - } - + // Set the descriptor handles now as the stack does not set these when the service is started + for (const auto& chr : svc->m_vChars) { for (auto& desc : chr->m_vDescriptors) { ble_gatts_find_dsc(svc->getUUID().getBase(), chr->getUUID().getBase(), desc->getUUID().getBase(), &desc->m_handle); } @@ -519,22 +512,25 @@ int NimBLEServer::handleGapEvent(ble_gap_event* event, void* arg) { event->subscribe.attr_handle, (event->subscribe.cur_notify ? "true" : "false")); - for (const auto& chr : pServer->m_notifyChrVec) { - if (chr->getHandle() == event->subscribe.attr_handle) { - rc = ble_gap_conn_find(event->subscribe.conn_handle, &peerInfo.m_desc); - if (rc != 0) { - break; - } + for (const auto& svc : pServer->m_svcVec) { + for (const auto& chr : svc->m_vChars) { + if (chr->getHandle() == event->subscribe.attr_handle) { + rc = ble_gap_conn_find(event->subscribe.conn_handle, &peerInfo.m_desc); + if (rc != 0) { + break; + } - auto chrProps = chr->getProperties(); - if (!peerInfo.isEncrypted() && - (chrProps & BLE_GATT_CHR_F_READ_AUTHEN || chrProps & BLE_GATT_CHR_F_READ_AUTHOR || - chrProps & BLE_GATT_CHR_F_READ_ENC)) { - NimBLEDevice::startSecurity(event->subscribe.conn_handle); - } + auto chrProps = chr->getProperties(); + if (!peerInfo.isEncrypted() && + (chrProps & BLE_GATT_CHR_F_READ_AUTHEN || chrProps & BLE_GATT_CHR_F_READ_AUTHOR || + chrProps & BLE_GATT_CHR_F_READ_ENC)) { + NimBLEDevice::startSecurity(event->subscribe.conn_handle); + } - chr->setSubscribe(event, peerInfo); - break; + chr->m_pCallbacks->onSubscribe(chr, + peerInfo, + event->subscribe.cur_notify + event->subscribe.cur_indicate); + } } } @@ -553,9 +549,11 @@ int NimBLEServer::handleGapEvent(ble_gap_event* event, void* arg) { case BLE_GAP_EVENT_NOTIFY_TX: { NimBLECharacteristic* pChar = nullptr; - for (auto& chr : pServer->m_notifyChrVec) { - if (chr->getHandle() == event->notify_tx.attr_handle) { - pChar = chr; + for (const auto& svc : pServer->m_svcVec) { + for (auto& chr : svc->m_vChars) { + if (chr->getHandle() == event->notify_tx.attr_handle) { + pChar = chr; + } } } @@ -567,7 +565,6 @@ int NimBLEServer::handleGapEvent(ble_gap_event* event, void* arg) { if (event->notify_tx.status == 0) { return 0; // Indication sent but not yet acknowledged. } - pServer->clearIndicateWait(event->notify_tx.conn_handle); } pChar->m_pCallbacks->onStatus(pChar, event->notify_tx.status); @@ -703,19 +700,21 @@ int NimBLEServer::handleGattEvent(uint16_t connHandle, uint16_t attrHandle, ble_ NIMBLE_LOGD(LOG_TAG, "Gatt %s event", (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR || ctxt->op == BLE_GATT_ACCESS_OP_READ_DSC) ? "Read" : "Write"); - auto pAtt = static_cast(arg); - const auto& val = pAtt->getAttVal(); + auto pAtt = static_cast(arg); + auto val = pAtt->getAttVal(); NimBLEConnInfo peerInfo{}; ble_gap_conn_find(connHandle, &peerInfo.m_desc); switch (ctxt->op) { case BLE_GATT_ACCESS_OP_READ_DSC: case BLE_GATT_ACCESS_OP_READ_CHR: { - // If the packet header is only 8 bytes this is a follow up of a long read - // so we don't want to call the onRead() callback again. - if (ctxt->om->om_pkthdr_len > 8 || connHandle == BLE_HS_CONN_HANDLE_NONE || - val.size() <= (ble_att_mtu(connHandle) - 3)) { - pAtt->readEvent(peerInfo); + // Don't call readEvent if this is an internal read (handle is NONE) + if (connHandle != BLE_HS_CONN_HANDLE_NONE) { + // If the packet header is only 8 bytes then this is a follow up of a long read + // so we don't want to call the onRead() callback again. + if (ctxt->om->om_pkthdr_len > 8 || pAtt->getAttVal().size() <= (ble_att_mtu(connHandle) - 3)) { + pAtt->readEvent(peerInfo); + } } ble_npl_hw_enter_critical(); @@ -726,19 +725,19 @@ int NimBLEServer::handleGattEvent(uint16_t connHandle, uint16_t attrHandle, ble_ case BLE_GATT_ACCESS_OP_WRITE_DSC: case BLE_GATT_ACCESS_OP_WRITE_CHR: { - uint16_t att_max_len = val.max_size(); - if (ctxt->om->om_len > att_max_len) { + uint16_t maxLen = val.max_size(); + if (ctxt->om->om_len > maxLen) { return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; } - uint8_t buf[att_max_len]; + uint8_t buf[maxLen]; uint16_t len = ctxt->om->om_len; memcpy(buf, ctxt->om->om_data, len); os_mbuf* next; next = SLIST_NEXT(ctxt->om, om_next); while (next != NULL) { - if ((len + next->om_len) > att_max_len) { + if ((len + next->om_len) > maxLen) { return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; } memcpy(&buf[len], next->om_data, next->om_len); @@ -760,8 +759,8 @@ int NimBLEServer::handleGattEvent(uint16_t connHandle, uint16_t attrHandle, ble_ /** * @brief Set the server callbacks. * - * As a BLE server operates, it will generate server level events such as a new client connecting or a previous client - * disconnecting. This function can be called to register a callback handler that will be invoked when these + * As a BLE server operates, it will generate server level events such as a new client connecting or a previous + * client disconnecting. This function can be called to register a callback handler that will be invoked when these * events are detected. * * @param [in] pCallbacks The callbacks to be invoked. @@ -1026,25 +1025,6 @@ void NimBLEServer::setDataLen(uint16_t connHandle, uint16_t octets) const { # endif } // setDataLen -bool NimBLEServer::setIndicateWait(uint16_t connHandle) { - for (auto i = 0; i < CONFIG_BT_NIMBLE_MAX_CONNECTIONS; i++) { - if (m_indWait[i] == connHandle) { - return false; - } - } - - return true; -} - -void NimBLEServer::clearIndicateWait(uint16_t connHandle) { - for (auto i = 0; i < CONFIG_BT_NIMBLE_MAX_CONNECTIONS; i++) { - if (m_indWait[i] == connHandle) { - m_indWait[i] = BLE_HS_CONN_HANDLE_NONE; - return; - } - } -} - /** Default callback handlers */ void NimBLEServerCallbacks::onConnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo) { NIMBLE_LOGD("NimBLEServerCallbacks", "onConnect(): Default"); diff --git a/src/NimBLEServer.h b/src/NimBLEServer.h index d926e11..1bc2bf0 100644 --- a/src/NimBLEServer.h +++ b/src/NimBLEServer.h @@ -113,9 +113,7 @@ class NimBLEServer { # endif NimBLEServerCallbacks* m_pServerCallbacks; std::vector m_svcVec; - std::vector m_notifyChrVec; std::array m_connectedPeers; - std::array m_indWait; static int handleGapEvent(struct ble_gap_event* event, void* arg); static int handleGattEvent(uint16_t connHandle, uint16_t attrHandle, ble_gatt_access_ctxt* ctxt, void* arg); @@ -123,8 +121,6 @@ class NimBLEServer { std::string getPeerNameImpl(uint16_t connHandle, int cb_type = -1) const; void serviceChanged(); void resetGATT(); - bool setIndicateWait(uint16_t connHandle); - void clearIndicateWait(uint16_t connHandle); }; // NimBLEServer