2222# include " NimBLEDevice.h"
2323# include " NimBLELog.h"
2424
25+ # if defined(CONFIG_NIMBLE_CPP_IDF)
26+ # include " nimble/nimble_port.h"
27+ # else
28+ # include " nimble/porting/nimble/include/nimble/nimble_port.h"
29+ # endif
30+
2531# include < string>
2632# include < climits>
2733
34+ # define SR_TIMEOUT CONFIG_NIMBLE_CPP_SCAN_RSP_TIMEOUT
35+
2836static const char * LOG_TAG = " NimBLEScan" ;
2937static NimBLEScanCallbacks defaultScanCallbacks;
3038
39+ # if SR_TIMEOUT
40+ static ble_npl_event dummySrTimerEvent;
41+ static ble_gap_disc_desc dummyDesc{
42+ .event_type = BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP, .length_data = 0 , .addr {}, .rssi = 127 , .data = nullptr , .direct_addr {}};
43+
44+ extern " C" void ble_gap_rx_adv_report (ble_gap_disc_desc* desc);
45+
46+ /* *
47+ * @brief Sends dummy (null) scan response data to the scan event handler in order to
48+ * provide the scan result to the callbacks when a device hasn't responded to the
49+ * scan request in time. This is called by the host task from the default event queue.
50+ */
51+ static void sendDummyScanResponse (ble_npl_event* ev) {
52+ (void )ev;
53+ ble_gap_rx_adv_report (&dummyDesc);
54+ }
55+
56+ /* *
57+ * @brief This will schedule an event to run in the host task that will call sendDummyScanResponse
58+ * which will send a null data scan response to the scan event handler if the device
59+ * hasn't responded to a scan response request within the timeout period.
60+ */
61+ void NimBLEScan::srTimerCb (ble_npl_event* event) {
62+ NimBLEScan* pScan = (NimBLEScan*)ble_npl_event_get_arg (event);
63+ NimBLEAdvertisedDevice* curDev = nullptr ;
64+ NimBLEAdvertisedDevice* nextDev = nullptr ;
65+ ble_npl_time_t now = ble_npl_time_get ();
66+
67+ for (auto & dev : pScan->m_scanResults .m_deviceVec ) {
68+ if (dev->m_callbackSent < 2 && dev->isScannable ()) {
69+ if (!curDev || (now - dev->m_time > now - curDev->m_time )) {
70+ nextDev = curDev;
71+ curDev = dev;
72+ continue ;
73+ }
74+
75+ if (!nextDev || now - dev->m_time > now - nextDev->m_time ) {
76+ nextDev = dev;
77+ }
78+ }
79+ }
80+
81+ // Add the event to the host queue
82+ if (curDev) {
83+ memcpy (&dummyDesc.addr , curDev->getAddress ().getBase (), sizeof (dummyDesc.addr ));
84+ NIMBLE_LOGI (LOG_TAG, " Scan response timeout for: %s" , curDev->getAddress ().toString ().c_str ());
85+ ble_npl_eventq_put (nimble_port_get_dflt_eventq (), &dummySrTimerEvent);
86+ }
87+
88+ // Restart the timer for the next device that we are expecting a scan response from
89+ if (nextDev) {
90+ auto nextTime = now - nextDev->m_time ;
91+ if (nextTime >= SR_TIMEOUT) {
92+ nextTime = 1 ;
93+ } else {
94+ nextTime = SR_TIMEOUT - nextTime;
95+ }
96+
97+ ble_npl_time_t ticks;
98+ ble_npl_time_ms_to_ticks (nextTime, &ticks);
99+ ble_npl_callout_reset (&pScan->m_srTimer , ticks);
100+ }
101+ }
102+ # endif
103+
31104/* *
32105 * @brief Scan constructor.
33106 */
@@ -36,13 +109,22 @@ NimBLEScan::NimBLEScan()
36109 // default interval + window, no whitelist scan filter,not limited scan, no scan response, filter_duplicates
37110 m_scanParams{0 , 0 , BLE_HCI_SCAN_FILT_NO_WL, 0 , 1 , 1 },
38111 m_pTaskData{nullptr },
39- m_maxResults{0xFF } {}
112+ m_maxResults{0xFF } {
113+ # if SR_TIMEOUT
114+ ble_npl_callout_init (&m_srTimer, nimble_port_get_dflt_eventq (), NimBLEScan::srTimerCb, this );
115+ ble_npl_event_init (&dummySrTimerEvent, sendDummyScanResponse, NULL );
116+ # endif
117+ }
40118
41119/* *
42120 * @brief Scan destructor, release any allocated resources.
43121 */
44122NimBLEScan::~NimBLEScan () {
45123 clearResults ();
124+ # if SR_TIMEOUT
125+ ble_npl_callout_deinit (&m_srTimer);
126+ ble_npl_event_deinit (&dummySrTimerEvent);
127+ # endif
46128}
47129
48130/* *
@@ -114,6 +196,9 @@ int NimBLEScan::handleGapEvent(ble_gap_event* event, void* arg) {
114196 advertisedDevice = new NimBLEAdvertisedDevice (event, event_type);
115197 pScan->m_scanResults .m_deviceVec .push_back (advertisedDevice);
116198 NIMBLE_LOGI (LOG_TAG, " New advertiser: %s" , advertisedAddress.toString ().c_str ());
199+ # if SR_TIMEOUT
200+ advertisedDevice->m_time = ble_npl_time_get ();
201+ # endif
117202 } else {
118203 advertisedDevice->update (event, event_type);
119204 if (isLegacyAdv && event_type == BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP) {
@@ -137,6 +222,13 @@ int NimBLEScan::handleGapEvent(ble_gap_event* event, void* arg) {
137222 advertisedDevice->m_callbackSent ++;
138223 // got the scan response report the full data.
139224 pScan->m_pScanCallbacks ->onResult (advertisedDevice);
225+ # if SR_TIMEOUT
226+ } else if (isLegacyAdv && !ble_npl_callout_is_active (&pScan->m_srTimer )) {
227+ // Start the timer to wait for the scan response.
228+ ble_npl_time_t ticks;
229+ ble_npl_time_ms_to_ticks (SR_TIMEOUT, &ticks);
230+ ble_npl_callout_reset (&pScan->m_srTimer , ticks);
231+ # endif
140232 }
141233
142234 // If not storing results and we have invoked the callback, delete the device.
@@ -149,13 +241,15 @@ int NimBLEScan::handleGapEvent(ble_gap_event* event, void* arg) {
149241
150242 case BLE_GAP_EVENT_DISC_COMPLETE: {
151243 NIMBLE_LOGD (LOG_TAG, " discovery complete; reason=%d" , event->disc_complete .reason );
244+ # if SR_TIMEOUT
245+ ble_npl_callout_stop (&pScan->m_srTimer );
246+ # endif
247+ pScan->m_pScanCallbacks ->onScanEnd (pScan->m_scanResults , event->disc_complete .reason );
152248
153249 if (pScan->m_maxResults == 0 ) {
154250 pScan->clearResults ();
155251 }
156252
157- pScan->m_pScanCallbacks ->onScanEnd (pScan->m_scanResults , event->disc_complete .reason );
158-
159253 if (pScan->m_pTaskData != nullptr ) {
160254 NimBLEUtils::taskRelease (*pScan->m_pTaskData , event->disc_complete .reason );
161255 }
@@ -384,6 +478,10 @@ bool NimBLEScan::stop() {
384478 return false ;
385479 }
386480
481+ # if SR_TIMEOUT
482+ ble_npl_callout_stop (&m_srTimer);
483+ # endif
484+
387485 if (m_maxResults == 0 ) {
388486 clearResults ();
389487 }
0 commit comments