diff --git a/nimble/controller/include/controller/ble_ll_ctrl.h b/nimble/controller/include/controller/ble_ll_ctrl.h index fa50c98f2..7e5d50bae 100644 --- a/nimble/controller/include/controller/ble_ll_ctrl.h +++ b/nimble/controller/include/controller/ble_ll_ctrl.h @@ -272,6 +272,7 @@ void ble_ll_hci_ev_databuf_overflow(void); void ble_ll_hci_ev_le_csa(struct ble_ll_conn_sm *connsm); void ble_ll_hci_ev_send_scan_req_recv(uint8_t adv_handle, const uint8_t *peer, uint8_t peer_addr_type); +void ble_ll_hci_ev_send_scan_timeout(void); void ble_ll_hci_ev_send_adv_set_terminated(uint8_t status, uint8_t adv_handle, uint16_t conn_handle, uint8_t events); int ble_ll_hci_ev_phy_update(struct ble_ll_conn_sm *connsm, uint8_t status); diff --git a/nimble/controller/include/controller/ble_ll_scan.h b/nimble/controller/include/controller/ble_ll_scan.h index 35cbeb44a..d01647bf8 100644 --- a/nimble/controller/include/controller/ble_ll_scan.h +++ b/nimble/controller/include/controller/ble_ll_scan.h @@ -132,12 +132,16 @@ struct ble_ll_scan_sm struct os_event scan_sched_ev; struct hal_timer scan_timer; - uint16_t duration; - uint16_t period; +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + struct hal_timer duration_timer; + struct hal_timer period_timer; + uint32_t duration_ticks; + uint32_t period_ticks; + uint8_t ext_scanning; +#endif uint8_t cur_phy; uint8_t next_phy; - uint8_t ext_scanning; uint8_t restart_timer_needed; struct ble_ll_aux_data *cur_aux_data; struct ble_ll_scan_params phy_data[BLE_LL_SCAN_PHY_NUMBER]; diff --git a/nimble/controller/src/ble_ll_conn.c b/nimble/controller/src/ble_ll_conn.c index 2ed695f14..d5b0b47ee 100644 --- a/nimble/controller/src/ble_ll_conn.c +++ b/nimble/controller/src/ble_ll_conn.c @@ -3028,13 +3028,11 @@ int ble_ll_init_rx_isr_start(uint8_t pdu_type, struct ble_mbuf_hdr *ble_hdr) { struct ble_ll_conn_sm *connsm; - struct ble_ll_scan_sm *scansm; connsm = g_ble_ll_conn_create_sm; if (!connsm) { return 0; } - scansm = connsm->scansm; if ((pdu_type == BLE_ADV_PDU_TYPE_ADV_IND) || (pdu_type == BLE_ADV_PDU_TYPE_ADV_DIRECT_IND || @@ -3042,7 +3040,9 @@ ble_ll_init_rx_isr_start(uint8_t pdu_type, struct ble_mbuf_hdr *ble_hdr) return 1; } - if (pdu_type == BLE_ADV_PDU_TYPE_ADV_EXT_IND && scansm->ext_scanning) { +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + if (pdu_type == BLE_ADV_PDU_TYPE_ADV_EXT_IND && + connsm->scansm->ext_scanning) { if (connsm->scansm->cur_aux_data) { STATS_INC(ble_ll_stats, aux_received); } @@ -3050,6 +3050,7 @@ ble_ll_init_rx_isr_start(uint8_t pdu_type, struct ble_mbuf_hdr *ble_hdr) ble_hdr->rxinfo.flags |= BLE_MBUF_HDR_F_EXT_ADV; return 1; } +#endif return 0; } diff --git a/nimble/controller/src/ble_ll_hci_ev.c b/nimble/controller/src/ble_ll_hci_ev.c index 6abfdd6d5..815338aac 100644 --- a/nimble/controller/src/ble_ll_hci_ev.c +++ b/nimble/controller/src/ble_ll_hci_ev.c @@ -320,6 +320,28 @@ ble_ll_hci_ev_send_scan_req_recv(uint8_t adv_handle, const uint8_t *peer, } #endif +/** + * Sends the LE Scan Timeout Event + * + */ +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) +void +ble_ll_hci_ev_send_scan_timeout(void) +{ + uint8_t *evbuf; + + if (ble_ll_hci_is_le_event_enabled(BLE_HCI_LE_SUBEV_SCAN_TIMEOUT)) { + evbuf = ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_HI); + if (evbuf) { + evbuf[0] = BLE_HCI_EVCODE_LE_META; + evbuf[1] = BLE_HCI_LE_SUBEV_SCAN_TIMEOUT_LEN; + evbuf[2] = BLE_HCI_LE_SUBEV_SCAN_TIMEOUT; + ble_ll_hci_event_send(evbuf); + } + } +} +#endif + /** * Sends the LE Advertising Set Terminated event * diff --git a/nimble/controller/src/ble_ll_scan.c b/nimble/controller/src/ble_ll_scan.c index 4ddad14b9..bd43abe87 100644 --- a/nimble/controller/src/ble_ll_scan.c +++ b/nimble/controller/src/ble_ll_scan.c @@ -1154,7 +1154,9 @@ ble_ll_scan_sm_stop(int chk_disable) if (chk_disable) { OS_ENTER_CRITICAL(sr); lls = ble_ll_state_get(); - if ((lls == BLE_LL_STATE_SCANNING) || (lls == BLE_LL_STATE_INITIATING)) { + + if ((lls == BLE_LL_STATE_SCANNING) || + (lls == BLE_LL_STATE_INITIATING && chk_disable == 1)) { /* Disable phy */ ble_phy_disable(); @@ -1509,10 +1511,12 @@ ble_ll_scan_rx_isr_start(uint8_t pdu_type, uint16_t *rxflags) rc = 1; } +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) if ((pdu_type == BLE_ADV_PDU_TYPE_ADV_EXT_IND && scansm->ext_scanning)) { *rxflags |= BLE_MBUF_HDR_F_EXT_ADV; rc = 1; } +#endif /* * If this is the first PDU after we sent the scan response (as @@ -2712,6 +2716,57 @@ ble_ll_set_ext_scan_params(uint8_t *cmd) #endif +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) +static void +ble_ll_scan_duration_period_timers_restart(struct ble_ll_scan_sm *scansm) +{ + uint32_t now; + + now = os_cputime_get32(); + + os_cputime_timer_stop(&scansm->duration_timer); + os_cputime_timer_stop(&scansm->period_timer); + + if (scansm->duration_ticks) { + os_cputime_timer_start(&scansm->duration_timer, + now + scansm->duration_ticks); + + if (scansm->period_ticks) { + os_cputime_timer_start(&scansm->period_timer, + now + scansm->period_ticks); + } + } +} + +static void +ble_ll_scan_duration_timer_cb(void *arg) +{ + struct ble_ll_scan_sm *scansm; + + scansm = (struct ble_ll_scan_sm *)arg; + + ble_ll_scan_sm_stop(2); + + /* if period is set both timers get started from period cb */ + if (!scansm->period_ticks) { + ble_ll_hci_ev_send_scan_timeout(); + } +} + +static void +ble_ll_scan_period_timer_cb(void *arg) +{ + struct ble_ll_scan_sm *scansm = arg; + + ble_ll_scan_sm_start(scansm); + + /* always start timer regardless of ble_ll_scan_sm_start result + * if it failed will restart in next period + */ + ble_ll_scan_duration_period_timers_restart(scansm); +} +#endif + /** * ble ll scan set enable * @@ -2732,9 +2787,13 @@ ble_ll_scan_set_enable(uint8_t *cmd, uint8_t ext) struct ble_ll_scan_sm *scansm; struct ble_ll_scan_params *scanp; struct ble_ll_scan_params *scanphy; - uint16_t dur; - uint16_t period; int i; +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + uint32_t period_ticks = 0; + uint32_t dur_ticks = 0; + uint16_t period; + uint16_t dur; +#endif /* Check for valid parameters */ enable = cmd[0]; @@ -2745,70 +2804,114 @@ ble_ll_scan_set_enable(uint8_t *cmd, uint8_t ext) scansm = &g_ble_ll_scan_sm; +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + /* we can do that here since value will never change until reset */ scansm->ext_scanning = ext; if (ext) { - /* TODO: Mark that extended scan is ongoing in order to send - * correct advertising report - */ - dur = get_le16(cmd + 2); - period = get_le16(cmd + 4); - /* TODO Use period and duration */ - (void) dur; - (void) period; - } - - rc = BLE_ERR_SUCCESS; - if (enable) { - /* If already enabled, do nothing */ - if (!scansm->scan_enabled) { - - scansm->cur_phy = PHY_NOT_CONFIGURED; - scansm->next_phy = PHY_NOT_CONFIGURED; - - for (i = 0; i < BLE_LL_SCAN_PHY_NUMBER; i++) { - scanphy = &scansm->phy_data[i]; - scanp = &g_ble_ll_scan_params[i]; - - if (!scanp->configured) { - continue; - } - - scanphy->configured = scanp->configured; - scanphy->scan_type = scanp->scan_type; - scanphy->scan_itvl = scanp->scan_itvl; - scanphy->scan_window = scanp->scan_window; - scanphy->scan_filt_policy = scanp->scan_filt_policy; - scanphy->own_addr_type = scanp->own_addr_type; - scansm->scan_filt_dups = filter_dups; - - if (scansm->cur_phy == PHY_NOT_CONFIGURED) { - scansm->cur_phy = i; - } else { - scansm->next_phy = i; - } - } - - rc = ble_ll_scan_sm_start(scansm); + /* Period parameter is ignored when the Duration parameter is zero */ + if (dur) { + period = get_le16(cmd + 4); } else { - /* Controller does not allow initiating and scanning.*/ - for (i = 0; i < BLE_LL_SCAN_PHY_NUMBER; i++) { - scanphy = &scansm->phy_data[i]; - if (scanphy->configured && - scanphy->scan_type == BLE_SCAN_TYPE_INITIATE) { - rc = BLE_ERR_CMD_DISALLOWED; - break; - } - } + period = 0; } - } else { + + /* period is in 1.28 sec units + * TODO support full range, would require os_cputime milliseconds API + */ + if (period > 3355) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + period_ticks = os_cputime_usecs_to_ticks(period * 1280000); + + /* duration is in 10ms units */ + dur_ticks = os_cputime_usecs_to_ticks(dur * 10000); + + if (dur_ticks && period_ticks && (dur_ticks >= period_ticks)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + } +#endif + + /* disable*/ + if (!enable) { if (scansm->scan_enabled) { ble_ll_scan_sm_stop(1); } +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + os_cputime_timer_stop(&scansm->duration_timer); + os_cputime_timer_stop(&scansm->period_timer); +#endif + + return BLE_ERR_SUCCESS; } + /* if already enable we just need to update parameters */ + if (scansm->scan_enabled) { + /* Controller does not allow initiating and scanning.*/ + for (i = 0; i < BLE_LL_SCAN_PHY_NUMBER; i++) { + scanphy = &scansm->phy_data[i]; + if (scanphy->configured && + scanphy->scan_type == BLE_SCAN_TYPE_INITIATE) { + return BLE_ERR_CMD_DISALLOWED; + } + } + + /* update filter policy */ + scansm->scan_filt_dups = filter_dups; + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + /* restart timers according to new settings */ + scansm->duration_ticks = dur_ticks; + scansm->period_ticks = period_ticks; + ble_ll_scan_duration_period_timers_restart(scansm); +#endif + + return BLE_ERR_SUCCESS; + } + + /* we can store those upfront regardless of start scan result since scan is + * disabled now + */ + + scansm->scan_filt_dups = filter_dups; + scansm->cur_phy = PHY_NOT_CONFIGURED; + scansm->next_phy = PHY_NOT_CONFIGURED; + + for (i = 0; i < BLE_LL_SCAN_PHY_NUMBER; i++) { + scanphy = &scansm->phy_data[i]; + scanp = &g_ble_ll_scan_params[i]; + + if (!scanp->configured) { + continue; + } + + scanphy->configured = scanp->configured; + scanphy->scan_type = scanp->scan_type; + scanphy->scan_itvl = scanp->scan_itvl; + scanphy->scan_window = scanp->scan_window; + scanphy->scan_filt_policy = scanp->scan_filt_policy; + scanphy->own_addr_type = scanp->own_addr_type; + + if (scansm->cur_phy == PHY_NOT_CONFIGURED) { + scansm->cur_phy = i; + } else { + scansm->next_phy = i; + } + } + + rc = ble_ll_scan_sm_start(scansm); + +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + if (rc == BLE_ERR_SUCCESS) { + scansm->duration_ticks = dur_ticks; + scansm->period_ticks = period_ticks; + ble_ll_scan_duration_period_timers_restart(scansm); + } +#endif + return rc; } @@ -2846,7 +2949,9 @@ ble_ll_scan_initiator_start(struct hci_create_conn *hcc, scansm = &g_ble_ll_scan_sm; scansm->own_addr_type = hcc->own_addr_type; +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) scansm->ext_scanning = 0; +#endif scansm->cur_phy = PHY_UNCODED; scansm->next_phy = PHY_NOT_CONFIGURED; @@ -3061,6 +3166,14 @@ ble_ll_scan_common_init(void) /* Initialize scanning timer */ os_cputime_timer_init(&scansm->scan_timer, ble_ll_scan_timer_cb, scansm); + /* Initialize extended scan timers */ +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + os_cputime_timer_init(&g_ble_ll_scan_sm.duration_timer, + ble_ll_scan_duration_timer_cb, &g_ble_ll_scan_sm); + os_cputime_timer_init(&g_ble_ll_scan_sm.period_timer, + ble_ll_scan_period_timer_cb, &g_ble_ll_scan_sm); +#endif + /* Get a scan request mbuf (packet header) and attach to state machine */ scansm->scan_req_pdu = os_msys_get_pkthdr(BLE_SCAN_LEGACY_MAX_PKT_LEN, sizeof(struct ble_mbuf_hdr)); @@ -3078,12 +3191,19 @@ ble_ll_scan_reset(void) { struct ble_ll_scan_sm *scansm; - /* If enabled, stop it. */ scansm = &g_ble_ll_scan_sm; + + /* If enabled, stop it. */ if (scansm->scan_enabled) { ble_ll_scan_sm_stop(0); } + /* stop extended scan timers */ +#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) + os_cputime_timer_stop(&scansm->duration_timer); + os_cputime_timer_stop(&scansm->period_timer); +#endif + /* Free the scan request pdu */ os_mbuf_free_chain(scansm->scan_req_pdu); diff --git a/nimble/host/src/ble_gap.c b/nimble/host/src/ble_gap.c index 6c79842a8..1bfaad067 100644 --- a/nimble/host/src/ble_gap.c +++ b/nimble/host/src/ble_gap.c @@ -1257,6 +1257,14 @@ ble_gap_rx_adv_report(struct ble_gap_disc_desc *desc) ble_gap_disc_report(desc); } +#if MYNEWT_VAL(BLE_EXT_ADV) && NIMBLE_BLE_SCAN +void +ble_gap_rx_le_scan_timeout(void) +{ + ble_gap_disc_complete(); +} +#endif + #if MYNEWT_VAL(BLE_EXT_ADV) void ble_gap_rx_ext_adv_report(struct ble_gap_ext_disc_desc *desc) diff --git a/nimble/host/src/ble_gap_priv.h b/nimble/host/src/ble_gap_priv.h index 1da9aa7aa..a789f3976 100644 --- a/nimble/host/src/ble_gap_priv.h +++ b/nimble/host/src/ble_gap_priv.h @@ -75,6 +75,10 @@ extern STATS_SECT_DECL(ble_gap_stats) ble_gap_stats; #define BLE_GAP_CONN_MODE_MAX 3 #define BLE_GAP_DISC_MODE_MAX 3 +#if MYNEWT_VAL(BLE_EXT_ADV) && NIMBLE_BLE_SCAN +void ble_gap_rx_le_scan_timeout(void); +#endif + #if MYNEWT_VAL(BLE_EXT_ADV) void ble_gap_rx_ext_adv_report(struct ble_gap_ext_disc_desc *desc); void ble_gap_rx_adv_set_terminated(struct hci_le_adv_set_terminated *evt); diff --git a/nimble/host/src/ble_hs_hci_evt.c b/nimble/host/src/ble_hs_hci_evt.c index b5174e053..612508455 100644 --- a/nimble/host/src/ble_hs_hci_evt.c +++ b/nimble/host/src/ble_hs_hci_evt.c @@ -50,6 +50,7 @@ static ble_hs_hci_evt_le_fn ble_hs_hci_evt_le_dir_adv_rpt; static ble_hs_hci_evt_le_fn ble_hs_hci_evt_le_phy_update_complete; static ble_hs_hci_evt_le_fn ble_hs_hci_evt_le_ext_adv_rpt; static ble_hs_hci_evt_le_fn ble_hs_hci_evt_le_rd_rem_used_feat_complete; +static ble_hs_hci_evt_le_fn ble_hs_hci_evt_le_scan_timeout; static ble_hs_hci_evt_le_fn ble_hs_hci_evt_le_adv_set_terminated; /* Statistics */ @@ -102,6 +103,8 @@ static const struct ble_hs_hci_evt_le_dispatch_entry { BLE_HCI_LE_SUBEV_EXT_ADV_RPT, ble_hs_hci_evt_le_ext_adv_rpt }, { BLE_HCI_LE_SUBEV_RD_REM_USED_FEAT, ble_hs_hci_evt_le_rd_rem_used_feat_complete }, + { BLE_HCI_LE_SUBEV_SCAN_TIMEOUT, + ble_hs_hci_evt_le_scan_timeout }, { BLE_HCI_LE_SUBEV_ADV_SET_TERMINATED, ble_hs_hci_evt_le_adv_set_terminated }, }; @@ -604,6 +607,15 @@ ble_hs_hci_evt_le_ext_adv_rpt(uint8_t subevent, uint8_t *data, int len) return 0; } +static int +ble_hs_hci_evt_le_scan_timeout(uint8_t subevent, uint8_t *data, int len) +{ +#if MYNEWT_VAL(BLE_EXT_ADV) && NIMBLE_BLE_SCAN + ble_gap_rx_le_scan_timeout(); +#endif + return 0; +} + static int ble_hs_hci_evt_le_adv_set_terminated(uint8_t subevent, uint8_t *data, int len) { diff --git a/nimble/include/nimble/hci_common.h b/nimble/include/nimble/hci_common.h index e887e21c8..4ce941eeb 100644 --- a/nimble/include/nimble/hci_common.h +++ b/nimble/include/nimble/hci_common.h @@ -775,6 +775,9 @@ extern "C" { /* LE PHY update complete event (sub event 0x0C) */ #define BLE_HCI_LE_PHY_UPD_LEN (6) +/* LE Scan Timeout Event (sub event 0x11) */ +#define BLE_HCI_LE_SUBEV_SCAN_TIMEOUT_LEN (1) + /* LE Advertising Set Terminated Event (sub event 0x12) */ #define BLE_HCI_LE_SUBEV_ADV_SET_TERMINATED_LEN (6)