/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #include #include #include #include #include "syscfg/syscfg.h" #include "os/os.h" #include "os/os_cputime.h" #include "nimble/ble.h" #include "nimble/nimble_opt.h" #include "nimble/hci_common.h" #include "nimble/ble_hci_trans.h" #include "controller/ble_phy.h" #include "controller/ble_hw.h" #include "controller/ble_ll.h" #include "controller/ble_ll_sched.h" #include "controller/ble_ll_adv.h" #include "controller/ble_ll_scan.h" #include "controller/ble_ll_hci.h" #include "controller/ble_ll_whitelist.h" #include "controller/ble_ll_resolv.h" #include "controller/ble_ll_rfmgmt.h" #include "controller/ble_ll_trace.h" #include "controller/ble_ll_sync.h" #include "ble_ll_conn_priv.h" /* * XXX: * 1) I think I can guarantee that we dont process things out of order if * I send an event when a scan request is sent. The scan_rsp_pending flag * code might be made simpler. * * 2) Interleave sending scan requests to different advertisers? I guess I need * a list of advertisers to which I sent a scan request and have yet to * receive a scan response from? Implement this. */ /* Dont allow more than 255 of these entries */ #if MYNEWT_VAL(BLE_LL_NUM_SCAN_RSP_ADVS) > 255 #error "Cannot have more than 255 scan response entries!" #endif #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY) #define SCAN_VALID_PHY_MASK (BLE_HCI_LE_PHY_1M_PREF_MASK | BLE_HCI_LE_PHY_CODED_PREF_MASK) #else #define SCAN_VALID_PHY_MASK (BLE_HCI_LE_PHY_1M_PREF_MASK) #endif /* The scanning parameters set by host */ static struct ble_ll_scan_params g_ble_ll_scan_params[BLE_LL_SCAN_PHY_NUMBER]; /* The scanning state machine global object */ static struct ble_ll_scan_sm g_ble_ll_scan_sm; struct ble_ll_ext_adv_hdr { uint8_t mode; uint8_t hdr_len; uint8_t hdr[0]; }; struct ble_ll_scan_addr_data { bool adva_present; uint8_t adva_type; uint8_t *adva; uint8_t targeta_type; uint8_t *targeta; uint8_t adv_addr_type; uint8_t *adv_addr; struct ble_ll_resolv_entry *rl; }; /* * Structure used to store advertisers. This is used to limit sending scan * requests to the same advertiser and also to filter duplicate events sent * to the host. */ struct ble_ll_scan_advertisers { uint16_t sc_adv_flags; uint16_t adi; struct ble_dev_addr adv_addr; }; #define BLE_LL_SC_ADV_F_RANDOM_ADDR (0x01) #define BLE_LL_SC_ADV_F_SCAN_RSP_RXD (0x02) #define BLE_LL_SC_ADV_F_DIRECT_RPT_SENT (0x04) #define BLE_LL_SC_ADV_F_ADV_RPT_SENT (0x08) #define BLE_LL_SC_ADV_F_SCAN_RSP_SENT (0x10) /* Contains list of advertisers that we have heard scan responses from */ static uint8_t g_ble_ll_scan_num_rsp_advs; struct ble_ll_scan_advertisers g_ble_ll_scan_rsp_advs[MYNEWT_VAL(BLE_LL_NUM_SCAN_RSP_ADVS)]; /* Duplicates filtering data */ #define BLE_LL_SCAN_ENTRY_TYPE_LEGACY(addr_type) \ ((addr_type) & 1) #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) #define BLE_LL_SCAN_ENTRY_TYPE_EXT(addr_type, has_aux, is_anon, adi) \ (((adi >> 8) & 0xF0) | (1 << 3) | (is_anon << 2) | (has_aux << 1) | ((addr_type) & 1)) #endif #define BLE_LL_SCAN_DUP_F_ADV_REPORT_SENT (0x01) #define BLE_LL_SCAN_DUP_F_DIR_ADV_REPORT_SENT (0x02) #define BLE_LL_SCAN_DUP_F_SCAN_RSP_SENT (0x04) struct ble_ll_scan_dup_entry { uint8_t type; /* entry type, see BLE_LL_SCAN_ENTRY_TYPE_* */ uint8_t addr[6]; uint8_t flags; /* use BLE_LL_SCAN_DUP_F_xxx */ #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) uint16_t adi; #endif TAILQ_ENTRY(ble_ll_scan_dup_entry) link; }; static os_membuf_t g_scan_dup_mem[ OS_MEMPOOL_SIZE( MYNEWT_VAL(BLE_LL_NUM_SCAN_DUP_ADVS), sizeof(struct ble_ll_scan_dup_entry)) ]; static struct os_mempool g_scan_dup_pool; static TAILQ_HEAD(ble_ll_scan_dup_list, ble_ll_scan_dup_entry) g_scan_dup_list; #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) #if MYNEWT_VAL(BLE_LL_EXT_ADV_AUX_PTR_CNT) != 0 static os_membuf_t ext_scan_aux_mem[ OS_MEMPOOL_SIZE( MYNEWT_VAL(BLE_LL_EXT_ADV_AUX_PTR_CNT), sizeof (struct ble_ll_aux_data)) ]; #else #define ext_scan_aux_mem NULL #endif static struct os_mempool ext_scan_aux_pool; static int ble_ll_scan_start(struct ble_ll_scan_sm *scansm, struct ble_ll_sched_item *sch); static void ble_ll_aux_scan_drop_event_cb(struct ble_npl_event *ev) { struct ble_ll_aux_data *aux_data = ble_npl_event_get_arg(ev); ble_ll_scan_end_adv_evt(aux_data); ble_ll_scan_aux_data_unref(aux_data); } static void ble_ll_aux_scan_drop(struct ble_ll_aux_data *aux_data) { BLE_LL_ASSERT(aux_data); STATS_INC(ble_ll_stats, aux_scan_drop); ble_npl_event_init(&aux_data->ev, ble_ll_aux_scan_drop_event_cb, aux_data); ble_ll_event_send(&aux_data->ev); } static int ble_ll_aux_scan_cb(struct ble_ll_sched_item *sch) { struct ble_ll_scan_sm *scansm = &g_ble_ll_scan_sm; uint8_t lls = ble_ll_state_get(); uint32_t wfr_usec; STATS_INC(ble_ll_stats, aux_sched_cb); /* Drop the scheduled item if scan was disable or there is aux or scan * response pending */ if (!scansm->scan_enabled || scansm->cur_aux_data || scansm->scan_rsp_pending) { ble_ll_aux_scan_drop(sch->cb_arg); sch->cb_arg = NULL; goto done; } /* Check if there is no aux connect sent. If so drop the sched item */ if (lls == BLE_LL_STATE_INITIATING && ble_ll_conn_init_pending_aux_conn_rsp()) { ble_ll_aux_scan_drop(sch->cb_arg); sch->cb_arg = NULL; goto done; } /* This function is called only when scanner is running. This can happen * in 3 states: * BLE_LL_STATE_SCANNING * BLE_LL_STATE_INITIATING * BLE_LL_STATE_STANDBY */ if (lls != BLE_LL_STATE_STANDBY) { ble_phy_disable(); ble_ll_state_set(BLE_LL_STATE_STANDBY); } /* When doing RX for AUX pkt, cur_aux_data keeps valid aux data */ scansm->cur_aux_data = sch->cb_arg; sch->cb_arg = NULL; BLE_LL_ASSERT(scansm->cur_aux_data != NULL); scansm->cur_aux_data->scanning = 1; if (ble_ll_scan_start(scansm, sch)) { ble_ll_scan_interrupted(scansm); goto done; } STATS_INC(ble_ll_stats, aux_fired_for_read); wfr_usec = scansm->cur_aux_data->offset_units ? 300 : 30; ble_phy_wfr_enable(BLE_PHY_WFR_ENABLE_RX, 0, wfr_usec); done: return BLE_LL_SCHED_STATE_DONE; } static int ble_ll_scan_ext_adv_init(struct ble_ll_aux_data **aux_data) { struct ble_ll_aux_data *e; e = os_memblock_get(&ext_scan_aux_pool); if (!e) { return -1; } memset(e, 0, sizeof(*e)); e->sch.sched_cb = ble_ll_aux_scan_cb; e->sch.sched_type = BLE_LL_SCHED_TYPE_AUX_SCAN; e->ref_cnt = 1; #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) e->rpa_index = -1; #endif ble_ll_trace_u32x2(BLE_LL_TRACE_ID_AUX_REF, (uint32_t)e, e->ref_cnt); *aux_data = e; STATS_INC(ble_ll_stats, aux_allocated); return 0; } #endif static inline uint32_t ble_ll_scan_time_hci_to_ticks(uint16_t value) { return os_cputime_usecs_to_ticks(value * BLE_HCI_SCAN_ITVL); } /* See Vol 6 Part B Section 4.4.3.2. Active scanning backoff */ static void ble_ll_scan_req_backoff(struct ble_ll_scan_sm *scansm, int success) { BLE_LL_ASSERT(scansm->backoff_count == 0); BLE_LL_ASSERT(scansm->scan_rsp_pending == 0); if (success) { scansm->scan_rsp_cons_fails = 0; ++scansm->scan_rsp_cons_ok; if (scansm->scan_rsp_cons_ok == 2) { scansm->scan_rsp_cons_ok = 0; if (scansm->upper_limit > 1) { scansm->upper_limit >>= 1; } } STATS_INC(ble_ll_stats, scan_req_txg); } else { scansm->scan_rsp_cons_ok = 0; ++scansm->scan_rsp_cons_fails; if (scansm->scan_rsp_cons_fails == 2) { scansm->scan_rsp_cons_fails = 0; if (scansm->upper_limit < 256) { scansm->upper_limit <<= 1; } } STATS_INC(ble_ll_stats, scan_req_txf); } scansm->backoff_count = ble_ll_rand() & (scansm->upper_limit - 1); ++scansm->backoff_count; BLE_LL_ASSERT(scansm->backoff_count <= 256); } #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) static void ble_ll_scan_refresh_nrpa(struct ble_ll_scan_sm *scansm) { ble_npl_time_t now; now = ble_npl_time_get(); if (CPUTIME_GEQ(now, scansm->scan_nrpa_timer)) { /* Generate new NRPA */ ble_ll_rand_data_get(scansm->scan_nrpa, BLE_DEV_ADDR_LEN); scansm->scan_nrpa[5] &= ~0xc0; /* We'll use the same timeout as for RPA rotation */ scansm->scan_nrpa_timer = now + ble_ll_resolv_get_rpa_tmo(); } } #endif static void ble_ll_scan_req_pdu_prepare(struct ble_ll_scan_sm *scansm, const uint8_t *adv_addr, uint8_t adv_addr_type, struct ble_ll_resolv_entry *rl) { uint8_t hdr_byte; struct ble_ll_scan_pdu_data *pdu_data; uint8_t *scana; #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) uint8_t rpa[BLE_DEV_ADDR_LEN]; #endif pdu_data = &scansm->pdu_data; /* Construct first PDU header byte */ hdr_byte = BLE_ADV_PDU_TYPE_SCAN_REQ; if (adv_addr_type) { hdr_byte |= BLE_ADV_PDU_HDR_RXADD_RAND; } /* Determine ScanA */ if (scansm->own_addr_type & 0x01) { hdr_byte |= BLE_ADV_PDU_HDR_TXADD_RAND; scana = g_random_addr; } else { scana = g_dev_addr; } #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) if (scansm->own_addr_type & 0x02) { /* * If device is on RL and we have local IRK, we use RPA generated using * that IRK as ScanA. Otherwise we use NRPA as ScanA to prevent our * device from being tracked when doing an active scan (Core 5.1, Vol 6, * Part B, section 6.3) */ if (rl && rl->rl_has_local) { ble_ll_resolv_get_priv_addr(rl, 1, rpa); scana = rpa; } else { ble_ll_scan_refresh_nrpa(scansm); scana = scansm->scan_nrpa; } hdr_byte |= BLE_ADV_PDU_HDR_TXADD_RAND; } #endif /* Save scan request data */ pdu_data->hdr_byte = hdr_byte; memcpy(pdu_data->scana, scana, BLE_DEV_ADDR_LEN); memcpy(pdu_data->adva, adv_addr, BLE_DEV_ADDR_LEN); } static uint8_t ble_ll_scan_req_tx_pdu_cb(uint8_t *dptr, void *pducb_arg, uint8_t *hdr_byte) { struct ble_ll_scan_sm *scansm = pducb_arg; struct ble_ll_scan_pdu_data *pdu_data = &scansm->pdu_data; memcpy(dptr, pdu_data->scana, BLE_DEV_ADDR_LEN); memcpy(dptr + BLE_DEV_ADDR_LEN, pdu_data->adva, BLE_DEV_ADDR_LEN); *hdr_byte = pdu_data->hdr_byte; return BLE_DEV_ADDR_LEN * 2; } #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) /* if copy_from is provided new report is initialized with that instead of * defaults */ static struct ble_hci_ev * ble_ll_scan_get_ext_adv_report(struct ext_adv_report *copy_from) { struct ble_hci_ev_le_subev_ext_adv_rpt *ev; struct ext_adv_report *report; struct ble_hci_ev *hci_ev; hci_ev = ( void *) ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_LO); if (!hci_ev) { return NULL; } hci_ev->opcode = BLE_HCI_EVCODE_LE_META; hci_ev->length = sizeof(*ev) + sizeof(*report); ev = (void *) hci_ev->data; memset(ev, 0, sizeof(*ev)); ev->subev_code = BLE_HCI_LE_SUBEV_EXT_ADV_RPT; /* We support only one report per event now */ ev->num_reports = 1; report = ev->reports; if (copy_from) { memcpy(report, copy_from, sizeof(*report)); report->data_len = 0; } else { memset(report, 0, sizeof(*report)); report->pri_phy = BLE_PHY_1M; /* Init SID with "Not available" which is 0xFF */ report->sid = 0xFF; /* Init TX Power with "Not available" which is 127 */ report->tx_power = 127; /* Init RSSI with "Not available" which is 127 */ report->rssi = 127; /* Init address type with "anonymous" which is 0xFF */ report->addr_type = 0xFF; } return hci_ev; } static void ble_ll_scan_send_truncated(struct ble_ll_aux_data *aux_data) { struct ble_hci_ev_le_subev_ext_adv_rpt *ev; struct ext_adv_report *report; struct ble_hci_ev *hci_ev; if (!ble_ll_hci_is_le_event_enabled(BLE_HCI_LE_SUBEV_EXT_ADV_RPT)) { return; } BLE_LL_ASSERT(aux_data); /* No need to send if we did not send any report or sent truncated already */ if (!(aux_data->flags_ll & BLE_LL_AUX_FLAG_HCI_SENT_ANY) || (aux_data->flags_ll & BLE_LL_AUX_FLAG_HCI_SENT_TRUNCATED)) { return; } BLE_LL_ASSERT(aux_data->evt); hci_ev = aux_data->evt; aux_data->evt = NULL; hci_ev->length = sizeof(*ev) + sizeof(*report); ev = (void *) hci_ev->data; report = ev->reports; report->data_len = 0; report->evt_type = aux_data->evt_type; report->evt_type |= BLE_HCI_ADV_DATA_STATUS_TRUNCATED; if (aux_data->flags & BLE_LL_AUX_HAS_ADVA) { memcpy(report->addr, aux_data->adva, 6); report->addr_type = aux_data->adva_type; } if (aux_data->flags & BLE_LL_AUX_HAS_TARGETA) { memcpy(report->dir_addr, aux_data->targeta, 6); report->dir_addr_type = aux_data->targeta_type; } report->sid = aux_data->adi >> 12; ble_ll_hci_event_send(hci_ev); aux_data->flags_ll |= BLE_LL_AUX_FLAG_SCAN_ERROR; aux_data->flags_ll |= BLE_LL_AUX_FLAG_HCI_SENT_ANY; aux_data->flags_ll |= BLE_LL_AUX_FLAG_HCI_SENT_TRUNCATED; } static int ble_ll_scan_get_adi(struct ble_ll_aux_data *aux_data, uint16_t *adi) { if (!aux_data || !(aux_data->flags & BLE_LL_AUX_HAS_ADI)) { return -1; } *adi = aux_data->adi; return 0; } void ble_ll_scan_end_adv_evt(struct ble_ll_aux_data *aux_data) { /* Make sure we send report with 'truncated' data state if needed */ ble_ll_scan_send_truncated(aux_data); } #endif static void ble_ll_scan_clean_cur_aux_data(void) { #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) struct ble_ll_scan_sm *scansm = &g_ble_ll_scan_sm; /* If scanner was reading aux ptr, we need to clean it up */ if (scansm->cur_aux_data) { ble_ll_scan_end_adv_evt(scansm->cur_aux_data); ble_ll_scan_aux_data_unref(scansm->cur_aux_data); scansm->cur_aux_data = NULL; } #endif } void ble_ll_scan_halt(void) { struct ble_ll_scan_sm *scansm = &g_ble_ll_scan_sm; ble_ll_scan_clean_cur_aux_data(); /* Update backoff if we failed to receive scan response */ if (scansm->scan_rsp_pending) { scansm->scan_rsp_pending = 0; ble_ll_scan_req_backoff(scansm, 0); } } /** * Checks to see if we have received a scan response from this advertiser. * * @param adv_addr Address of advertiser * @param txadd TxAdd bit (0: public; random otherwise) * * @return int 0: have not received a scan response; 1 otherwise. */ static int ble_ll_scan_have_rxd_scan_rsp(uint8_t *addr, uint8_t txadd, uint8_t ext_adv, uint16_t adi) { uint8_t num_advs; struct ble_ll_scan_advertisers *adv; /* Do we have an address match? Must match address type */ adv = &g_ble_ll_scan_rsp_advs[0]; num_advs = g_ble_ll_scan_num_rsp_advs; while (num_advs) { if (!memcmp(&adv->adv_addr, addr, BLE_DEV_ADDR_LEN)) { /* Address type must match */ if (txadd) { if (adv->sc_adv_flags & BLE_LL_SC_ADV_F_RANDOM_ADDR) { if (ext_adv) { if (adi == adv->adi) { return 1; } goto next; } return 1; } } else { if ((adv->sc_adv_flags & BLE_LL_SC_ADV_F_RANDOM_ADDR) == 0) { if (ext_adv) { if (adi == adv->adi) { return 1; } goto next; } return 1; } } } next: ++adv; --num_advs; } return 0; } #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) static void ble_ll_scan_add_scan_rsp_adv(uint8_t *addr, uint8_t txadd, uint8_t ext_adv, uint16_t adi) { uint8_t num_advs; struct ble_ll_scan_advertisers *adv; /* XXX: for now, if we dont have room, just leave */ num_advs = g_ble_ll_scan_num_rsp_advs; if (num_advs == MYNEWT_VAL(BLE_LL_NUM_SCAN_RSP_ADVS)) { return; } /* Check if address is already on the list */ if (ble_ll_scan_have_rxd_scan_rsp(addr, txadd, ext_adv, adi)) { return; } /* Add the advertiser to the array */ adv = &g_ble_ll_scan_rsp_advs[num_advs]; memcpy(&adv->adv_addr, addr, BLE_DEV_ADDR_LEN); adv->sc_adv_flags = BLE_LL_SC_ADV_F_SCAN_RSP_RXD; if (txadd) { adv->sc_adv_flags |= BLE_LL_SC_ADV_F_RANDOM_ADDR; } adv->adi = adi; ++g_ble_ll_scan_num_rsp_advs; return; } static int ble_ll_hci_send_legacy_ext_adv_report(uint8_t evtype, const uint8_t *addr, uint8_t addr_type, int8_t rssi, uint8_t adv_data_len, struct os_mbuf *adv_data, const uint8_t *inita, uint8_t inita_type) { struct ble_hci_ev_le_subev_ext_adv_rpt *ev; struct ext_adv_report *report; struct ble_hci_ev *hci_ev; if (!ble_ll_hci_is_le_event_enabled(BLE_HCI_LE_SUBEV_EXT_ADV_RPT)) { return -1; } /* Drop packet if adv data doesn't fit */ if ((sizeof(*ev) + sizeof(ev->reports[0]) + adv_data_len) > BLE_HCI_MAX_DATA_LEN) { STATS_INC(ble_ll_stats, adv_evt_dropped); return -1; } hci_ev = ble_ll_scan_get_ext_adv_report(NULL); if (!hci_ev) { return -1; } ev = (void *) hci_ev->data; report = ev->reports; switch (evtype) { case BLE_HCI_ADV_RPT_EVTYPE_ADV_IND: report->evt_type = BLE_HCI_LEGACY_ADV_EVTYPE_ADV_IND; break; case BLE_HCI_ADV_RPT_EVTYPE_DIR_IND: report->evt_type = BLE_HCI_LEGACY_ADV_EVTYPE_ADV_DIRECT_IND; break; case BLE_HCI_ADV_RPT_EVTYPE_NONCONN_IND: report->evt_type = BLE_HCI_LEGACY_ADV_EVTYPE_ADV_NONCON_IND; break; case BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP: report->evt_type = BLE_HCI_LEGACY_ADV_EVTYPE_SCAN_RSP_ADV_IND; break; case BLE_HCI_ADV_RPT_EVTYPE_SCAN_IND: report->evt_type = BLE_HCI_LEGACY_ADV_EVTYPE_ADV_SCAN_IND; break; default: BLE_LL_ASSERT(0); ble_hci_trans_buf_free((uint8_t *) hci_ev); return -1; } report->addr_type = addr_type; memcpy(report->addr, addr, BLE_DEV_ADDR_LEN); report->pri_phy = BLE_PHY_1M; report->sid = 0xFF; report->tx_power = 127; report->rssi = rssi; if (inita) { report->dir_addr_type = inita_type; memcpy(report->dir_addr, inita, BLE_DEV_ADDR_LEN); } if (adv_data_len) { hci_ev->length += adv_data_len; report->data_len = adv_data_len; os_mbuf_copydata(adv_data, 0, adv_data_len, report->data); } return ble_ll_hci_event_send(hci_ev); } #endif static int ble_ll_hci_send_adv_report(uint8_t evtype, const uint8_t *addr, uint8_t addr_type, int8_t rssi, uint8_t adv_data_len, struct os_mbuf *adv_data) { struct ble_hci_ev_le_subev_adv_rpt *ev; struct ble_hci_ev *hci_ev; int8_t *ev_rssi; if (!ble_ll_hci_is_le_event_enabled(BLE_HCI_LE_SUBEV_ADV_RPT)) { return -1; } /* Drop packet if adv data doesn't fit, note extra 1 is for RSSI */ if ((sizeof(*ev) + sizeof(ev->reports[0]) + adv_data_len + 1) > BLE_HCI_MAX_DATA_LEN) { STATS_INC(ble_ll_stats, adv_evt_dropped); return -1; } hci_ev = (void *) ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_LO); if (!hci_ev) { return -1; } hci_ev->opcode = BLE_HCI_EVCODE_LE_META; hci_ev->length = sizeof(*ev) + sizeof(ev->reports[0]) + adv_data_len + 1; ev = (void *) hci_ev->data; ev->subev_code = BLE_HCI_LE_SUBEV_ADV_RPT; ev->num_reports = 1; ev->reports[0].type = evtype; ev->reports[0].addr_type = addr_type; memcpy(ev->reports[0].addr, addr, BLE_DEV_ADDR_LEN); ev->reports[0].data_len = adv_data_len; os_mbuf_copydata(adv_data, 0, adv_data_len, ev->reports[0].data); /* RSSI is after adv data... */ ev_rssi = (int8_t *) (hci_ev->data + sizeof(*ev) + sizeof(ev->reports[0]) + adv_data_len); *ev_rssi = rssi; return ble_ll_hci_event_send(hci_ev); } static int ble_ll_hci_send_dir_adv_report(const uint8_t *addr, uint8_t addr_type, const uint8_t *inita, uint8_t inita_type, int8_t rssi) { struct ble_hci_ev_le_subev_direct_adv_rpt *ev; struct ble_hci_ev *hci_ev; if (!ble_ll_hci_is_le_event_enabled(BLE_HCI_LE_SUBEV_DIRECT_ADV_RPT)) { return -1; } hci_ev = (void *) ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_LO); if (!hci_ev) { return -1; } hci_ev->opcode = BLE_HCI_EVCODE_LE_META; hci_ev->length = sizeof(*ev) + sizeof(*(ev->reports)); ev = (void *) hci_ev->data; ev->subev_code = BLE_HCI_LE_SUBEV_DIRECT_ADV_RPT; ev->num_reports = 1; ev->reports[0].type = BLE_HCI_ADV_RPT_EVTYPE_DIR_IND; ev->reports[0].addr_type = addr_type; memcpy(ev->reports[0].addr, addr, BLE_DEV_ADDR_LEN); ev->reports[0].dir_addr_type = inita_type; memcpy(ev->reports[0].dir_addr, inita, BLE_DEV_ADDR_LEN); ev->reports[0].rssi = rssi; return ble_ll_hci_event_send(hci_ev); } static int ble_ll_scan_dup_update_legacy(uint8_t addr_type, const uint8_t *addr, uint8_t subev, uint8_t evtype) { struct ble_ll_scan_dup_entry *e; uint8_t type; type = BLE_LL_SCAN_ENTRY_TYPE_LEGACY(addr_type); /* * We assume ble_ll_scan_dup_check() was called before which either matched * some entry or allocated new one and placed in on the top of queue. */ e = TAILQ_FIRST(&g_scan_dup_list); BLE_LL_ASSERT(e && e->type == type && !memcmp(e->addr, addr, 6)); if (subev == BLE_HCI_LE_SUBEV_DIRECT_ADV_RPT) { e->flags |= BLE_LL_SCAN_DUP_F_DIR_ADV_REPORT_SENT; } else { if (evtype == BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP) { e->flags |= BLE_LL_SCAN_DUP_F_SCAN_RSP_SENT; } else { e->flags |= BLE_LL_SCAN_DUP_F_ADV_REPORT_SENT; } } return 0; } /** * Send an advertising report to the host. * * NOTE: while we are allowed to send multiple devices in one report, we * will just send for one for now. * * @param pdu_type * @param txadd * @param rxbuf * @param hdr * @param scansm */ static void ble_ll_scan_send_adv_report(uint8_t pdu_type, const uint8_t *adva, uint8_t adva_type, const uint8_t *inita, uint8_t inita_type, struct os_mbuf *om, struct ble_mbuf_hdr *hdr, struct ble_ll_scan_sm *scansm) { uint8_t subev = BLE_HCI_LE_SUBEV_ADV_RPT; uint8_t adv_data_len; uint8_t evtype; int rc; if (pdu_type == BLE_ADV_PDU_TYPE_ADV_DIRECT_IND) { if (ble_ll_is_rpa(inita, inita_type)) { /* For resolvable we send separate subevent */ subev = BLE_HCI_LE_SUBEV_DIRECT_ADV_RPT; } evtype = BLE_HCI_ADV_RPT_EVTYPE_DIR_IND; adv_data_len = 0; } else { if (pdu_type == BLE_ADV_PDU_TYPE_ADV_IND) { evtype = BLE_HCI_ADV_RPT_EVTYPE_ADV_IND; } else if (pdu_type == BLE_ADV_PDU_TYPE_ADV_SCAN_IND) { evtype = BLE_HCI_ADV_RPT_EVTYPE_SCAN_IND; } else if (pdu_type == BLE_ADV_PDU_TYPE_ADV_NONCONN_IND) { evtype = BLE_HCI_ADV_RPT_EVTYPE_NONCONN_IND; } else { evtype = BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP; } adv_data_len = om->om_data[1] - BLE_DEV_ADDR_LEN; os_mbuf_adj(om, BLE_LL_PDU_HDR_LEN + BLE_DEV_ADDR_LEN); } #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) /* If RPA has been used, make sure we use correct address types * in the advertising report. */ if (BLE_MBUF_HDR_RESOLVED(hdr)) { adva_type += 2; } if (BLE_MBUF_HDR_TARGETA_RESOLVED(hdr)) { inita_type += 2; } #endif #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) if (scansm->ext_scanning) { rc = ble_ll_hci_send_legacy_ext_adv_report(evtype, adva, adva_type, hdr->rxinfo.rssi, adv_data_len, om, inita, inita_type); goto done; } #endif if (subev == BLE_HCI_LE_SUBEV_DIRECT_ADV_RPT) { rc = ble_ll_hci_send_dir_adv_report(adva, adva_type, inita, inita_type, hdr->rxinfo.rssi); goto done; } rc = ble_ll_hci_send_adv_report(evtype, adva, adva_type, hdr->rxinfo.rssi, adv_data_len, om); done: if (!rc && scansm->scan_filt_dups) { ble_ll_scan_dup_update_legacy(adva_type, adva, subev, evtype); } } static void ble_ll_get_chan_to_scan(struct ble_ll_scan_sm *scansm, uint8_t *chan, int *phy) { struct ble_ll_scan_params *scanp = scansm->scanp; #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) struct ble_ll_aux_data *aux_data = scansm->cur_aux_data; if (!scansm->ext_scanning || !aux_data || !aux_data->scanning) { *chan = scanp->scan_chan; *phy = scanp->phy; return; } *chan = aux_data->chan; *phy = aux_data->aux_phy; #else *chan = scanp->scan_chan; *phy = scanp->phy; #endif } /** * Called to enable the receiver for scanning. * * Context: Link Layer task * * @param sch * * @return int */ static int ble_ll_scan_start(struct ble_ll_scan_sm *scansm, struct ble_ll_sched_item *sch) { int rc; struct ble_ll_scan_params *scanp = scansm->scanp; uint8_t scan_chan; #if (BLE_LL_BT5_PHY_SUPPORTED == 1) uint8_t phy_mode; #endif int phy; BLE_LL_ASSERT(scansm->scan_rsp_pending == 0); ble_ll_get_chan_to_scan(scansm, &scan_chan, &phy); /* XXX: right now scheduled item is only present if we schedule for aux * scan just make sanity check that we have proper combination of * sch and resulting scan_chan */ BLE_LL_ASSERT(!sch || scan_chan < BLE_PHY_ADV_CHAN_START); BLE_LL_ASSERT(sch || scan_chan >= BLE_PHY_ADV_CHAN_START); /* Set channel */ rc = ble_phy_setchan(scan_chan, BLE_ACCESS_ADDR_ADV, BLE_LL_CRCINIT_ADV); BLE_LL_ASSERT(rc == 0); /* * Set transmit end callback to NULL in case we transmit a scan request. * There is a callback for the connect request. */ ble_phy_set_txend_cb(NULL, NULL); #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) ble_phy_encrypt_disable(); #endif #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) if (ble_ll_resolv_enabled()) { ble_phy_resolv_list_enable(); } else { ble_phy_resolv_list_disable(); } #endif #if (BLE_LL_BT5_PHY_SUPPORTED == 1) phy_mode = ble_ll_phy_to_phy_mode(phy, BLE_HCI_LE_PHY_CODED_ANY); ble_phy_mode_set(phy_mode, phy_mode); #endif /* XXX: probably need to make sure hfxo is running too */ /* XXX: can make this better; want to just start asap. */ if (sch) { rc = ble_phy_rx_set_start_time(sch->start_time + g_ble_ll_sched_offset_ticks, sch->remainder); } else { rc = ble_phy_rx_set_start_time(os_cputime_get32() + g_ble_ll_sched_offset_ticks, 0); } if (!rc || rc == BLE_PHY_ERR_RX_LATE) { /* If we are late here, it is still OK because we keep scanning. * Clear error */ rc = 0; /* Enable/disable whitelisting */ if (scanp->scan_filt_policy & 1) { ble_ll_whitelist_enable(); } else { ble_ll_whitelist_disable(); } /* Set link layer state to scanning */ if (scanp->scan_type == BLE_SCAN_TYPE_INITIATE) { ble_ll_state_set(BLE_LL_STATE_INITIATING); } else { ble_ll_state_set(BLE_LL_STATE_SCANNING); } } return rc; } static uint8_t ble_ll_scan_get_next_adv_prim_chan(uint8_t chan) { ++chan; if (chan == BLE_PHY_NUM_CHANS) { chan = BLE_PHY_ADV_CHAN_START; } return chan; } static uint32_t ble_ll_scan_move_window_to(struct ble_ll_scan_params *scanp, uint32_t time) { uint32_t end_time; /* * Move window until given tick is before or inside window and move to next * channel for each skipped interval. */ end_time = scanp->timing.start_time + scanp->timing.window; while (CPUTIME_GEQ(time, end_time)) { scanp->timing.start_time += scanp->timing.interval; scanp->scan_chan = ble_ll_scan_get_next_adv_prim_chan(scanp->scan_chan); end_time = scanp->timing.start_time + scanp->timing.window; } return scanp->timing.start_time; } static bool ble_ll_scan_is_inside_window(struct ble_ll_scan_params *scanp, uint32_t time) { uint32_t start_time; /* Make sure we are checking against closest window */ start_time = ble_ll_scan_move_window_to(scanp, time); if (scanp->timing.window == scanp->timing.interval) { /* always inside window in continuous scan */ return true; } return CPUTIME_GEQ(time, start_time) && CPUTIME_LT(time, start_time + scanp->timing.window); } #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) static void ble_ll_scan_aux_data_free(struct ble_ll_aux_data *aux_data) { if (aux_data) { if (aux_data->evt) { ble_hci_trans_buf_free((uint8_t *)aux_data->evt); aux_data->evt = NULL; } os_memblock_put(&ext_scan_aux_pool, aux_data); STATS_INC(ble_ll_stats, aux_freed); } } struct ble_ll_aux_data * ble_ll_scan_aux_data_ref(struct ble_ll_aux_data *aux_data) { os_sr_t sr; BLE_LL_ASSERT(aux_data); OS_ENTER_CRITICAL(sr); aux_data->ref_cnt++; ble_ll_trace_u32x2(BLE_LL_TRACE_ID_AUX_REF, (uint32_t) aux_data, aux_data->ref_cnt); OS_EXIT_CRITICAL(sr); return aux_data; } void ble_ll_scan_aux_data_unref(struct ble_ll_aux_data *aux_data) { os_sr_t sr; BLE_LL_ASSERT(aux_data); OS_ENTER_CRITICAL(sr); aux_data->ref_cnt--; ble_ll_trace_u32x2(BLE_LL_TRACE_ID_AUX_UNREF, (uint32_t) aux_data, aux_data->ref_cnt); if (aux_data->ref_cnt == 0) { /* * Some validation to make sure that we completed scan properly: * - we either did not send any report or sent completed/truncated * - we only sent one of completed/truncated * - in case of error, we wither did not send anything or sent truncated */ BLE_LL_ASSERT(!(aux_data->flags_ll & BLE_LL_AUX_FLAG_HCI_SENT_ANY) || ((aux_data->flags_ll & BLE_LL_AUX_FLAG_HCI_SENT_ANY) && (aux_data->flags_ll & (BLE_LL_AUX_FLAG_HCI_SENT_COMPLETED | BLE_LL_AUX_FLAG_HCI_SENT_TRUNCATED)))); BLE_LL_ASSERT(!(aux_data->flags_ll & BLE_LL_AUX_FLAG_HCI_SENT_COMPLETED) || !(aux_data->flags_ll & BLE_LL_AUX_FLAG_HCI_SENT_TRUNCATED)); BLE_LL_ASSERT(!(aux_data->flags_ll & BLE_LL_AUX_FLAG_SCAN_ERROR) || !(aux_data->flags_ll & BLE_LL_AUX_FLAG_HCI_SENT_ANY) || (aux_data->flags_ll & BLE_LL_AUX_FLAG_HCI_SENT_TRUNCATED)); ble_ll_scan_aux_data_free(aux_data); } OS_EXIT_CRITICAL(sr); } static void ble_ll_scan_sched_remove(struct ble_ll_sched_item *sch) { ble_ll_scan_end_adv_evt(sch->cb_arg); ble_ll_scan_aux_data_unref(sch->cb_arg); sch->cb_arg = NULL; } #endif /** * Stop the scanning state machine */ void ble_ll_scan_sm_stop(int chk_disable) { os_sr_t sr; uint8_t lls; struct ble_ll_scan_sm *scansm; /* Stop the scanning timer */ scansm = &g_ble_ll_scan_sm; os_cputime_timer_stop(&scansm->scan_timer); /* Only set state if we are currently in a scan window */ if (chk_disable) { OS_ENTER_CRITICAL(sr); lls = ble_ll_state_get(); if ((lls == BLE_LL_STATE_SCANNING) || (lls == BLE_LL_STATE_INITIATING && chk_disable == 1)) { /* Disable phy */ ble_phy_disable(); /* Set LL state to standby */ ble_ll_state_set(BLE_LL_STATE_STANDBY); } OS_EXIT_CRITICAL(sr); } OS_ENTER_CRITICAL(sr); /* Disable scanning state machine */ scansm->scan_enabled = 0; scansm->restart_timer_needed = 0; #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) if (scansm->ext_scanning) { ble_ll_scan_clean_cur_aux_data(); ble_ll_sched_rmv_elem_type(BLE_LL_SCHED_TYPE_AUX_SCAN, ble_ll_scan_sched_remove); scansm->ext_scanning = 0; } #endif /* Update backoff if we failed to receive scan response */ if (scansm->scan_rsp_pending) { scansm->scan_rsp_pending = 0; ble_ll_scan_req_backoff(scansm, 0); } OS_EXIT_CRITICAL(sr); /* Count # of times stopped */ STATS_INC(ble_ll_stats, scan_stops); /* No need for RF anymore */ OS_ENTER_CRITICAL(sr); ble_ll_rfmgmt_scan_changed(false, 0); ble_ll_rfmgmt_release(); OS_EXIT_CRITICAL(sr); } static int ble_ll_scan_sm_start(struct ble_ll_scan_sm *scansm) { struct ble_ll_scan_params *scanp; struct ble_ll_scan_params *scanp_next; if (!ble_ll_is_valid_own_addr_type(scansm->own_addr_type, g_random_addr)) { return BLE_ERR_INV_HCI_CMD_PARMS; } BLE_LL_ASSERT(scansm->scanp); scanp = scansm->scanp; scanp_next = scansm->scanp_next; /* Count # of times started */ STATS_INC(ble_ll_stats, scan_starts); /* Set flag telling us that scanning is enabled */ scansm->scan_enabled = 1; /* Set first advertising channel */ scanp->scan_chan = BLE_PHY_ADV_CHAN_START; if (scanp_next) { scanp_next->scan_chan = BLE_PHY_ADV_CHAN_START; } /* Reset scan request backoff parameters to default */ scansm->upper_limit = 1; scansm->backoff_count = 1; scansm->scan_rsp_pending = 0; /* Forget filtered advertisers from previous scan. */ g_ble_ll_scan_num_rsp_advs = 0; os_mempool_clear(&g_scan_dup_pool); TAILQ_INIT(&g_scan_dup_list); /* * First scan window can start when RF is enabled. Add 1 tick since we are * most likely not aligned with ticks so RF may be effectively enabled 1 * tick later. */ scanp->timing.start_time = ble_ll_rfmgmt_enable_now(); ble_ll_rfmgmt_scan_changed(true, scanp->timing.start_time); if (scanp_next) { /* Schedule start time right after first phy */ scanp_next->timing.start_time = scanp->timing.start_time + scanp->timing.window; } /* Start scan at 1st window */ os_cputime_timer_start(&scansm->scan_timer, scanp->timing.start_time); return BLE_ERR_SUCCESS; } #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) static void ble_ll_aux_scan_rsp_failed(struct ble_ll_scan_sm *scansm) { if (!scansm->cur_aux_data) { return; } STATS_INC(ble_ll_stats, aux_scan_rsp_err); ble_ll_scan_interrupted(scansm); } #endif static void ble_ll_scan_interrupted_event_cb(struct ble_npl_event *ev) { struct ble_ll_scan_sm *scansm = &g_ble_ll_scan_sm; #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) struct ble_ll_aux_data *aux_data; #endif if (!scansm->scan_enabled) { return; } #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) aux_data = ble_npl_event_get_arg(ev); if (aux_data) { if (scansm->scan_rsp_pending) { STATS_INC(ble_ll_stats, aux_scan_rsp_err); } ble_ll_scan_end_adv_evt(aux_data); ble_ll_scan_aux_data_unref(aux_data); ble_npl_event_set_arg(ev, NULL); STATS_INC(ble_ll_stats, aux_missed_adv); } #endif /* * If we timed out waiting for a response, the scan response pending * flag should be set. Deal with scan backoff. Put device back into rx. */ if (scansm->scan_rsp_pending) { scansm->scan_rsp_pending = 0; ble_ll_scan_req_backoff(scansm, 0); } ble_ll_scan_chk_resume(); } /** * Called to process the scanning OS event which was posted to the LL task * * Context: Link Layer task. * * @param arg */ static void ble_ll_scan_event_proc(struct ble_npl_event *ev) { struct ble_ll_scan_sm *scansm; os_sr_t sr; bool start_scan; bool inside_window; struct ble_ll_scan_params *scanp; #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) bool inside_window_next; struct ble_ll_scan_params *scanp_next; #endif uint32_t next_proc_time; uint32_t now; /* * Get the scanning state machine. If not enabled (this is possible), just * leave and do nothing (just make sure timer is stopped). */ scansm = (struct ble_ll_scan_sm *)ble_npl_event_get_arg(ev); scanp = scansm->scanp; #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) scanp_next = scansm->scanp_next; #endif OS_ENTER_CRITICAL(sr); if (!scansm->scan_enabled) { os_cputime_timer_stop(&scansm->scan_timer); ble_ll_rfmgmt_scan_changed(false, 0); ble_ll_rfmgmt_release(); OS_EXIT_CRITICAL(sr); return; } if (scansm->cur_aux_data || scansm->scan_rsp_pending) { /* Aux scan in progress. Wait */ STATS_INC(ble_ll_stats, scan_timer_stopped); scansm->restart_timer_needed = 1; OS_EXIT_CRITICAL(sr); return; } now = os_cputime_get32(); inside_window = ble_ll_scan_is_inside_window(scanp, now); #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) /* Update also next PHY if configured */ if (scanp_next) { inside_window_next = ble_ll_scan_is_inside_window(scanp_next, now); /* * Switch PHY if current PHY is outside window and next PHY is either * inside window or has next window earlier than current PHY. */ if (!inside_window && ((inside_window_next || CPUTIME_LEQ(scanp_next->timing.start_time, scanp->timing.start_time)))) { scansm->scanp = scanp_next; scansm->scanp_next = scanp; scanp = scansm->scanp; scanp_next = scansm->scanp_next; inside_window = inside_window_next; } } #endif /* * At this point scanp and scanp_next point to current or closest scan * window on both PHYs (scanp is the closer one). Make sure RF is enabled * on time. */ ble_ll_rfmgmt_scan_changed(true, scanp->timing.start_time); /* * If we are inside window, next scan proc should happen at the end of * current window to either disable scan or switch to next PHY. * If we are outside window, next scan proc should happen at the time of * closest scan window. */ if (inside_window) { next_proc_time = scanp->timing.start_time + scanp->timing.window; } else { next_proc_time = scanp->timing.start_time; } /* * If we are not in the standby state it means that the scheduled * scanning event was overlapped in the schedule. In this case all we do * is post the scan schedule end event. */ start_scan = inside_window; switch (ble_ll_state_get()) { case BLE_LL_STATE_ADV: case BLE_LL_STATE_CONNECTION: case BLE_LL_STATE_SYNC: start_scan = false; break; case BLE_LL_STATE_INITIATING: /* Must disable PHY since we will move to a new channel */ ble_phy_disable(); if (!inside_window) { ble_ll_state_set(BLE_LL_STATE_STANDBY); } /* PHY is disabled - make sure we do not wait for AUX_CONNECT_RSP */ ble_ll_conn_reset_pending_aux_conn_rsp(); break; case BLE_LL_STATE_SCANNING: /* Must disable PHY since we will move to a new channel */ ble_phy_disable(); if (!inside_window) { ble_ll_state_set(BLE_LL_STATE_STANDBY); } break; case BLE_LL_STATE_STANDBY: break; default: BLE_LL_ASSERT(0); break; } if (start_scan) { ble_ll_scan_start(scansm, NULL); } else { ble_ll_rfmgmt_release(); } OS_EXIT_CRITICAL(sr); os_cputime_timer_start(&scansm->scan_timer, next_proc_time); } /** * ble ll scan rx pdu start * * Called when a PDU reception has started and the Link Layer is in the * scanning state. * * Context: Interrupt * * @param pdu_type * @param rxflags * * @return int * 0: we will not attempt to reply to this frame * 1: we may send a response to this frame. */ int ble_ll_scan_rx_isr_start(uint8_t pdu_type, uint16_t *rxflags) { int rc; struct ble_ll_scan_sm *scansm; struct ble_ll_scan_params *scanp; rc = 0; scansm = &g_ble_ll_scan_sm; scanp = scansm->scanp; switch (scanp->scan_type) { case BLE_SCAN_TYPE_ACTIVE: /* If adv ind or scan ind, we may send scan request */ if ((pdu_type == BLE_ADV_PDU_TYPE_ADV_IND) || (pdu_type == BLE_ADV_PDU_TYPE_ADV_SCAN_IND)) { 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 (scansm->cur_aux_data && !scansm->scan_rsp_pending ) { STATS_INC(ble_ll_stats, aux_received); } /* * If this is the first PDU after we sent the scan response (as * denoted by the scan rsp pending flag), we set a bit in the ble * header so the link layer can check to see if the scan request * was successful. We do it this way to let the Link Layer do the * work for successful scan requests. If failed, we do the work here. */ if (scansm->scan_rsp_pending) { scansm->scan_rsp_pending = 0; if (pdu_type == BLE_ADV_PDU_TYPE_SCAN_RSP) { *rxflags |= BLE_MBUF_HDR_F_SCAN_RSP_RXD; } else if (pdu_type == BLE_ADV_PDU_TYPE_AUX_SCAN_RSP) { *rxflags |= BLE_MBUF_HDR_F_SCAN_RSP_RXD; } else { ble_ll_scan_req_backoff(scansm, 0); #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) ble_ll_aux_scan_rsp_failed(scansm); #endif } } break; case BLE_SCAN_TYPE_PASSIVE: #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; } break; #endif default: break; } return rc; } #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) static uint8_t ble_ll_ext_adv_phy_mode_to_local_phy(uint8_t adv_phy_mode) { switch (adv_phy_mode) { case 0x00: return BLE_PHY_1M; #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_2M_PHY) case 0x01: return BLE_PHY_2M; #endif #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY) case 0x02: return BLE_PHY_CODED; #endif } return 0; } static int ble_ll_ext_scan_parse_aux_ptr(struct ble_ll_aux_data *aux_data, uint8_t *buf) { uint32_t aux_ptr_field = get_le32(buf) & 0x00FFFFFF; aux_data->chan = (aux_ptr_field) & 0x3F; if (aux_data->chan >= BLE_PHY_NUM_DATA_CHANS) { return -1; } /* TODO use CA aux_ptr_field >> 6 */ aux_data->offset = 30 * ((aux_ptr_field >> 8) & 0x1FFF); if ((aux_ptr_field >> 7) & 0x01) { aux_data->offset *= 10; aux_data->offset_units = 1; } if (aux_data->offset < BLE_LL_MAFS) { return -1; } aux_data->aux_phy = ble_ll_ext_adv_phy_mode_to_local_phy((aux_ptr_field >> 21) & 0x07); if (aux_data->aux_phy == 0) { return -1; } return 0; } static void ble_ll_ext_scan_parse_adv_info(struct ext_adv_report *report, const uint8_t *buf) { uint16_t adv_info = get_le16(buf); /* TODO Use DID */ report->sid = (adv_info >> 12); } /** * ble_ll_scan_update_aux_data * * Update aux_data stored in ble_hdr.rxinfo.user_data. If no aux_data is present * (i.e. processing ADV_EXT_IND) this will try to allocate new aux_data. * * Context: Interrupt * * @param ble_hdr * @param rxbuf * * @return int * 1: do not scan for next AUX (no AuxPtr or malformed data) * 0: scan for next AUX (valid AuxPtr found) * -1: error */ int ble_ll_scan_update_aux_data(struct ble_mbuf_hdr *ble_hdr, uint8_t *rxbuf, bool *adva_present) { uint8_t pdu_hdr; uint8_t pdu_len; uint8_t adv_mode; uint8_t eh_len; uint8_t eh_flags; uint8_t *eh; struct ble_ll_aux_data *aux_data; bool is_aux; aux_data = ble_hdr->rxinfo.user_data; /* aux_data is initially not set only for ADV_EXT_IND */ is_aux = aux_data; pdu_hdr = rxbuf[0]; pdu_len = rxbuf[1]; /* PDU without at least Extended Header Length is invalid */ if (pdu_len == 0) { return -1; } adv_mode = rxbuf[2] >> 6; eh_len = rxbuf[2] & 0x3f; eh_flags = rxbuf[3]; eh = &rxbuf[4]; /* * PDU without Extended Header is valid in case of last AUX_CHAIN_IND in * chain so aux_data has to be set and advertising mode has to be 00b, * otherwise it's an invalid PDU. */ if (eh_len == 0) { if (!aux_data || adv_mode) { return -1; } aux_data->flags_isr |= BLE_LL_AUX_FLAG_SCAN_COMPLETE; return 1; } /* * If aux_data is not set, this is ADV_EXT_IND which starts new extended * advertising event. */ if (!aux_data) { if (ble_ll_scan_ext_adv_init(&aux_data)) { return -1; } aux_data->aux_primary_phy = ble_hdr->rxinfo.phy; } else { if (aux_data->flags_isr & BLE_LL_AUX_FLAG_AUX_ADV_RECEIVED) { aux_data->flags_isr |= BLE_LL_AUX_FLAG_AUX_CHAIN_RECEIVED; } else { aux_data->flags_isr |= BLE_LL_AUX_FLAG_AUX_ADV_RECEIVED; } } /* Now parse extended header... */ if (eh_flags & (1 << BLE_LL_EXT_ADV_ADVA_BIT)) { aux_data->flags |= BLE_LL_AUX_HAS_ADVA; memcpy(aux_data->adva, eh, 6); aux_data->adva_type = !!(pdu_hdr & BLE_ADV_PDU_HDR_TXADD_MASK); eh += BLE_LL_EXT_ADV_ADVA_SIZE; if (adva_present) { *adva_present = true; } } else if (adva_present) { *adva_present = false; } if (eh_flags & (1 << BLE_LL_EXT_ADV_TARGETA_BIT)) { aux_data->flags |= BLE_LL_AUX_HAS_TARGETA; memcpy(aux_data->targeta, eh, 6); aux_data->targeta_type = !!(pdu_hdr & BLE_ADV_PDU_HDR_RXADD_MASK); eh += BLE_LL_EXT_ADV_TARGETA_SIZE; } if (eh_flags & (1 << BLE_LL_EXT_ADV_CTE_INFO_BIT)) { eh += 1; } if (eh_flags & (1 << BLE_LL_EXT_ADV_DATA_INFO_BIT)) { aux_data->flags |= BLE_LL_AUX_HAS_ADI; if (is_aux) { if (get_le16(eh) != aux_data->adi) { aux_data->flags_isr |= BLE_LL_AUX_FLAG_SCAN_ERROR; STATS_INC(ble_ll_stats, aux_chain_err); } } else { aux_data->adi = get_le16(eh); } eh += BLE_LL_EXT_ADV_DATA_INFO_SIZE; } if (eh_flags & (1 << BLE_LL_EXT_ADV_AUX_PTR_BIT)) { if (ble_ll_ext_scan_parse_aux_ptr(aux_data, eh)) { aux_data->flags_isr |= BLE_LL_AUX_FLAG_SCAN_ERROR; } } else if (!(adv_mode & BLE_LL_EXT_ADV_MODE_SCAN)) { /* No AuxPtr for scannable PDU is ignored since we can still scan it */ aux_data->flags_isr |= BLE_LL_AUX_FLAG_SCAN_COMPLETE; } ble_hdr->rxinfo.user_data = aux_data; /* Do not scan for next AUX if either no AuxPtr or malformed data found */ return !(eh_flags & (1 << BLE_LL_EXT_ADV_AUX_PTR_BIT)) || (aux_data->flags_isr & BLE_LL_AUX_FLAG_SCAN_ERROR); } /** * Called when a receive ADV_EXT PDU has ended. * * Context: Interrupt * * @return int * < 0 Error * >= 0: Success (number of bytes left in PDU) * */ static int ble_ll_scan_parse_ext_hdr(struct os_mbuf *om, const uint8_t *adva, uint8_t adva_type, const uint8_t *inita, uint8_t inita_type, struct ble_mbuf_hdr *ble_hdr, struct ext_adv_report *report) { uint8_t pdu_len; uint8_t ext_hdr_len; uint8_t ext_hdr_flags; uint8_t *ext_hdr; uint8_t *rxbuf = om->om_data; int i = 1; struct ble_ll_scan_sm *scansm; struct ble_ll_aux_data *aux_data = ble_hdr->rxinfo.user_data; BLE_LL_ASSERT(report); scansm = &g_ble_ll_scan_sm; if (!scansm->ext_scanning) { /* Ignore ext adv if host does not want it*/ return -1; } pdu_len = rxbuf[1]; if (pdu_len == 0) { return -1; } report->evt_type = rxbuf[2] >> 6; if ( report->evt_type > BLE_LL_EXT_ADV_MODE_SCAN) { return -1; } if (BLE_MBUF_HDR_SCAN_RSP_RXD(ble_hdr)) { report->evt_type |= BLE_HCI_ADV_SCAN_RSP_MASK; } ext_hdr_len = rxbuf[2] & 0x3F; os_mbuf_adj(om, 3); ext_hdr_flags = rxbuf[3]; ext_hdr = &rxbuf[4]; if (ext_hdr_len) { i = 0; if (ext_hdr_flags & (1 << BLE_LL_EXT_ADV_ADVA_BIT)) { i += BLE_LL_EXT_ADV_ADVA_SIZE; } if (adva) { memcpy(report->addr, adva, 6); report->addr_type = adva_type; } if (ext_hdr_flags & (1 << BLE_LL_EXT_ADV_TARGETA_BIT)) { i += BLE_LL_EXT_ADV_TARGETA_SIZE; } if (inita) { memcpy(report->dir_addr, inita, 6); report->dir_addr_type = inita_type; report->evt_type |= BLE_HCI_ADV_DIRECT_MASK; } if (ext_hdr_flags & (1 << BLE_LL_EXT_ADV_CTE_INFO_BIT)) { /* Just skip it for now*/ i += 1; } if (ext_hdr_flags & (1 << BLE_LL_EXT_ADV_DATA_INFO_BIT)) { ble_ll_ext_scan_parse_adv_info(report, (ext_hdr + i)); i += BLE_LL_EXT_ADV_DATA_INFO_SIZE; } else if (report->evt_type & BLE_HCI_ADV_SCAN_RSP_MASK) { report->sid = (aux_data->adi >> 12); } /* In this point of time we don't want to care about aux ptr */ if (ext_hdr_flags & (1 << BLE_LL_EXT_ADV_AUX_PTR_BIT)) { i += BLE_LL_EXT_ADV_AUX_PTR_SIZE; } if (ext_hdr_flags & (1 << BLE_LL_EXT_ADV_SYNC_INFO_BIT)) { report->periodic_itvl = get_le16(ext_hdr + i + 2); i += BLE_LL_EXT_ADV_SYNC_INFO_SIZE; } if (ext_hdr_flags & (1 << BLE_LL_EXT_ADV_TX_POWER_BIT)) { report->tx_power = *(ext_hdr + i); i += BLE_LL_EXT_ADV_TX_POWER_SIZE; } /* TODO Handle ACAD if needed */ } /* In the event we need information on primary and secondary PHY used during * advertising. */ if (!aux_data) { report->pri_phy = ble_hdr->rxinfo.phy; goto done; } report->sec_phy = aux_data->aux_phy; report->pri_phy = aux_data->aux_primary_phy; if (ext_hdr_len) { /* Adjust mbuf to contain advertising data only */ os_mbuf_adj(om, ext_hdr_len); } /* Let us first keep update event type in aux data. * Note that in aux chain and aux scan response packets * we do miss original event type, which we need for advertising report. */ aux_data->evt_type |= report->evt_type; report->evt_type = aux_data->evt_type; done: return pdu_len - ext_hdr_len - 1; } static int ble_ll_scan_get_addr_from_ext_adv(uint8_t *rxbuf, struct ble_mbuf_hdr *ble_hdr, uint8_t **addr, uint8_t *addr_type, uint8_t **inita, uint8_t *inita_type, int *ext_mode) { uint8_t pdu_len; uint8_t ext_hdr_len; uint8_t ext_hdr_flags; uint8_t *ext_hdr; bool has_adva = false; bool has_inita = false; int i; struct ble_ll_aux_data *aux_data = ble_hdr->rxinfo.user_data; *addr = NULL; *inita = NULL; pdu_len = rxbuf[1]; if (pdu_len == 0) { return -1; } *ext_mode = rxbuf[2] >> 6; if (*ext_mode > BLE_LL_EXT_ADV_MODE_SCAN) { return -1; } ext_hdr_len = rxbuf[2] & 0x3F; if (ext_hdr_len == 0) { goto done; } ext_hdr_flags = rxbuf[3]; ext_hdr = &rxbuf[4]; i = 0; if (ext_hdr_flags & (1 << BLE_LL_EXT_ADV_ADVA_BIT)) { if (ext_hdr_len < BLE_LL_EXT_ADV_ADVA_SIZE) { return -1; } *addr = ext_hdr + i; *addr_type = ble_ll_get_addr_type(rxbuf[0] & BLE_ADV_PDU_HDR_TXADD_MASK); i += BLE_LL_EXT_ADV_ADVA_SIZE; has_adva = true; } if (ext_hdr_flags & (1 << BLE_LL_EXT_ADV_TARGETA_BIT)) { *inita = ext_hdr + i; *inita_type = ble_ll_get_addr_type(rxbuf[0] & BLE_ADV_PDU_HDR_RXADD_MASK); i += BLE_LL_EXT_ADV_TARGETA_SIZE; has_inita = true; } done: /* Check if we had address already. If yes, replace it with new one */ if (aux_data) { /* If address has been provided, we do have it already in aux_data.*/ if (aux_data->flags & BLE_LL_AUX_HAS_ADVA) { if (!has_adva) { *addr = aux_data->adva; *addr_type = aux_data->adva_type; } else { memcpy(aux_data->adva, *addr, 6); aux_data->adva_type = *addr_type; } } if (aux_data->flags & BLE_LL_AUX_HAS_TARGETA) { if (!has_inita) { *inita = aux_data->targeta; *inita_type = aux_data->targeta_type; } else { memcpy(aux_data->targeta, *inita, 6); aux_data->targeta_type = *inita_type; } } } return 0; } #endif int ble_ll_scan_adv_decode_addr(uint8_t pdu_type, uint8_t *rxbuf, struct ble_mbuf_hdr *ble_hdr, uint8_t **addr, uint8_t *addr_type, uint8_t **inita, uint8_t *inita_type, int *ext_mode) { /* * XXX this should be only used for legacy advertising, but need to refactor * code in ble_ll_init first so it does not call this for ext */ if (pdu_type != BLE_ADV_PDU_TYPE_ADV_EXT_IND && pdu_type != BLE_ADV_PDU_TYPE_AUX_CONNECT_RSP) { /* Legacy advertising */ *addr_type = ble_ll_get_addr_type(rxbuf[0] & BLE_ADV_PDU_HDR_TXADD_MASK); *addr = rxbuf + BLE_LL_PDU_HDR_LEN; if (pdu_type != BLE_ADV_PDU_TYPE_ADV_DIRECT_IND) { *inita = NULL; *inita_type = 0; return 0; } *inita = rxbuf + BLE_LL_PDU_HDR_LEN + BLE_DEV_ADDR_LEN; *inita_type = ble_ll_get_addr_type(rxbuf[0] & BLE_ADV_PDU_HDR_RXADD_MASK); return 0; } /* Extended advertising */ #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) return ble_ll_scan_get_addr_from_ext_adv(rxbuf, ble_hdr, addr, addr_type, inita, inita_type, ext_mode); #else return -1; #endif return 0; } static void ble_ll_scan_get_addr_data_from_legacy(uint8_t pdu_type, uint8_t *rxbuf, struct ble_ll_scan_addr_data *addrd) { BLE_LL_ASSERT(pdu_type < BLE_ADV_PDU_TYPE_ADV_EXT_IND); addrd->adva_present = true; addrd->adva = rxbuf + BLE_LL_PDU_HDR_LEN; addrd->adva_type = ble_ll_get_addr_type(rxbuf[0] & BLE_ADV_PDU_HDR_TXADD_MASK); if (pdu_type == BLE_ADV_PDU_TYPE_ADV_DIRECT_IND) { addrd->targeta = rxbuf + BLE_LL_PDU_HDR_LEN + BLE_DEV_ADDR_LEN; addrd->targeta_type = ble_ll_get_addr_type(rxbuf[0] & BLE_ADV_PDU_HDR_RXADD_MASK); } else { addrd->targeta = NULL; addrd->targeta_type = 0; } } /* * Matches incoming PDU using scan filter policy and whitelist, if applicable. * This will also resolve addresses and update flags/fields in header and * addr_data as needed. * * @return 0 = no match * 1 = match * 2 = match, but do not scan */ static int ble_ll_scan_rx_filter(struct ble_mbuf_hdr *hdr, struct ble_ll_scan_addr_data *addrd) { struct ble_ll_scan_sm *scansm = &g_ble_ll_scan_sm; struct ble_ll_scan_params *scanp = scansm->scanp; #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) struct ble_ll_aux_data *aux_data = hdr->rxinfo.user_data; #endif struct ble_mbuf_hdr_rxinfo *rxinfo = &hdr->rxinfo; struct ble_ll_resolv_entry *rl = NULL; #endif bool scan_req_allowed = true; int resolved = 0; /* Use AdvA as initial advertiser address, we may try to resolve it later */ addrd->adv_addr = addrd->adva; addrd->adv_addr_type = addrd->adva_type; #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) /* By default, assume AdvA is not resolved */ rxinfo->rpa_index = -1; switch (ble_ll_addr_subtype(addrd->adva, addrd->adva_type)) { case BLE_LL_ADDR_SUBTYPE_RPA: /* * Only resolve if packet actually contained AdvA. * In extended advertising PDUs we may use RL index from a PDU that * already had AdvA (e.g. ADV_EXT_IND in case of AUX_ADV_IND without * AdvA). In legacy advertising PDUs we always need to resolve AdvA. */ if (addrd->adva_present) { rxinfo->rpa_index = ble_hw_resolv_list_match(); } else { #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) BLE_LL_ASSERT(aux_data); rxinfo->rpa_index = aux_data->rpa_index; #else BLE_LL_ASSERT(false); rxinfo->rpa_index = -1; #endif } if (rxinfo->rpa_index < 0) { break; } #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) if (aux_data) { aux_data->rpa_index = rxinfo->rpa_index; } #endif /* Use resolved identity address as advertiser address */ rl = &g_ble_ll_resolv_list[rxinfo->rpa_index]; addrd->adv_addr = rl->rl_identity_addr; addrd->adv_addr_type = rl->rl_addr_type; addrd->rl = rl; rxinfo->flags |= BLE_MBUF_HDR_F_RESOLVED; resolved = 1; break; case BLE_LL_ADDR_SUBTYPE_IDENTITY: /* * If AdvA is an identity address, we need to check if that device was * added to RL in order to use proper privacy mode. */ rl = ble_ll_resolv_list_find(addrd->adva, addrd->adva_type); if (!rl) { break; } addrd->rl = rl; /* Ignore device if using network privacy mode and it has IRK */ if ((rl->rl_priv_mode == BLE_HCI_PRIVACY_NETWORK) && rl->rl_has_peer) { return 0; } break; default: /* NRPA goes through filtering policy directly */ break; } if (addrd->targeta) { switch (ble_ll_addr_subtype(addrd->targeta, addrd->targeta_type)) { case BLE_LL_ADDR_SUBTYPE_RPA: /* Check if TargetA can be resolved using the same RL entry as AdvA */ if (rl && ble_ll_resolv_rpa(addrd->targeta, rl->rl_local_irk)) { rxinfo->flags |= BLE_MBUF_HDR_F_TARGETA_RESOLVED; break; } /* Check if scan filter policy allows unresolved RPAs to be processed */ if (!(scanp->scan_filt_policy & 0x02)) { return 0; } /* * We will notify host as requited by scan policy, but make sure we * do not send scan request since we do not know if this is directed * to us. */ scan_req_allowed = false; break; case BLE_LL_ADDR_SUBTYPE_IDENTITY: /* We shall ignore identity in TargetA if we are using RPA */ if ((scanp->own_addr_type & 0x02) && rl && rl->rl_has_local) { return 0; } /* Ignore if not directed to us */ if (!ble_ll_is_our_devaddr(addrd->targeta, addrd->targeta_type)) { return 0; } break; default: /* NRPA goes through filtering policy directly */ break; } } #else /* Ignore if not directed to us */ if (addrd->targeta && !ble_ll_is_our_devaddr(addrd->targeta, addrd->targeta_type)) { return 0; } #endif /* Check on WL if required by scan filter policy */ if (scanp->scan_filt_policy & 0x01) { if (!ble_ll_whitelist_match(addrd->adv_addr, addrd->adv_addr_type, resolved)) { return 0; } } return scan_req_allowed ? 1 : 2; } static int ble_ll_scan_rx_isr_on_legacy(uint8_t pdu_type, uint8_t *rxbuf, struct ble_mbuf_hdr *hdr, struct ble_ll_scan_addr_data *addrd) { struct ble_ll_scan_sm *scansm = &g_ble_ll_scan_sm; struct ble_ll_scan_params *scanp = scansm->scanp; struct ble_mbuf_hdr_rxinfo *rxinfo = &hdr->rxinfo; uint8_t sreq_adva_type; uint8_t *sreq_adva; int rc; ble_ll_scan_get_addr_data_from_legacy(pdu_type, rxbuf, addrd); if (pdu_type == BLE_ADV_PDU_TYPE_SCAN_RSP) { if (!BLE_MBUF_HDR_SCAN_RSP_RXD(hdr)) { /* * We were not expecting scan response so just ignore and do not * update backoff. */ return -1; } sreq_adva_type = !!(scansm->pdu_data.hdr_byte & BLE_ADV_PDU_HDR_RXADD_MASK); sreq_adva = scansm->pdu_data.adva; /* * Ignore scan response if AdvA does not match AdvA in request and also * update backoff as if there was no scan response. */ if ((addrd->adva_type != sreq_adva_type) || memcmp(addrd->adva, sreq_adva, BLE_DEV_ADDR_LEN)) { ble_ll_scan_req_backoff(scansm, 0); return -1; } #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) /* * We are not pushing this one through filters so need to update * rpa_index here as otherwise pkt_in won't be able to determine * advertiser address properly. */ rxinfo->rpa_index = ble_hw_resolv_list_match(); if (rxinfo->rpa_index >= 0) { rxinfo->flags |= BLE_MBUF_HDR_F_RESOLVED; } #endif rxinfo->flags |= BLE_MBUF_HDR_F_DEVMATCH; return 0; } rc = ble_ll_scan_rx_filter(hdr, addrd); if (!rc) { return 0; } rxinfo->flags |= BLE_MBUF_HDR_F_DEVMATCH; if (rc == 2) { /* Scan request forbidden by filter policy */ return 0; } return (scanp->scan_type == BLE_SCAN_TYPE_ACTIVE) && ((pdu_type == BLE_ADV_PDU_TYPE_ADV_IND) || (pdu_type == BLE_ADV_PDU_TYPE_ADV_SCAN_IND)); } #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) static int ble_ll_scan_rx_isr_on_aux(uint8_t pdu_type, uint8_t *rxbuf, struct ble_mbuf_hdr *hdr, struct ble_ll_scan_addr_data *addrd) { struct ble_ll_scan_sm *scansm = &g_ble_ll_scan_sm; struct ble_ll_scan_params *scanp = scansm->scanp; struct ble_mbuf_hdr_rxinfo *rxinfo = &hdr->rxinfo; struct ble_ll_aux_data *aux_data; int rc; if (!scansm->ext_scanning) { return -1; } rc = ble_ll_scan_update_aux_data(hdr, rxbuf, &addrd->adva_present); if (rc < 0) { rxinfo->flags |= BLE_MBUF_HDR_F_AUX_INVALID; return -1; } else if (rc == 0) { rxinfo->flags |= BLE_MBUF_HDR_F_AUX_PTR_WAIT; } /* Now we can update aux_data from header since it may have just been created */ aux_data = rxinfo->user_data; /* * Restore proper header flags if filtering was already done successfully on * some previous PDU in an event. */ if (aux_data->flags & BLE_LL_AUX_IS_MATCHED) { rxinfo->flags |= BLE_MBUF_HDR_F_DEVMATCH; #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) rxinfo->rpa_index = aux_data->rpa_index; if (rxinfo->rpa_index >= 0) { rxinfo->flags |= BLE_MBUF_HDR_F_RESOLVED; } if (aux_data->flags & BLE_LL_AUX_IS_TARGETA_RESOLVED) { rxinfo->flags |= BLE_MBUF_HDR_F_TARGETA_RESOLVED; } #endif goto done; } if (aux_data->flags & BLE_LL_AUX_HAS_ADVA) { addrd->adva = aux_data->adva; addrd->adva_type = aux_data->adva_type; } else { /* Accept this PDU and wait for AdvA in aux */ rxinfo->flags |= BLE_MBUF_HDR_F_DEVMATCH; return 0; } if (aux_data->flags & BLE_LL_AUX_HAS_TARGETA) { addrd->targeta = aux_data->targeta; addrd->targeta_type = aux_data->targeta_type; } else { addrd->targeta = NULL; } rc = ble_ll_scan_rx_filter(hdr, addrd); if (!rc) { return 0; } rxinfo->flags |= BLE_MBUF_HDR_F_DEVMATCH; /* * Once we matched device, there's no need to go through filtering on every * other PDU in an event so just store info required to restore state for * subsequent PDUs in aux_data. */ aux_data->flags |= BLE_LL_AUX_IS_MATCHED; if (rxinfo->flags & BLE_MBUF_HDR_F_TARGETA_RESOLVED) { aux_data->flags |= BLE_LL_AUX_IS_TARGETA_RESOLVED; /* AdvA state is already stored in rpa_index */ } if (rc == 2) { /* Scan request forbidden by filter policy */ return 0; } done: return (scanp->scan_type == BLE_SCAN_TYPE_ACTIVE) && ((rxbuf[2] >> 6) == BLE_LL_EXT_ADV_MODE_SCAN); } #endif static bool ble_ll_scan_send_scan_req(uint8_t pdu_type, uint8_t *rxbuf, struct ble_mbuf_hdr *hdr, struct ble_ll_scan_addr_data *addrd) { struct ble_ll_scan_sm *scansm = &g_ble_ll_scan_sm; struct ble_mbuf_hdr_rxinfo *rxinfo = &hdr->rxinfo; #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) struct ble_ll_aux_data *aux_data = rxinfo->user_data; uint8_t phy_mode; #endif bool is_ext_adv = false; uint16_t adi = 0; int rc; #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) if (pdu_type == BLE_ADV_PDU_TYPE_ADV_EXT_IND) { if (ble_ll_scan_get_adi(aux_data, &adi) < 0) { return false; } is_ext_adv = true; } #endif /* Check if we already scanned this device successfully */ if (ble_ll_scan_have_rxd_scan_rsp(addrd->adv_addr, addrd->adv_addr_type, is_ext_adv, adi)) { return false; } /* Better not be a scan response pending */ BLE_LL_ASSERT(scansm->scan_rsp_pending == 0); /* We want to send a request. See if backoff allows us */ if (scansm->backoff_count > 0) { if (--scansm->backoff_count != 0) { return false; } } #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) phy_mode = ble_ll_phy_to_phy_mode(rxinfo->phy, BLE_HCI_LE_PHY_CODED_ANY); if (ble_ll_sched_scan_req_over_aux_ptr(rxinfo->channel, phy_mode)) { return false; } #endif /* Use original AdvA in scan request (Core 5.1, Vol 6, Part B, section 6.3) */ ble_ll_scan_req_pdu_prepare(scansm, addrd->adva, addrd->adva_type, addrd->rl); rc = ble_phy_tx(ble_ll_scan_req_tx_pdu_cb, scansm, BLE_PHY_TRANSITION_TX_RX); if (rc) { return false; } scansm->scan_rsp_pending = 1; rxinfo->flags |= BLE_MBUF_HDR_F_SCAN_REQ_TXD; #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) if (rxinfo->channel < BLE_PHY_NUM_DATA_CHANS) { /* Keep aux_data for expected scan response */ scansm->cur_aux_data = ble_ll_scan_aux_data_ref(aux_data); STATS_INC(ble_ll_stats, aux_scan_req_tx); } #endif return true; } /** * Called when a receive PDU has ended. * * Context: Interrupt * * @param rxpdu * * @return int * < 0: Disable the phy after reception. * == 0: Success. Do not disable the PHY. * > 0: Do not disable PHY as that has already been done. */ int ble_ll_scan_rx_isr_end(struct os_mbuf *rxpdu, uint8_t crcok) { struct ble_ll_scan_sm *scansm = &g_ble_ll_scan_sm; struct ble_mbuf_hdr *hdr = BLE_MBUF_HDR_PTR(rxpdu); struct ble_mbuf_hdr_rxinfo *rxinfo = &hdr->rxinfo; uint8_t *rxbuf; uint8_t pdu_type; struct ble_ll_scan_addr_data addrd; int rc; /* * If buffer for incoming PDU was not allocated we need to force scan to be * restarted since LL will not be notified. Keep PHY enabled. */ if (rxpdu == NULL) { ble_ll_scan_interrupted(scansm); return 0; } rxbuf = rxpdu->om_data; pdu_type = rxbuf[0] & BLE_ADV_PDU_HDR_TYPE_MASK; #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) /* * In case aux was expected, copy aux_data for LL to use. Make sure this was * indeed an aux as otherwise there's no need to process it and just pass to * LL immediately. */ if (scansm->cur_aux_data) { rxinfo->user_data = scansm->cur_aux_data; scansm->cur_aux_data = NULL; if (pdu_type != BLE_ADV_PDU_TYPE_ADV_EXT_IND) { ble_ll_state_set(BLE_LL_STATE_STANDBY); return -1; } } #endif if (!crcok) { goto scan_rx_isr_ignore; } /* * Addresses will be always set in handlers, no need to initialize them. We * only need to initialize rl which may not be always set, depending on how * filtering goes. */ addrd.rl = NULL; switch (pdu_type) { case BLE_ADV_PDU_TYPE_ADV_IND: case BLE_ADV_PDU_TYPE_ADV_DIRECT_IND: case BLE_ADV_PDU_TYPE_ADV_NONCONN_IND: case BLE_ADV_PDU_TYPE_SCAN_RSP: case BLE_ADV_PDU_TYPE_ADV_SCAN_IND: rc = ble_ll_scan_rx_isr_on_legacy(pdu_type, rxbuf, hdr, &addrd); break; #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) case BLE_ADV_PDU_TYPE_ADV_EXT_IND: rc = ble_ll_scan_rx_isr_on_aux(pdu_type, rxbuf, hdr, &addrd); break; #endif default: /* This is not something we would like to process here */ rc = -1; break; } if (rc == -1) { goto scan_rx_isr_ignore; } else if (rc == 1) { if (ble_ll_scan_send_scan_req(pdu_type, rxbuf, hdr, &addrd)) { /* Keep PHY active and LL in scanning state */ return 0; } } /* We are done with this PDU so go to standby and let LL resume if needed */ ble_ll_state_set(BLE_LL_STATE_STANDBY); return -1; scan_rx_isr_ignore: rxinfo->flags |= BLE_MBUF_HDR_F_IGNORED; ble_ll_state_set(BLE_LL_STATE_STANDBY); return -1; } /** * Called to resume scanning. This is called after an advertising event or * connection event has ended. It is also called if we receive a packet while * in the initiating or scanning state. * * If periodic advertising is enabled this is also called on sync event end * or sync packet received if chaining * * Context: Link Layer task */ void ble_ll_scan_chk_resume(void) { os_sr_t sr; struct ble_ll_scan_sm *scansm; uint32_t now; scansm = &g_ble_ll_scan_sm; if (scansm->scan_enabled) { OS_ENTER_CRITICAL(sr); if (scansm->restart_timer_needed) { scansm->restart_timer_needed = 0; ble_ll_event_send(&scansm->scan_sched_ev); STATS_INC(ble_ll_stats, scan_timer_restarted); OS_EXIT_CRITICAL(sr); return; } now = os_cputime_get32(); if (ble_ll_state_get() == BLE_LL_STATE_STANDBY && ble_ll_scan_is_inside_window(scansm->scanp, now)) { /* Turn on the receiver and set state */ ble_ll_scan_start(scansm, NULL); } OS_EXIT_CRITICAL(sr); } } /** * Scan timer callback; means that the scan window timeout has been reached * and we should perform the appropriate actions. * * Context: Interrupt (cputimer) * * @param arg Pointer to scan state machine. */ void ble_ll_scan_timer_cb(void *arg) { struct ble_ll_scan_sm *scansm; scansm = (struct ble_ll_scan_sm *)arg; ble_ll_event_send(&scansm->scan_sched_ev); } void ble_ll_scan_interrupted(struct ble_ll_scan_sm *scansm) { #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) ble_npl_event_set_arg(&scansm->scan_interrupted_ev, scansm->cur_aux_data); scansm->cur_aux_data = NULL; #endif ble_ll_event_send(&scansm->scan_interrupted_ev); } /** * Called when the wait for response timer expires while in the scanning * state. * * Context: Interrupt. */ void ble_ll_scan_wfr_timer_exp(void) { struct ble_ll_scan_sm *scansm; uint8_t chan; int phy; int rc; #if (BLE_LL_BT5_PHY_SUPPORTED == 1) uint8_t phy_mode; #endif uint32_t now; scansm = &g_ble_ll_scan_sm; /* Update backoff if we failed to receive scan response */ if (scansm->scan_rsp_pending) { scansm->scan_rsp_pending = 0; ble_ll_scan_req_backoff(scansm, 0); } if (scansm->cur_aux_data) { /* We actually care about interrupted scan only for EXT ADV because only * then we might consider to send truncated event to the host. */ ble_ll_scan_interrupted(scansm); /* Need to disable phy since we are going to move to BLE_LL_STATE_STANDBY * or we will need to change channel to primary one */ ble_phy_disable(); now = os_cputime_get32(); if (!ble_ll_scan_is_inside_window(scansm->scanp, now)) { /* Outside the window scan */ ble_ll_state_set(BLE_LL_STATE_STANDBY); return; } ble_ll_get_chan_to_scan(scansm, &chan, &phy); #if (BLE_LL_BT5_PHY_SUPPORTED == 1) phy_mode = ble_ll_phy_to_phy_mode(phy, BLE_HCI_LE_PHY_CODED_ANY); ble_phy_mode_set(phy_mode, phy_mode); #endif rc = ble_phy_setchan(chan, BLE_ACCESS_ADDR_ADV, BLE_LL_CRCINIT_ADV); BLE_LL_ASSERT(rc == 0); } ble_phy_restart_rx(); } #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) /* * Send extended advertising report * * @return -1 on error (data truncated or other error) * 0 on success (data status is "completed") * 1 on success (data status is not "completed") */ static int ble_ll_hci_send_ext_adv_report(uint8_t ptype, uint8_t *adva, uint8_t adva_type, uint8_t *inita, uint8_t inita_type, struct os_mbuf *om, struct ble_mbuf_hdr *hdr) { struct ble_ll_aux_data *aux_data = hdr->rxinfo.user_data; struct ble_hci_ev_le_subev_ext_adv_rpt *ev; struct ext_adv_report *report; struct ble_hci_ev *hci_ev; struct ble_hci_ev *hci_ev_next; int offset; int datalen; int rc; bool need_event; bool is_scannable_aux; uint8_t max_data_len; if (!ble_ll_hci_is_le_event_enabled(BLE_HCI_LE_SUBEV_EXT_ADV_RPT)) { rc = -1; goto done; } /* * We keep one allocated event in aux_data to be able to truncate chain * properly in case of error. If there is no event in aux_data it means this * is the first event for this chain. */ if (aux_data && aux_data->evt) { hci_ev = aux_data->evt; aux_data->evt = NULL; hci_ev->length = sizeof(*ev) + sizeof(*report); } else { hci_ev = ble_ll_scan_get_ext_adv_report(NULL); if (!hci_ev) { rc = -1; goto done; } } ev = (void *) hci_ev->data; #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) /* If RPA has been used, make sure we use correct address types * in the advertising report. */ if (BLE_MBUF_HDR_RESOLVED(hdr)) { adva_type += 2; } if (BLE_MBUF_HDR_TARGETA_RESOLVED(hdr)) { inita_type += 2; } #endif datalen = ble_ll_scan_parse_ext_hdr(om, adva, adva_type, inita, inita_type, hdr, ev->reports); if (datalen < 0) { rc = -1; /* Need to send truncated event if we already sent some reports */ if (aux_data && (aux_data->flags_ll & BLE_LL_AUX_FLAG_HCI_SENT_ANY)) { BLE_LL_ASSERT(!(aux_data->flags_ll & BLE_LL_AUX_FLAG_HCI_SENT_COMPLETED)); BLE_LL_ASSERT(!(aux_data->flags_ll & BLE_LL_AUX_FLAG_HCI_SENT_TRUNCATED)); aux_data->flags_ll |= BLE_LL_AUX_FLAG_SCAN_ERROR; aux_data->flags_ll |= BLE_LL_AUX_FLAG_HCI_SENT_TRUNCATED; report = ev->reports; report->data_len = 0; report->evt_type |= BLE_HCI_ADV_DATA_STATUS_TRUNCATED; ble_ll_hci_event_send(hci_ev); goto done; } ble_hci_trans_buf_free((uint8_t *)hci_ev); goto done; } is_scannable_aux = aux_data && (aux_data->evt_type & BLE_HCI_ADV_SCAN_MASK) && !(aux_data->evt_type & BLE_HCI_ADV_SCAN_RSP_MASK); max_data_len = BLE_LL_MAX_EVT_LEN - sizeof(*hci_ev) - sizeof(*ev) - sizeof(*report); offset = 0; do { hci_ev_next = NULL; ev = (void *) hci_ev->data; report = ev->reports; report->data_len = min(max_data_len, datalen - offset); /* adjust event length */ hci_ev->length += report->data_len; report->rssi = hdr->rxinfo.rssi; os_mbuf_copydata(om, offset, report->data_len, report->data); offset += report->data_len; /* * We need another event if either there are still some data left to * send in current PDU or scan is not completed. The only exception is * when this is a scannable event which is not a scan response. */ need_event = ((offset < datalen) || (aux_data && !(aux_data->flags_ll & BLE_LL_AUX_FLAG_SCAN_COMPLETE))) && !is_scannable_aux; if (need_event) { /* * We will need another event so let's try to allocate one now. If * we cannot do this, need to mark event as truncated. */ hci_ev_next = ble_ll_scan_get_ext_adv_report(report); if (hci_ev_next) { report->evt_type |= BLE_HCI_ADV_DATA_STATUS_INCOMPLETE; rc = 1; } else { report->evt_type |= BLE_HCI_ADV_DATA_STATUS_TRUNCATED; rc = -1; } } else if (aux_data && (aux_data->flags_ll & BLE_LL_AUX_FLAG_SCAN_ERROR)) { report->evt_type |= BLE_HCI_ADV_DATA_STATUS_TRUNCATED; rc = -1; } else { rc = 0; } if ((rc == -1) && aux_data) { aux_data->flags_ll |= BLE_LL_AUX_FLAG_SCAN_ERROR; if (!(aux_data->flags_ll & BLE_LL_AUX_FLAG_HCI_SENT_ANY)) { ble_hci_trans_buf_free((uint8_t *)hci_ev); goto done; } aux_data->flags_ll |= BLE_LL_AUX_FLAG_HCI_SENT_TRUNCATED; } else if (!is_scannable_aux) { /* * We do not set 'sent' flags for scannable AUX since we only care * about scan response that will come next. */ aux_data->flags_ll |= BLE_LL_AUX_FLAG_HCI_SENT_ANY; if (rc == 0) { aux_data->flags_ll |= BLE_LL_AUX_FLAG_HCI_SENT_COMPLETED; } } ble_ll_hci_event_send(hci_ev); hci_ev = hci_ev_next; } while ((offset < datalen) && hci_ev); BLE_LL_ASSERT(offset <= datalen); if (aux_data) { /* Store any event left for later use */ aux_data->evt = hci_ev; } else { /* If it is empty beacon, evt shall be NULL */ BLE_LL_ASSERT(!hci_ev); } done: if (!aux_data) { return rc; } if (rc == 0) { if (aux_data->evt_type & BLE_HCI_ADV_SCAN_RSP_MASK) { /* Complete scan response can be added to duplicates list */ ble_ll_scan_add_scan_rsp_adv(aux_data->adva, aux_data->adva_type, 1, aux_data->adi); } else if (is_scannable_aux) { /* * Scannable AUX is marked as incomplete because we do not want to * add this to duplicates list now, this should happen only after * we receive complete scan response. The drawback here is that we * will keep receiving reports for scannable PDUs until complete * scan response is received. * * XXX ^^ extend duplicates list to fix */ rc = 1; } } else if (rc < 0) { aux_data->flags_ll |= BLE_LL_AUX_FLAG_SCAN_ERROR; } return rc; } #endif #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV) static void ble_ll_scan_check_periodic_sync(const struct os_mbuf *om, struct ble_mbuf_hdr *rxhdr, uint8_t *adva, uint8_t adva_type, int rpa_index) { uint8_t pdu_len; uint8_t ext_hdr_len; uint8_t ext_hdr_flags; uint8_t *ext_hdr; uint8_t *rxbuf = om->om_data; uint8_t sid; int i; pdu_len = rxbuf[1]; if (pdu_len == 0) { return; } ext_hdr_len = rxbuf[2] & 0x3F; if (ext_hdr_len) { ext_hdr_flags = rxbuf[3]; ext_hdr = &rxbuf[4]; i = 0; if (ext_hdr_flags & (1 << BLE_LL_EXT_ADV_ADVA_BIT)) { i += BLE_LL_EXT_ADV_ADVA_SIZE; } if (ext_hdr_flags & (1 << BLE_LL_EXT_ADV_TARGETA_BIT)) { i += BLE_LL_EXT_ADV_TARGETA_SIZE; } if (ext_hdr_flags & (1 << BLE_LL_EXT_ADV_CTE_INFO_BIT)) { i += 1; } if (ext_hdr_flags & (1 << BLE_LL_EXT_ADV_DATA_INFO_BIT)) { sid = (get_le16(ext_hdr + i) >> 12); i += BLE_LL_EXT_ADV_DATA_INFO_SIZE; } else { /* ADI is mandatory */ return; } if (ext_hdr_flags & (1 << BLE_LL_EXT_ADV_AUX_PTR_BIT)) { i += BLE_LL_EXT_ADV_AUX_PTR_SIZE; } if (ext_hdr_flags & (1 << BLE_LL_EXT_ADV_SYNC_INFO_BIT)) { ble_ll_sync_info_event(adva, adva_type, rpa_index, sid, rxhdr, ext_hdr + i); } } } #endif static inline void ble_ll_scan_dup_move_to_head(struct ble_ll_scan_dup_entry *e) { if (e != TAILQ_FIRST(&g_scan_dup_list)) { TAILQ_REMOVE(&g_scan_dup_list, e, link); TAILQ_INSERT_HEAD(&g_scan_dup_list, e, link); } } static inline struct ble_ll_scan_dup_entry * ble_ll_scan_dup_new(void) { struct ble_ll_scan_dup_entry *e; e = os_memblock_get(&g_scan_dup_pool); if (!e) { e = TAILQ_LAST(&g_scan_dup_list, ble_ll_scan_dup_list); TAILQ_REMOVE(&g_scan_dup_list, e, link); } memset(e, 0, sizeof(*e)); return e; } static int ble_ll_scan_dup_check_legacy(uint8_t addr_type, uint8_t *addr, uint8_t pdu_type) { struct ble_ll_scan_dup_entry *e; uint8_t type; int rc; type = BLE_LL_SCAN_ENTRY_TYPE_LEGACY(addr_type); TAILQ_FOREACH(e, &g_scan_dup_list, link) { if ((e->type == type) && !memcmp(e->addr, addr, 6)) { break; } } if (e) { if (pdu_type == BLE_ADV_PDU_TYPE_ADV_DIRECT_IND) { rc = e->flags & BLE_LL_SCAN_DUP_F_DIR_ADV_REPORT_SENT; } else if (pdu_type == BLE_ADV_PDU_TYPE_SCAN_RSP) { rc = e->flags & BLE_LL_SCAN_DUP_F_SCAN_RSP_SENT; } else { rc = e->flags & BLE_LL_SCAN_DUP_F_ADV_REPORT_SENT; } ble_ll_scan_dup_move_to_head(e); } else { rc = 0; e = ble_ll_scan_dup_new(); e->flags = 0; e->type = type; memcpy(e->addr, addr, 6); TAILQ_INSERT_HEAD(&g_scan_dup_list, e, link); } return rc; } #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) static int ble_ll_scan_dup_check_ext(uint8_t addr_type, uint8_t *addr, struct ble_ll_aux_data *aux_data) { struct ble_ll_scan_dup_entry *e; bool has_aux; bool is_anon; uint16_t adi; uint8_t type; int rc; has_aux = aux_data != NULL; is_anon = addr == NULL; adi = has_aux ? aux_data->adi : 0; type = BLE_LL_SCAN_ENTRY_TYPE_EXT(addr_type, has_aux, is_anon, adi); TAILQ_FOREACH(e, &g_scan_dup_list, link) { if ((e->type == type) && (is_anon || !memcmp(e->addr, addr, BLE_DEV_ADDR_LEN))) { break; } } if (e) { if (e->adi != adi) { rc = 0; e->flags = 0; e->adi = adi; } else { rc = e->flags & BLE_LL_SCAN_DUP_F_ADV_REPORT_SENT; } ble_ll_scan_dup_move_to_head(e); } else { rc = 0; e = ble_ll_scan_dup_new(); e->flags = 0; e->type = type; e->adi = adi; if (!is_anon) { memcpy(e->addr, addr, 6); } TAILQ_INSERT_HEAD(&g_scan_dup_list, e, link); } return rc; } static int ble_ll_scan_dup_update_ext(uint8_t addr_type, uint8_t *addr, struct ble_ll_aux_data *aux_data) { struct ble_ll_scan_dup_entry *e; bool has_aux; bool is_anon; uint16_t adi; uint8_t type; has_aux = aux_data != NULL; is_anon = addr == NULL; adi = has_aux ? aux_data->adi : 0; type = BLE_LL_SCAN_ENTRY_TYPE_EXT(addr_type, has_aux, is_anon, adi); /* * We assume ble_ll_scan_dup_check() was called before which either matched * some entry or allocated new one and placed in on the top of queue. */ e = TAILQ_FIRST(&g_scan_dup_list); BLE_LL_ASSERT(e && e->type == type && (is_anon || !memcmp(e->addr, addr, 6))); e->flags |= BLE_LL_SCAN_DUP_F_ADV_REPORT_SENT; return 0; } #endif static void ble_ll_scan_rx_pkt_in_restore_addr_data(struct ble_mbuf_hdr *hdr, struct ble_ll_scan_addr_data *addrd) { #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) struct ble_ll_scan_sm *scansm = &g_ble_ll_scan_sm; struct ble_mbuf_hdr_rxinfo *rxinfo = &hdr->rxinfo; struct ble_ll_resolv_entry *rl; #endif addrd->adv_addr = addrd->adva; addrd->adv_addr_type = addrd->adva_type; #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) if (rxinfo->rpa_index >= 0) { rl = &g_ble_ll_resolv_list[rxinfo->rpa_index]; addrd->adv_addr = rl->rl_identity_addr; addrd->adv_addr_type = rl->rl_addr_type; addrd->rl = rl; } if (hdr->rxinfo.flags & BLE_MBUF_HDR_F_TARGETA_RESOLVED) { addrd->targeta = ble_ll_get_our_devaddr(scansm->own_addr_type & 1); addrd->targeta_type = scansm->own_addr_type & 1; } #endif } static void ble_ll_scan_rx_pkt_in_on_legacy(uint8_t pdu_type, struct os_mbuf *om, struct ble_mbuf_hdr *hdr, struct ble_ll_scan_addr_data *addrd) { struct ble_ll_scan_sm *scansm = &g_ble_ll_scan_sm; uint8_t *rxbuf = om->om_data; bool send_hci_report; if (!BLE_MBUF_HDR_DEVMATCH(hdr) || !BLE_MBUF_HDR_CRC_OK(hdr) || BLE_MBUF_HDR_IGNORED(hdr) || !scansm->scan_enabled) { return; } ble_ll_scan_get_addr_data_from_legacy(pdu_type, rxbuf, addrd); ble_ll_scan_rx_pkt_in_restore_addr_data(hdr, addrd); send_hci_report = !scansm->scan_filt_dups || !ble_ll_scan_dup_check_legacy(addrd->adv_addr_type, addrd->adv_addr, pdu_type); if (send_hci_report) { /* Sending advertising report will also update scan_dup list */ ble_ll_scan_send_adv_report(pdu_type, addrd->adv_addr, addrd->adv_addr_type, addrd->targeta, addrd->targeta_type, om, hdr, scansm); } if (BLE_MBUF_HDR_SCAN_RSP_RXD(hdr)) { ble_ll_scan_req_backoff(scansm, 1); } } #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) static void ble_ll_scan_rx_pkt_in_on_aux(uint8_t pdu_type, struct os_mbuf *om, struct ble_mbuf_hdr *hdr, struct ble_ll_scan_addr_data *addrd) { struct ble_ll_scan_sm *scansm = &g_ble_ll_scan_sm; #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV) uint8_t *rxbuf = om->om_data; #endif struct ble_mbuf_hdr_rxinfo *rxinfo = &hdr->rxinfo; struct ble_ll_aux_data *aux_data = rxinfo->user_data; bool send_hci_report; int rc; if (aux_data) { aux_data->flags_ll |= aux_data->flags_isr; } /* * For every new extended advertising event scanned, rx_isr_end will either * allocate new aux_data or set 'invalid' flag. This means if no 'invalid' * flag is set, aux_data is always valid. */ /* Drop on scan error or if we received not what we expected to receive */ if (!BLE_MBUF_HDR_CRC_OK(hdr) || BLE_MBUF_HDR_IGNORED(hdr) || BLE_MBUF_HDR_AUX_INVALID(hdr) || (aux_data->flags_ll & BLE_LL_AUX_FLAG_SCAN_ERROR) || (pdu_type != BLE_ADV_PDU_TYPE_ADV_EXT_IND) || !scansm->scan_enabled) { if (aux_data) { ble_ll_scan_end_adv_evt(aux_data); ble_ll_scan_aux_data_unref(aux_data); rxinfo->user_data = NULL; } return; } BLE_LL_ASSERT(aux_data); if (aux_data->flags & BLE_LL_AUX_HAS_ADVA) { addrd->adva = aux_data->adva; addrd->adva_type = aux_data->adva_type; } else { addrd->adva = NULL; addrd->adva_type = 0; } if (aux_data->flags & BLE_LL_AUX_HAS_TARGETA) { addrd->targeta = aux_data->targeta; addrd->targeta_type = aux_data->targeta_type; } else { addrd->targeta = NULL; addrd->targeta_type = 0; } #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV) /* * Periodic scan uses own filter list so we need to let it do own filtering * regardless of scanner filtering. Just make sure we already have AdvA. */ if (ble_ll_sync_enabled() && ((rxbuf[2] >> 6) == BLE_LL_EXT_ADV_MODE_NON_CONN) && addrd->adva && !(aux_data->flags_ll & BLE_LL_AUX_FLAG_AUX_CHAIN_RECEIVED)) { ble_ll_scan_check_periodic_sync(om, hdr, addrd->adva, addrd->adva_type, rxinfo->rpa_index); } #endif /* Ignore if device was not matched by either whitelist or scan policy */ if (!BLE_MBUF_HDR_DEVMATCH(hdr)) { goto scan_continue; } ble_ll_scan_rx_pkt_in_restore_addr_data(hdr, addrd); /* * If there is AuxPtr in this PDU, we should first try to schedule scan for * subsequent aux. */ if (BLE_MBUF_HDR_WAIT_AUX(hdr)) { if (ble_ll_sched_aux_scan(hdr, scansm, aux_data)) { rxinfo->flags &= ~BLE_MBUF_HDR_F_AUX_PTR_WAIT; aux_data->flags_ll |= BLE_LL_AUX_FLAG_SCAN_ERROR; /* Silently ignore if no HCI event was sent to host */ if (!(aux_data->flags_ll & BLE_LL_AUX_FLAG_HCI_SENT_ANY)) { goto scan_continue; } } /* Ignore if this was just ADV_EXT_IND with AuxPtr, will process aux */ if (!(aux_data->flags_ll & BLE_LL_AUX_FLAG_AUX_ADV_RECEIVED)) { goto scan_continue; } STATS_INC(ble_ll_stats, aux_chain_cnt); } send_hci_report = !scansm->scan_filt_dups || !ble_ll_scan_dup_check_ext(addrd->adv_addr_type, addrd->adv_addr, aux_data); if (send_hci_report) { rc = ble_ll_hci_send_ext_adv_report(pdu_type, addrd->adv_addr, addrd->adv_addr_type, addrd->targeta, addrd->targeta_type, om, hdr); if ((rc < 0) && BLE_MBUF_HDR_WAIT_AUX(hdr)) { /* Data were truncated so stop scanning for subsequent auxes */ aux_data->flags_ll |= BLE_LL_AUX_FLAG_SCAN_ERROR; if (ble_ll_sched_rmv_elem(&aux_data->sch) == 0) { ble_ll_scan_aux_data_unref(aux_data->sch.cb_arg); aux_data->sch.cb_arg = NULL; } } else if ((rc == 0) && scansm->scan_filt_dups) { /* Complete data were send so we can update scan_dup list */ ble_ll_scan_dup_update_ext(addrd->adv_addr_type, addrd->adv_addr, aux_data); } } if (BLE_MBUF_HDR_SCAN_RSP_RXD(hdr)) { /* * For now assume success if we just received direct scan response, * don't care about complete aux chain. */ ble_ll_scan_req_backoff(scansm, 1); } scan_continue: ble_ll_scan_aux_data_unref(rxinfo->user_data); rxinfo->user_data = NULL; } #endif /** * Process a received PDU while in the scanning state. * * Context: Link Layer task. * * @param pdu_type * @param rxbuf */ void ble_ll_scan_rx_pkt_in(uint8_t ptype, struct os_mbuf *om, struct ble_mbuf_hdr *hdr) { #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) struct ble_mbuf_hdr_rxinfo *rxinfo = &hdr->rxinfo; struct ble_ll_aux_data *aux_data = rxinfo->user_data; #endif struct ble_ll_scan_addr_data addrd; #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) if (aux_data || (ptype == BLE_ADV_PDU_TYPE_ADV_EXT_IND)) { ble_ll_scan_rx_pkt_in_on_aux(ptype, om, hdr, &addrd); ble_ll_scan_chk_resume(); return; } #endif ble_ll_scan_rx_pkt_in_on_legacy(ptype, om, hdr, &addrd); ble_ll_scan_chk_resume(); } int ble_ll_scan_set_scan_params(const uint8_t *cmdbuf, uint8_t len) { const struct ble_hci_le_set_scan_params_cp *cmd = (const void *)cmdbuf; uint16_t scan_itvl; uint16_t scan_window; struct ble_ll_scan_sm *scansm; struct ble_ll_scan_params *scanp; if (len != sizeof(*cmd)) { return BLE_ERR_INV_HCI_CMD_PARMS; } /* If already enabled, we return an error */ scansm = &g_ble_ll_scan_sm; if (scansm->scan_enabled) { return BLE_ERR_CMD_DISALLOWED; } /* Get the scan interval and window */ scan_itvl = le16toh(cmd->scan_itvl); scan_window = le16toh(cmd->scan_window); /* Check scan type */ if ((cmd->scan_type != BLE_HCI_SCAN_TYPE_PASSIVE) && (cmd->scan_type != BLE_HCI_SCAN_TYPE_ACTIVE)) { return BLE_ERR_INV_HCI_CMD_PARMS; } /* Check interval and window */ if ((scan_itvl < BLE_HCI_SCAN_ITVL_MIN) || (scan_itvl > BLE_HCI_SCAN_ITVL_MAX) || (scan_window < BLE_HCI_SCAN_WINDOW_MIN) || (scan_window > BLE_HCI_SCAN_WINDOW_MAX) || (scan_itvl < scan_window)) { return BLE_ERR_INV_HCI_CMD_PARMS; } /* Check own addr type */ if (cmd->own_addr_type > BLE_HCI_ADV_OWN_ADDR_MAX) { return BLE_ERR_INV_HCI_CMD_PARMS; } /* Check scanner filter policy */ if (cmd->filter_policy > BLE_HCI_SCAN_FILT_MAX) { return BLE_ERR_INV_HCI_CMD_PARMS; } /* Store scan parameters */ scanp = &g_ble_ll_scan_params[PHY_UNCODED]; scanp->configured = 1; scanp->scan_type = cmd->scan_type; scanp->timing.interval = ble_ll_scan_time_hci_to_ticks(scan_itvl); scanp->timing.window = ble_ll_scan_time_hci_to_ticks(scan_window); scanp->scan_filt_policy = cmd->filter_policy; scanp->own_addr_type = cmd->own_addr_type; #if (BLE_LL_SCAN_PHY_NUMBER == 2) g_ble_ll_scan_params[PHY_CODED].configured = 0; #endif return 0; } #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) static int ble_ll_check_scan_params(uint8_t type, uint16_t itvl, uint16_t window) { /* Check scan type */ if ((type != BLE_HCI_SCAN_TYPE_PASSIVE) && (type != BLE_HCI_SCAN_TYPE_ACTIVE)) { return BLE_ERR_INV_HCI_CMD_PARMS; } /* Check interval and window */ if ((itvl < BLE_HCI_SCAN_ITVL_MIN) || (itvl > BLE_HCI_SCAN_ITVL_MAX) || (window < BLE_HCI_SCAN_WINDOW_MIN) || (window > BLE_HCI_SCAN_WINDOW_MAX) || (itvl < window)) { return BLE_ERR_INV_HCI_CMD_PARMS; } return 0; } int ble_ll_set_ext_scan_params(const uint8_t *cmdbuf, uint8_t len) { const struct ble_hci_le_set_ext_scan_params_cp *cmd = (const void *) cmdbuf; const struct scan_params *params = cmd->scans; struct ble_ll_scan_params new_params[BLE_LL_SCAN_PHY_NUMBER] = { }; struct ble_ll_scan_params *uncoded = &new_params[PHY_UNCODED]; struct ble_ll_scan_params *coded = &new_params[PHY_CODED]; uint16_t interval; uint16_t window; int rc; if (len <= sizeof(*cmd)) { return BLE_ERR_INV_HCI_CMD_PARMS; } len -= sizeof(*cmd); /* If already enabled, we return an error */ if (g_ble_ll_scan_sm.scan_enabled) { return BLE_ERR_CMD_DISALLOWED; } /* Check own addr type */ if (cmd->own_addr_type > BLE_HCI_ADV_OWN_ADDR_MAX) { return BLE_ERR_INV_HCI_CMD_PARMS; } coded->own_addr_type = cmd->own_addr_type; uncoded->own_addr_type = cmd->own_addr_type; /* Check scanner filter policy */ if (cmd->filter_policy > BLE_HCI_SCAN_FILT_MAX) { return BLE_ERR_INV_HCI_CMD_PARMS; } coded->scan_filt_policy = cmd->filter_policy; uncoded->scan_filt_policy = cmd->filter_policy; /* Check if no reserved bits in PHYS are set and that at least one valid PHY * is set. */ if (!(cmd->phys & SCAN_VALID_PHY_MASK) || (cmd->phys & ~SCAN_VALID_PHY_MASK)) { return BLE_ERR_INV_HCI_CMD_PARMS; } if (cmd->phys & BLE_HCI_LE_PHY_1M_PREF_MASK) { if (len < sizeof(*params)) { return BLE_ERR_INV_HCI_CMD_PARMS; } interval = le16toh(params->itvl); window = le16toh(params->window); rc = ble_ll_check_scan_params(params->type, interval, window); if (rc) { return rc; } uncoded->scan_type = params->type; uncoded->timing.interval = ble_ll_scan_time_hci_to_ticks(interval); uncoded->timing.window = ble_ll_scan_time_hci_to_ticks(window); /* That means user wants to use this PHY for scanning */ uncoded->configured = 1; params++; len -= sizeof(*params); } #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY) if (cmd->phys & BLE_HCI_LE_PHY_CODED_PREF_MASK) { if (len < sizeof(*params)) { return BLE_ERR_INV_HCI_CMD_PARMS; } interval = le16toh(params->itvl); window = le16toh(params->window); rc = ble_ll_check_scan_params(params->type, interval, window); if (rc) { return rc; } coded->scan_type = params->type; coded->timing.interval = ble_ll_scan_time_hci_to_ticks(interval); coded->timing.window = ble_ll_scan_time_hci_to_ticks(window); /* That means user wants to use this PHY for scanning */ coded->configured = 1; } #endif /* if any of PHYs is configured for continuous scan we alter interval to * fit other PHY */ if (coded->configured && uncoded->configured) { if (coded->timing.interval == coded->timing.window) { coded->timing.interval += uncoded->timing.window; } if (uncoded->timing.interval == uncoded->timing.window) { uncoded->timing.window += coded->timing.window; } } memcpy(g_ble_ll_scan_params, new_params, sizeof(new_params)); return 0; } #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 * * HCI scan set enable command processing function * * Context: Link Layer task (HCI Command parser). * * @return int BLE error code. */ static int ble_ll_scan_set_enable(uint8_t enable, uint8_t filter_dups, uint16_t period, uint16_t dur, bool ext) { int rc; struct ble_ll_scan_sm *scansm; struct ble_ll_scan_params *scanp; struct ble_ll_scan_params *scanp_phy; int i; #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) uint32_t period_ticks = 0; uint32_t dur_ticks = 0; #endif /* Check for valid parameters */ if ((filter_dups > 1) || (enable > 1)) { return BLE_ERR_INV_HCI_CMD_PARMS; } 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) { /* Period parameter is ignored when the Duration parameter is zero */ if (!dur) { period = 0; } /* 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++) { scanp_phy = &scansm->scanp_phys[i]; if (scanp_phy->configured && scanp_phy->scan_type == BLE_SCAN_TYPE_INITIATE) { return BLE_ERR_CMD_DISALLOWED; } } #if MYNEWT_VAL(BLE_LL_NUM_SCAN_DUP_ADVS) /* update filter policy */ scansm->scan_filt_dups = filter_dups; #endif #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 */ #if MYNEWT_VAL(BLE_LL_NUM_SCAN_DUP_ADVS) scansm->scan_filt_dups = filter_dups; #endif scansm->scanp = NULL; scansm->scanp_next = NULL; for (i = 0; i < BLE_LL_SCAN_PHY_NUMBER; i++) { scanp_phy = &scansm->scanp_phys[i]; scanp = &g_ble_ll_scan_params[i]; if (!scanp->configured) { continue; } scanp_phy->configured = scanp->configured; scanp_phy->scan_type = scanp->scan_type; scanp_phy->timing = scanp->timing; scanp_phy->scan_filt_policy = scanp->scan_filt_policy; scanp_phy->own_addr_type = scanp->own_addr_type; if (!scansm->scanp) { scansm->scanp = scanp_phy; /* Take own_addr_type from the first configured PHY. * Note: All configured PHYs shall have the same own_addr_type */ scansm->own_addr_type = scanp_phy->own_addr_type; } else { scansm->scanp_next = scanp_phy; } } /* spec is not really clear if we should use defaults in this case * or just disallow starting scan without explicit configuration * For now be nice to host and just use values based on LE Set Scan * Parameters defaults. */ if (!scansm->scanp) { scansm->scanp = &scansm->scanp_phys[PHY_UNCODED]; scansm->own_addr_type = BLE_ADDR_PUBLIC; scanp_phy = scansm->scanp; scanp_phy->configured = 1; scanp_phy->scan_type = BLE_SCAN_TYPE_PASSIVE; scanp_phy->timing.interval = ble_ll_scan_time_hci_to_ticks(BLE_HCI_SCAN_ITVL_DEF); scanp_phy->timing.window = ble_ll_scan_time_hci_to_ticks(BLE_HCI_SCAN_WINDOW_DEF); scanp_phy->scan_filt_policy = BLE_HCI_SCAN_FILT_NO_WL; scanp_phy->own_addr_type = BLE_ADDR_PUBLIC; } 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; } int ble_ll_hci_scan_set_enable(const uint8_t *cmdbuf, uint8_t len) { const struct ble_hci_le_set_scan_enable_cp *cmd = (const void *) cmdbuf; if (len != sizeof(*cmd)) { return BLE_ERR_INV_HCI_CMD_PARMS; } return ble_ll_scan_set_enable(cmd->enable, cmd->filter_duplicates, 0, 0, false); } #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) int ble_ll_hci_ext_scan_set_enable(const uint8_t *cmdbuf, uint8_t len) { const struct ble_hci_le_set_ext_scan_enable_cp *cmd = (const void *) cmdbuf; if (len != sizeof(*cmd)) { return BLE_ERR_INV_HCI_CMD_PARMS; } return ble_ll_scan_set_enable(cmd->enable, cmd->filter_dup, le16toh(cmd->period), le16toh(cmd->duration), true); } #endif /** * Checks if controller can change the whitelist. If scanning is enabled and * using the whitelist the controller is not allowed to change the whitelist. * * @return int 0: not allowed to change whitelist; 1: change allowed. */ int ble_ll_scan_can_chg_whitelist(void) { int rc; struct ble_ll_scan_sm *scansm; struct ble_ll_scan_params *scanp; scansm = &g_ble_ll_scan_sm; scanp = scansm->scanp; if (scansm->scan_enabled && (scanp->scan_filt_policy & 1)) { rc = 0; } else { rc = 1; } return rc; } int ble_ll_scan_initiator_start(struct hci_create_conn *hcc, struct ble_ll_scan_sm **sm) { struct ble_ll_scan_sm *scansm; struct ble_ll_scan_params *scanp; int rc; 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->scanp = &scansm->scanp_phys[PHY_UNCODED]; scansm->scanp_next = NULL; scanp = scansm->scanp; scanp->scan_filt_policy = hcc->filter_policy; scanp->timing.interval = ble_ll_scan_time_hci_to_ticks(hcc->scan_itvl); scanp->timing.window = ble_ll_scan_time_hci_to_ticks(hcc->scan_window); scanp->scan_type = BLE_SCAN_TYPE_INITIATE; rc = ble_ll_scan_sm_start(scansm); if (sm == NULL) { return rc; } if (rc == BLE_ERR_SUCCESS) { *sm = scansm; } else { *sm = NULL; } return rc; } #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) int ble_ll_scan_ext_initiator_start(struct hci_ext_create_conn *hcc, struct ble_ll_scan_sm **sm) { struct ble_ll_scan_sm *scansm; struct ble_ll_scan_params *scanp_uncoded; struct ble_ll_scan_params *scanp_coded; struct hci_ext_conn_params *params; int rc; scansm = &g_ble_ll_scan_sm; scansm->own_addr_type = hcc->own_addr_type; scansm->scanp = NULL; scansm->scanp_next = NULL; scansm->ext_scanning = 1; if (hcc->init_phy_mask & BLE_PHY_MASK_1M) { params = &hcc->params[0]; scanp_uncoded = &scansm->scanp_phys[PHY_UNCODED]; scanp_uncoded->timing.interval = ble_ll_scan_time_hci_to_ticks(params->scan_itvl); scanp_uncoded->timing.window = ble_ll_scan_time_hci_to_ticks(params->scan_window); scanp_uncoded->scan_type = BLE_SCAN_TYPE_INITIATE; scanp_uncoded->scan_filt_policy = hcc->filter_policy; scansm->scanp = scanp_uncoded; } if (hcc->init_phy_mask & BLE_PHY_MASK_CODED) { params = &hcc->params[2]; scanp_coded = &scansm->scanp_phys[PHY_CODED]; scanp_coded->timing.interval = ble_ll_scan_time_hci_to_ticks(params->scan_itvl); scanp_coded->timing.window = ble_ll_scan_time_hci_to_ticks(params->scan_window); scanp_coded->scan_type = BLE_SCAN_TYPE_INITIATE; scanp_coded->scan_filt_policy = hcc->filter_policy; if (scansm->scanp) { scansm->scanp_next = scanp_coded; } else { scansm->scanp = scanp_coded; } } /* if any of PHYs is configured for continuous scan we alter interval to * fit other PHY */ if (scansm->scanp && scansm->scanp_next && scanp_coded->configured && scanp_uncoded->configured) { if (scanp_coded->timing.interval == scanp_coded->timing.window) { scanp_coded->timing.interval += scanp_uncoded->timing.window; } if (scanp_uncoded->timing.interval == scanp_uncoded->timing.window) { scanp_uncoded->timing.interval += scanp_coded->timing.window; } } rc = ble_ll_scan_sm_start(scansm); if (sm == NULL) { return rc; } if (rc == BLE_ERR_SUCCESS) { *sm = scansm; } else { *sm = NULL; } return rc; } #endif /** * Checks to see if the scanner is enabled. * * @return int 0: not enabled; enabled otherwise */ int ble_ll_scan_enabled(void) { return (int)g_ble_ll_scan_sm.scan_enabled; } /** * Returns the peer resolvable private address of last device connecting to us * * @return uint8_t* */ uint8_t * ble_ll_scan_get_peer_rpa(void) { struct ble_ll_scan_sm *scansm; /* XXX: should this go into IRK list or connection? */ scansm = &g_ble_ll_scan_sm; return scansm->scan_peer_rpa; } /** * Returns the local resolvable private address currently being using by * the scanner/initiator * * @return uint8_t* */ uint8_t * ble_ll_scan_get_local_rpa(void) { return g_ble_ll_scan_sm.pdu_data.scana; } /** * Set the Resolvable Private Address in the scanning (or initiating) state * machine. * * XXX: should this go into IRK list or connection? * * @param rpa */ void ble_ll_scan_set_peer_rpa(uint8_t *rpa) { struct ble_ll_scan_sm *scansm; scansm = &g_ble_ll_scan_sm; memcpy(scansm->scan_peer_rpa, rpa, BLE_DEV_ADDR_LEN); } struct ble_ll_scan_pdu_data * ble_ll_scan_get_pdu_data(void) { return &g_ble_ll_scan_sm.pdu_data; } /* Returns true if whitelist is enabled for scanning */ int ble_ll_scan_whitelist_enabled(void) { return g_ble_ll_scan_sm.scanp->scan_filt_policy & 1; } static void ble_ll_scan_common_init(void) { struct ble_ll_scan_sm *scansm; struct ble_ll_scan_params *scanp; int i; /* Clear state machine in case re-initialized */ scansm = &g_ble_ll_scan_sm; memset(scansm, 0, sizeof(struct ble_ll_scan_sm)); /* Clear scan parameters in case re-initialized */ memset(g_ble_ll_scan_params, 0, sizeof(g_ble_ll_scan_params)); /* Initialize scanning window end event */ ble_npl_event_init(&scansm->scan_sched_ev, ble_ll_scan_event_proc, scansm); for (i = 0; i < BLE_LL_SCAN_PHY_NUMBER; i++) { /* Set all non-zero default parameters */ scanp = &g_ble_ll_scan_params[i]; scanp->timing.interval = ble_ll_scan_time_hci_to_ticks(BLE_HCI_SCAN_ITVL_DEF); scanp->timing.window = ble_ll_scan_time_hci_to_ticks(BLE_HCI_SCAN_WINDOW_DEF); } scansm->scanp_phys[PHY_UNCODED].phy = BLE_PHY_1M; #if (BLE_LL_SCAN_PHY_NUMBER == 2) scansm->scanp_phys[PHY_CODED].phy = BLE_PHY_CODED; #endif #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) /* Make sure we'll generate new NRPA if necessary */ scansm->scan_nrpa_timer = ble_npl_time_get(); #endif /* 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(&scansm->duration_timer, ble_ll_scan_duration_timer_cb, scansm); os_cputime_timer_init(&scansm->period_timer, ble_ll_scan_period_timer_cb, scansm); #endif ble_npl_event_init(&scansm->scan_interrupted_ev, ble_ll_scan_interrupted_event_cb, NULL); } /** * Called when the controller receives the reset command. Resets the * scanning state machine to its initial state. * * @return int */ void ble_ll_scan_reset(void) { struct ble_ll_scan_sm *scansm; 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 /* Reset duplicate advertisers and those from which we rxd a response */ g_ble_ll_scan_num_rsp_advs = 0; memset(&g_ble_ll_scan_rsp_advs[0], 0, sizeof(g_ble_ll_scan_rsp_advs)); os_mempool_clear(&g_scan_dup_pool); TAILQ_INIT(&g_scan_dup_list); #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) /* clear memory pool for AUX scan results */ os_mempool_clear(&ext_scan_aux_pool); #endif /* Call the common init function again */ ble_ll_scan_common_init(); } /** * ble ll scan init * * Initialize a scanner. Must be called before scanning can be started. * Expected to be called with a un-initialized scanning state machine. */ void ble_ll_scan_init(void) { os_error_t err; #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) err = os_mempool_init(&ext_scan_aux_pool, MYNEWT_VAL(BLE_LL_EXT_ADV_AUX_PTR_CNT), sizeof (struct ble_ll_aux_data), ext_scan_aux_mem, "ble_ll_aux_scan_pool"); BLE_LL_ASSERT(err == 0); #endif err = os_mempool_init(&g_scan_dup_pool, MYNEWT_VAL(BLE_LL_NUM_SCAN_DUP_ADVS), sizeof(struct ble_ll_scan_dup_entry), g_scan_dup_mem, "ble_ll_scan_dup_pool"); BLE_LL_ASSERT(err == 0); TAILQ_INIT(&g_scan_dup_list); ble_ll_scan_common_init(); }