Skip to content

Commit 78cce34

Browse files
committed
Refactor client connection establishment and client deletion.
* Removes the connection established flag as it should not be necessary. * Deleting the client instance from the `onDisconnect` callback is now supported. * `NimBLEDevice::deleteClient` no longer blocks tasks * Adds a new `Config` struct to `NimBLEClient` to efficiently set single bit config settings. * Adds `NimBLEClient::setSelfDelete` that takes the bool parameters `deleteOnDisconnect` and `deleteOnConnectFail` This will configure the client to delete itself when disconnected or the connection attempt fails. * Adds `NimBLEClient::setConfig` and `NimBLEClient::getConfig` which takes or returns a `NimBLEClient::Config` object respectively. * Reword `BLE_HS_EAPP` error string to be more accurate.
1 parent f425382 commit 78cce34

File tree

7 files changed

+146
-100
lines changed

7 files changed

+146
-100
lines changed

src/NimBLEClient.cpp

Lines changed: 88 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -63,10 +63,7 @@ NimBLEClient::NimBLEClient(const NimBLEAddress& peerAddress)
6363
m_pClientCallbacks{&defaultCallbacks},
6464
m_connHandle{BLE_HS_CONN_HANDLE_NONE},
6565
m_terminateFailCount{0},
66-
m_deleteCallbacks{false},
67-
m_connEstablished{false},
68-
m_asyncConnect{false},
69-
m_exchangeMTU{true},
66+
m_config{},
7067
# if CONFIG_BT_NIMBLE_EXT_ADV
7168
m_phyMask{BLE_GAP_LE_PHY_1M_MASK | BLE_GAP_LE_PHY_2M_MASK | BLE_GAP_LE_PHY_CODED_MASK},
7269
# endif
@@ -89,7 +86,7 @@ NimBLEClient::~NimBLEClient() {
8986
// Before we are finished with the client, we must release resources.
9087
deleteServices();
9188

92-
if (m_deleteCallbacks) {
89+
if (m_config.deleteCallbacks) {
9390
delete m_pClientCallbacks;
9491
}
9592
} // ~NimBLEClient
@@ -122,7 +119,7 @@ size_t NimBLEClient::deleteService(const NimBLEUUID& uuid) {
122119
}
123120

124121
return m_svcVec.size();
125-
} // deleteServices
122+
} // deleteService
126123

127124
/**
128125
* @brief Connect to the BLE Server using the address of the last connected device, or the address\n
@@ -174,7 +171,7 @@ bool NimBLEClient::connect(const NimBLEAddress& address, bool deleteAttributes,
174171
return false;
175172
}
176173

177-
if (isConnected() || m_connEstablished) {
174+
if (isConnected()) {
178175
NIMBLE_LOGE(LOG_TAG, "Client already connected");
179176
return false;
180177
}
@@ -201,9 +198,9 @@ bool NimBLEClient::connect(const NimBLEAddress& address, bool deleteAttributes,
201198
deleteServices();
202199
}
203200

204-
int rc = 0;
205-
m_asyncConnect = asyncConnect;
206-
m_exchangeMTU = exchangeMTU;
201+
int rc = 0;
202+
m_config.asyncConnect = asyncConnect;
203+
m_config.exchangeMTU = exchangeMTU;
207204

208205
// Set the connection in progress flag to prevent a scan from starting while connecting.
209206
NimBLEDevice::setConnectionInProgress(true);
@@ -265,7 +262,7 @@ bool NimBLEClient::connect(const NimBLEAddress& address, bool deleteAttributes,
265262
return false;
266263
}
267264

268-
if (m_asyncConnect) {
265+
if (m_config.asyncConnect) {
269266
return true;
270267
}
271268

@@ -289,19 +286,14 @@ bool NimBLEClient::connect(const NimBLEAddress& address, bool deleteAttributes,
289286
rc = taskData.m_flags;
290287
if (rc != 0) {
291288
NIMBLE_LOGE(LOG_TAG, "Connection failed; status=%d %s", rc, NimBLEUtils::returnCodeToString(rc));
292-
// If the failure was not a result of a disconnection, make sure we disconnect now to avoid dangling connections
293-
if (isConnected()) {
294-
disconnect();
295-
}
296289
m_lastErr = rc;
290+
if (m_config.deleteOnConnectFail) {
291+
NimBLEDevice::deleteClient(this);
292+
}
297293
return false;
298-
} else {
299-
NIMBLE_LOGI(LOG_TAG, "Connection established");
300294
}
301295

302-
m_connEstablished = true;
303296
m_pClientCallbacks->onConnect(this);
304-
305297
NIMBLE_LOGD(LOG_TAG, "<< connect()");
306298
// Check if still connected before returning
307299
return isConnected();
@@ -357,7 +349,7 @@ bool NimBLEClient::disconnect(uint8_t reason) {
357349
* @brief Cancel an ongoing connection attempt.
358350
* @return True if the command was successfully sent.
359351
*/
360-
bool NimBLEClient::cancelConnect() {
352+
bool NimBLEClient::cancelConnect() const {
361353
int rc = ble_gap_conn_cancel();
362354
if (rc != 0 && rc != BLE_HS_EALREADY) {
363355
NIMBLE_LOGE(LOG_TAG, "ble_gap_conn_cancel failed: rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc));
@@ -368,6 +360,32 @@ bool NimBLEClient::cancelConnect() {
368360
return true;
369361
} // cancelConnect
370362

363+
/**
364+
* @brief Set or unset a flag to delete this client when disconnected or connection failed.
365+
* @param [in] deleteOnDisconnect Set the client to self delete when disconnected.
366+
* @param [in] deleteOnConnectFail Set the client to self delete when a connection attempt fails.
367+
*/
368+
void NimBLEClient::setSelfDelete(bool deleteOnDisconnect, bool deleteOnConnectFail) {
369+
m_config.deleteOnDisconnect = deleteOnDisconnect;
370+
m_config.deleteOnConnectFail = deleteOnConnectFail;
371+
} // setSelfDelete
372+
373+
/**
374+
* @brief Get a copy of the clients configuration.
375+
* @return A copy of the clients configuration.
376+
*/
377+
NimBLEClient::Config NimBLEClient::getConfig() const {
378+
return m_config;
379+
} // getConfig
380+
381+
/**
382+
* @brief Set the client configuration options.
383+
* @param [in] config The config options instance to set the client configuration to.
384+
*/
385+
void NimBLEClient::setConfig(NimBLEClient::Config config) {
386+
m_config = config;
387+
} // setConfig
388+
371389
# if CONFIG_BT_NIMBLE_EXT_ADV
372390
/**
373391
* @brief Set the PHY types to use when connecting to a server.
@@ -492,9 +510,8 @@ uint16_t NimBLEClient::getConnHandle() const {
492510
* To disconnect from a peer, use disconnect().
493511
*/
494512
void NimBLEClient::clearConnection() {
495-
m_connHandle = BLE_HS_CONN_HANDLE_NONE;
496-
m_connEstablished = false;
497-
m_peerAddress = NimBLEAddress{};
513+
m_connHandle = BLE_HS_CONN_HANDLE_NONE;
514+
m_peerAddress = NimBLEAddress{};
498515
} // clearConnection
499516

500517
/**
@@ -509,15 +526,13 @@ void NimBLEClient::clearConnection() {
509526
* This enables the GATT Server to read the attributes of the client connected to it.
510527
*/
511528
bool NimBLEClient::setConnection(const NimBLEConnInfo& connInfo) {
512-
if (isConnected() || m_connEstablished) {
529+
if (isConnected()) {
513530
NIMBLE_LOGE(LOG_TAG, "Already connected");
514531
return false;
515532
}
516533

517-
m_peerAddress = connInfo.getAddress();
518-
m_connHandle = connInfo.getConnHandle();
519-
m_connEstablished = true;
520-
534+
m_peerAddress = connInfo.getAddress();
535+
m_connHandle = connInfo.getConnHandle();
521536
return true;
522537
} // setConnection
523538

@@ -763,6 +778,12 @@ int NimBLEClient::serviceDiscoveredCB(uint16_t connHandle,
763778
NimBLETaskData* pTaskData = (NimBLETaskData*)arg;
764779
NimBLEClient* pClient = (NimBLEClient*)pTaskData->m_pInstance;
765780

781+
if (error->status == BLE_HS_ENOTCONN) {
782+
NIMBLE_LOGE(LOG_TAG, "<< Service Discovered; Not connected");
783+
NimBLEUtils::taskRelease(*pTaskData, error->status);
784+
return error->status;
785+
}
786+
766787
// Make sure the service discovery is for this device
767788
if (pClient->getConnHandle() != connHandle) {
768789
return 0;
@@ -902,8 +923,9 @@ bool NimBLEClient::exchangeMTU() {
902923
* @param [in] arg A pointer to the client instance that registered for this callback.
903924
*/
904925
int NimBLEClient::handleGapEvent(struct ble_gap_event* event, void* arg) {
905-
NimBLEClient* pClient = (NimBLEClient*)arg;
906-
int rc = 0;
926+
NimBLEClient* pClient = (NimBLEClient*)arg;
927+
int rc = 0;
928+
NimBLETaskData* pTaskData = pClient->m_pTaskData; // save a copy in case client is deleted
907929

908930
NIMBLE_LOGD(LOG_TAG, "Got Client event %s", NimBLEUtils::gapEventToString(event->type));
909931

@@ -917,7 +939,7 @@ int NimBLEClient::handleGapEvent(struct ble_gap_event* event, void* arg) {
917939

918940
rc = event->disconnect.reason;
919941
// If Host reset tell the device now before returning to prevent
920-
// any errors caused by calling host functions before resyncing.
942+
// any errors caused by calling host functions before re-syncing.
921943
switch (rc) {
922944
case BLE_HS_ECONTROLLER:
923945
case BLE_HS_ETIMEOUT_HCI:
@@ -930,41 +952,46 @@ int NimBLEClient::handleGapEvent(struct ble_gap_event* event, void* arg) {
930952
break;
931953
}
932954

955+
NIMBLE_LOGD(LOG_TAG, "disconnect; reason=%d, %s", rc, NimBLEUtils::returnCodeToString(rc));
956+
933957
pClient->m_terminateFailCount = 0;
934958
NimBLEDevice::removeIgnored(pClient->m_peerAddress);
935-
pClient->m_connHandle = BLE_HS_CONN_HANDLE_NONE;
936959

937-
// If we received a connected event but did not get established
938-
// then a disconnect event will be sent but we should not send it to the
939-
// app for processing. Instead we will ensure the task is released
940-
// and report the error.
941-
if (!pClient->m_connEstablished) {
942-
break;
960+
// Don't call the disconnect callback if we are waiting for a connection to complete and it fails
961+
if (rc != (BLE_HS_ERR_HCI_BASE + BLE_ERR_CONN_ESTABLISHMENT) || pClient->m_config.asyncConnect) {
962+
pClient->m_pClientCallbacks->onDisconnect(pClient, rc);
943963
}
944964

945-
NIMBLE_LOGI(LOG_TAG, "disconnect; reason=%d, %s", rc, NimBLEUtils::returnCodeToString(rc));
965+
pClient->m_connHandle = BLE_HS_CONN_HANDLE_NONE;
966+
967+
if (pClient->m_config.deleteOnDisconnect) {
968+
// If we are set to self delete on disconnect but we have a task waiting on the connection
969+
// completion we will set the flag to delete on connect fail instead of deleting here
970+
// to prevent segmentation faults or double deleting
971+
if (pTaskData != nullptr && rc == (BLE_HS_ERR_HCI_BASE + BLE_ERR_CONN_ESTABLISHMENT)) {
972+
pClient->m_config.deleteOnConnectFail = true;
973+
break;
974+
}
975+
NimBLEDevice::deleteClient(pClient);
976+
}
946977

947-
pClient->m_connEstablished = false;
948-
pClient->m_pClientCallbacks->onDisconnect(pClient, rc);
949978
break;
950979
} // BLE_GAP_EVENT_DISCONNECT
951980

952981
case BLE_GAP_EVENT_CONNECT: {
953982
// If we aren't waiting for this connection response we should drop the connection immediately.
954-
if (pClient->isConnected() || (!pClient->m_asyncConnect && pClient->m_pTaskData == nullptr)) {
983+
if (pClient->isConnected() || (!pClient->m_config.asyncConnect && pClient->m_pTaskData == nullptr)) {
955984
ble_gap_terminate(event->connect.conn_handle, BLE_ERR_REM_USER_CONN_TERM);
956985
return 0;
957986
}
958987

959988
NimBLEDevice::setConnectionInProgress(false);
960989
rc = event->connect.status;
961990
if (rc == 0) {
962-
NIMBLE_LOGI(LOG_TAG, "Connected event");
963-
964991
pClient->m_connHandle = event->connect.conn_handle;
965-
if (pClient->m_exchangeMTU) {
966-
if (!pClient->exchangeMTU() && !pClient->m_asyncConnect) {
967-
rc = pClient->m_lastErr;
992+
if (pClient->m_config.exchangeMTU) {
993+
if (!pClient->exchangeMTU() && !pClient->m_config.asyncConnect) {
994+
rc = pClient->m_lastErr; // sets the error in the task data
968995
break;
969996
}
970997
}
@@ -974,15 +1001,19 @@ int NimBLEClient::handleGapEvent(struct ble_gap_event* event, void* arg) {
9741001
NimBLEDevice::addIgnored(pClient->m_peerAddress);
9751002
} else {
9761003
pClient->m_connHandle = BLE_HS_CONN_HANDLE_NONE;
977-
if (!pClient->m_asyncConnect) {
1004+
if (!pClient->m_config.asyncConnect) {
9781005
break;
9791006
}
1007+
1008+
if (pClient->m_config.deleteOnConnectFail) { // async connect
1009+
delete pClient;
1010+
return 0;
1011+
}
9801012
}
9811013

982-
if (pClient->m_asyncConnect) {
983-
pClient->m_connEstablished = rc == 0;
1014+
if (pClient->m_config.asyncConnect) {
9841015
pClient->m_pClientCallbacks->onConnect(pClient);
985-
} else if (!pClient->m_exchangeMTU) {
1016+
} else if (!pClient->m_config.exchangeMTU) {
9861017
break; // not waiting for MTU exchange so release the task now.
9871018
}
9881019

@@ -1005,13 +1036,6 @@ int NimBLEClient::handleGapEvent(struct ble_gap_event* event, void* arg) {
10051036

10061037
case BLE_GAP_EVENT_NOTIFY_RX: {
10071038
if (pClient->m_connHandle != event->notify_rx.conn_handle) return 0;
1008-
1009-
// If a notification comes before this flag is set we might
1010-
// access a vector while it is being cleared in connect()
1011-
if (!pClient->m_connEstablished) {
1012-
return 0;
1013-
}
1014-
10151039
NIMBLE_LOGD(LOG_TAG, "Notify Received for handle: %d", event->notify_rx.attr_handle);
10161040

10171041
for (const auto& svc : pClient->m_svcVec) {
@@ -1170,8 +1194,8 @@ int NimBLEClient::handleGapEvent(struct ble_gap_event* event, void* arg) {
11701194
}
11711195
} // Switch
11721196

1173-
if (pClient->m_pTaskData != nullptr) {
1174-
NimBLEUtils::taskRelease(*pClient->m_pTaskData, rc);
1197+
if (pTaskData != nullptr) {
1198+
NimBLEUtils::taskRelease(*pTaskData, rc);
11751199
}
11761200

11771201
return 0;
@@ -1192,11 +1216,11 @@ bool NimBLEClient::isConnected() const {
11921216
*/
11931217
void NimBLEClient::setClientCallbacks(NimBLEClientCallbacks* pClientCallbacks, bool deleteCallbacks) {
11941218
if (pClientCallbacks != nullptr) {
1195-
m_pClientCallbacks = pClientCallbacks;
1196-
m_deleteCallbacks = deleteCallbacks;
1219+
m_pClientCallbacks = pClientCallbacks;
1220+
m_config.deleteCallbacks = deleteCallbacks;
11971221
} else {
1198-
m_pClientCallbacks = &defaultCallbacks;
1199-
m_deleteCallbacks = false;
1222+
m_pClientCallbacks = &defaultCallbacks;
1223+
m_config.deleteCallbacks = false;
12001224
}
12011225
} // setClientCallbacks
12021226

src/NimBLEClient.h

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,8 @@ class NimBLEClient {
5151
bool connect(const NimBLEAddress& address, bool deleteAttributes = true, bool asyncConnect = false, bool exchangeMTU = true);
5252
bool connect(bool deleteAttributes = true, bool asyncConnect = false, bool exchangeMTU = true);
5353
bool disconnect(uint8_t reason = BLE_ERR_REM_USER_CONN_TERM);
54-
bool cancelConnect();
54+
bool cancelConnect() const;
55+
void setSelfDelete(bool deleteOnDisconnect, bool deleteOnConnectFail);
5556
NimBLEAddress getPeerAddress() const;
5657
bool setPeerAddress(const NimBLEAddress& address);
5758
int getRssi() const;
@@ -95,6 +96,17 @@ class NimBLEClient {
9596
void setConnectPhy(uint8_t mask);
9697
# endif
9798

99+
struct Config {
100+
uint8_t deleteCallbacks : 1; // Delete the callback object when the client is deleted.
101+
uint8_t deleteOnDisconnect : 1; // Delete the client when disconnected.
102+
uint8_t deleteOnConnectFail : 1; // Delete the client when a connection attempt fails.
103+
uint8_t asyncConnect : 1; // Connect asynchronously.
104+
uint8_t exchangeMTU : 1; // Exchange MTU after connection.
105+
};
106+
107+
Config getConfig() const;
108+
void setConfig(Config config);
109+
98110
private:
99111
NimBLEClient(const NimBLEAddress& peerAddress);
100112
~NimBLEClient();
@@ -117,10 +129,8 @@ class NimBLEClient {
117129
NimBLEClientCallbacks* m_pClientCallbacks;
118130
uint16_t m_connHandle;
119131
uint8_t m_terminateFailCount;
120-
bool m_deleteCallbacks;
121-
bool m_connEstablished;
122-
bool m_asyncConnect;
123-
bool m_exchangeMTU;
132+
Config m_config;
133+
124134
# if CONFIG_BT_NIMBLE_EXT_ADV
125135
uint8_t m_phyMask;
126136
# endif

0 commit comments

Comments
 (0)