[BREAKING] - Refactor NimBLEUtils

* Add functions `NimBLEUtils::taskWait` and `NimBLEUtils::taskRelease` to simplify task blocking/messaging.
* `NimBLEUtils::buildHexData` replaced with `NimBLEUtils::dataToHexString`
* Add missing GAP event strings.
* Add missing return code strings.
* `NimBLEUtils::dumpGapEvent` function removed.
* Event/error code strings optimized.
This commit is contained in:
h2zero 2024-11-10 13:31:37 -07:00 committed by h2zero
parent 65e05e6c57
commit beac19cc92
12 changed files with 481 additions and 524 deletions

View file

@ -695,11 +695,9 @@ std::string NimBLEAdvertisedDevice::toString() const {
} }
if (haveManufacturerData()) { if (haveManufacturerData()) {
char* pHex = auto mfgData = getManufacturerData();
NimBLEUtils::buildHexData(nullptr, (uint8_t*)getManufacturerData().data(), getManufacturerData().length());
res += ", manufacturer data: "; res += ", manufacturer data: ";
res += pHex; res += NimBLEUtils::dataToHexString(reinterpret_cast<const uint8_t*>(mfgData.data()), mfgData.length());
free(pHex);
} }
if (haveServiceUUID()) { if (haveServiceUUID()) {

View file

@ -204,8 +204,7 @@ bool NimBLEClient::connect(const NimBLEAddress& address, bool deleteAttributes,
int rc = 0; int rc = 0;
m_asyncConnect = asyncConnect; m_asyncConnect = asyncConnect;
m_exchangeMTU = exchangeMTU; m_exchangeMTU = exchangeMTU;
TaskHandle_t curTask = xTaskGetCurrentTaskHandle(); NimBLETaskData taskData(this);
BleTaskData taskData = {this, curTask, 0, nullptr};
if (!asyncConnect) { if (!asyncConnect) {
m_pTaskData = &taskData; m_pTaskData = &taskData;
} }
@ -276,12 +275,8 @@ bool NimBLEClient::connect(const NimBLEAddress& address, bool deleteAttributes,
return true; return true;
} }
# ifdef ulTaskNotifyValueClear
// Clear the task notification value to ensure we block
ulTaskNotifyValueClear(curTask, ULONG_MAX);
# endif
// Wait for the connect timeout time +1 second for the connection to complete // Wait for the connect timeout time +1 second for the connection to complete
if (ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(m_connectTimeout + 1000)) == pdFALSE) { if (!NimBLEUtils::taskWait(*m_pTaskData, m_connectTimeout + 1000)) {
m_pTaskData = nullptr; m_pTaskData = nullptr;
// If a connection was made but no response from MTU exchange; disconnect // If a connection was made but no response from MTU exchange; disconnect
if (isConnected()) { if (isConnected()) {
@ -296,9 +291,9 @@ bool NimBLEClient::connect(const NimBLEAddress& address, bool deleteAttributes,
return false; return false;
} else if (taskData.rc != 0) { } else if (taskData.m_flags != 0) {
m_lastErr = taskData.rc; m_lastErr = taskData.m_flags;
NIMBLE_LOGE(LOG_TAG, "Connection failed; status=%d %s", taskData.rc, NimBLEUtils::returnCodeToString(taskData.rc)); NIMBLE_LOGE(LOG_TAG, "Connection failed; status=%d %s", m_lastErr, NimBLEUtils::returnCodeToString(m_lastErr));
// If the failure was not a result of a disconnection, make sure we disconnect now to avoid dangling connections // If the failure was not a result of a disconnection, make sure we disconnect now to avoid dangling connections
if (isConnected()) { if (isConnected()) {
disconnect(); disconnect();
@ -324,8 +319,7 @@ bool NimBLEClient::connect(const NimBLEAddress& address, bool deleteAttributes,
*/ */
bool NimBLEClient::secureConnection() const { bool NimBLEClient::secureConnection() const {
NIMBLE_LOGD(LOG_TAG, ">> secureConnection()"); NIMBLE_LOGD(LOG_TAG, ">> secureConnection()");
TaskHandle_t cur_task = xTaskGetCurrentTaskHandle(); NimBLETaskData taskData(const_cast<NimBLEClient*>(this));
BleTaskData taskData = {const_cast<NimBLEClient*>(this), cur_task, 0, nullptr};
int retryCount = 1; int retryCount = 1;
do { do {
@ -337,16 +331,12 @@ bool NimBLEClient::secureConnection() const {
return false; return false;
} }
# ifdef ulTaskNotifyValueClear NimBLEUtils::taskWait(*m_pTaskData, BLE_NPL_TIME_FOREVER);
// Clear the task notification value to ensure we block } while (taskData.m_flags == (BLE_HS_ERR_HCI_BASE + BLE_ERR_PINKEY_MISSING) && retryCount--);
ulTaskNotifyValueClear(cur_task, ULONG_MAX);
# endif
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
} while (taskData.rc == (BLE_HS_ERR_HCI_BASE + BLE_ERR_PINKEY_MISSING) && retryCount--);
if (taskData.rc != 0) { if (taskData.m_flags != 0) {
m_lastErr = taskData.rc; m_lastErr = taskData.m_flags;
NIMBLE_LOGE(LOG_TAG, "secureConnection: failed rc=%d", taskData.rc); NIMBLE_LOGE(LOG_TAG, "secureConnection: failed rc=%d", taskData.m_flags);
return false; return false;
} }
@ -737,8 +727,7 @@ bool NimBLEClient::retrieveServices(const NimBLEUUID* uuidFilter) {
} }
int rc = 0; int rc = 0;
TaskHandle_t cur_task = xTaskGetCurrentTaskHandle(); NimBLETaskData taskData(this);
BleTaskData taskData = {this, cur_task, 0, nullptr};
if (uuidFilter == nullptr) { if (uuidFilter == nullptr) {
rc = ble_gattc_disc_all_svcs(m_connHandle, NimBLEClient::serviceDiscoveredCB, &taskData); rc = ble_gattc_disc_all_svcs(m_connHandle, NimBLEClient::serviceDiscoveredCB, &taskData);
@ -752,24 +741,15 @@ bool NimBLEClient::retrieveServices(const NimBLEUUID* uuidFilter) {
return false; return false;
} }
# ifdef ulTaskNotifyValueClear NimBLEUtils::taskWait(taskData, BLE_NPL_TIME_FOREVER);
// Clear the task notification value to ensure we block rc = taskData.m_flags;
ulTaskNotifyValueClear(cur_task, ULONG_MAX); if (rc == 0 || rc == BLE_HS_EDONE) {
# endif
// wait until we have all the services
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
m_lastErr = taskData.rc;
if (taskData.rc == 0) {
return true; return true;
} else {
NIMBLE_LOGE(LOG_TAG,
"Could not retrieve services, rc=%d %s",
taskData.rc,
NimBLEUtils::returnCodeToString(taskData.rc));
return false;
} }
m_lastErr = rc;
NIMBLE_LOGE(LOG_TAG, "Could not retrieve services, rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc));
return false;
} // getServices } // getServices
/** /**
@ -786,8 +766,8 @@ int NimBLEClient::serviceDiscoveredCB(uint16_t connHandle,
error->status, error->status,
(error->status == 0) ? service->start_handle : -1); (error->status == 0) ? service->start_handle : -1);
BleTaskData* pTaskData = (BleTaskData*)arg; NimBLETaskData* pTaskData = (NimBLETaskData*)arg;
NimBLEClient* pClient = (NimBLEClient*)pTaskData->pATT; NimBLEClient* pClient = (NimBLEClient*)pTaskData->m_pInstance;
// Make sure the service discovery is for this device // Make sure the service discovery is for this device
if (pClient->getConnHandle() != connHandle) { if (pClient->getConnHandle() != connHandle) {
@ -800,15 +780,7 @@ int NimBLEClient::serviceDiscoveredCB(uint16_t connHandle,
return 0; return 0;
} }
if (error->status == BLE_HS_EDONE) { NimBLEUtils::taskRelease(*pTaskData, error->status);
pTaskData->rc = 0;
} else {
NIMBLE_LOGE(LOG_TAG, "serviceDiscoveredCB() rc=%d %s", error->status, NimBLEUtils::returnCodeToString(error->status));
pTaskData->rc = error->status;
}
xTaskNotifyGive(pTaskData->task);
NIMBLE_LOGD(LOG_TAG, "<< Service Discovered"); NIMBLE_LOGD(LOG_TAG, "<< Service Discovered");
return error->status; return error->status;
} }
@ -1204,10 +1176,7 @@ int NimBLEClient::handleGapEvent(struct ble_gap_event* event, void* arg) {
} // Switch } // Switch
if (pClient->m_pTaskData != nullptr) { if (pClient->m_pTaskData != nullptr) {
pClient->m_pTaskData->rc = rc; NimBLEUtils::taskRelease(*pClient->m_pTaskData, rc);
if (pClient->m_pTaskData->task) {
xTaskNotifyGive(pClient->m_pTaskData->task);
}
pClient->m_pTaskData = nullptr; pClient->m_pTaskData = nullptr;
} }

View file

@ -37,7 +37,7 @@ class NimBLEAdvertisedDevice;
class NimBLEAttValue; class NimBLEAttValue;
class NimBLEClientCallbacks; class NimBLEClientCallbacks;
class NimBLEConnInfo; class NimBLEConnInfo;
struct BleTaskData; struct NimBLETaskData;
/** /**
* @brief A model of a BLE client. * @brief A model of a BLE client.
@ -112,7 +112,7 @@ class NimBLEClient {
NimBLEAddress m_peerAddress; NimBLEAddress m_peerAddress;
mutable int m_lastErr; mutable int m_lastErr;
int32_t m_connectTimeout; int32_t m_connectTimeout;
mutable BleTaskData* m_pTaskData; mutable NimBLETaskData* m_pTaskData;
std::vector<NimBLERemoteService*> m_svcVec; std::vector<NimBLERemoteService*> m_svcVec;
NimBLEClientCallbacks* m_pClientCallbacks; NimBLEClientCallbacks* m_pClientCallbacks;
uint16_t m_connHandle; uint16_t m_connHandle;

View file

@ -65,8 +65,8 @@ int NimBLERemoteCharacteristic::descriptorDiscCB(
NIMBLE_LOGD(LOG_TAG, "Descriptor Discovery >> status: %d handle: %d", rc, (rc == 0) ? dsc->handle : -1); NIMBLE_LOGD(LOG_TAG, "Descriptor Discovery >> status: %d handle: %d", rc, (rc == 0) ? dsc->handle : -1);
auto filter = (desc_filter_t*)arg; auto filter = (desc_filter_t*)arg;
auto pTaskData = (BleTaskData*)filter->task_data; auto pTaskData = (NimBLETaskData*)filter->task_data;
const auto pChr = (NimBLERemoteCharacteristic*)pTaskData->pATT; const auto pChr = (NimBLERemoteCharacteristic*)pTaskData->m_pInstance;
const NimBLEUUID* uuidFilter = filter->uuid; const NimBLEUUID* uuidFilter = filter->uuid;
if (pChr->getHandle() != chr_val_handle) { if (pChr->getHandle() != chr_val_handle) {
@ -85,12 +85,8 @@ int NimBLERemoteCharacteristic::descriptorDiscCB(
pChr->m_vDescriptors.push_back(new NimBLERemoteDescriptor(pChr, dsc)); pChr->m_vDescriptors.push_back(new NimBLERemoteDescriptor(pChr, dsc));
} }
if (rc != 0) { NimBLEUtils::taskRelease(*pTaskData, rc);
pTaskData->rc = rc == BLE_HS_EDONE ? 0 : rc;
NIMBLE_LOGD(LOG_TAG, "<< Descriptor Discovery"); NIMBLE_LOGD(LOG_TAG, "<< Descriptor Discovery");
xTaskNotifyGive(pTaskData->task);
}
return rc; return rc;
} }
@ -101,8 +97,7 @@ int NimBLERemoteCharacteristic::descriptorDiscCB(
bool NimBLERemoteCharacteristic::retrieveDescriptors(const NimBLEUUID* uuidFilter) const { bool NimBLERemoteCharacteristic::retrieveDescriptors(const NimBLEUUID* uuidFilter) const {
NIMBLE_LOGD(LOG_TAG, ">> retrieveDescriptors() for characteristic: %s", getUUID().toString().c_str()); NIMBLE_LOGD(LOG_TAG, ">> retrieveDescriptors() for characteristic: %s", getUUID().toString().c_str());
TaskHandle_t cur_task = xTaskGetCurrentTaskHandle(); NimBLETaskData taskData(const_cast<NimBLERemoteCharacteristic*>(this));
BleTaskData taskData = {const_cast<NimBLERemoteCharacteristic*>(this), cur_task, 0, nullptr};
desc_filter_t filter = {uuidFilter, &taskData}; desc_filter_t filter = {uuidFilter, &taskData};
int rc = ble_gattc_disc_all_dscs(getClient()->getConnHandle(), int rc = ble_gattc_disc_all_dscs(getClient()->getConnHandle(),
@ -115,22 +110,15 @@ bool NimBLERemoteCharacteristic::retrieveDescriptors(const NimBLEUUID* uuidFilte
return false; return false;
} }
# ifdef ulTaskNotifyValueClear NimBLEUtils::taskWait(taskData, BLE_NPL_TIME_FOREVER);
// Clear the task notification value to ensure we block rc = taskData.m_flags;
ulTaskNotifyValueClear(cur_task, ULONG_MAX); if (rc == 0 || rc == BLE_HS_EDONE) {
# endif
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
if (taskData.rc != 0) {
NIMBLE_LOGE(LOG_TAG,
"<< retrieveDescriptors(): failed: rc=%d %s",
taskData.rc,
NimBLEUtils::returnCodeToString(taskData.rc));
} else {
NIMBLE_LOGD(LOG_TAG, "<< retrieveDescriptors(): found %d descriptors.", m_vDescriptors.size()); NIMBLE_LOGD(LOG_TAG, "<< retrieveDescriptors(): found %d descriptors.", m_vDescriptors.size());
return true;
} }
return taskData.rc == 0; NIMBLE_LOGE(LOG_TAG, "<< retrieveDescriptors(): failed: rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc));
return false;
} // retrieveDescriptors } // retrieveDescriptors
/** /**

View file

@ -145,8 +145,8 @@ int NimBLERemoteService::characteristicDiscCB(uint16_t conn_handle,
const ble_gatt_chr* chr, const ble_gatt_chr* chr,
void* arg) { void* arg) {
NIMBLE_LOGD(LOG_TAG, "Characteristic Discovery >>"); NIMBLE_LOGD(LOG_TAG, "Characteristic Discovery >>");
auto pTaskData = (BleTaskData*)arg; auto pTaskData = (NimBLETaskData*)arg;
const auto pSvc = (NimBLERemoteService*)pTaskData->pATT; const auto pSvc = (NimBLERemoteService*)pTaskData->m_pInstance;
// Make sure the discovery is for this device // Make sure the discovery is for this device
if (pSvc->getClient()->getConnHandle() != conn_handle) { if (pSvc->getClient()->getConnHandle() != conn_handle) {
@ -155,12 +155,11 @@ int NimBLERemoteService::characteristicDiscCB(uint16_t conn_handle,
if (error->status == 0) { if (error->status == 0) {
pSvc->m_vChars.push_back(new NimBLERemoteCharacteristic(pSvc, chr)); pSvc->m_vChars.push_back(new NimBLERemoteCharacteristic(pSvc, chr));
} else { return 0;
pTaskData->rc = error->status == BLE_HS_EDONE ? 0 : error->status;
NIMBLE_LOGD(LOG_TAG, "<< Characteristic Discovery");
xTaskNotifyGive(pTaskData->task);
} }
NimBLEUtils::taskRelease(*pTaskData, error->status);
NIMBLE_LOGD(LOG_TAG, "<< Characteristic Discovery");
return error->status; return error->status;
} }
@ -172,8 +171,7 @@ int NimBLERemoteService::characteristicDiscCB(uint16_t conn_handle,
bool NimBLERemoteService::retrieveCharacteristics(const NimBLEUUID* uuidFilter) const { bool NimBLERemoteService::retrieveCharacteristics(const NimBLEUUID* uuidFilter) const {
NIMBLE_LOGD(LOG_TAG, ">> retrieveCharacteristics()"); NIMBLE_LOGD(LOG_TAG, ">> retrieveCharacteristics()");
int rc = 0; int rc = 0;
TaskHandle_t cur_task = xTaskGetCurrentTaskHandle(); NimBLETaskData taskData(const_cast<NimBLERemoteService*>(this));
BleTaskData taskData = {const_cast<NimBLERemoteService*>(this), cur_task, 0, nullptr};
if (uuidFilter == nullptr) { if (uuidFilter == nullptr) {
rc = ble_gattc_disc_all_chrs(m_pClient->getConnHandle(), rc = ble_gattc_disc_all_chrs(m_pClient->getConnHandle(),
@ -190,21 +188,20 @@ bool NimBLERemoteService::retrieveCharacteristics(const NimBLEUUID* uuidFilter)
&taskData); &taskData);
} }
if (rc == 0) { if (rc != 0) {
# ifdef ulTaskNotifyValueClear NIMBLE_LOGE(LOG_TAG, "ble_gattc_disc_chrs rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc));
// Clear the task notification value to ensure we block return false;
ulTaskNotifyValueClear(cur_task, ULONG_MAX);
# endif
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
} }
if (taskData.rc != 0) { NimBLEUtils::taskWait(taskData, BLE_NPL_TIME_FOREVER);
NIMBLE_LOGE(LOG_TAG, "<< retrieveCharacteristics() rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc)); rc = taskData.m_flags;
} else { if (rc == 0 || rc == BLE_HS_EDONE) {
NIMBLE_LOGD(LOG_TAG, "<< retrieveCharacteristics()"); NIMBLE_LOGD(LOG_TAG, "<< retrieveCharacteristics()");
return true;
} }
return taskData.rc == 0; NIMBLE_LOGE(LOG_TAG, "<< retrieveCharacteristics() rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc));
return false;
} // retrieveCharacteristics } // retrieveCharacteristics
/** /**

View file

@ -20,11 +20,10 @@ bool NimBLERemoteValueAttribute::writeValue(const uint8_t* data, size_t length,
NIMBLE_LOGD(LOG_TAG, ">> writeValue()"); NIMBLE_LOGD(LOG_TAG, ">> writeValue()");
const NimBLEClient* pClient = getClient(); const NimBLEClient* pClient = getClient();
TaskHandle_t cur_task = xTaskGetCurrentTaskHandle();
BleTaskData taskData = {const_cast<NimBLERemoteValueAttribute*>(this), cur_task, 0, nullptr};
int retryCount = 1; int retryCount = 1;
int rc = 0; int rc = 0;
uint16_t mtu = pClient->getMTU() - 3; uint16_t mtu = pClient->getMTU() - 3;
NimBLETaskData taskData(const_cast<NimBLERemoteValueAttribute*>(this));
// Check if the data length is longer than we can write in one connection event. // 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. // If so we must do a long write which requires a response.
@ -51,13 +50,8 @@ bool NimBLERemoteValueAttribute::writeValue(const uint8_t* data, size_t length,
goto Done; goto Done;
} }
# ifdef ulTaskNotifyValueClear NimBLEUtils::taskWait(taskData, BLE_NPL_TIME_FOREVER);
// Clear the task notification value to ensure we block rc = taskData.m_flags;
ulTaskNotifyValueClear(cur_task, ULONG_MAX);
# endif
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
rc = taskData.rc;
switch (rc) { switch (rc) {
case 0: case 0:
case BLE_HS_EDONE: case BLE_HS_EDONE:
@ -83,7 +77,7 @@ Done:
if (rc != 0) { if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "<< writeValue failed, rc: %d %s", rc, NimBLEUtils::returnCodeToString(rc)); NIMBLE_LOGE(LOG_TAG, "<< writeValue failed, rc: %d %s", rc, NimBLEUtils::returnCodeToString(rc));
} else { } else {
NIMBLE_LOGD(LOG_TAG, "<< writeValue, rc: %d", rc); NIMBLE_LOGD(LOG_TAG, "<< writeValue");
} }
return (rc == 0); return (rc == 0);
@ -94,16 +88,15 @@ Done:
* @return success == 0 or error code. * @return success == 0 or error code.
*/ */
int NimBLERemoteValueAttribute::onWriteCB(uint16_t conn_handle, const ble_gatt_error* error, ble_gatt_attr* attr, void* arg) { int NimBLERemoteValueAttribute::onWriteCB(uint16_t conn_handle, const ble_gatt_error* error, ble_gatt_attr* attr, void* arg) {
auto pTaskData = static_cast<BleTaskData*>(arg); auto pTaskData = static_cast<NimBLETaskData*>(arg);
const auto pAtt = static_cast<NimBLERemoteValueAttribute*>(pTaskData->pATT); const auto pAtt = static_cast<NimBLERemoteValueAttribute*>(pTaskData->m_pInstance);
if (pAtt->getClient()->getConnHandle() != conn_handle) { if (pAtt->getClient()->getConnHandle() != conn_handle) {
return 0; return 0;
} }
NIMBLE_LOGI(LOG_TAG, "Write complete; status=%d", error->status); NIMBLE_LOGI(LOG_TAG, "Write complete; status=%d", error->status);
pTaskData->rc = error->status; NimBLEUtils::taskRelease(*pTaskData, error->status);
xTaskNotifyGive(pTaskData->task);
return 0; return 0;
} }
@ -119,8 +112,7 @@ NimBLEAttValue NimBLERemoteValueAttribute::readValue(time_t* timestamp) const {
const NimBLEClient* pClient = getClient(); const NimBLEClient* pClient = getClient();
int rc = 0; int rc = 0;
int retryCount = 1; int retryCount = 1;
TaskHandle_t cur_task = xTaskGetCurrentTaskHandle(); NimBLETaskData taskData(const_cast<NimBLERemoteValueAttribute*>(this), 0, &value);
BleTaskData taskData = {const_cast<NimBLERemoteValueAttribute*>(this), cur_task, 0, &value};
do { do {
rc = ble_gattc_read_long(pClient->getConnHandle(), getHandle(), 0, NimBLERemoteValueAttribute::onReadCB, &taskData); rc = ble_gattc_read_long(pClient->getConnHandle(), getHandle(), 0, NimBLERemoteValueAttribute::onReadCB, &taskData);
@ -128,13 +120,8 @@ NimBLEAttValue NimBLERemoteValueAttribute::readValue(time_t* timestamp) const {
goto Done; goto Done;
} }
# ifdef ulTaskNotifyValueClear NimBLEUtils::taskWait(taskData, BLE_NPL_TIME_FOREVER);
// Clear the task notification value to ensure we block rc = taskData.m_flags;
ulTaskNotifyValueClear(cur_task, ULONG_MAX);
# endif
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
rc = taskData.rc;
switch (rc) { switch (rc) {
case 0: case 0:
case BLE_HS_EDONE: case BLE_HS_EDONE:
@ -169,7 +156,7 @@ Done:
if (rc != 0) { if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "<< readValue failed rc=%d, %s", rc, NimBLEUtils::returnCodeToString(rc)); NIMBLE_LOGE(LOG_TAG, "<< readValue failed rc=%d, %s", rc, NimBLEUtils::returnCodeToString(rc));
} else { } else {
NIMBLE_LOGD(LOG_TAG, "<< readValue rc=%d", rc); NIMBLE_LOGD(LOG_TAG, "<< readValue");
} }
return value; return value;
@ -180,8 +167,8 @@ Done:
* @return success == 0 or error code. * @return success == 0 or error code.
*/ */
int NimBLERemoteValueAttribute::onReadCB(uint16_t conn_handle, const ble_gatt_error* error, ble_gatt_attr* attr, void* arg) { int NimBLERemoteValueAttribute::onReadCB(uint16_t conn_handle, const ble_gatt_error* error, ble_gatt_attr* attr, void* arg) {
auto pTaskData = static_cast<BleTaskData*>(arg); auto pTaskData = static_cast<NimBLETaskData*>(arg);
const auto pAtt = static_cast<NimBLERemoteValueAttribute*>(pTaskData->pATT); const auto pAtt = static_cast<NimBLERemoteValueAttribute*>(pTaskData->m_pInstance);
if (pAtt->getClient()->getConnHandle() != conn_handle) { if (pAtt->getClient()->getConnHandle() != conn_handle) {
return 0; return 0;
@ -192,7 +179,7 @@ int NimBLERemoteValueAttribute::onReadCB(uint16_t conn_handle, const ble_gatt_er
if (rc == 0) { if (rc == 0) {
if (attr) { if (attr) {
auto valBuf = static_cast<NimBLEAttValue*>(pTaskData->buf); auto valBuf = static_cast<NimBLEAttValue*>(pTaskData->m_pBuf);
uint16_t data_len = OS_MBUF_PKTLEN(attr->om); uint16_t data_len = OS_MBUF_PKTLEN(attr->om);
if ((valBuf->size() + data_len) > BLE_ATT_ATTR_MAX_LEN) { if ((valBuf->size() + data_len) > BLE_ATT_ATTR_MAX_LEN) {
rc = BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; rc = BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
@ -204,9 +191,7 @@ int NimBLERemoteValueAttribute::onReadCB(uint16_t conn_handle, const ble_gatt_er
} }
} }
pTaskData->rc = rc; NimBLEUtils::taskRelease(*pTaskData, rc);
xTaskNotifyGive(pTaskData->task);
return rc; return rc;
} // onReadCB } // onReadCB

View file

@ -167,8 +167,7 @@ int NimBLEScan::handleGapEvent(ble_gap_event* event, void* arg) {
} }
if(pScan->m_pTaskData != nullptr) { if(pScan->m_pTaskData != nullptr) {
pScan->m_pTaskData->rc = event->disc_complete.reason; NimBLEUtils::taskRelease(*pScan->m_pTaskData, event->disc_complete.reason);
xTaskNotifyGive(pScan->m_pTaskData->task);
} }
return 0; return 0;
@ -393,7 +392,7 @@ bool NimBLEScan::stop() {
} }
if(m_pTaskData != nullptr) { if(m_pTaskData != nullptr) {
xTaskNotifyGive(m_pTaskData->task); NimBLEUtils::taskRelease(*m_pTaskData);
} }
NIMBLE_LOGD(LOG_TAG, "<< stop()"); NIMBLE_LOGD(LOG_TAG, "<< stop()");
@ -461,16 +460,11 @@ NimBLEScanResults NimBLEScan::getResults(uint32_t duration, bool is_continue) {
NIMBLE_LOGW(LOG_TAG, "Blocking scan called with duration = forever"); NIMBLE_LOGW(LOG_TAG, "Blocking scan called with duration = forever");
} }
TaskHandle_t cur_task = xTaskGetCurrentTaskHandle(); NimBLETaskData taskData;
BleTaskData taskData = {nullptr, cur_task, 0, nullptr};
m_pTaskData = &taskData; m_pTaskData = &taskData;
if(start(duration, is_continue)) { if(start(duration, is_continue)) {
#ifdef ulTaskNotifyValueClear NimBLEUtils::taskWait(taskData, BLE_NPL_TIME_FOREVER);
// Clear the task notification value to ensure we block
ulTaskNotifyValueClear(cur_task, ULONG_MAX);
#endif
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
} }
m_pTaskData = nullptr; m_pTaskData = nullptr;

View file

@ -94,7 +94,7 @@ private:
bool m_ignoreResults; bool m_ignoreResults;
NimBLEScanResults m_scanResults; NimBLEScanResults m_scanResults;
uint32_t m_duration; uint32_t m_duration;
BleTaskData *m_pTaskData; NimBLETaskData *m_pTaskData;
uint8_t m_maxResults; uint8_t m_maxResults;
}; };

View file

@ -368,8 +368,8 @@ int NimBLEServer::peerNameCB(uint16_t conn_handle,
const struct ble_gatt_error *error, const struct ble_gatt_error *error,
struct ble_gatt_attr *attr, struct ble_gatt_attr *attr,
void *arg) { void *arg) {
BleTaskData *pTaskData = (BleTaskData*)arg; NimBLETaskData *pTaskData = (NimBLETaskData*)arg;
std::string *name = (std::string*)pTaskData->buf; std::string *name = (std::string*)pTaskData->m_pBuf;
int rc = error->status; int rc = error->status;
if (rc == 0) { if (rc == 0) {
@ -380,16 +380,15 @@ int NimBLEServer::peerNameCB(uint16_t conn_handle,
} }
if (rc == BLE_HS_EDONE) { if (rc == BLE_HS_EDONE) {
// No ask means this was read for a callback. if (pTaskData->m_flags != -1) {
if (pTaskData->task == nullptr) { NimBLEServer* pServer = (NimBLEServer*)pTaskData->m_pInstance;
NimBLEServer* pServer = (NimBLEServer*)pTaskData->pATT;
NimBLEConnInfo peerInfo{}; NimBLEConnInfo peerInfo{};
ble_gap_conn_find(conn_handle, &peerInfo.m_desc); ble_gap_conn_find(conn_handle, &peerInfo.m_desc);
// Use the rc value as a flag to indicate which callback should be called. // check the flag to indicate which callback should be called.
if (pTaskData->rc == NIMBLE_SERVER_GET_PEER_NAME_ON_CONNECT_CB) { if (pTaskData->m_flags == NIMBLE_SERVER_GET_PEER_NAME_ON_CONNECT_CB) {
pServer->m_pServerCallbacks->onConnect(pServer, peerInfo, *name); pServer->m_pServerCallbacks->onConnect(pServer, peerInfo, *name);
} else if (pTaskData->rc == NIMBLE_SERVER_GET_PEER_NAME_ON_AUTH_CB) { } else if (pTaskData->m_flags == NIMBLE_SERVER_GET_PEER_NAME_ON_AUTH_CB) {
pServer->m_pServerCallbacks->onAuthenticationComplete(peerInfo, *name); pServer->m_pServerCallbacks->onAuthenticationComplete(peerInfo, *name);
} }
} }
@ -397,9 +396,8 @@ int NimBLEServer::peerNameCB(uint16_t conn_handle,
NIMBLE_LOGE(LOG_TAG, "NimBLEServerPeerNameCB rc=%d; %s", rc, NimBLEUtils::returnCodeToString(rc)); NIMBLE_LOGE(LOG_TAG, "NimBLEServerPeerNameCB rc=%d; %s", rc, NimBLEUtils::returnCodeToString(rc));
} }
if (pTaskData->task != nullptr) { if (pTaskData->m_flags == -1) {
pTaskData->rc = rc; NimBLEUtils::taskRelease(*pTaskData, rc);
xTaskNotifyGive(pTaskData->task);
} else { } else {
// If the read was triggered for callback use then these were allocated. // If the read was triggered for callback use then these were allocated.
delete name; delete name;
@ -412,16 +410,16 @@ int NimBLEServer::peerNameCB(uint16_t conn_handle,
/** /**
* @brief Internal method that sends the read command. * @brief Internal method that sends the read command.
*/ */
std::string NimBLEServer::getPeerNameInternal(uint16_t conn_handle, TaskHandle_t task, int cb_type) { std::string NimBLEServer::getPeerNameInternal(uint16_t conn_handle, int cb_type) {
std::string *buf = new std::string{}; std::string *buf = new std::string{};
BleTaskData *taskData = new BleTaskData{this, task, cb_type, buf}; NimBLETaskData *pTaskData = new NimBLETaskData(this, cb_type, buf);
ble_uuid16_t uuid {{BLE_UUID_TYPE_16}, BLE_SVC_GAP_CHR_UUID16_DEVICE_NAME}; ble_uuid16_t uuid {{BLE_UUID_TYPE_16}, BLE_SVC_GAP_CHR_UUID16_DEVICE_NAME};
int rc = ble_gattc_read_by_uuid(conn_handle, int rc = ble_gattc_read_by_uuid(conn_handle,
1, 1,
0xffff, 0xffff,
&uuid.u, &uuid.u,
NimBLEServer::peerNameCB, NimBLEServer::peerNameCB,
taskData); pTaskData);
if (rc != 0) { if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "ble_gattc_read_by_uuid rc=%d, %s", rc, NimBLEUtils::returnCodeToString(rc)); NIMBLE_LOGE(LOG_TAG, "ble_gattc_read_by_uuid rc=%d, %s", rc, NimBLEUtils::returnCodeToString(rc));
NimBLEConnInfo peerInfo{}; NimBLEConnInfo peerInfo{};
@ -432,17 +430,13 @@ std::string NimBLEServer::getPeerNameInternal(uint16_t conn_handle, TaskHandle_t
m_pServerCallbacks->onAuthenticationComplete(peerInfo, *buf); m_pServerCallbacks->onAuthenticationComplete(peerInfo, *buf);
} }
delete buf; delete buf;
delete taskData; delete pTaskData;
} else if (task != nullptr) { } else if (cb_type == -1) {
#ifdef ulTaskNotifyValueClear NimBLEUtils::taskWait(*pTaskData, BLE_NPL_TIME_FOREVER);
// Clear the task notification value to ensure we block rc = pTaskData->m_flags;
ulTaskNotifyValueClear(task, ULONG_MAX); std::string name{*(std::string*)pTaskData->m_pBuf};
#endif
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
rc = taskData->rc;
std::string name{*(std::string*)taskData->buf};
delete buf; delete buf;
delete taskData; delete pTaskData;
if (rc != 0 && rc != BLE_HS_EDONE) { if (rc != 0 && rc != BLE_HS_EDONE) {
NIMBLE_LOGE(LOG_TAG, "getPeerName rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc)); NIMBLE_LOGE(LOG_TAG, "getPeerName rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc));
@ -461,7 +455,7 @@ std::string NimBLEServer::getPeerNameInternal(uint16_t conn_handle, TaskHandle_t
* @note This is a blocking call and should NOT be called from any callbacks! * @note This is a blocking call and should NOT be called from any callbacks!
*/ */
std::string NimBLEServer::getPeerName(const NimBLEConnInfo& connInfo) { std::string NimBLEServer::getPeerName(const NimBLEConnInfo& connInfo) {
std::string name = getPeerNameInternal(connInfo.getConnHandle(), xTaskGetCurrentTaskHandle()); std::string name = getPeerNameInternal(connInfo.getConnHandle());
return name; return name;
} }
@ -500,7 +494,6 @@ int NimBLEServer::handleGapEvent(struct ble_gap_event *event, void *arg) {
if (pServer->m_getPeerNameOnConnect) { if (pServer->m_getPeerNameOnConnect) {
pServer->getPeerNameInternal(event->connect.conn_handle, pServer->getPeerNameInternal(event->connect.conn_handle,
nullptr,
NIMBLE_SERVER_GET_PEER_NAME_ON_CONNECT_CB); NIMBLE_SERVER_GET_PEER_NAME_ON_CONNECT_CB);
} else { } else {
pServer->m_pServerCallbacks->onConnect(pServer, peerInfo); pServer->m_pServerCallbacks->onConnect(pServer, peerInfo);
@ -661,7 +654,6 @@ int NimBLEServer::handleGapEvent(struct ble_gap_event *event, void *arg) {
if (pServer->m_getPeerNameOnConnect) { if (pServer->m_getPeerNameOnConnect) {
pServer->getPeerNameInternal(event->enc_change.conn_handle, pServer->getPeerNameInternal(event->enc_change.conn_handle,
nullptr,
NIMBLE_SERVER_GET_PEER_NAME_ON_AUTH_CB); NIMBLE_SERVER_GET_PEER_NAME_ON_AUTH_CB);
} else { } else {
pServer->m_pServerCallbacks->onAuthenticationComplete(peerInfo); pServer->m_pServerCallbacks->onAuthenticationComplete(peerInfo);

View file

@ -115,7 +115,7 @@ private:
static int handleGapEvent(struct ble_gap_event *event, void *arg); static int handleGapEvent(struct ble_gap_event *event, void *arg);
static int peerNameCB(uint16_t conn_handle, const struct ble_gatt_error *error, static int peerNameCB(uint16_t conn_handle, const struct ble_gatt_error *error,
struct ble_gatt_attr *attr, void *arg); struct ble_gatt_attr *attr, void *arg);
std::string getPeerNameInternal(uint16_t conn_handle, TaskHandle_t task, int cb_type = -1); std::string getPeerNameInternal(uint16_t conn_handle, int cb_type = -1);
void serviceChanged(); void serviceChanged();
void resetGATT(); void resetGATT();
bool setIndicateWait(uint16_t conn_handle); bool setIndicateWait(uint16_t conn_handle);

View file

@ -9,9 +9,9 @@
#include "nimconfig.h" #include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) #if defined(CONFIG_BT_ENABLED)
#include "NimBLEUtils.h" # include "NimBLEUtils.h"
#include "NimBLEAddress.h" # include "NimBLEAddress.h"
#include "NimBLELog.h" # include "NimBLELog.h"
# if defined(CONFIG_NIMBLE_CPP_IDF) # if defined(CONFIG_NIMBLE_CPP_IDF)
# include "host/ble_hs.h" # include "host/ble_hs.h"
@ -20,474 +20,497 @@
# endif # endif
/**** FIX COMPILATION ****/ /**** FIX COMPILATION ****/
#undef min # undef min
#undef max # undef max
/**************************/ /**************************/
#include <stdlib.h> # include <stdlib.h>
# include <climits>
static const char* LOG_TAG = "NimBLEUtils"; static const char* LOG_TAG = "NimBLEUtils";
/**
* @brief Blocks the calling task until released or timeout.
* @param [in] taskData A pointer to the task data structure.
* @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) {
ble_npl_time_t ticks;
if (timeout == BLE_NPL_TIME_FOREVER) {
ticks = BLE_NPL_TIME_FOREVER;
} else {
ble_npl_time_ms_to_ticks(timeout, &ticks);
}
# if defined INC_FREERTOS_H
taskData.m_pHandle = xTaskGetCurrentTaskHandle();
# ifdef ulTaskNotifyValueClear
// Clear the task notification value to ensure we block
ulTaskNotifyValueClear(static_cast<TaskHandle_t>(taskData.m_pHandle), ULONG_MAX);
# endif
return ulTaskNotifyTake(pdTRUE, ticks) == pdTRUE;
# else
ble_npl_sem sem;
ble_npl_error_t err = ble_npl_sem_init(&sem, 0);
if (err != BLE_NPL_OK) {
NIMBLE_LOGE(LOG_TAG, "Failed to create semaphore");
return false;
}
taskData.m_pHandle = &sem;
err = ble_npl_sem_pend(&sem, ticks);
ble_npl_sem_deinit(&sem);
taskData.m_pHandle = nullptr;
return err == BLE_NPL_OK;
# endif
} // taskWait
/**
* @brief Release a task.
* @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) {
if (taskData.m_pHandle != nullptr) {
taskData.m_flags = flags;
# if defined INC_FREERTOS_H
xTaskNotifyGive(static_cast<TaskHandle_t>(taskData.m_pHandle));
# else
ble_npl_sem_release(static_cast<ble_npl_sem*>(taskData.m_pHandle));
# endif
}
} // taskRelease
/** /**
* @brief Converts a return code from the NimBLE stack to a text string. * @brief Converts a return code from the NimBLE stack to a text string.
* @param [in] rc The return code to convert. * @param [in] rc The return code to convert.
* @return A string representation of the return code. * @return A string representation of the return code.
*/ */
const char* NimBLEUtils::returnCodeToString(int rc) { const char* NimBLEUtils::returnCodeToString(int rc) {
#if defined(CONFIG_NIMBLE_CPP_ENABLE_RETURN_CODE_TEXT) # if defined(CONFIG_NIMBLE_CPP_ENABLE_RETURN_CODE_TEXT)
switch(rc) { switch (rc) {
case 0: case 0:
return "SUCCESS"; return "SUCCESS";
case BLE_HS_EAGAIN: case BLE_HS_EAGAIN:
return "Temporary failure; try again."; return "Temporary failure; try again";
case BLE_HS_EALREADY: case BLE_HS_EALREADY:
return "Operation already in progress or completed."; return "Operation already in progress or complete";
case BLE_HS_EINVAL: case BLE_HS_EINVAL:
return "One or more arguments are invalid."; return "One or more arguments are invalid";
case BLE_HS_EMSGSIZE: case BLE_HS_EMSGSIZE:
return "The provided buffer is too small."; return "Buffer too small";
case BLE_HS_ENOENT: case BLE_HS_ENOENT:
return "No entry matching the specified criteria."; return "No matching entry found";
case BLE_HS_ENOMEM: case BLE_HS_ENOMEM:
return "Operation failed due to resource exhaustion."; return "Not enough memory";
case BLE_HS_ENOTCONN: case BLE_HS_ENOTCONN:
return "No open connection with the specified handle."; return "No open connection with handle";
case BLE_HS_ENOTSUP: case BLE_HS_ENOTSUP:
return "Operation disabled at compile time."; return "Operation disabled at compile time";
case BLE_HS_EAPP: case BLE_HS_EAPP:
return "Application callback behaved unexpectedly."; return "Application error";
case BLE_HS_EBADDATA: case BLE_HS_EBADDATA:
return "Command from peer is invalid."; return "Invalid command from peer";
case BLE_HS_EOS: case BLE_HS_EOS:
return "Mynewt OS error."; return "OS error";
case BLE_HS_ECONTROLLER: case BLE_HS_ECONTROLLER:
return "Event from controller is invalid."; return "Controller error";
case BLE_HS_ETIMEOUT: case BLE_HS_ETIMEOUT:
return "Operation timed out."; return "Operation timed out";
case BLE_HS_EDONE: case BLE_HS_EDONE:
return "Operation completed successfully."; return "Operation completed successfully";
case BLE_HS_EBUSY: case BLE_HS_EBUSY:
return "Operation cannot be performed until procedure completes."; return "Busy";
case BLE_HS_EREJECT: case BLE_HS_EREJECT:
return "Peer rejected a connection parameter update request."; return "Peer rejected request";
case BLE_HS_EUNKNOWN: case BLE_HS_EUNKNOWN:
return "Unexpected failure; catch all."; return "Unknown failure";
case BLE_HS_EROLE: case BLE_HS_EROLE:
return "Operation requires different role (e.g., central vs. peripheral)."; return "Operation requires different role";
case BLE_HS_ETIMEOUT_HCI: case BLE_HS_ETIMEOUT_HCI:
return "HCI request timed out; controller unresponsive."; return "HCI request timed out";
case BLE_HS_ENOMEM_EVT: case BLE_HS_ENOMEM_EVT:
return "Controller failed to send event due to memory exhaustion (combined host-controller only)."; return "Controller error; not enough memory";
case BLE_HS_ENOADDR: case BLE_HS_ENOADDR:
return "Operation requires an identity address but none configured."; return "No identity address";
case BLE_HS_ENOTSYNCED: case BLE_HS_ENOTSYNCED:
return "Attempt to use the host before it is synced with controller."; return "Host not synced with controller";
case BLE_HS_EAUTHEN: case BLE_HS_EAUTHEN:
return "Insufficient authentication."; return "Insufficient authentication";
case BLE_HS_EAUTHOR: case BLE_HS_EAUTHOR:
return "Insufficient authorization."; return "Insufficient authorization";
case BLE_HS_EENCRYPT: case BLE_HS_EENCRYPT:
return "Insufficient encryption level."; return "Insufficient encryption level";
case BLE_HS_EENCRYPT_KEY_SZ: case BLE_HS_EENCRYPT_KEY_SZ:
return "Insufficient key size."; return "Insufficient key size";
case BLE_HS_ESTORE_CAP: case BLE_HS_ESTORE_CAP:
return "Storage at capacity."; return "Storage at capacity";
case BLE_HS_ESTORE_FAIL: case BLE_HS_ESTORE_FAIL:
return "Storage IO error."; return "Storage IO error";
case (0x0100+BLE_ATT_ERR_INVALID_HANDLE ): case BLE_HS_EPREEMPTED:
return "The attribute handle given was not valid on this server."; return "Host preempted";
case (0x0100+BLE_ATT_ERR_READ_NOT_PERMITTED ): case BLE_HS_EDISABLED:
return "The attribute cannot be read."; return "Host disabled";
case (0x0100+BLE_ATT_ERR_WRITE_NOT_PERMITTED ): case BLE_HS_ESTALLED:
return "The attribute cannot be written."; return "CoC module is stalled";
case (0x0100+BLE_ATT_ERR_INVALID_PDU ): case (BLE_HS_ERR_ATT_BASE + BLE_ATT_ERR_INVALID_HANDLE):
return "The attribute PDU was invalid."; return "Invalid attribute handle";
case (0x0100+BLE_ATT_ERR_INSUFFICIENT_AUTHEN ): case (BLE_HS_ERR_ATT_BASE + BLE_ATT_ERR_READ_NOT_PERMITTED):
return "The attribute requires authentication before it can be read or written."; return "The attribute cannot be read";
case (0x0100+BLE_ATT_ERR_REQ_NOT_SUPPORTED ): case (BLE_HS_ERR_ATT_BASE + BLE_ATT_ERR_WRITE_NOT_PERMITTED):
return "Attribute server does not support the request received from the client."; return "The attribute cannot be written";
case (0x0100+BLE_ATT_ERR_INVALID_OFFSET ): case (BLE_HS_ERR_ATT_BASE + BLE_ATT_ERR_INVALID_PDU):
return "Offset specified was past the end of the attribute."; return "Invalid attribute PDU";
case (0x0100+BLE_ATT_ERR_INSUFFICIENT_AUTHOR ): case (BLE_HS_ERR_ATT_BASE + BLE_ATT_ERR_INSUFFICIENT_AUTHEN):
return "The attribute requires authorization before it can be read or written."; return "Insufficient authentication";
case (0x0100+BLE_ATT_ERR_PREPARE_QUEUE_FULL ): case (BLE_HS_ERR_ATT_BASE + BLE_ATT_ERR_REQ_NOT_SUPPORTED):
return "Too many prepare writes have been queued."; return "Unsupported request";
case (0x0100+BLE_ATT_ERR_ATTR_NOT_FOUND ): case (BLE_HS_ERR_ATT_BASE + BLE_ATT_ERR_INVALID_OFFSET):
return "No attribute found within the given attribute handle range."; return "Invalid offset";
case (0x0100+BLE_ATT_ERR_ATTR_NOT_LONG ): case (BLE_HS_ERR_ATT_BASE + BLE_ATT_ERR_INSUFFICIENT_AUTHOR):
return "The attribute cannot be read or written using the Read Blob Request."; return "Insufficient authorization";
case (0x0100+BLE_ATT_ERR_INSUFFICIENT_KEY_SZ ): case (BLE_HS_ERR_ATT_BASE + BLE_ATT_ERR_PREPARE_QUEUE_FULL):
return "The Encryption Key Size used for encrypting this link is insufficient."; return "Prepare write queue full";
case (0x0100+BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN ): case (BLE_HS_ERR_ATT_BASE + BLE_ATT_ERR_ATTR_NOT_FOUND):
return "The attribute value length is invalid for the operation."; return "Attribute not found";
case (0x0100+BLE_ATT_ERR_UNLIKELY ): case (BLE_HS_ERR_ATT_BASE + BLE_ATT_ERR_ATTR_NOT_LONG):
return "The attribute request has encountered an error that was unlikely, could not be completed as requested."; return "Long read not supported";
case (0x0100+BLE_ATT_ERR_INSUFFICIENT_ENC ): case (BLE_HS_ERR_ATT_BASE + BLE_ATT_ERR_INSUFFICIENT_KEY_SZ):
return "The attribute requires encryption before it can be read or written."; return "Insufficient encryption key size";
case (0x0100+BLE_ATT_ERR_UNSUPPORTED_GROUP ): case (BLE_HS_ERR_ATT_BASE + BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN):
return "The attribute type is not a supported grouping attribute as defined by a higher layer specification."; return "Invalid attribute value length";
case (0x0100+BLE_ATT_ERR_INSUFFICIENT_RES ): case (BLE_HS_ERR_ATT_BASE + BLE_ATT_ERR_UNLIKELY):
return "Insufficient Resources to complete the request."; return "Unlikely attribute error";
case (0x0200+BLE_ERR_UNKNOWN_HCI_CMD ): case (BLE_HS_ERR_ATT_BASE + BLE_ATT_ERR_INSUFFICIENT_ENC):
return "Insufficient encryption";
case (BLE_HS_ERR_ATT_BASE + BLE_ATT_ERR_UNSUPPORTED_GROUP):
return "Not a supported grouping attribute type";
case (BLE_HS_ERR_ATT_BASE + BLE_ATT_ERR_INSUFFICIENT_RES):
return "Insufficient Resources";
case (BLE_HS_ERR_HCI_BASE + BLE_ERR_UNKNOWN_HCI_CMD):
return "Unknown HCI Command"; return "Unknown HCI Command";
case (0x0200+BLE_ERR_UNK_CONN_ID ): case (BLE_HS_ERR_HCI_BASE + BLE_ERR_UNK_CONN_ID):
return "Unknown Connection Identifier"; return "Unknown Connection Identifier";
case (0x0200+BLE_ERR_HW_FAIL ): case (BLE_HS_ERR_HCI_BASE + BLE_ERR_HW_FAIL):
return "Hardware Failure"; return "Hardware Failure";
case (0x0200+BLE_ERR_PAGE_TMO ): case (BLE_HS_ERR_HCI_BASE + BLE_ERR_PAGE_TMO):
return "Page Timeout"; return "Page Timeout";
case (0x0200+BLE_ERR_AUTH_FAIL ): case (BLE_HS_ERR_HCI_BASE + BLE_ERR_AUTH_FAIL):
return "Authentication Failure"; return "Authentication Failure";
case (0x0200+BLE_ERR_PINKEY_MISSING ): case (BLE_HS_ERR_HCI_BASE + BLE_ERR_PINKEY_MISSING):
return "PIN or Key Missing"; return "PIN or Key Missing";
case (0x0200+BLE_ERR_MEM_CAPACITY ): case (BLE_HS_ERR_HCI_BASE + BLE_ERR_MEM_CAPACITY):
return "Memory Capacity Exceeded"; return "Memory Capacity Exceeded";
case (0x0200+BLE_ERR_CONN_SPVN_TMO ): case (BLE_HS_ERR_HCI_BASE + BLE_ERR_CONN_SPVN_TMO):
return "Connection Timeout"; return "Connection Timeout";
case (0x0200+BLE_ERR_CONN_LIMIT ): case (BLE_HS_ERR_HCI_BASE + BLE_ERR_CONN_LIMIT):
return "Connection Limit Exceeded"; return "Connection Limit Exceeded";
case (0x0200+BLE_ERR_SYNCH_CONN_LIMIT ): case (BLE_HS_ERR_HCI_BASE + BLE_ERR_SYNCH_CONN_LIMIT):
return "Synchronous Connection Limit To A Device Exceeded"; return "Synchronous Connection Limit To A Device Exceeded";
case (0x0200+BLE_ERR_ACL_CONN_EXISTS ): case (BLE_HS_ERR_HCI_BASE + BLE_ERR_ACL_CONN_EXISTS):
return "ACL Connection Already Exists"; return "ACL Connection Already Exists";
case (0x0200+BLE_ERR_CMD_DISALLOWED ): case (BLE_HS_ERR_HCI_BASE + BLE_ERR_CMD_DISALLOWED):
return "Command Disallowed"; return "Command Disallowed";
case (0x0200+BLE_ERR_CONN_REJ_RESOURCES ): case (BLE_HS_ERR_HCI_BASE + BLE_ERR_CONN_REJ_RESOURCES):
return "Connection Rejected due to Limited Resources"; return "Connection Rejected due to Limited Resources";
case (0x0200+BLE_ERR_CONN_REJ_SECURITY ): case (BLE_HS_ERR_HCI_BASE + BLE_ERR_CONN_REJ_SECURITY):
return "Connection Rejected Due To Security Reasons"; return "Connection Rejected Due To Security Reasons";
case (0x0200+BLE_ERR_CONN_REJ_BD_ADDR ): case (BLE_HS_ERR_HCI_BASE + BLE_ERR_CONN_REJ_BD_ADDR):
return "Connection Rejected due to Unacceptable BD_ADDR"; return "Connection Rejected due to Unacceptable BD_ADDR";
case (0x0200+BLE_ERR_CONN_ACCEPT_TMO ): case (BLE_HS_ERR_HCI_BASE + BLE_ERR_CONN_ACCEPT_TMO):
return "Connection Accept Timeout Exceeded"; return "Connection Accept Timeout Exceeded";
case (0x0200+BLE_ERR_UNSUPPORTED ): case (BLE_HS_ERR_HCI_BASE + BLE_ERR_UNSUPPORTED):
return "Unsupported Feature or Parameter Value"; return "Unsupported Feature or Parameter Value";
case (0x0200+BLE_ERR_INV_HCI_CMD_PARMS ): case (BLE_HS_ERR_HCI_BASE + BLE_ERR_INV_HCI_CMD_PARMS):
return "Invalid HCI Command Parameters"; return "Invalid HCI Command Parameters";
case (0x0200+BLE_ERR_REM_USER_CONN_TERM ): case (BLE_HS_ERR_HCI_BASE + BLE_ERR_REM_USER_CONN_TERM):
return "Remote User Terminated Connection"; return "Remote User Terminated Connection";
case (0x0200+BLE_ERR_RD_CONN_TERM_RESRCS ): case (BLE_HS_ERR_HCI_BASE + BLE_ERR_RD_CONN_TERM_RESRCS):
return "Remote Device Terminated Connection due to Low Resources"; return "Remote Device Terminated Connection; Low Resources";
case (0x0200+BLE_ERR_RD_CONN_TERM_PWROFF ): case (BLE_HS_ERR_HCI_BASE + BLE_ERR_RD_CONN_TERM_PWROFF):
return "Remote Device Terminated Connection due to Power Off"; return "Remote Device Terminated Connection; Power Off";
case (0x0200+BLE_ERR_CONN_TERM_LOCAL ): case (BLE_HS_ERR_HCI_BASE + BLE_ERR_CONN_TERM_LOCAL):
return "Connection Terminated By Local Host"; return "Connection Terminated By Local Host";
case (0x0200+BLE_ERR_REPEATED_ATTEMPTS ): case (BLE_HS_ERR_HCI_BASE + BLE_ERR_REPEATED_ATTEMPTS):
return "Repeated Attempts"; return "Repeated Pairing Attempts";
case (0x0200+BLE_ERR_NO_PAIRING ): case (BLE_HS_ERR_HCI_BASE + BLE_ERR_NO_PAIRING):
return "Pairing Not Allowed"; return "Pairing Not Allowed";
case (0x0200+BLE_ERR_UNK_LMP ): case (BLE_HS_ERR_HCI_BASE + BLE_ERR_UNK_LMP):
return "Unknown LMP PDU"; return "Unknown LMP PDU";
case (0x0200+BLE_ERR_UNSUPP_REM_FEATURE ): case (BLE_HS_ERR_HCI_BASE + BLE_ERR_UNSUPP_REM_FEATURE):
return "Unsupported Remote Feature / Unsupported LMP Feature"; return "Unsupported Remote Feature";
case (0x0200+BLE_ERR_SCO_OFFSET ): case (BLE_HS_ERR_HCI_BASE + BLE_ERR_SCO_OFFSET):
return "SCO Offset Rejected"; return "SCO Offset Rejected";
case (0x0200+BLE_ERR_SCO_ITVL ): case (BLE_HS_ERR_HCI_BASE + BLE_ERR_SCO_ITVL):
return "SCO Interval Rejected"; return "SCO Interval Rejected";
case (0x0200+BLE_ERR_SCO_AIR_MODE ): case (BLE_HS_ERR_HCI_BASE + BLE_ERR_SCO_AIR_MODE):
return "SCO Air Mode Rejected"; return "SCO Air Mode Rejected";
case (0x0200+BLE_ERR_INV_LMP_LL_PARM ): case (BLE_HS_ERR_HCI_BASE + BLE_ERR_INV_LMP_LL_PARM):
return "Invalid LMP Parameters / Invalid LL Parameters"; return "Invalid LL Parameters";
case (0x0200+BLE_ERR_UNSPECIFIED ): case (BLE_HS_ERR_HCI_BASE + BLE_ERR_UNSPECIFIED):
return "Unspecified Error"; return "Unspecified Error";
case (0x0200+BLE_ERR_UNSUPP_LMP_LL_PARM ): case (BLE_HS_ERR_HCI_BASE + BLE_ERR_UNSUPP_LMP_LL_PARM):
return "Unsupported LMP Parameter Value / Unsupported LL Parameter Value"; return "Unsupported LL Parameter Value";
case (0x0200+BLE_ERR_NO_ROLE_CHANGE ): case (BLE_HS_ERR_HCI_BASE + BLE_ERR_NO_ROLE_CHANGE):
return "Role Change Not Allowed"; return "Role Change Not Allowed";
case (0x0200+BLE_ERR_LMP_LL_RSP_TMO ): case (BLE_HS_ERR_HCI_BASE + BLE_ERR_LMP_LL_RSP_TMO):
return "LMP Response Timeout / LL Response Timeout"; return "LL Response Timeout";
case (0x0200+BLE_ERR_LMP_COLLISION ): case (BLE_HS_ERR_HCI_BASE + BLE_ERR_LMP_COLLISION):
return "LMP Error Transaction Collision"; return "LMP Error Transaction Collision";
case (0x0200+BLE_ERR_LMP_PDU ): case (BLE_HS_ERR_HCI_BASE + BLE_ERR_LMP_PDU):
return "LMP PDU Not Allowed"; return "LMP PDU Not Allowed";
case (0x0200+BLE_ERR_ENCRYPTION_MODE ): case (BLE_HS_ERR_HCI_BASE + BLE_ERR_ENCRYPTION_MODE):
return "Encryption Mode Not Acceptable"; return "Encryption Mode Not Acceptable";
case (0x0200+BLE_ERR_LINK_KEY_CHANGE ): case (BLE_HS_ERR_HCI_BASE + BLE_ERR_LINK_KEY_CHANGE):
return "Link Key cannot be Changed"; return "Link Key cannot be Changed";
case (0x0200+BLE_ERR_UNSUPP_QOS ): case (BLE_HS_ERR_HCI_BASE + BLE_ERR_UNSUPP_QOS):
return "Requested QoS Not Supported"; return "Requested QoS Not Supported";
case (0x0200+BLE_ERR_INSTANT_PASSED ): case (BLE_HS_ERR_HCI_BASE + BLE_ERR_INSTANT_PASSED):
return "Instant Passed"; return "Instant Passed";
case (0x0200+BLE_ERR_UNIT_KEY_PAIRING ): case (BLE_HS_ERR_HCI_BASE + BLE_ERR_UNIT_KEY_PAIRING):
return "Pairing With Unit Key Not Supported"; return "Pairing With Unit Key Not Supported";
case (0x0200+BLE_ERR_DIFF_TRANS_COLL ): case (BLE_HS_ERR_HCI_BASE + BLE_ERR_DIFF_TRANS_COLL):
return "Different Transaction Collision"; return "Different Transaction Collision";
case (0x0200+BLE_ERR_QOS_PARM ): case (BLE_HS_ERR_HCI_BASE + BLE_ERR_QOS_PARM):
return "QoS Unacceptable Parameter"; return "QoS Unacceptable Parameter";
case (0x0200+BLE_ERR_QOS_REJECTED ): case (BLE_HS_ERR_HCI_BASE + BLE_ERR_QOS_REJECTED):
return "QoS Rejected"; return "QoS Rejected";
case (0x0200+BLE_ERR_CHAN_CLASS ): case (BLE_HS_ERR_HCI_BASE + BLE_ERR_CHAN_CLASS):
return "Channel Classification Not Supported"; return "Channel Classification Not Supported";
case (0x0200+BLE_ERR_INSUFFICIENT_SEC ): case (BLE_HS_ERR_HCI_BASE + BLE_ERR_INSUFFICIENT_SEC):
return "Insufficient Security"; return "Insufficient Security";
case (0x0200+BLE_ERR_PARM_OUT_OF_RANGE ): case (BLE_HS_ERR_HCI_BASE + BLE_ERR_PARM_OUT_OF_RANGE):
return "Parameter Out Of Mandatory Range"; return "Parameter Out Of Mandatory Range";
case (0x0200+BLE_ERR_PENDING_ROLE_SW ): case (BLE_HS_ERR_HCI_BASE + BLE_ERR_PENDING_ROLE_SW):
return "Role Switch Pending"; return "Role Switch Pending";
case (0x0200+BLE_ERR_RESERVED_SLOT ): case (BLE_HS_ERR_HCI_BASE + BLE_ERR_RESERVED_SLOT):
return "Reserved Slot Violation"; return "Reserved Slot Violation";
case (0x0200+BLE_ERR_ROLE_SW_FAIL ): case (BLE_HS_ERR_HCI_BASE + BLE_ERR_ROLE_SW_FAIL):
return "Role Switch Failed"; return "Role Switch Failed";
case (0x0200+BLE_ERR_INQ_RSP_TOO_BIG ): case (BLE_HS_ERR_HCI_BASE + BLE_ERR_INQ_RSP_TOO_BIG):
return "Extended Inquiry Response Too Large"; return "Extended Inquiry Response Too Large";
case (0x0200+BLE_ERR_SEC_SIMPLE_PAIR ): case (BLE_HS_ERR_HCI_BASE + BLE_ERR_SEC_SIMPLE_PAIR):
return "Secure Simple Pairing Not Supported By Host"; return "Secure Simple Pairing Not Supported By Host";
case (0x0200+BLE_ERR_HOST_BUSY_PAIR ): case (BLE_HS_ERR_HCI_BASE + BLE_ERR_HOST_BUSY_PAIR):
return "Host Busy - Pairing"; return "Host Busy - Pairing";
case (0x0200+BLE_ERR_CONN_REJ_CHANNEL ): case (BLE_HS_ERR_HCI_BASE + BLE_ERR_CONN_REJ_CHANNEL):
return "Connection Rejected, No Suitable Channel Found"; return "Connection Rejected, No Suitable Channel Found";
case (0x0200+BLE_ERR_CTLR_BUSY ): case (BLE_HS_ERR_HCI_BASE + BLE_ERR_CTLR_BUSY):
return "Controller Busy"; return "Controller Busy";
case (0x0200+BLE_ERR_CONN_PARMS ): case (BLE_HS_ERR_HCI_BASE + BLE_ERR_CONN_PARMS):
return "Unacceptable Connection Parameters"; return "Unacceptable Connection Parameters";
case (0x0200+BLE_ERR_DIR_ADV_TMO ): case (BLE_HS_ERR_HCI_BASE + BLE_ERR_DIR_ADV_TMO):
return "Directed Advertising Timeout"; return "Directed Advertising Timeout";
case (0x0200+BLE_ERR_CONN_TERM_MIC ): case (BLE_HS_ERR_HCI_BASE + BLE_ERR_CONN_TERM_MIC):
return "Connection Terminated due to MIC Failure"; return "Connection Terminated; MIC Failure";
case (0x0200+BLE_ERR_CONN_ESTABLISHMENT ): case (BLE_HS_ERR_HCI_BASE + BLE_ERR_CONN_ESTABLISHMENT):
return "Connection Failed to be Established"; return "Connection Failed to be Established";
case (0x0200+BLE_ERR_MAC_CONN_FAIL ): case (BLE_HS_ERR_HCI_BASE + BLE_ERR_MAC_CONN_FAIL):
return "MAC Connection Failed"; return "MAC Connection Failed";
case (0x0200+BLE_ERR_COARSE_CLK_ADJ ): case (BLE_HS_ERR_HCI_BASE + BLE_ERR_COARSE_CLK_ADJ):
return "Coarse Clock Adjustment Rejected"; return "Coarse Clock Adjustment Rejected";
case (0x0300+BLE_L2CAP_SIG_ERR_CMD_NOT_UNDERSTOOD ): case (BLE_HS_ERR_HCI_BASE + BLE_ERR_TYPE0_SUBMAP_NDEF):
return "Invalid or unsupported incoming L2CAP sig command."; return "Type0 Submap Not Defined";
case (0x0300+BLE_L2CAP_SIG_ERR_MTU_EXCEEDED ): case (BLE_HS_ERR_HCI_BASE + BLE_ERR_UNK_ADV_INDENT):
return "Incoming packet too large."; return "Unknown Advertising Identifier";
case (0x0300+BLE_L2CAP_SIG_ERR_INVALID_CID ): case (BLE_HS_ERR_HCI_BASE + BLE_ERR_LIMIT_REACHED):
return "No channel with specified ID."; return "Limit Reached";
case (0x0400+BLE_SM_ERR_PASSKEY ): case (BLE_HS_ERR_HCI_BASE + BLE_ERR_OPERATION_CANCELLED):
return "The user input of passkey failed, for example, the user cancelled the operation."; return "Operation Cancelled by Host";
case (0x0400+BLE_SM_ERR_OOB ): case (BLE_HS_ERR_HCI_BASE + BLE_ERR_PACKET_TOO_LONG):
return "The OOB data is not available."; return "Packet Too Long";
case (0x0400+BLE_SM_ERR_AUTHREQ ): case (BLE_HS_ERR_L2C_BASE + BLE_L2CAP_SIG_ERR_CMD_NOT_UNDERSTOOD):
return "The pairing procedure cannot be performed as authentication requirements cannot be met due to IO capabilities of one or both devices."; return "Invalid or unsupported incoming L2CAP sig command";
case (0x0400+BLE_SM_ERR_CONFIRM_MISMATCH ): case (BLE_HS_ERR_L2C_BASE + BLE_L2CAP_SIG_ERR_MTU_EXCEEDED):
return "The confirm value does not match the calculated compare value."; return "Incoming packet too large";
case (0x0400+BLE_SM_ERR_PAIR_NOT_SUPP ): case (BLE_HS_ERR_L2C_BASE + BLE_L2CAP_SIG_ERR_INVALID_CID):
return "Pairing is not supported by the device."; return "No channel with specified ID";
case (0x0400+BLE_SM_ERR_ENC_KEY_SZ ): case (BLE_HS_ERR_SM_US_BASE + BLE_SM_ERR_PASSKEY):
return "The resultant encryption key size is insufficient for the security requirements of this device."; case (BLE_HS_ERR_SM_PEER_BASE + BLE_SM_ERR_PASSKEY):
case (0x0400+BLE_SM_ERR_CMD_NOT_SUPP ): return "Incorrect passkey or the user cancelled";
return "The SMP command received is not supported on this device."; case (BLE_HS_ERR_SM_US_BASE + BLE_SM_ERR_OOB):
case (0x0400+BLE_SM_ERR_UNSPECIFIED ): case (BLE_HS_ERR_SM_PEER_BASE + BLE_SM_ERR_OOB):
return "Pairing failed due to an unspecified reason."; return "The OOB data is not available";
case (0x0400+BLE_SM_ERR_REPEATED ): case (BLE_HS_ERR_SM_US_BASE + BLE_SM_ERR_AUTHREQ):
return "Pairing or authentication procedure disallowed, too little time has elapsed since last pairing request or security request."; case (BLE_HS_ERR_SM_PEER_BASE + BLE_SM_ERR_AUTHREQ):
case (0x0400+BLE_SM_ERR_INVAL ): return "Authentication requirements cannot be met due to IO capabilities";
return "Command length is invalid or that a parameter is outside of the specified range."; case (BLE_HS_ERR_SM_US_BASE + BLE_SM_ERR_CONFIRM_MISMATCH):
case (0x0400+BLE_SM_ERR_DHKEY ): case (BLE_HS_ERR_SM_PEER_BASE + BLE_SM_ERR_CONFIRM_MISMATCH):
return "DHKey Check value received doesn't match the one calculated by the local device."; return "The confirm value does not match the calculated compare value";
case (0x0400+BLE_SM_ERR_NUMCMP ): case (BLE_HS_ERR_SM_US_BASE + BLE_SM_ERR_PAIR_NOT_SUPP):
return "Confirm values in the numeric comparison protocol do not match."; case (BLE_HS_ERR_SM_PEER_BASE + BLE_SM_ERR_PAIR_NOT_SUPP):
case (0x0400+BLE_SM_ERR_ALREADY ): return "Pairing is not supported by the device";
return "Pairing over the LE transport failed - Pairing Request sent over the BR/EDR transport in process."; case (BLE_HS_ERR_SM_US_BASE + BLE_SM_ERR_ENC_KEY_SZ):
case (0x0400+BLE_SM_ERR_CROSS_TRANS ): case (BLE_HS_ERR_SM_PEER_BASE + BLE_SM_ERR_ENC_KEY_SZ):
return "BR/EDR Link Key generated on the BR/EDR transport cannot be used to derive and distribute keys for the LE transport."; return "Insufficient encryption key size for this device";
case (0x0500+BLE_SM_ERR_PASSKEY ): case (BLE_HS_ERR_SM_US_BASE + BLE_SM_ERR_CMD_NOT_SUPP):
return "The user input of passkey failed or the user cancelled the operation."; case (BLE_HS_ERR_SM_PEER_BASE + BLE_SM_ERR_CMD_NOT_SUPP):
case (0x0500+BLE_SM_ERR_OOB ): return "The SMP command received is not supported on this device";
return "The OOB data is not available."; case (BLE_HS_ERR_SM_US_BASE + BLE_SM_ERR_UNSPECIFIED):
case (0x0500+BLE_SM_ERR_AUTHREQ ): case (BLE_HS_ERR_SM_PEER_BASE + BLE_SM_ERR_UNSPECIFIED):
return "The pairing procedure cannot be performed as authentication requirements cannot be met due to IO capabilities of one or both devices."; return "Pairing failed; unspecified reason";
case (0x0500+BLE_SM_ERR_CONFIRM_MISMATCH ): case (BLE_HS_ERR_SM_US_BASE + BLE_SM_ERR_REPEATED):
return "The confirm value does not match the calculated compare value."; case (BLE_HS_ERR_SM_PEER_BASE + BLE_SM_ERR_REPEATED):
case (0x0500+BLE_SM_ERR_PAIR_NOT_SUPP ): return "Repeated pairing attempt";
return "Pairing is not supported by the device."; case (BLE_HS_ERR_SM_US_BASE + BLE_SM_ERR_INVAL):
case (0x0500+BLE_SM_ERR_ENC_KEY_SZ ): case (BLE_HS_ERR_SM_PEER_BASE + BLE_SM_ERR_INVAL):
return "The resultant encryption key size is insufficient for the security requirements of this device."; return "Invalid command length or parameter";
case (0x0500+BLE_SM_ERR_CMD_NOT_SUPP ): case (BLE_HS_ERR_SM_US_BASE + BLE_SM_ERR_DHKEY):
return "The SMP command received is not supported on this device."; case (BLE_HS_ERR_SM_PEER_BASE + BLE_SM_ERR_DHKEY):
case (0x0500+BLE_SM_ERR_UNSPECIFIED ): return "DHKey check value received doesn't match";
return "Pairing failed due to an unspecified reason."; case (BLE_HS_ERR_SM_US_BASE + BLE_SM_ERR_NUMCMP):
case (0x0500+BLE_SM_ERR_REPEATED ): case (BLE_HS_ERR_SM_PEER_BASE + BLE_SM_ERR_NUMCMP):
return "Pairing or authentication procedure is disallowed because too little time has elapsed since last pairing request or security request."; return "Confirm values do not match";
case (0x0500+BLE_SM_ERR_INVAL ): case (BLE_HS_ERR_SM_US_BASE + BLE_SM_ERR_ALREADY):
return "Command length is invalid or a parameter is outside of the specified range."; case (BLE_HS_ERR_SM_PEER_BASE + BLE_SM_ERR_ALREADY):
case (0x0500+BLE_SM_ERR_DHKEY ): return "Pairing already in process";
return "Indicates to the remote device that the DHKey Check value received doesnt match the one calculated by the local device."; case (BLE_HS_ERR_SM_US_BASE + BLE_SM_ERR_CROSS_TRANS):
case (0x0500+BLE_SM_ERR_NUMCMP ): case (BLE_HS_ERR_SM_PEER_BASE + BLE_SM_ERR_CROSS_TRANS):
return "Confirm values in the numeric comparison protocol do not match."; return "Invalid link key for the LE transport";
case (0x0500+BLE_SM_ERR_ALREADY ):
return "Pairing over the LE transport failed - Pairing Request sent over the BR/EDR transport in process.";
case (0x0500+BLE_SM_ERR_CROSS_TRANS ):
return "BR/EDR Link Key generated on the BR/EDR transport cannot be used to derive and distribute keys for the LE transport.";
default: default:
return "Unknown"; return "Unknown";
} }
#else // #if defined(CONFIG_NIMBLE_CPP_ENABLE_RETURN_CODE_TEXT) # else // #if defined(CONFIG_NIMBLE_CPP_ENABLE_RETURN_CODE_TEXT)
(void)rc; (void)rc;
return ""; return "";
#endif // #if defined(CONFIG_NIMBLE_CPP_ENABLE_RETURN_CODE_TEXT) # endif // #if defined(CONFIG_NIMBLE_CPP_ENABLE_RETURN_CODE_TEXT)
} }
/** /**
* @brief Convert the advertising type flag to a string. * @brief Convert the advertising type flag to a string.
* @param advType The type to convert. * @param advType The type to convert.
* @return A string representation of the advertising flags. * @return A string representation of the advertising flags.
*/ */
const char* NimBLEUtils::advTypeToString(uint8_t advType) { const char* NimBLEUtils::advTypeToString(uint8_t advType) {
#if defined(CONFIG_NIMBLE_CPP_ENABLE_ADVERTISEMENT_TYPE_TEXT) # if defined(CONFIG_NIMBLE_CPP_ENABLE_ADVERTISEMENT_TYPE_TEXT)
switch(advType) { switch (advType) {
case BLE_HCI_ADV_TYPE_ADV_IND : //0 case BLE_HCI_ADV_TYPE_ADV_IND: // 0
return "Undirected - Connectable / Scannable"; return "Undirected - Connectable / Scannable";
case BLE_HCI_ADV_TYPE_ADV_DIRECT_IND_HD: //1 case BLE_HCI_ADV_TYPE_ADV_DIRECT_IND_HD: // 1
return "Directed High Duty - Connectable"; return "Directed High Duty - Connectable";
case BLE_HCI_ADV_TYPE_ADV_SCAN_IND: //2 case BLE_HCI_ADV_TYPE_ADV_SCAN_IND: // 2
return "Non-Connectable - Scan Response Available"; return "Non-Connectable - Scan Response Available";
case BLE_HCI_ADV_TYPE_ADV_NONCONN_IND: //3 case BLE_HCI_ADV_TYPE_ADV_NONCONN_IND: // 3
return "Non-Connectable - No Scan Response"; return "Non-Connectable - No Scan Response";
case BLE_HCI_ADV_TYPE_ADV_DIRECT_IND_LD: //4 case BLE_HCI_ADV_TYPE_ADV_DIRECT_IND_LD: // 4
return "Directed Low Duty - Connectable"; return "Directed Low Duty - Connectable";
default: default:
return "Unknown flag"; return "Unknown flag";
} }
#else // #if defined(CONFIG_NIMBLE_CPP_ENABLE_ADVERTISEMENT_TYPE_TEXT) # else // #if defined(CONFIG_NIMBLE_CPP_ENABLE_ADVERTISEMENT_TYPE_TEXT)
(void)advType; (void)advType;
return ""; return "";
#endif // #if defined(CONFIG_NIMBLE_CPP_ENABLE_ADVERTISEMENT_TYPE_TEXT) # endif // #if defined(CONFIG_NIMBLE_CPP_ENABLE_ADVERTISEMENT_TYPE_TEXT)
} // adFlagsToString } // adFlagsToString
/**
* @brief Create a hex representation of data.
*
* @param [in] target Where to write the hex string. If this is null, we malloc storage.
* @param [in] source The start of the binary data.
* @param [in] length The length of the data to convert.
* @return A pointer to the formatted buffer.
*/
char* NimBLEUtils::buildHexData(uint8_t* target, const uint8_t* source, uint8_t length) {
// Guard against too much data.
if (length > 100) length = 100;
if (target == nullptr) {
target = (uint8_t*) malloc(length * 2 + 1);
if (target == nullptr) {
NIMBLE_LOGE(LOG_TAG, "buildHexData: malloc failed");
return nullptr;
}
}
char* startOfData = (char*) target;
for (int i = 0; i < length; i++) {
sprintf((char*) target, "%.2x", (char) *source);
source++;
target += 2;
}
// Handle the special case where there was no data.
if (length == 0) {
*startOfData = 0;
}
return startOfData;
} // buildHexData
/**
* @brief Utility function to log the gap event info.
* @param [in] event A pointer to the gap event structure.
* @param [in] arg Unused.
*/
void NimBLEUtils::dumpGapEvent(ble_gap_event *event, void *arg){
(void)arg;
#if defined(CONFIG_NIMBLE_CPP_ENABLE_GAP_EVENT_CODE_TEXT)
NIMBLE_LOGD(LOG_TAG, "Received a GAP event: %s", gapEventToString(event->type));
#else
(void)event;
#endif
}
/** /**
* @brief Convert a GAP event type to a string representation. * @brief Convert a GAP event type to a string representation.
* @param [in] eventType The type of event. * @param [in] eventType The type of event.
* @return A string representation of the event type. * @return A string representation of the event type.
*/ */
const char* NimBLEUtils::gapEventToString(uint8_t eventType) { const char* NimBLEUtils::gapEventToString(uint8_t eventType) {
#if defined(CONFIG_NIMBLE_CPP_ENABLE_GAP_EVENT_CODE_TEXT) # if defined(CONFIG_NIMBLE_CPP_ENABLE_GAP_EVENT_CODE_TEXT)
switch (eventType) { switch (eventType) {
case BLE_GAP_EVENT_CONNECT : //0 case BLE_GAP_EVENT_CONNECT: // 0
return "BLE_GAP_EVENT_CONNECT "; return "BLE_GAP_EVENT_CONNECT ";
case BLE_GAP_EVENT_DISCONNECT: // 1
case BLE_GAP_EVENT_DISCONNECT: //1
return "BLE_GAP_EVENT_DISCONNECT"; return "BLE_GAP_EVENT_DISCONNECT";
case BLE_GAP_EVENT_CONN_UPDATE: // 3
case BLE_GAP_EVENT_CONN_UPDATE: //3
return "BLE_GAP_EVENT_CONN_UPDATE"; return "BLE_GAP_EVENT_CONN_UPDATE";
case BLE_GAP_EVENT_CONN_UPDATE_REQ: // 4
case BLE_GAP_EVENT_CONN_UPDATE_REQ: //4
return "BLE_GAP_EVENT_CONN_UPDATE_REQ"; return "BLE_GAP_EVENT_CONN_UPDATE_REQ";
case BLE_GAP_EVENT_L2CAP_UPDATE_REQ: // 5
case BLE_GAP_EVENT_L2CAP_UPDATE_REQ: //5
return "BLE_GAP_EVENT_L2CAP_UPDATE_REQ"; return "BLE_GAP_EVENT_L2CAP_UPDATE_REQ";
case BLE_GAP_EVENT_TERM_FAILURE: // 6
case BLE_GAP_EVENT_TERM_FAILURE: //6
return "BLE_GAP_EVENT_TERM_FAILURE"; return "BLE_GAP_EVENT_TERM_FAILURE";
case BLE_GAP_EVENT_DISC: // 7
case BLE_GAP_EVENT_DISC: //7
return "BLE_GAP_EVENT_DISC"; return "BLE_GAP_EVENT_DISC";
case BLE_GAP_EVENT_DISC_COMPLETE: // 8
case BLE_GAP_EVENT_DISC_COMPLETE: //8
return "BLE_GAP_EVENT_DISC_COMPLETE"; return "BLE_GAP_EVENT_DISC_COMPLETE";
case BLE_GAP_EVENT_ADV_COMPLETE: // 9
case BLE_GAP_EVENT_ADV_COMPLETE: //9
return "BLE_GAP_EVENT_ADV_COMPLETE"; return "BLE_GAP_EVENT_ADV_COMPLETE";
case BLE_GAP_EVENT_ENC_CHANGE: // 10
case BLE_GAP_EVENT_ENC_CHANGE: //10
return "BLE_GAP_EVENT_ENC_CHANGE"; return "BLE_GAP_EVENT_ENC_CHANGE";
case BLE_GAP_EVENT_PASSKEY_ACTION: // 11
case BLE_GAP_EVENT_PASSKEY_ACTION : //11
return "BLE_GAP_EVENT_PASSKEY_ACTION"; return "BLE_GAP_EVENT_PASSKEY_ACTION";
case BLE_GAP_EVENT_NOTIFY_RX: // 12
case BLE_GAP_EVENT_NOTIFY_RX: //12
return "BLE_GAP_EVENT_NOTIFY_RX"; return "BLE_GAP_EVENT_NOTIFY_RX";
case BLE_GAP_EVENT_NOTIFY_TX: // 13
case BLE_GAP_EVENT_NOTIFY_TX : //13
return "BLE_GAP_EVENT_NOTIFY_TX"; return "BLE_GAP_EVENT_NOTIFY_TX";
case BLE_GAP_EVENT_SUBSCRIBE: // 14
case BLE_GAP_EVENT_SUBSCRIBE : //14
return "BLE_GAP_EVENT_SUBSCRIBE"; return "BLE_GAP_EVENT_SUBSCRIBE";
case BLE_GAP_EVENT_MTU: // 15
case BLE_GAP_EVENT_MTU: //15
return "BLE_GAP_EVENT_MTU"; return "BLE_GAP_EVENT_MTU";
case BLE_GAP_EVENT_IDENTITY_RESOLVED: // 16
case BLE_GAP_EVENT_IDENTITY_RESOLVED: //16
return "BLE_GAP_EVENT_IDENTITY_RESOLVED"; return "BLE_GAP_EVENT_IDENTITY_RESOLVED";
case BLE_GAP_EVENT_REPEAT_PAIRING: // 17
case BLE_GAP_EVENT_REPEAT_PAIRING: //17
return "BLE_GAP_EVENT_REPEAT_PAIRING"; return "BLE_GAP_EVENT_REPEAT_PAIRING";
case BLE_GAP_EVENT_PHY_UPDATE_COMPLETE: // 18
case BLE_GAP_EVENT_PHY_UPDATE_COMPLETE: //18
return "BLE_GAP_EVENT_PHY_UPDATE_COMPLETE"; return "BLE_GAP_EVENT_PHY_UPDATE_COMPLETE";
case BLE_GAP_EVENT_EXT_DISC: // 19
case BLE_GAP_EVENT_EXT_DISC: //19
return "BLE_GAP_EVENT_EXT_DISC"; return "BLE_GAP_EVENT_EXT_DISC";
#ifdef BLE_GAP_EVENT_PERIODIC_SYNC // IDF 4.0 does not support these # ifdef BLE_GAP_EVENT_PERIODIC_SYNC // IDF 4.0 does not support these
case BLE_GAP_EVENT_PERIODIC_SYNC: //20 case BLE_GAP_EVENT_PERIODIC_SYNC: // 20
return "BLE_GAP_EVENT_PERIODIC_SYNC"; return "BLE_GAP_EVENT_PERIODIC_SYNC";
case BLE_GAP_EVENT_PERIODIC_REPORT: // 21
case BLE_GAP_EVENT_PERIODIC_REPORT: //21
return "BLE_GAP_EVENT_PERIODIC_REPORT"; return "BLE_GAP_EVENT_PERIODIC_REPORT";
case BLE_GAP_EVENT_PERIODIC_SYNC_LOST: // 22
case BLE_GAP_EVENT_PERIODIC_SYNC_LOST: //22
return "BLE_GAP_EVENT_PERIODIC_SYNC_LOST"; return "BLE_GAP_EVENT_PERIODIC_SYNC_LOST";
case BLE_GAP_EVENT_SCAN_REQ_RCVD: // 23
case BLE_GAP_EVENT_SCAN_REQ_RCVD: //23
return "BLE_GAP_EVENT_SCAN_REQ_RCVD"; return "BLE_GAP_EVENT_SCAN_REQ_RCVD";
#endif case BLE_GAP_EVENT_PERIODIC_TRANSFER: // 24
return "BLE_GAP_EVENT_PERIODIC_TRANSFER";
case BLE_GAP_EVENT_PATHLOSS_THRESHOLD: // 25
return "BLE_GAP_EVENT_PATHLOSS_THRESHOLD";
case BLE_GAP_EVENT_TRANSMIT_POWER: // 26
return "BLE_GAP_EVENT_TRANSMIT_POWER";
case BLE_GAP_EVENT_SUBRATE_CHANGE: // 27
return "BLE_GAP_EVENT_SUBRATE_CHANGE";
case BLE_GAP_EVENT_VS_HCI: // 28
return "BLE_GAP_EVENT_VS_HCI";
case BLE_GAP_EVENT_REATTEMPT_COUNT: // 29
return "BLE_GAP_EVENT_REATTEMPT_COUNT";
case BLE_GAP_EVENT_AUTHORIZE: // 30
return "BLE_GAP_EVENT_AUTHORIZE";
case BLE_GAP_EVENT_TEST_UPDATE: // 31
return "BLE_GAP_EVENT_TEST_UPDATE";
case BLE_GAP_EVENT_DATA_LEN_CHG: // 32
return "BLE_GAP_EVENT_DATA_LEN_CHG";
case BLE_GAP_EVENT_LINK_ESTAB: // 33
return "BLE_GAP_EVENT_LINK_ESTAB";
# endif
default: default:
NIMBLE_LOGD(LOG_TAG, "gapEventToString: Unknown event type %d 0x%.2x", eventType, eventType); NIMBLE_LOGD(LOG_TAG, "Unknown event type %d 0x%.2x", eventType, eventType);
return "Unknown event type"; return "Unknown event type";
} }
#else // #if defined(CONFIG_NIMBLE_CPP_ENABLE_GAP_EVENT_CODE_TEXT) # else // #if defined(CONFIG_NIMBLE_CPP_ENABLE_GAP_EVENT_CODE_TEXT)
(void)eventType; (void)eventType;
return ""; return "";
#endif // #if defined(CONFIG_NIMBLE_CPP_ENABLE_GAP_EVENT_CODE_TEXT) # endif // #if defined(CONFIG_NIMBLE_CPP_ENABLE_GAP_EVENT_CODE_TEXT)
} // gapEventToString } // gapEventToString
/**
* @brief Create a hexadecimal string representation of the input data.
* @param [in] source The start of the binary data.
* @param [in] length The length of the data to convert.
* @return A string representation of the data.
*/
std::string NimBLEUtils::dataToHexString(const uint8_t* source, uint8_t length) {
std::string str{};
str.reserve(length * 2 + 1);
for (uint8_t i = 0; i < length; i++) {
char c = (source[i] >> 4) & 0x0f;
str.push_back(c > 9 ? c + 'A' - 10 : c + '0');
c = source[i] & 0x0f;
str.push_back(c > 9 ? c + 'A' - 10 : c + '0');
}
return str;
} // hexDataToString
/** /**
* @brief Generate a random BLE address. * @brief Generate a random BLE address.
* @param [in] nrpa True to generate a non-resolvable private address, * @param [in] nrpa True to generate a non-resolvable private address,
@ -504,4 +527,4 @@ NimBLEAddress NimBLEUtils::generateAddr(bool nrpa) {
return NimBLEAddress{addr}; return NimBLEAddress{addr};
} // generateAddr } // generateAddr
#endif //CONFIG_BT_ENABLED #endif // CONFIG_BT_ENABLED

View file

@ -6,40 +6,51 @@
* *
*/ */
#ifndef COMPONENTS_NIMBLEUTILS_H_ #ifndef NIMBLE_CPP_UTILS_H_
#define COMPONENTS_NIMBLEUTILS_H_ #define NIMBLE_CPP_UTILS_H_
#include "nimconfig.h" #include "nimconfig.h"
#if defined(CONFIG_BT_ENABLED) #if defined(CONFIG_BT_ENABLED)
# include <string>
# include <freertos/FreeRTOS.h>
# include <freertos/task.h>
#include <string>
class NimBLEAddress; class NimBLEAddress;
struct BleTaskData { /**
void* pATT; * @brief A structure to hold data for a task that is waiting for a response.
TaskHandle_t task; * @details This structure is used in conjunction with NimBLEUtils::taskWait() and NimBLEUtils::taskRelease().
int rc; * All items are optional, the m_pHandle will be set in taskWait().
void* buf; */
}; struct NimBLETaskData {
/**
* @brief Constructor.
* @param [in] pInstance An instance of the class that is waiting.
* @param [in] flags General purpose flags for the caller.
* @param [in] buf A buffer for data.
*/
NimBLETaskData(void* pInstance = nullptr, int flags = 0, void* buf = nullptr)
: m_pInstance(pInstance), m_flags(flags), m_pBuf(buf) {}
void* m_pInstance{nullptr};
mutable int m_flags{0};
void* m_pBuf{nullptr};
struct ble_gap_event; private:
mutable void* m_pHandle{nullptr}; // semaphore or task handle
friend class NimBLEUtils;
};
/** /**
* @brief A BLE Utility class with methods for debugging and general purpose use. * @brief A BLE Utility class with methods for debugging and general purpose use.
*/ */
class NimBLEUtils { class NimBLEUtils {
public: public:
static void dumpGapEvent(ble_gap_event *event, void *arg);
static const char* gapEventToString(uint8_t eventType); static const char* gapEventToString(uint8_t eventType);
static char* buildHexData(uint8_t* target, const uint8_t* source, uint8_t length); static std::string dataToHexString(const uint8_t* source, uint8_t length);
static const char* advTypeToString(uint8_t advType); static const char* advTypeToString(uint8_t advType);
static const char* returnCodeToString(int rc); static const char* returnCodeToString(int rc);
static NimBLEAddress generateAddr(bool nrpa); static NimBLEAddress generateAddr(bool nrpa);
static bool taskWait(const NimBLETaskData& taskData, uint32_t timeout);
static void taskRelease(const NimBLETaskData& taskData, int rc = 0);
}; };
#endif // CONFIG_BT_ENABLED #endif // CONFIG_BT_ENABLED
#endif // COMPONENTS_NIMBLEUTILS_H_ #endif // NIMBLE_CPP_UTILS_H_