@@ -63,10 +63,8 @@ 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_asyncSecureAttempt{0 },
67+ m_config{},
7068# if CONFIG_BT_NIMBLE_EXT_ADV
7169 m_phyMask{BLE_GAP_LE_PHY_1M_MASK | BLE_GAP_LE_PHY_2M_MASK | BLE_GAP_LE_PHY_CODED_MASK},
7270# endif
@@ -89,7 +87,7 @@ NimBLEClient::~NimBLEClient() {
8987 // Before we are finished with the client, we must release resources.
9088 deleteServices ();
9189
92- if (m_deleteCallbacks ) {
90+ if (m_config. deleteCallbacks ) {
9391 delete m_pClientCallbacks;
9492 }
9593} // ~NimBLEClient
@@ -122,7 +120,7 @@ size_t NimBLEClient::deleteService(const NimBLEUUID& uuid) {
122120 }
123121
124122 return m_svcVec.size ();
125- } // deleteServices
123+ } // deleteService
126124
127125/* *
128126 * @brief Connect to the BLE Server using the address of the last connected device, or the address\n
@@ -174,16 +172,11 @@ bool NimBLEClient::connect(const NimBLEAddress& address, bool deleteAttributes,
174172 return false ;
175173 }
176174
177- if (isConnected () || m_connEstablished ) {
175+ if (isConnected ()) {
178176 NIMBLE_LOGE (LOG_TAG, " Client already connected" );
179177 return false ;
180178 }
181179
182- if (NimBLEDevice::isConnectionInProgress ()) {
183- NIMBLE_LOGE (LOG_TAG, " Connection already in progress" );
184- return false ;
185- }
186-
187180 const ble_addr_t * peerAddr = address.getBase ();
188181 if (ble_gap_conn_find_by_addr (peerAddr, NULL ) == 0 ) {
189182 NIMBLE_LOGE (LOG_TAG, " A connection to %s already exists" , address.toString ().c_str ());
@@ -201,12 +194,9 @@ bool NimBLEClient::connect(const NimBLEAddress& address, bool deleteAttributes,
201194 deleteServices ();
202195 }
203196
204- int rc = 0 ;
205- m_asyncConnect = asyncConnect;
206- m_exchangeMTU = exchangeMTU;
207-
208- // Set the connection in progress flag to prevent a scan from starting while connecting.
209- NimBLEDevice::setConnectionInProgress (true );
197+ int rc = 0 ;
198+ m_config.asyncConnect = asyncConnect;
199+ m_config.exchangeMTU = exchangeMTU;
210200
211201 do {
212202# if CONFIG_BT_NIMBLE_EXT_ADV
@@ -261,11 +251,10 @@ bool NimBLEClient::connect(const NimBLEAddress& address, bool deleteAttributes,
261251
262252 if (rc != 0 ) {
263253 m_lastErr = rc;
264- NimBLEDevice::setConnectionInProgress (false );
265254 return false ;
266255 }
267256
268- if (m_asyncConnect ) {
257+ if (m_config. asyncConnect ) {
269258 return true ;
270259 }
271260
@@ -289,19 +278,14 @@ bool NimBLEClient::connect(const NimBLEAddress& address, bool deleteAttributes,
289278 rc = taskData.m_flags ;
290279 if (rc != 0 ) {
291280 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- }
296281 m_lastErr = rc;
282+ if (m_config.deleteOnConnectFail ) {
283+ NimBLEDevice::deleteClient (this );
284+ }
297285 return false ;
298- } else {
299- NIMBLE_LOGI (LOG_TAG, " Connection established" );
300286 }
301287
302- m_connEstablished = true ;
303288 m_pClientCallbacks->onConnect (this );
304-
305289 NIMBLE_LOGD (LOG_TAG, " << connect()" );
306290 // Check if still connected before returning
307291 return isConnected ();
@@ -313,9 +297,21 @@ bool NimBLEClient::connect(const NimBLEAddress& address, bool deleteAttributes,
313297 * @return True on success.
314298 * @details This is a blocking function and should not be used in a callback.
315299 */
316- bool NimBLEClient::secureConnection () const {
300+ bool NimBLEClient::secureConnection (bool async ) const {
317301 NIMBLE_LOGD (LOG_TAG, " >> secureConnection()" );
318302
303+ int rc = 0 ;
304+ if (async && !NimBLEDevice::startSecurity (m_connHandle, &rc)) {
305+ m_lastErr = rc;
306+ m_asyncSecureAttempt = 0 ;
307+ return false ;
308+ }
309+
310+ if (async) {
311+ m_asyncSecureAttempt++;
312+ return true ;
313+ }
314+
319315 NimBLETaskData taskData (const_cast <NimBLEClient*>(this ), BLE_HS_ENOTCONN);
320316 m_pTaskData = &taskData;
321317 int retryCount = 1 ;
@@ -357,7 +353,7 @@ bool NimBLEClient::disconnect(uint8_t reason) {
357353 * @brief Cancel an ongoing connection attempt.
358354 * @return True if the command was successfully sent.
359355 */
360- bool NimBLEClient::cancelConnect () {
356+ bool NimBLEClient::cancelConnect () const {
361357 int rc = ble_gap_conn_cancel ();
362358 if (rc != 0 && rc != BLE_HS_EALREADY) {
363359 NIMBLE_LOGE (LOG_TAG, " ble_gap_conn_cancel failed: rc=%d %s" , rc, NimBLEUtils::returnCodeToString (rc));
@@ -368,6 +364,32 @@ bool NimBLEClient::cancelConnect() {
368364 return true ;
369365} // cancelConnect
370366
367+ /* *
368+ * @brief Set or unset a flag to delete this client when disconnected or connection failed.
369+ * @param [in] deleteOnDisconnect Set the client to self delete when disconnected.
370+ * @param [in] deleteOnConnectFail Set the client to self delete when a connection attempt fails.
371+ */
372+ void NimBLEClient::setSelfDelete (bool deleteOnDisconnect, bool deleteOnConnectFail) {
373+ m_config.deleteOnDisconnect = deleteOnDisconnect;
374+ m_config.deleteOnConnectFail = deleteOnConnectFail;
375+ } // setSelfDelete
376+
377+ /* *
378+ * @brief Get a copy of the clients configuration.
379+ * @return A copy of the clients configuration.
380+ */
381+ NimBLEClient::Config NimBLEClient::getConfig () const {
382+ return m_config;
383+ } // getConfig
384+
385+ /* *
386+ * @brief Set the client configuration options.
387+ * @param [in] config The config options instance to set the client configuration to.
388+ */
389+ void NimBLEClient::setConfig (NimBLEClient::Config config) {
390+ m_config = config;
391+ } // setConfig
392+
371393# if CONFIG_BT_NIMBLE_EXT_ADV
372394/* *
373395 * @brief Set the PHY types to use when connecting to a server.
@@ -492,9 +514,8 @@ uint16_t NimBLEClient::getConnHandle() const {
492514 * To disconnect from a peer, use disconnect().
493515 */
494516void NimBLEClient::clearConnection () {
495- m_connHandle = BLE_HS_CONN_HANDLE_NONE;
496- m_connEstablished = false ;
497- m_peerAddress = NimBLEAddress{};
517+ m_connHandle = BLE_HS_CONN_HANDLE_NONE;
518+ m_peerAddress = NimBLEAddress{};
498519} // clearConnection
499520
500521/* *
@@ -509,15 +530,13 @@ void NimBLEClient::clearConnection() {
509530 * This enables the GATT Server to read the attributes of the client connected to it.
510531 */
511532bool NimBLEClient::setConnection (const NimBLEConnInfo& connInfo) {
512- if (isConnected () || m_connEstablished ) {
533+ if (isConnected ()) {
513534 NIMBLE_LOGE (LOG_TAG, " Already connected" );
514535 return false ;
515536 }
516537
517- m_peerAddress = connInfo.getAddress ();
518- m_connHandle = connInfo.getConnHandle ();
519- m_connEstablished = true ;
520-
538+ m_peerAddress = connInfo.getAddress ();
539+ m_connHandle = connInfo.getConnHandle ();
521540 return true ;
522541} // setConnection
523542
@@ -763,6 +782,12 @@ int NimBLEClient::serviceDiscoveredCB(uint16_t connHandle,
763782 NimBLETaskData* pTaskData = (NimBLETaskData*)arg;
764783 NimBLEClient* pClient = (NimBLEClient*)pTaskData->m_pInstance ;
765784
785+ if (error->status == BLE_HS_ENOTCONN) {
786+ NIMBLE_LOGE (LOG_TAG, " << Service Discovered; Not connected" );
787+ NimBLEUtils::taskRelease (*pTaskData, error->status );
788+ return error->status ;
789+ }
790+
766791 // Make sure the service discovery is for this device
767792 if (pClient->getConnHandle () != connHandle) {
768793 return 0 ;
@@ -902,16 +927,23 @@ bool NimBLEClient::exchangeMTU() {
902927 * @param [in] arg A pointer to the client instance that registered for this callback.
903928 */
904929int NimBLEClient::handleGapEvent (struct ble_gap_event * event, void * arg) {
905- NimBLEClient* pClient = (NimBLEClient*)arg;
906- int rc = 0 ;
930+ NimBLEClient* pClient = (NimBLEClient*)arg;
931+ int rc = 0 ;
932+ NimBLETaskData* pTaskData = pClient->m_pTaskData ; // save a copy in case client is deleted
907933
908934 NIMBLE_LOGD (LOG_TAG, " Got Client event %s" , NimBLEUtils::gapEventToString (event->type ));
909935
910936 switch (event->type ) {
911937 case BLE_GAP_EVENT_DISCONNECT: {
938+ // workaround for bug in NimBLE stack where disconnect event argument is not passed correctly
939+ pClient = NimBLEDevice::getClientByHandle (event->disconnect .conn .conn_handle );
940+ if (pClient == nullptr ) {
941+ return 0 ;
942+ }
943+
912944 rc = event->disconnect .reason ;
913945 // If Host reset tell the device now before returning to prevent
914- // any errors caused by calling host functions before resyncing .
946+ // any errors caused by calling host functions before re-syncing .
915947 switch (rc) {
916948 case BLE_HS_ECONTROLLER:
917949 case BLE_HS_ETIMEOUT_HCI:
@@ -921,49 +953,49 @@ int NimBLEClient::handleGapEvent(struct ble_gap_event* event, void* arg) {
921953 NimBLEDevice::onReset (rc);
922954 break ;
923955 default :
924- // Check that the event is for this client.
925- if (pClient->m_connHandle != event->disconnect .conn .conn_handle ) {
926- return 0 ;
927- }
928-
929956 break ;
930957 }
931958
959+ NIMBLE_LOGD (LOG_TAG, " disconnect; reason=%d, %s" , rc, NimBLEUtils::returnCodeToString (rc));
960+
932961 pClient->m_terminateFailCount = 0 ;
962+ pClient->m_asyncSecureAttempt = 0 ;
933963 NimBLEDevice::removeIgnored (pClient->m_peerAddress );
934- pClient->m_connHandle = BLE_HS_CONN_HANDLE_NONE;
935964
936- // If we received a connected event but did not get established
937- // then a disconnect event will be sent but we should not send it to the
938- // app for processing. Instead we will ensure the task is released
939- // and report the error.
940- if (!pClient->m_connEstablished ) {
941- break ;
965+ // Don't call the disconnect callback if we are waiting for a connection to complete and it fails
966+ if (rc != (BLE_HS_ERR_HCI_BASE + BLE_ERR_CONN_ESTABLISHMENT) || pClient->m_config .asyncConnect ) {
967+ pClient->m_pClientCallbacks ->onDisconnect (pClient, rc);
942968 }
943969
944- NIMBLE_LOGI (LOG_TAG, " disconnect; reason=%d, %s" , rc, NimBLEUtils::returnCodeToString (rc));
970+ pClient->m_connHandle = BLE_HS_CONN_HANDLE_NONE;
971+
972+ if (pClient->m_config .deleteOnDisconnect ) {
973+ // If we are set to self delete on disconnect but we have a task waiting on the connection
974+ // completion we will set the flag to delete on connect fail instead of deleting here
975+ // to prevent segmentation faults or double deleting
976+ if (pTaskData != nullptr && rc == (BLE_HS_ERR_HCI_BASE + BLE_ERR_CONN_ESTABLISHMENT)) {
977+ pClient->m_config .deleteOnConnectFail = true ;
978+ break ;
979+ }
980+ NimBLEDevice::deleteClient (pClient);
981+ }
945982
946- pClient->m_connEstablished = false ;
947- pClient->m_pClientCallbacks ->onDisconnect (pClient, rc);
948983 break ;
949984 } // BLE_GAP_EVENT_DISCONNECT
950985
951986 case BLE_GAP_EVENT_CONNECT: {
952987 // If we aren't waiting for this connection response we should drop the connection immediately.
953- if (pClient->isConnected () || (!pClient->m_asyncConnect && pClient->m_pTaskData == nullptr )) {
988+ if (pClient->isConnected () || (!pClient->m_config . asyncConnect && pClient->m_pTaskData == nullptr )) {
954989 ble_gap_terminate (event->connect .conn_handle , BLE_ERR_REM_USER_CONN_TERM);
955990 return 0 ;
956991 }
957992
958- NimBLEDevice::setConnectionInProgress (false );
959993 rc = event->connect .status ;
960994 if (rc == 0 ) {
961- NIMBLE_LOGI (LOG_TAG, " Connected event" );
962-
963995 pClient->m_connHandle = event->connect .conn_handle ;
964- if (pClient->m_exchangeMTU ) {
965- if (!pClient->exchangeMTU () && !pClient->m_asyncConnect ) {
966- rc = pClient->m_lastErr ;
996+ if (pClient->m_config . exchangeMTU ) {
997+ if (!pClient->exchangeMTU () && !pClient->m_config . asyncConnect ) {
998+ rc = pClient->m_lastErr ; // sets the error in the task data
967999 break ;
9681000 }
9691001 }
@@ -973,15 +1005,19 @@ int NimBLEClient::handleGapEvent(struct ble_gap_event* event, void* arg) {
9731005 NimBLEDevice::addIgnored (pClient->m_peerAddress );
9741006 } else {
9751007 pClient->m_connHandle = BLE_HS_CONN_HANDLE_NONE;
976- if (!pClient->m_asyncConnect ) {
1008+ if (!pClient->m_config . asyncConnect ) {
9771009 break ;
9781010 }
1011+
1012+ if (pClient->m_config .deleteOnConnectFail ) { // async connect
1013+ delete pClient;
1014+ return 0 ;
1015+ }
9791016 }
9801017
981- if (pClient->m_asyncConnect ) {
982- pClient->m_connEstablished = rc == 0 ;
1018+ if (pClient->m_config .asyncConnect ) {
9831019 pClient->m_pClientCallbacks ->onConnect (pClient);
984- } else if (!pClient->m_exchangeMTU ) {
1020+ } else if (!pClient->m_config . exchangeMTU ) {
9851021 break ; // not waiting for MTU exchange so release the task now.
9861022 }
9871023
@@ -1004,13 +1040,6 @@ int NimBLEClient::handleGapEvent(struct ble_gap_event* event, void* arg) {
10041040
10051041 case BLE_GAP_EVENT_NOTIFY_RX: {
10061042 if (pClient->m_connHandle != event->notify_rx .conn_handle ) return 0 ;
1007-
1008- // If a notification comes before this flag is set we might
1009- // access a vector while it is being cleared in connect()
1010- if (!pClient->m_connEstablished ) {
1011- return 0 ;
1012- }
1013-
10141043 NIMBLE_LOGD (LOG_TAG, " Notify Received for handle: %d" , event->notify_rx .attr_handle );
10151044
10161045 for (const auto & svc : pClient->m_svcVec ) {
@@ -1099,7 +1128,12 @@ int NimBLEClient::handleGapEvent(struct ble_gap_event* event, void* arg) {
10991128 if (event->enc_change .status == (BLE_HS_ERR_HCI_BASE + BLE_ERR_PINKEY_MISSING)) {
11001129 // Key is missing, try deleting.
11011130 ble_store_util_delete_peer (&peerInfo.m_desc .peer_id_addr );
1131+ // Attempt a retry if async secure failed.
1132+ if (pClient->m_asyncSecureAttempt == 1 ) {
1133+ pClient->secureConnection (true );
1134+ }
11021135 } else {
1136+ pClient->m_asyncSecureAttempt = 0 ;
11031137 pClient->m_pClientCallbacks ->onAuthenticationComplete (peerInfo);
11041138 }
11051139 }
@@ -1169,8 +1203,8 @@ int NimBLEClient::handleGapEvent(struct ble_gap_event* event, void* arg) {
11691203 }
11701204 } // Switch
11711205
1172- if (pClient-> m_pTaskData != nullptr ) {
1173- NimBLEUtils::taskRelease (*pClient-> m_pTaskData , rc);
1206+ if (pTaskData != nullptr ) {
1207+ NimBLEUtils::taskRelease (*pTaskData , rc);
11741208 }
11751209
11761210 return 0 ;
@@ -1191,11 +1225,11 @@ bool NimBLEClient::isConnected() const {
11911225 */
11921226void NimBLEClient::setClientCallbacks (NimBLEClientCallbacks* pClientCallbacks, bool deleteCallbacks) {
11931227 if (pClientCallbacks != nullptr ) {
1194- m_pClientCallbacks = pClientCallbacks;
1195- m_deleteCallbacks = deleteCallbacks;
1228+ m_pClientCallbacks = pClientCallbacks;
1229+ m_config. deleteCallbacks = deleteCallbacks;
11961230 } else {
1197- m_pClientCallbacks = &defaultCallbacks;
1198- m_deleteCallbacks = false ;
1231+ m_pClientCallbacks = &defaultCallbacks;
1232+ m_config. deleteCallbacks = false ;
11991233 }
12001234} // setClientCallbacks
12011235
0 commit comments