2020
2121# include " NimBLEDevice.h"
2222# include " NimBLELog.h"
23+ # if defined(CONFIG_NIMBLE_CPP_IDF)
24+ # include " nimble/nimble_port.h"
25+ # else
26+ # include " nimble/porting/nimble/include/nimble/nimble_port.h"
27+ # endif
2328
2429# include < string>
2530# include < climits>
2631
32+ # define SR_TIMEOUT MYNEWT_VAL (NIMBLE_CPP_SCAN_RSP_TIMEOUT )
33+
2734static const char* LOG_TAG = "NimBLEScan";
2835static NimBLEScanCallbacks defaultScanCallbacks;
2936
37+ # if SR_TIMEOUT
38+ static ble_npl_event dummySrTimerEvent;
39+ static ble_npl_time_t srTimeoutTicks;
40+
41+ # if MYNEWT_VAL(BLE_EXT_ADV)
42+ struct ble_gap_ext_disc_desc dummyDesc{.props = BLE_HCI_ADV_LEGACY_MASK ,
43+ .legacy_event_type = BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP ,
44+ .addr{},
45+ .rssi = 127 ,
46+ .data = nullptr ,
47+ .direct_addr{},
48+ .sid = 0 };
49+
50+ extern " C" void ble_gap_rx_ext_adv_report (struct ble_gap_ext_disc_desc * desc);
51+ # else
52+ static ble_gap_disc_desc dummyDesc{
53+ .event_type = BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP , .length_data = 0 , .addr {}, .rssi = 127 , .data = nullptr , .direct_addr {}};
54+ extern " C" void ble_gap_rx_adv_report (ble_gap_disc_desc* desc);
55+ # endif
56+
57+ /* *
58+ * @brief Sends dummy (null) scan response data to the scan event handler in order to
59+ * provide the scan result to the callbacks when a device hasn't responded to the
60+ * scan request in time. This is called by the host task from the default event queue.
61+ */
62+ static void sendDummyScanResponse (ble_npl_event* ev) {
63+ (void )ev;
64+ # if MYNEWT_VAL(BLE_EXT_ADV)
65+ ble_gap_rx_ext_adv_report (&dummyDesc);
66+ # else
67+ ble_gap_rx_adv_report (&dummyDesc);
68+ # endif
69+ }
70+
71+ /* *
72+ * @brief This will schedule an event to run in the host task that will call sendDummyScanResponse
73+ * which will send a null data scan response to the scan event handler if the device
74+ * hasn't responded to a scan response request within the timeout period.
75+ */
76+ void NimBLEScan::srTimerCb (ble_npl_event* event) {
77+ NimBLEScan* pScan = static_cast <NimBLEScan*>(ble_npl_event_get_arg (event));
78+ NimBLEAdvertisedDevice* curDev = nullptr ;
79+ NimBLEAdvertisedDevice* nextDev = nullptr ;
80+ ble_npl_time_t now = ble_npl_time_get ();
81+
82+ for (auto & dev : pScan->m_scanResults .m_deviceVec ) {
83+ if (dev->m_callbackSent < 2 && dev->isScannable ()) {
84+ if (!curDev || (now - dev->m_time > now - curDev->m_time )) {
85+ nextDev = curDev;
86+ curDev = dev;
87+ continue ;
88+ }
89+
90+ if (!nextDev || now - dev->m_time > now - nextDev->m_time ) {
91+ nextDev = dev;
92+ }
93+ }
94+ }
95+
96+ // Add the event to the host queue
97+ if (curDev) {
98+ memcpy (&dummyDesc.addr , curDev->getAddress ().getBase (), sizeof (dummyDesc.addr ));
99+ NIMBLE_LOGI (LOG_TAG , " Scan response timeout for: %s" , curDev->getAddress ().toString ().c_str ());
100+ ble_npl_eventq_put (nimble_port_get_dflt_eventq (), &dummySrTimerEvent);
101+ }
102+
103+ // Restart the timer for the next device that we are expecting a scan response from
104+ if (nextDev) {
105+ auto nextTime = now - nextDev->m_time ;
106+ if (nextTime >= srTimeoutTicks) {
107+ nextTime = 1 ;
108+ } else {
109+ nextTime = srTimeoutTicks - nextTime;
110+ }
111+
112+ ble_npl_callout_reset (&pScan->m_srTimer , nextTime);
113+ }
114+ }
115+ # endif // SR_TIMEOUT
116+
30117/* *
31118 * @brief Scan constructor.
32119 */
@@ -35,7 +122,13 @@ NimBLEScan::NimBLEScan()
35122 // default interval + window, no whitelist scan filter,not limited scan, no scan response, filter_duplicates
36123 m_scanParams{0 , 0 , BLE_HCI_SCAN_FILT_NO_WL , 0 , 1 , 1 },
37124 m_pTaskData{nullptr },
38- m_maxResults{0xFF } {}
125+ m_maxResults{0xFF } {
126+ # if SR_TIMEOUT
127+ ble_npl_callout_init (&m_srTimer, nimble_port_get_dflt_eventq (), NimBLEScan::srTimerCb, this );
128+ ble_npl_event_init (&dummySrTimerEvent, sendDummyScanResponse, NULL );
129+ ble_npl_time_ms_to_ticks (SR_TIMEOUT , &srTimeoutTicks);
130+ # endif
131+ } // NimBLEScan::NimBLEScan
39132
40133/* *
41134 * @brief Scan destructor, release any allocated resources.
@@ -44,6 +137,10 @@ NimBLEScan::~NimBLEScan() {
44137 for (const auto & dev : m_scanResults.m_deviceVec ) {
45138 delete dev;
46139 }
140+ # if SR_TIMEOUT
141+ ble_npl_callout_deinit (&m_srTimer);
142+ ble_npl_event_deinit (&dummySrTimerEvent);
143+ # endif
47144}
48145
49146/* *
@@ -114,6 +211,9 @@ int NimBLEScan::handleGapEvent(ble_gap_event* event, void* arg) {
114211
115212 advertisedDevice = new NimBLEAdvertisedDevice (event, event_type);
116213 pScan->m_scanResults .m_deviceVec .push_back (advertisedDevice);
214+ # if SR_TIMEOUT
215+ advertisedDevice->m_time = ble_npl_time_get ();
216+ # endif
117217 NIMBLE_LOGI (LOG_TAG , " New advertiser: %s" , advertisedAddress.toString ().c_str ());
118218 } else {
119219 advertisedDevice->update (event, event_type);
@@ -122,6 +222,11 @@ int NimBLEScan::handleGapEvent(ble_gap_event* event, void* arg) {
122222 NIMBLE_LOGI (LOG_TAG , " Scan response from: %s" , advertisedAddress.toString ().c_str ());
123223 } else {
124224 NIMBLE_LOGI (LOG_TAG , " Duplicate; updated: %s" , advertisedAddress.toString ().c_str ());
225+ # if SR_TIMEOUT
226+ // Restart scan-response timeout when we see a new non-scan-response
227+ // legacy advertisement during active scanning for a scannable device.
228+ advertisedDevice->m_time = ble_npl_time_get ();
229+ # endif
125230 }
126231 }
127232 }
@@ -147,6 +252,11 @@ int NimBLEScan::handleGapEvent(ble_gap_event* event, void* arg) {
147252 advertisedDevice->m_callbackSent ++;
148253 // got the scan response report the full data.
149254 pScan->m_pScanCallbacks ->onResult (advertisedDevice);
255+ # if SR_TIMEOUT
256+ } else if (isLegacyAdv && advertisedDevice->isScannable () &&!ble_npl_callout_is_active (&pScan->m_srTimer )) {
257+ // Start the timer to wait for the scan response.
258+ ble_npl_callout_reset (&pScan->m_srTimer , srTimeoutTicks);
259+ # endif
150260 }
151261
152262 // If not storing results and we have invoked the callback, delete the device.
@@ -158,14 +268,16 @@ int NimBLEScan::handleGapEvent(ble_gap_event* event, void* arg) {
158268 }
159269
160270 case BLE_GAP_EVENT_DISC_COMPLETE : {
271+ # if SR_TIMEOUT
272+ ble_npl_callout_stop (&pScan->m_srTimer );
273+ # endif
161274 NIMBLE_LOGD (LOG_TAG , " discovery complete; reason=%d" , event->disc_complete .reason );
162275
276+ pScan->m_pScanCallbacks ->onScanEnd (pScan->m_scanResults , event->disc_complete .reason );
163277 if (pScan->m_maxResults == 0 ) {
164278 pScan->clearResults ();
165279 }
166280
167- pScan->m_pScanCallbacks ->onScanEnd (pScan->m_scanResults , event->disc_complete .reason );
168-
169281 if (pScan->m_pTaskData != nullptr ) {
170282 NimBLEUtils::taskRelease (*pScan->m_pTaskData , event->disc_complete .reason );
171283 }
@@ -394,6 +506,10 @@ bool NimBLEScan::stop() {
394506 return false ;
395507 }
396508
509+ # if SR_TIMEOUT
510+ ble_npl_callout_stop (&m_srTimer);
511+ # endif
512+
397513 if (m_maxResults == 0 ) {
398514 clearResults ();
399515 }
0 commit comments