| /* |
| * 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 <stdint.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <assert.h> |
| #include "syscfg/syscfg.h" |
| #include "os/os.h" |
| #include "nimble/ble.h" |
| #include "nimble/hci_common.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_scan.h" |
| #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) |
| #include "controller/ble_ll_scan_aux.h" |
| #endif |
| #include "controller/ble_ll_tmr.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" |
| #include "ble_ll_priv.h" |
| |
| #if MYNEWT_VAL(BLE_LL_ROLE_OBSERVER) |
| |
| /* |
| * 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 |
| |
| struct ble_ll_scan_params { |
| uint8_t own_addr_type; |
| uint8_t scan_filt_policy; |
| struct ble_ll_scan_phy scan_phys[BLE_LL_SCAN_PHY_NUMBER]; |
| }; |
| |
| static struct ble_ll_scan_params g_ble_ll_scan_params; |
| |
| /* The scanning state machine global object */ |
| static struct ble_ll_scan_sm g_ble_ll_scan_sm; |
| |
| /* |
| * 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) |
| static int |
| ble_ll_scan_start(struct ble_ll_scan_sm *scansm); |
| |
| #endif |
| |
| static inline uint32_t |
| ble_ll_scan_time_hci_to_ticks(uint16_t value) |
| { |
| return ble_ll_tmr_u2t(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_SCAN_ACTIVE_SCAN_NRPA) |
| static void |
| ble_ll_scan_refresh_nrpa(struct ble_ll_scan_sm *scansm) |
| { |
| ble_npl_time_t now; |
| |
| now = ble_npl_time_get(); |
| if (LL_TMR_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 |
| |
| struct ble_ll_scan_sm * |
| ble_ll_scan_sm_get(void) |
| { |
| return &g_ble_ll_scan_sm; |
| } |
| |
| uint8_t |
| ble_ll_scan_get_own_addr_type(void) |
| { |
| return g_ble_ll_scan_sm.own_addr_type; |
| } |
| |
| uint8_t |
| ble_ll_scan_get_filt_policy(void) |
| { |
| return g_ble_ll_scan_sm.scan_filt_policy; |
| } |
| |
| uint8_t |
| ble_ll_scan_get_filt_dups(void) |
| { |
| return g_ble_ll_scan_sm.scan_filt_dups; |
| } |
| |
| uint8_t |
| ble_ll_scan_backoff_kick(void) |
| { |
| struct ble_ll_scan_sm *scansm = &g_ble_ll_scan_sm; |
| |
| if (scansm->backoff_count > 0) { |
| scansm->backoff_count--; |
| } |
| |
| return scansm->backoff_count; |
| } |
| |
| void |
| ble_ll_scan_backoff_update(int success) |
| { |
| struct ble_ll_scan_sm *scansm = &g_ble_ll_scan_sm; |
| |
| ble_ll_scan_req_backoff(scansm, success); |
| } |
| |
| void |
| ble_ll_scan_make_req_pdu(struct ble_ll_scan_sm *scansm, uint8_t *pdu, |
| uint8_t *hdr_byte, uint8_t adva_type, |
| const uint8_t *adva, int rpa_index) |
| { |
| uint8_t *scana; |
| #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) |
| struct ble_ll_resolv_entry *rl; |
| uint8_t rpa[BLE_DEV_ADDR_LEN]; |
| #endif |
| |
| /* Construct first PDU header byte */ |
| *hdr_byte = BLE_ADV_PDU_TYPE_SCAN_REQ; |
| if (adva_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 (rpa_index >= 0) { |
| rl = &g_ble_ll_resolv_list[rpa_index]; |
| } else { |
| rl = NULL; |
| } |
| |
| /* Check if we should use RPA/NRPA instead of public/random address: |
| * - use RPA if device is on RL and has local IRK set |
| * - use RPA generated from local IRK if set |
| * - use NRPA if allowed by configuration |
| * */ |
| |
| if (rl && rl->rl_has_local) { |
| ble_ll_resolv_get_priv_addr(rl, 1, rpa); |
| scana = rpa; |
| } else { |
| if (ble_ll_resolv_local_rpa_get(scansm->own_addr_type & 0x01, rpa) == 0) { |
| scana = rpa; |
| } else { |
| #if MYNEWT_VAL(BLE_LL_SCAN_ACTIVE_SCAN_NRPA) |
| ble_ll_scan_refresh_nrpa(scansm); |
| scana = scansm->scan_nrpa; |
| #endif |
| } |
| } |
| |
| *hdr_byte |= BLE_ADV_PDU_HDR_TXADD_RAND; |
| } |
| #endif |
| |
| memcpy(pdu, scana, BLE_DEV_ADDR_LEN); |
| memcpy(pdu + 6, adva, BLE_DEV_ADDR_LEN); |
| } |
| |
| static void |
| ble_ll_scan_req_pdu_prepare(struct ble_ll_scan_sm *scansm, |
| const uint8_t *adv_addr, uint8_t adv_addr_type, |
| int8_t rpa_index) |
| { |
| ble_ll_scan_make_req_pdu(scansm, scansm->pdu_data.scana, |
| &scansm->pdu_data.hdr_byte, adv_addr_type, adv_addr, |
| rpa_index); |
| } |
| |
| 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 = ble_transport_alloc_evt(1); |
| 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; |
| } |
| #endif |
| |
| void |
| ble_ll_scan_halt(void) |
| { |
| struct ble_ll_scan_sm *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); |
| } |
| } |
| |
| /** |
| * 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. |
| */ |
| 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) |
| 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_transport_free(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 = ble_transport_alloc_evt(1); |
| 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 = ble_transport_alloc_evt(1); |
| 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; |
| } else { |
| #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) |
| if (scansm->ext_scanning) { |
| if (ble_ll_is_rpa(inita, inita_type)) { |
| inita_type = 0xfe; |
| } |
| } |
| #endif |
| } |
| #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 - ble_ll_rx_gain(), |
| 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 - ble_ll_rx_gain()); |
| goto done; |
| } |
| |
| rc = ble_ll_hci_send_adv_report(evtype, adva, adva_type, |
| hdr->rxinfo.rssi - ble_ll_rx_gain(), |
| adv_data_len, om); |
| done: |
| if (!rc && scansm->scan_filt_dups) { |
| ble_ll_scan_dup_update_legacy(adva_type, adva, subev, evtype); |
| } |
| } |
| |
| /** |
| * 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) |
| { |
| int rc; |
| struct ble_ll_scan_phy *scanp = scansm->scanp; |
| uint8_t chan; |
| #if MYNEWT_VAL(BLE_LL_PHY) |
| uint8_t phy_mode; |
| int phy; |
| #endif |
| |
| BLE_LL_ASSERT(scansm->scan_rsp_pending == 0); |
| |
| /* Set channel */ |
| chan = scanp->scan_chan; |
| rc = ble_phy_setchan(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 MYNEWT_VAL(BLE_LL_PHY) |
| phy = scanp->phy; |
| phy_mode = ble_ll_phy_to_phy_mode(phy, BLE_HCI_LE_PHY_CODED_ANY); |
| ble_phy_mode_set(phy_mode, phy_mode); |
| #endif |
| |
| /* if scan is not passive we need to set tx power as we may end up sending |
| * package |
| */ |
| if (scansm->scanp->scan_type != BLE_SCAN_TYPE_PASSIVE) { |
| ble_ll_tx_power_set(g_ble_ll_tx_power); |
| } |
| |
| rc = ble_phy_rx_set_start_time(ble_ll_tmr_get() + |
| 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 (scansm->scan_filt_policy & 1) { |
| ble_ll_whitelist_enable(); |
| } else { |
| ble_ll_whitelist_disable(); |
| } |
| |
| 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_phy *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 (LL_TMR_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_phy *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 LL_TMR_GEQ(time, start_time) && |
| LL_TMR_LT(time, start_time + scanp->timing.window); |
| } |
| |
| /** |
| * 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; |
| ble_ll_tmr_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) { |
| /* 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_sched_rmv_elem_type(BLE_LL_SCHED_TYPE_SCAN_AUX, ble_ll_scan_aux_sched_remove); |
| scansm->ext_scanning = 0; |
| } |
| #endif |
| |
| #if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL) |
| scansm->connsm = NULL; |
| #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_phy *scanp; |
| struct ble_ll_scan_phy *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 */ |
| ble_ll_tmr_start(&scansm->scan_timer, scanp->timing.start_time); |
| |
| return BLE_ERR_SUCCESS; |
| } |
| |
| 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 (!scansm->scan_enabled) { |
| return; |
| } |
| |
| /* |
| * 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_phy *scanp; |
| #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) |
| bool inside_window_next; |
| struct ble_ll_scan_phy *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) { |
| ble_ll_tmr_stop(&scansm->scan_timer); |
| ble_ll_rfmgmt_scan_changed(false, 0); |
| ble_ll_rfmgmt_release(); |
| OS_EXIT_CRITICAL(sr); |
| return; |
| } |
| |
| if (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 = ble_ll_tmr_get(); |
| |
| 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 || LL_TMR_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()) { |
| #if MYNEWT_VAL(BLE_LL_ROLE_BROADCASTER) |
| case BLE_LL_STATE_ADV: |
| start_scan = false; |
| break; |
| #endif |
| #if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL) || MYNEWT_VAL(BLE_LL_ROLE_CENTRAL) |
| case BLE_LL_STATE_CONNECTION: |
| start_scan = false; |
| break; |
| #endif |
| #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV) |
| case BLE_LL_STATE_SYNC: |
| start_scan = false; |
| break; |
| #endif |
| #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) |
| case BLE_LL_STATE_SCAN_AUX: |
| start_scan = false; |
| break; |
| #endif |
| #if MYNEWT_VAL(BLE_LL_EXT) |
| case BLE_LL_STATE_EXTERNAL: |
| start_scan = false; |
| break; |
| #endif |
| 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; |
| #if MYNEWT_VAL(BLE_LL_ISO_BROADCASTER) |
| case BLE_LL_STATE_BIG: |
| start_scan = false; |
| break; |
| #endif |
| case BLE_LL_STATE_STANDBY: |
| break; |
| default: |
| BLE_LL_ASSERT(0); |
| break; |
| } |
| |
| if (start_scan) { |
| ble_ll_scan_start(scansm); |
| } else { |
| ble_ll_rfmgmt_release(); |
| } |
| |
| OS_EXIT_CRITICAL(sr); |
| ble_ll_tmr_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_phy *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 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); |
| } |
| } |
| 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 |
| #if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL) |
| case BLE_SCAN_TYPE_INITIATE: |
| if ((pdu_type == BLE_ADV_PDU_TYPE_ADV_IND) || |
| (pdu_type == BLE_ADV_PDU_TYPE_ADV_DIRECT_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 |
| break; |
| #endif |
| default: |
| break; |
| } |
| |
| return rc; |
| } |
| |
| 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 = 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; |
| } |
| } |
| |
| int |
| ble_ll_scan_rx_filter(uint8_t own_addr_type, uint8_t scan_filt_policy, |
| struct ble_ll_scan_addr_data *addrd, uint8_t *scan_ok) |
| { |
| #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) |
| struct ble_ll_resolv_entry *rl = NULL; |
| #endif |
| bool scan_req_allowed = true; |
| bool resolved; |
| |
| /* Note: caller is expected to fill adva, targeta and rpa_index in addrd */ |
| |
| /* Use AdvA as initial advertiser address, we may change it if resolved */ |
| addrd->adv_addr = addrd->adva; |
| addrd->adv_addr_type = addrd->adva_type; |
| |
| #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) |
| addrd->adva_resolved = 0; |
| addrd->targeta_resolved = 0; |
| |
| BLE_LL_ASSERT((addrd->rpa_index < 0) || |
| (ble_ll_addr_subtype(addrd->adva, addrd->adva_type) == |
| BLE_LL_ADDR_SUBTYPE_RPA)); |
| |
| switch (ble_ll_addr_subtype(addrd->adva, addrd->adva_type)) { |
| case BLE_LL_ADDR_SUBTYPE_RPA: |
| if (addrd->rpa_index >= 0) { |
| addrd->adva_resolved = 1; |
| |
| /* Use resolved identity address as advertiser address */ |
| rl = &g_ble_ll_resolv_list[addrd->rpa_index]; |
| addrd->adv_addr = rl->rl_identity_addr; |
| addrd->adv_addr_type = rl->rl_addr_type; |
| break; |
| } |
| |
| /* fall-through */ |
| 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; |
| } |
| |
| /* 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 -1; |
| } |
| |
| addrd->rpa_index = ble_ll_resolv_get_idx(rl); |
| 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)) { |
| addrd->targeta_resolved = 1; |
| break; |
| } |
| |
| /* Check if scan filter policy allows unresolved RPAs to be processed */ |
| if (!(scan_filt_policy & 0x02)) { |
| return -2; |
| } |
| |
| /* Do not send scan request even if scan policy allows unresolved |
| * RPAs - we do not know if this one if 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 ((own_addr_type & 0x02) && rl && rl->rl_has_local) { |
| return -1; |
| } |
| |
| /* Ignore if not directed to us */ |
| if ((addrd->targeta_type != (own_addr_type & 0x01)) || |
| !ble_ll_is_our_devaddr(addrd->targeta, addrd->targeta_type)) { |
| return -1; |
| } |
| break; |
| default: |
| /* NRPA goes through filtering policy directly */ |
| break; |
| } |
| } |
| |
| resolved = addrd->adva_resolved; |
| #else |
| /* Ignore if not directed to us */ |
| if (addrd->targeta && |
| ((addrd->targeta_type != (own_addr_type & 0x01)) || |
| !ble_ll_is_our_devaddr(addrd->targeta, addrd->targeta_type))) { |
| return -1; |
| } |
| |
| resolved = false; |
| #endif |
| |
| if (scan_filt_policy & 0x01) { |
| /* Check on WL if required by scan filter policy */ |
| if (!ble_ll_whitelist_match(addrd->adv_addr, addrd->adv_addr_type, |
| resolved)) { |
| return -2; |
| } |
| } |
| |
| if (scan_ok) { |
| *scan_ok = scan_req_allowed; |
| } |
| |
| return 0; |
| } |
| |
| #if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL) |
| int |
| ble_ll_scan_rx_check_init(struct ble_ll_scan_addr_data *addrd) |
| { |
| struct ble_ll_scan_sm *scansm; |
| struct ble_ll_conn_sm *connsm; |
| |
| scansm = &g_ble_ll_scan_sm; |
| connsm = scansm->connsm; |
| BLE_LL_ASSERT(connsm); |
| |
| #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) |
| if ((connsm->peer_addr_type > BLE_ADDR_RANDOM) && !addrd->adva_resolved) { |
| return -1; |
| } |
| #endif |
| if ((addrd->adv_addr_type != (connsm->peer_addr_type & 0x01)) || |
| memcmp(addrd->adv_addr, connsm->peer_addr, 6) != 0) { |
| return -1; |
| } |
| |
| return 0; |
| } |
| #endif |
| |
| static int |
| ble_ll_scan_rx_isr_end_on_adv(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_phy *scanp = scansm->scanp; |
| struct ble_mbuf_hdr_rxinfo *rxinfo = &hdr->rxinfo; |
| uint8_t scan_ok; |
| int rc; |
| |
| ble_ll_scan_get_addr_data_from_legacy(pdu_type, rxbuf, addrd); |
| |
| #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) |
| addrd->rpa_index = ble_hw_resolv_list_match(); |
| #endif |
| |
| rc = ble_ll_scan_rx_filter(scansm->own_addr_type, |
| scansm->scan_filt_policy, addrd, &scan_ok); |
| if (rc < 0) { |
| return 0; |
| } |
| |
| #if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL) |
| if ((scanp->scan_type == BLE_SCAN_TYPE_INITIATE) && |
| !(scansm->scan_filt_policy & 0x01)) { |
| rc = ble_ll_scan_rx_check_init(addrd); |
| if (rc < 0) { |
| return 0; |
| } |
| } |
| #endif |
| |
| rxinfo->flags |= BLE_MBUF_HDR_F_DEVMATCH; |
| #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) |
| rxinfo->rpa_index = addrd->rpa_index; |
| if (addrd->adva_resolved) { |
| rxinfo->flags |= BLE_MBUF_HDR_F_RESOLVED; |
| } |
| if (addrd->targeta_resolved) { |
| rxinfo->flags |= BLE_MBUF_HDR_F_TARGETA_RESOLVED; |
| } |
| #endif |
| |
| if (!scan_ok) { |
| /* Scan request forbidden by filter policy */ |
| return 0; |
| } |
| |
| /* Allow responding to all PDUs when initiating since unwanted PDUs were |
| * already filtered out in isr_start. |
| */ |
| if ((scanp->scan_type == BLE_SCAN_TYPE_ACTIVE) && |
| ((pdu_type == BLE_ADV_PDU_TYPE_ADV_IND) || |
| (pdu_type == BLE_ADV_PDU_TYPE_ADV_SCAN_IND))) { |
| return 1; |
| } |
| |
| #if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL) |
| if (scanp->scan_type == BLE_SCAN_TYPE_INITIATE) { |
| return 1; |
| } |
| #endif |
| |
| return 0; |
| } |
| |
| static int |
| ble_ll_scan_rx_isr_end_on_scan_rsp(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; |
| uint8_t sreq_adva_type; |
| uint8_t *sreq_adva; |
| |
| ble_ll_scan_get_addr_data_from_legacy(pdu_type, rxbuf, addrd); |
| |
| 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; |
| } |
| |
| 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; |
| bool is_ext_adv = false; |
| int8_t rpa_index; |
| uint16_t adi = 0; |
| int rc; |
| |
| /* 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 (ble_ll_scan_backoff_kick() != 0) { |
| return false; |
| } |
| |
| #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) |
| rpa_index = addrd->rpa_index; |
| #else |
| rpa_index = -1; |
| #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, |
| rpa_index); |
| |
| 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; |
| |
| 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; |
| } |
| |
| if (!crcok) { |
| goto scan_rx_isr_ignore; |
| } |
| |
| if (!scansm->scan_enabled) { |
| STATS_INC(ble_ll_stats, rx_pdu_on_scan_disabled); |
| goto scan_rx_isr_ignore; |
| } |
| |
| rxbuf = rxpdu->om_data; |
| pdu_type = rxbuf[0] & BLE_ADV_PDU_HDR_TYPE_MASK; |
| |
| switch (pdu_type) { |
| case BLE_ADV_PDU_TYPE_ADV_IND: |
| case BLE_ADV_PDU_TYPE_ADV_DIRECT_IND: |
| rc = ble_ll_scan_rx_isr_end_on_adv(pdu_type, rxbuf, hdr, &addrd); |
| break; |
| case BLE_ADV_PDU_TYPE_ADV_NONCONN_IND: |
| case BLE_ADV_PDU_TYPE_ADV_SCAN_IND: |
| #if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL) |
| if (scansm->scanp->scan_type == BLE_SCAN_TYPE_INITIATE) { |
| rc = -1; |
| break; |
| } |
| #endif |
| rc = ble_ll_scan_rx_isr_end_on_adv(pdu_type, rxbuf, hdr, &addrd); |
| break; |
| case BLE_ADV_PDU_TYPE_SCAN_RSP: |
| rc = ble_ll_scan_rx_isr_end_on_scan_rsp(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_aux_rx_isr_end_on_ext(&g_ble_ll_scan_sm, rxpdu); |
| if (rc < 0) { |
| rxinfo->flags |= BLE_MBUF_HDR_F_IGNORED; |
| } |
| ble_ll_state_set(BLE_LL_STATE_STANDBY); |
| /* Return here, we do not want any further processing since it's all |
| * handled in scan_aux. |
| */ |
| return -1; |
| #endif |
| default: |
| /* This is not something we would like to process here */ |
| rc = -1; |
| break; |
| } |
| |
| if (rc == -1) { |
| goto scan_rx_isr_ignore; |
| } |
| |
| if (rc == 1) { |
| switch (scansm->scanp->scan_type) { |
| case BLE_SCAN_TYPE_ACTIVE: |
| if (ble_ll_scan_send_scan_req(pdu_type, rxbuf, hdr, &addrd)) { |
| /* Keep PHY active and LL in scanning state */ |
| return 0; |
| } |
| break; |
| #if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL) |
| case BLE_SCAN_TYPE_INITIATE: |
| if (ble_ll_conn_send_connect_req(rxpdu, &addrd, 0) == 0) { |
| hdr->rxinfo.flags |= BLE_MBUF_HDR_F_CONNECT_IND_TXD; |
| return 0; |
| } |
| break; |
| #endif |
| } |
| } |
| |
| /* 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_add(&scansm->scan_sched_ev); |
| STATS_INC(ble_ll_stats, scan_timer_restarted); |
| OS_EXIT_CRITICAL(sr); |
| return; |
| } |
| |
| now = ble_ll_tmr_get(); |
| 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); |
| } |
| 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_add(&scansm->scan_sched_ev); |
| } |
| |
| void |
| ble_ll_scan_interrupted(struct ble_ll_scan_sm *scansm) |
| { |
| ble_ll_event_add(&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 = &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); |
| } |
| |
| ble_phy_restart_rx(); |
| } |
| |
| 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) |
| int |
| ble_ll_scan_dup_check_ext(uint8_t addr_type, uint8_t *addr, bool has_aux, |
| uint16_t adi) |
| { |
| struct ble_ll_scan_dup_entry *e; |
| bool is_anon; |
| uint8_t type; |
| int rc; |
| |
| is_anon = addr == NULL; |
| adi = has_aux ? 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; |
| } |
| |
| int |
| ble_ll_scan_dup_update_ext(uint8_t addr_type, uint8_t *addr, bool has_aux, |
| uint16_t adi) |
| { |
| struct ble_ll_scan_dup_entry *e; |
| bool is_anon; |
| uint8_t type; |
| |
| is_anon = addr == NULL; |
| adi = has_aux ? 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) |
| addrd->rpa_index = rxinfo->rpa_index; |
| |
| if (hdr->rxinfo.flags & BLE_MBUF_HDR_F_RESOLVED) { |
| BLE_LL_ASSERT(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->adva_resolved = 1; |
| } else { |
| addrd->adva_resolved = 0; |
| } |
| |
| 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; |
| addrd->targeta_resolved = 1; |
| } else { |
| addrd->targeta_resolved = 0; |
| } |
| #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); |
| |
| #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) |
| if (addrd->adva_resolved) { |
| BLE_LL_ASSERT(addrd->rpa_index >= 0); |
| ble_ll_resolv_set_peer_rpa(addrd->rpa_index, addrd->adva); |
| } |
| #endif |
| |
| 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); |
| } |
| } |
| |
| /** |
| * 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_ROLE_CENTRAL) || MYNEWT_VAL(BLE_LL_HCI_VS_SET_SCAN_CFG) |
| struct ble_mbuf_hdr_rxinfo *rxinfo = &hdr->rxinfo; |
| #endif |
| #if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL) |
| uint8_t *targeta; |
| #endif |
| struct ble_ll_scan_sm *scansm; |
| struct ble_ll_scan_addr_data addrd; |
| uint8_t max_pdu_type; |
| |
| scansm = &g_ble_ll_scan_sm; |
| |
| /* Ignore PDUs we do not expect here */ |
| max_pdu_type = BLE_ADV_PDU_TYPE_ADV_SCAN_IND; |
| #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) |
| if (scansm->ext_scanning) { |
| /* Note: We do not expect AUX_CONNECT_RSP here */ |
| max_pdu_type = BLE_ADV_PDU_TYPE_ADV_EXT_IND; |
| } |
| #endif |
| if (ptype > max_pdu_type) { |
| ble_ll_scan_chk_resume(); |
| return; |
| } |
| |
| #if MYNEWT_VAL(BLE_LL_HCI_VS_SET_SCAN_CFG) |
| if ((scansm->vs_config.ignore_ext) && |
| (ptype == BLE_ADV_PDU_TYPE_ADV_EXT_IND)) { |
| ble_ll_scan_chk_resume(); |
| return; |
| } |
| |
| if ((scansm->vs_config.ignore_legacy) && |
| (ptype != BLE_ADV_PDU_TYPE_ADV_EXT_IND)) { |
| ble_ll_scan_chk_resume(); |
| return; |
| } |
| |
| if ((scansm->vs_config.rssi_filter) && |
| (rxinfo->rssi < scansm->vs_config.rssi_threshold)) { |
| ble_ll_scan_chk_resume(); |
| return; |
| } |
| #endif |
| |
| #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) |
| if (ptype == BLE_ADV_PDU_TYPE_ADV_EXT_IND) { |
| ble_ll_scan_aux_pkt_in_on_ext(om, hdr); |
| ble_ll_scan_chk_resume(); |
| return; |
| } |
| #endif |
| |
| switch (scansm->scanp->scan_type) { |
| #if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL) |
| case BLE_SCAN_TYPE_INITIATE: |
| if (rxinfo->flags & BLE_MBUF_HDR_F_CONNECT_IND_TXD) { |
| /* We need to keep original TargetA in case it was resolved, so rl |
| * can be updated properly. |
| */ |
| ble_ll_scan_get_addr_data_from_legacy(ptype, om->om_data, &addrd); |
| targeta = addrd.targeta; |
| ble_ll_scan_rx_pkt_in_restore_addr_data(hdr, &addrd); |
| |
| ble_ll_scan_sm_stop(0); |
| ble_ll_conn_created_on_legacy(om, &addrd, targeta); |
| return; |
| } |
| break; |
| #endif |
| default: |
| ble_ll_scan_rx_pkt_in_on_legacy(ptype, om, hdr, &addrd); |
| break; |
| } |
| |
| ble_ll_scan_chk_resume(); |
| } |
| |
| int |
| ble_ll_scan_hci_set_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_phy *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 */ |
| g_ble_ll_scan_params.own_addr_type = cmd->own_addr_type; |
| g_ble_ll_scan_params.scan_filt_policy = cmd->filter_policy; |
| |
| scanp = &g_ble_ll_scan_params.scan_phys[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); |
| |
| #if (BLE_LL_SCAN_PHY_NUMBER == 2) |
| scanp = &g_ble_ll_scan_params.scan_phys[PHY_CODED]; |
| scanp->configured = 0; |
| #endif |
| |
| return 0; |
| } |
| |
| #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) |
| static int |
| ble_ll_scan_check_phy_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_EXT) || |
| (window < BLE_HCI_SCAN_WINDOW_MIN) || |
| (window > BLE_HCI_SCAN_WINDOW_MAX_EXT) || |
| (itvl < window)) { |
| return BLE_ERR_INV_HCI_CMD_PARMS; |
| } |
| |
| return 0; |
| } |
| |
| int |
| ble_ll_scan_hci_set_ext_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_phy new_params[BLE_LL_SCAN_PHY_NUMBER] = { }; |
| struct ble_ll_scan_phy *uncoded = &new_params[PHY_UNCODED]; |
| #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY) |
| struct ble_ll_scan_phy *coded = &new_params[PHY_CODED]; |
| #endif |
| 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; |
| } |
| |
| /* Check scanner filter policy */ |
| if (cmd->filter_policy > BLE_HCI_SCAN_FILT_MAX) { |
| return BLE_ERR_INV_HCI_CMD_PARMS; |
| } |
| |
| /* Check if valid phy is specified */ |
| if (cmd->phys & ~SCAN_VALID_PHY_MASK) { |
| return BLE_ERR_UNSUPPORTED; |
| } |
| |
| /* Check if at least one valid phy is specified */ |
| if (!(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_scan_check_phy_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_scan_check_phy_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; |
| } |
| |
| /* 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.interval += coded->timing.window; |
| } |
| } |
| #endif |
| |
| g_ble_ll_scan_params.own_addr_type = cmd->own_addr_type; |
| g_ble_ll_scan_params.scan_filt_policy = cmd->filter_policy; |
| |
| memcpy(g_ble_ll_scan_params.scan_phys, new_params, sizeof(new_params)); |
| |
| return 0; |
| } |
| |
| #endif |
| |
| #if MYNEWT_VAL(BLE_LL_HCI_VS_SET_SCAN_CFG) |
| int |
| ble_ll_scan_set_vs_config(uint32_t flags, int8_t rssi_threshold) |
| { |
| struct ble_ll_scan_sm *scansm; |
| |
| scansm = &g_ble_ll_scan_sm; |
| |
| if (scansm->scan_enabled || scansm->connsm) { |
| return 1; |
| } |
| |
| scansm->vs_config.ignore_legacy = !!(flags & BLE_HCI_VS_SET_SCAN_CFG_FLAG_NO_LEGACY); |
| scansm->vs_config.ignore_ext = !!(flags & BLE_HCI_VS_SET_SCAN_CFG_FLAG_NO_EXT); |
| scansm->vs_config.rssi_filter = !!(flags & BLE_HCI_VS_SET_SCAN_CFG_FLAG_RSSI_FILTER); |
| scansm->vs_config.rssi_threshold = rssi_threshold; |
| |
| 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) |
| { |
| ble_npl_callout_stop(&scansm->duration_timer); |
| ble_npl_callout_stop(&scansm->period_timer); |
| |
| if (scansm->duration_ticks) { |
| ble_npl_callout_reset(&scansm->duration_timer, |
| scansm->duration_ticks); |
| |
| if (scansm->period_ticks) { |
| ble_npl_callout_reset(&scansm->period_timer, |
| scansm->period_ticks); |
| } |
| } |
| } |
| |
| static void |
| ble_ll_scan_duration_timer_cb(struct ble_npl_event *ev) |
| { |
| struct ble_ll_scan_sm *scansm = ble_npl_event_get_arg(ev); |
| |
| 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(struct ble_npl_event *ev) |
| { |
| struct ble_ll_scan_sm *scansm = ble_npl_event_get_arg(ev); |
| |
| 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_phy *scanp; |
| struct ble_ll_scan_phy *scanp_phy; |
| int i; |
| #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) |
| ble_npl_time_t period_ticks = 0; |
| ble_npl_time_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) |
| if (ext) { |
| /* |
| * If Enable is set to 0x01 and the Host has not issued the |
| * HCI_LE_Set_Extended_Scan_Parameters command, the Controller shall |
| * either use vendor-specified parameters or return the error code |
| * Command Disallowed (0x0C) |
| * |
| * To keep things simple for devices without public address we |
| * reject in such case. |
| */ |
| for (i = 0; i < BLE_LL_SCAN_PHY_NUMBER; i++) { |
| if (g_ble_ll_scan_params.scan_phys[i].configured) { |
| break; |
| } |
| } |
| |
| if (i == BLE_LL_SCAN_PHY_NUMBER) { |
| return BLE_ERR_CMD_DISALLOWED; |
| } |
| } |
| |
| /* 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 */ |
| if (ble_npl_time_ms_to_ticks(period * 1280, &period_ticks)) { |
| return BLE_ERR_INV_HCI_CMD_PARMS; |
| } |
| |
| /* duration is in 10ms units */ |
| dur_ticks = ble_npl_time_ms_to_ticks32(dur * 10); |
| |
| 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) |
| ble_npl_callout_stop(&scansm->duration_timer); |
| ble_npl_callout_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.*/ |
| #if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL) |
| for (i = 0; i < BLE_LL_SCAN_PHY_NUMBER; i++) { |
| scanp_phy = &scansm->scan_phys[i]; |
| if (scanp_phy->configured && |
| scanp_phy->scan_type == BLE_SCAN_TYPE_INITIATE) { |
| return BLE_ERR_CMD_DISALLOWED; |
| } |
| } |
| #endif |
| |
| #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; |
| |
| scansm->own_addr_type = g_ble_ll_scan_params.own_addr_type; |
| scansm->scan_filt_policy = g_ble_ll_scan_params.scan_filt_policy; |
| |
| for (i = 0; i < BLE_LL_SCAN_PHY_NUMBER; i++) { |
| scanp_phy = &scansm->scan_phys[i]; |
| scanp = &g_ble_ll_scan_params.scan_phys[i]; |
| |
| if (!scanp->configured) { |
| continue; |
| } |
| |
| scanp_phy->configured = scanp->configured; |
| scanp_phy->scan_type = scanp->scan_type; |
| scanp_phy->timing = scanp->timing; |
| |
| if (!scansm->scanp) { |
| scansm->scanp = scanp_phy; |
| } 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->scan_phys[PHY_UNCODED]; |
| scansm->own_addr_type = BLE_ADDR_PUBLIC; |
| scansm->scan_filt_policy = BLE_HCI_SCAN_FILT_NO_WL; |
| |
| 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); |
| } |
| |
| 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_scan_hci_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_scan_hci_set_ext_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; |
| |
| scansm = &g_ble_ll_scan_sm; |
| if (scansm->scan_enabled && (scansm->scan_filt_policy & 1)) { |
| rc = 0; |
| } else { |
| rc = 1; |
| } |
| |
| return rc; |
| } |
| |
| #if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL) |
| int |
| ble_ll_scan_initiator_start(struct ble_ll_conn_sm *connsm, uint8_t ext, |
| struct ble_ll_conn_create_scan *cc_scan) |
| { |
| struct ble_ll_scan_sm *scansm; |
| struct ble_ll_scan_phy *scanp_uncoded; |
| #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) && MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY) |
| struct ble_ll_scan_phy *scanp_coded; |
| #endif |
| uint8_t init_phy_mask; |
| int rc; |
| |
| scansm = &g_ble_ll_scan_sm; |
| scansm->own_addr_type = cc_scan->own_addr_type; |
| scansm->scan_filt_policy = cc_scan->filter_policy; |
| scansm->scanp = NULL; |
| scansm->scanp_next = NULL; |
| #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) |
| scansm->ext_scanning = ext; |
| init_phy_mask = cc_scan->init_phy_mask; |
| #else |
| init_phy_mask = BLE_PHY_MASK_1M; |
| #endif |
| scansm->connsm = connsm; |
| |
| scanp_uncoded = &scansm->scan_phys[PHY_UNCODED]; |
| if (init_phy_mask & BLE_PHY_MASK_1M) { |
| scanp_uncoded->configured = 1; |
| scanp_uncoded->timing.interval = ble_ll_scan_time_hci_to_ticks( |
| cc_scan->scan_params[PHY_UNCODED].itvl); |
| scanp_uncoded->timing.window = ble_ll_scan_time_hci_to_ticks( |
| cc_scan->scan_params[PHY_UNCODED].window); |
| scanp_uncoded->scan_type = BLE_SCAN_TYPE_INITIATE; |
| scansm->scanp = scanp_uncoded; |
| } else { |
| scanp_uncoded->configured = 0; |
| } |
| |
| #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) && MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY) |
| scanp_coded = &scansm->scan_phys[PHY_CODED]; |
| if (init_phy_mask & BLE_PHY_MASK_CODED) { |
| scanp_coded->configured = 1; |
| scanp_coded->timing.interval = ble_ll_scan_time_hci_to_ticks( |
| cc_scan->scan_params[PHY_CODED].itvl); |
| scanp_coded->timing.window = ble_ll_scan_time_hci_to_ticks( |
| cc_scan->scan_params[PHY_CODED].window); |
| scanp_coded->scan_type = BLE_SCAN_TYPE_INITIATE; |
| if (scansm->scanp) { |
| scansm->scanp_next = scanp_coded; |
| } else { |
| scansm->scanp = scanp_coded; |
| } |
| } else { |
| scanp_coded->configured = 0; |
| } |
| |
| /* 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; |
| } |
| } |
| #endif |
| |
| rc = ble_ll_scan_sm_start(scansm); |
| if (rc == 0) { |
| g_ble_ll_conn_create_sm.connsm = connsm; |
| } |
| |
| 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; |
| } |
| |
| static void |
| ble_ll_scan_common_init(void) |
| { |
| struct ble_ll_scan_sm *scansm; |
| struct ble_ll_scan_phy *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.scan_phys[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->scan_phys[PHY_UNCODED].phy = BLE_PHY_1M; |
| #if (BLE_LL_SCAN_PHY_NUMBER == 2) |
| scansm->scan_phys[PHY_CODED].phy = BLE_PHY_CODED; |
| #endif |
| |
| #if MYNEWT_VAL(BLE_LL_SCAN_ACTIVE_SCAN_NRPA) |
| /* Make sure we'll generate new NRPA if necessary */ |
| scansm->scan_nrpa_timer = ble_npl_time_get(); |
| #endif |
| |
| /* Initialize scanning timer */ |
| ble_ll_tmr_init(&scansm->scan_timer, ble_ll_scan_timer_cb, scansm); |
| |
| /* Initialize extended scan timers */ |
| #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) |
| ble_npl_callout_init(&scansm->duration_timer, &g_ble_ll_data.ll_evq, |
| ble_ll_scan_duration_timer_cb, scansm); |
| ble_npl_callout_init(&scansm->period_timer, &g_ble_ll_data.ll_evq, |
| 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) |
| ble_npl_callout_stop(&scansm->duration_timer); |
| ble_npl_callout_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); |
| |
| /* Call the common init function again */ |
| ble_ll_scan_common_init(); |
| #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) |
| ble_ll_scan_aux_init(); |
| #endif |
| } |
| |
| /** |
| * 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; |
| |
| 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(); |
| #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) |
| ble_ll_scan_aux_init(); |
| #endif |
| } |
| |
| #endif |