/* * 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 #if MYNEWT_VAL(BLE_LL_ROLE_OBSERVER) && MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) #include #include #include #include #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" #include "controller/ble_ll_scan_aux.h" #include "controller/ble_ll_hci.h" #include "controller/ble_ll_whitelist.h" #include "controller/ble_ll_resolv.h" #include "controller/ble_ll_sync.h" #define BLE_LL_SCAN_AUX_F_AUX_ADV 0x0001 #define BLE_LL_SCAN_AUX_F_AUX_CHAIN 0x0002 #define BLE_LL_SCAN_AUX_F_MATCHED 0x0004 #define BLE_LL_SCAN_AUX_F_W4_SCAN_RSP 0x0008 #define BLE_LL_SCAN_AUX_F_SCANNED 0x0010 #define BLE_LL_SCAN_AUX_F_HAS_ADVA 0x0020 #define BLE_LL_SCAN_AUX_F_HAS_TARGETA 0x0040 #define BLE_LL_SCAN_AUX_F_HAS_ADI 0x0080 #define BLE_LL_SCAN_AUX_F_RESOLVED_ADVA 0x0100 #define BLE_LL_SCAN_AUX_F_RESOLVED_TARGETA 0x0200 #define BLE_LL_SCAN_AUX_F_CONNECTABLE 0x0400 #define BLE_LL_SCAN_AUX_F_W4_CONNECT_RSP 0x0800 #define BLE_LL_SCAN_AUX_H_SENT_ANY 0x01 #define BLE_LL_SCAN_AUX_H_DONE 0x02 #define BLE_LL_SCAN_AUX_H_TRUNCATED 0x04 struct ble_ll_scan_aux_data { uint16_t flags; uint8_t hci_state; uint8_t scan_type; uint8_t pri_phy; uint8_t sec_phy; uint8_t chan; uint8_t offset_unit : 1; uint32_t aux_ptr; struct ble_ll_sched_item sch; struct ble_npl_event break_ev; struct ble_hci_ev *hci_ev; uint16_t adi; uint8_t adva[6]; uint8_t targeta[6]; uint8_t adva_type : 1; uint8_t targeta_type : 1; #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) int8_t rpa_index; #endif }; #define AUX_MEMPOOL_SIZE (OS_MEMPOOL_SIZE( \ MYNEWT_VAL(BLE_LL_SCAN_AUX_SEGMENT_CNT), \ sizeof(struct ble_ll_scan_aux_data))) static os_membuf_t aux_data_mem[AUX_MEMPOOL_SIZE]; static struct os_mempool aux_data_pool; static struct ble_ll_scan_aux_data *aux_data_current; static void ble_ll_hci_ev_send_ext_adv_truncated_report(struct ble_ll_scan_aux_data *aux); static inline uint8_t * ble_ll_scan_aux_get_own_addr(void) { uint8_t own_addr_type; own_addr_type = ble_ll_scan_get_own_addr_type() & 1; return ble_ll_get_our_devaddr(own_addr_type); } static int ble_ll_scan_aux_sched_cb(struct ble_ll_sched_item *sch) { struct ble_ll_scan_aux_data *aux = sch->cb_arg; uint32_t wfr_us; #if BLE_LL_BT5_PHY_SUPPORTED uint8_t phy_mode; #endif uint8_t lls; int rc; BLE_LL_ASSERT(aux); lls = ble_ll_state_get(); BLE_LL_ASSERT(lls == BLE_LL_STATE_STANDBY); rc = ble_phy_setchan(aux->chan, BLE_ACCESS_ADDR_ADV, BLE_LL_CRCINIT_ADV); BLE_LL_ASSERT(rc == 0); #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 phy_mode = ble_ll_phy_to_phy_mode(aux->sec_phy, BLE_HCI_LE_PHY_CODED_ANY); ble_phy_mode_set(phy_mode, phy_mode); #endif rc = ble_phy_rx_set_start_time(sch->start_time + g_ble_ll_sched_offset_ticks, sch->remainder); if (rc != 0 && rc != BLE_PHY_ERR_RX_LATE) { ble_ll_scan_aux_break(aux); return BLE_LL_SCHED_STATE_DONE; } /* Keep listening even if we are late, we may still receive something */ if (ble_ll_scan_get_filt_policy() & 1) { ble_ll_whitelist_enable(); } else { ble_ll_whitelist_disable(); } wfr_us = aux->offset_unit ? 300 : 30; ble_phy_wfr_enable(BLE_PHY_WFR_ENABLE_RX, 0, wfr_us); aux_data_current = aux; ble_ll_state_set(BLE_LL_STATE_SCAN_AUX); return BLE_LL_SCHED_STATE_RUNNING; } static struct ble_ll_scan_aux_data * ble_ll_scan_aux_alloc(void) { struct ble_ll_scan_aux_data *aux; aux = os_memblock_get(&aux_data_pool); if (!aux) { return NULL; } memset(aux, 0, sizeof(*aux)); aux->sch.sched_cb = ble_ll_scan_aux_sched_cb; aux->sch.sched_type = BLE_LL_SCHED_TYPE_SCAN_AUX; aux->sch.cb_arg = aux; #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) aux->rpa_index = -1; #endif return aux; } static void ble_ll_scan_aux_free(struct ble_ll_scan_aux_data *aux) { BLE_LL_ASSERT(!aux->sch.enqueued); BLE_LL_ASSERT(aux->hci_ev == NULL); BLE_LL_ASSERT((aux->hci_state & BLE_LL_SCAN_AUX_H_DONE) || !(aux->hci_state & BLE_LL_SCAN_AUX_H_SENT_ANY)); os_memblock_put(&aux_data_pool, aux); } static void ble_ll_scan_aux_update_scan_backoff(struct ble_ll_scan_aux_data *aux) { if (!(aux->flags & BLE_LL_SCAN_AUX_F_W4_SCAN_RSP) && !(aux->flags & BLE_LL_SCAN_AUX_F_SCANNED)) { return; } if ((aux->hci_state & BLE_LL_SCAN_AUX_H_DONE) && !(aux->hci_state & BLE_LL_SCAN_AUX_H_TRUNCATED)) { ble_ll_scan_backoff_update(1); } else { ble_ll_scan_backoff_update(0); } } static inline bool ble_ll_scan_aux_need_truncation(struct ble_ll_scan_aux_data *aux) { return (aux->hci_state & BLE_LL_SCAN_AUX_H_SENT_ANY) && !(aux->hci_state & BLE_LL_SCAN_AUX_H_DONE); } static struct ble_hci_ev * ble_ll_hci_ev_alloc_ext_adv_report_for_aux(struct ble_ll_scan_addr_data *addrd, struct ble_ll_scan_aux_data *aux) { struct ble_hci_ev_le_subev_ext_adv_rpt *hci_subev; 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(*hci_subev) + sizeof(*report); hci_subev = (void *)hci_ev->data; hci_subev->subev_code = BLE_HCI_LE_SUBEV_EXT_ADV_RPT; hci_subev->num_reports = 1; report = hci_subev->reports; memset(report, 0, sizeof(*report)); report->evt_type = 0; if (addrd->adva) { report->addr_type = addrd->adv_addr_type; memcpy(report->addr, addrd->adv_addr, 6); #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) if (addrd->adva_resolved) { report->addr_type += 2; } #endif } else { report->addr_type = 0xff; } report->pri_phy = aux->pri_phy; report->sec_phy = aux->sec_phy; report->sid = aux->adi >> 12; report->tx_power = 0x7f; report->rssi = 0x7f; report->periodic_itvl = 0; if (addrd->targeta) { report->evt_type |= BLE_HCI_ADV_DIRECT_MASK; report->dir_addr_type = addrd->targeta_type; memcpy(report->dir_addr, addrd->targeta, 6); #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) if (addrd->targeta_resolved) { report->dir_addr_type += 2; } #endif } report->data_len = 0; return hci_ev; } static struct ble_hci_ev * ble_ll_hci_ev_dup_ext_adv_report(struct ble_hci_ev *hci_ev_src) { struct ble_hci_ev_le_subev_ext_adv_rpt *hci_subev; struct ext_adv_report *report; struct ble_hci_ev *hci_ev; hci_ev = ble_transport_alloc_evt(1); if (!hci_ev) { return NULL; } memcpy(hci_ev, hci_ev_src, sizeof(*hci_ev) + sizeof(*hci_subev) + sizeof(*report)); hci_ev->length = sizeof(*hci_subev) + sizeof(*report); hci_subev = (void *)hci_ev->data; report = hci_subev->reports; report->data_len = 0; return hci_ev; } static void ble_ll_hci_ev_update_ext_adv_report_from_aux(struct ble_hci_ev *hci_ev, struct os_mbuf *rxpdu, struct ble_mbuf_hdr *rxhdr) { struct ble_hci_ev_le_subev_ext_adv_rpt *hci_subev; struct ext_adv_report *report; uint8_t adv_mode; uint8_t eh_len; uint8_t eh_flags; uint8_t *eh_data; uint8_t *rxbuf; hci_subev = (void *)hci_ev->data; report = hci_subev->reports; rxbuf = rxpdu->om_data; adv_mode = rxbuf[2] >> 6; eh_len = rxbuf[2] & 0x3f; eh_flags = rxbuf[3]; eh_data = &rxbuf[4]; report->evt_type |= adv_mode; if (rxhdr->rxinfo.flags & BLE_MBUF_HDR_F_SCAN_RSP_RXD) { report->evt_type |= BLE_HCI_ADV_SCAN_MASK | BLE_HCI_ADV_SCAN_RSP_MASK; } report->sec_phy = rxhdr->rxinfo.phy; /* Strip PDU header and ext header, leave only AD */ os_mbuf_adj(rxpdu, 3 + eh_len); /* * We only care about SyncInfo and TxPower so don't bother parsing if they * are not present, just set to 'unknown' values. */ if ((eh_len == 0) || !(eh_flags & ((1 << BLE_LL_EXT_ADV_SYNC_INFO_BIT) | (1 << BLE_LL_EXT_ADV_TX_POWER_BIT)))) { report->periodic_itvl = 0; report->tx_power = 0x7f; return; } /* Now parse extended header... */ if (eh_flags & (1 << BLE_LL_EXT_ADV_ADVA_BIT)) { eh_data += BLE_LL_EXT_ADV_ADVA_SIZE; } if (eh_flags & (1 << BLE_LL_EXT_ADV_TARGETA_BIT)) { eh_data += BLE_LL_EXT_ADV_TARGETA_SIZE; } if (eh_flags & (1 << BLE_LL_EXT_ADV_CTE_INFO_BIT)) { eh_data += BLE_LL_EXT_ADV_CTE_INFO_SIZE; } if (eh_flags & (1 << BLE_LL_EXT_ADV_DATA_INFO_BIT)) { eh_data += BLE_LL_EXT_ADV_DATA_INFO_SIZE; } if (eh_flags & (1 << BLE_LL_EXT_ADV_AUX_PTR_BIT)) { eh_data += BLE_LL_EXT_ADV_AUX_PTR_SIZE; } if (eh_flags & (1 << BLE_LL_EXT_ADV_SYNC_INFO_BIT)) { report->periodic_itvl = get_le16(eh_data + 2); eh_data += BLE_LL_EXT_ADV_SYNC_INFO_SIZE; } else { report->periodic_itvl = 0; } if (eh_flags & (1 << BLE_LL_EXT_ADV_TX_POWER_BIT)) { report->tx_power = *eh_data; } else { report->tx_power = 0x7f; } } static void ble_ll_hci_ev_update_ext_adv_report_from_ext(struct ble_hci_ev *hci_ev, struct os_mbuf *rxpdu, struct ble_mbuf_hdr *rxhdr) { struct ble_mbuf_hdr_rxinfo *rxinfo = &rxhdr->rxinfo; struct ble_hci_ev_le_subev_ext_adv_rpt *hci_subev; struct ext_adv_report *report; #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) struct ble_ll_resolv_entry *rl; #endif uint8_t pdu_hdr; uint8_t adv_mode; uint8_t eh_len; uint8_t eh_flags; uint8_t *eh_data; uint8_t *rxbuf; rxbuf = rxpdu->om_data; pdu_hdr = rxbuf[0]; adv_mode = rxbuf[2] >> 6; eh_len = rxbuf[2] & 0x3f; eh_flags = rxbuf[3]; eh_data = &rxbuf[4]; hci_ev->opcode = BLE_HCI_EVCODE_LE_META; hci_ev->length = sizeof(*hci_subev) + sizeof(*report); hci_subev = (void *)hci_ev->data; hci_subev->subev_code = BLE_HCI_LE_SUBEV_EXT_ADV_RPT; hci_subev->num_reports = 1; report = hci_subev->reports; memset(report, 0, sizeof(*report)); report->evt_type = adv_mode; report->pri_phy = rxinfo->phy; report->sec_phy = 0; report->sid = 0xff; report->rssi = rxhdr->rxinfo.rssi; report->periodic_itvl = 0; report->data_len = 0; /* Now parse extended header... */ if (eh_flags & (1 << BLE_LL_EXT_ADV_ADVA_BIT)) { #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) if (rxinfo->rpa_index >= 0) { rl = &g_ble_ll_resolv_list[rxinfo->rpa_index]; report->addr_type = rl->rl_addr_type + 2; memcpy(report->addr, rl->rl_identity_addr, 6); } else { report->addr_type = !!(pdu_hdr & BLE_ADV_PDU_HDR_TXADD_MASK); memcpy(report->addr, eh_data, 6); } #else report->addr_type = !!(pdu_hdr & BLE_ADV_PDU_HDR_TXADD_MASK); memcpy(report->addr, eh_data, 6); #endif eh_data += BLE_LL_EXT_ADV_ADVA_SIZE; } if (eh_flags & (1 << BLE_LL_EXT_ADV_TARGETA_BIT)) { report->evt_type |= BLE_HCI_ADV_DIRECT_MASK; #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) if (rxinfo->flags & BLE_MBUF_HDR_F_TARGETA_RESOLVED) { report->dir_addr_type = (ble_ll_scan_get_own_addr_type() & 1) + 2; memcpy(report->dir_addr, ble_ll_scan_aux_get_own_addr(), 6); } else { report->dir_addr_type = !!(pdu_hdr & BLE_ADV_PDU_HDR_RXADD_MASK); memcpy(report->dir_addr, eh_data, 6); } #else report->dir_addr_type = !!(pdu_hdr & BLE_ADV_PDU_HDR_RXADD_MASK); memcpy(report->dir_addr, eh_data, 6); #endif eh_data += BLE_LL_EXT_ADV_TARGETA_SIZE; } if (eh_flags & (1 << BLE_LL_EXT_ADV_CTE_INFO_BIT)) { eh_data += BLE_LL_EXT_ADV_CTE_INFO_SIZE; } if (eh_flags & (1 << BLE_LL_EXT_ADV_DATA_INFO_BIT)) { eh_data += BLE_LL_EXT_ADV_DATA_INFO_SIZE; } if (eh_flags & (1 << BLE_LL_EXT_ADV_AUX_PTR_BIT)) { eh_data += BLE_LL_EXT_ADV_AUX_PTR_SIZE; } if (eh_flags & (1 << BLE_LL_EXT_ADV_SYNC_INFO_BIT)) { eh_data += BLE_LL_EXT_ADV_SYNC_INFO_SIZE; } if (eh_flags & (1 << BLE_LL_EXT_ADV_TX_POWER_BIT)) { report->tx_power = *eh_data; } else { report->tx_power = 0x7f; } /* Strip PDU header and ext header, leave only AD */ os_mbuf_adj(rxpdu, 3 + eh_len); } static void ble_ll_hci_ev_send_ext_adv_truncated_report(struct ble_ll_scan_aux_data *aux) { struct ble_hci_ev_le_subev_ext_adv_rpt *hci_subev; 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; } hci_ev = aux->hci_ev; aux->hci_ev = NULL; BLE_LL_ASSERT(hci_ev); hci_subev = (void *)hci_ev->data; report = hci_subev->reports; report->evt_type |= BLE_HCI_ADV_DATA_STATUS_TRUNCATED; ble_ll_hci_event_send(hci_ev); aux->hci_state |= BLE_LL_SCAN_AUX_H_DONE | BLE_LL_SCAN_AUX_H_TRUNCATED; } static int ble_ll_hci_ev_send_ext_adv_report(struct os_mbuf *rxpdu, struct ble_mbuf_hdr *rxhdr, struct ble_hci_ev **hci_ev) { struct ble_mbuf_hdr_rxinfo *rxinfo = &rxhdr->rxinfo; struct ble_hci_ev_le_subev_ext_adv_rpt *hci_subev; struct ble_hci_ev *hci_ev_next; struct ext_adv_report *report; bool truncated = false; int max_data_len; int data_len; int offset; data_len = OS_MBUF_PKTLEN(rxpdu); max_data_len = BLE_LL_MAX_EVT_LEN - sizeof(**hci_ev) - sizeof(*hci_subev) - sizeof(*report); offset = 0; hci_ev_next = NULL; do { hci_subev = (void *)(*hci_ev)->data; report = hci_subev->reports; report->rssi = rxinfo->rssi; report->data_len = min(max_data_len, data_len - offset); os_mbuf_copydata(rxpdu, offset, report->data_len, report->data); (*hci_ev)->length += report->data_len; offset += report->data_len; /* * We need another event if either there are still some data left in * this PDU or scan for next aux is scheduled. */ if ((offset < data_len) || (rxinfo->flags & BLE_MBUF_HDR_F_AUX_PTR_WAIT)) { hci_ev_next = ble_ll_hci_ev_dup_ext_adv_report(*hci_ev); if (hci_ev_next) { report->evt_type |= BLE_HCI_ADV_DATA_STATUS_INCOMPLETE; } else { report->evt_type |= BLE_HCI_ADV_DATA_STATUS_TRUNCATED; } } switch (report->evt_type & BLE_HCI_ADV_DATA_STATUS_MASK) { case BLE_HCI_ADV_DATA_STATUS_TRUNCATED: truncated = true; /* no break */ case BLE_HCI_ADV_DATA_STATUS_COMPLETE: BLE_LL_ASSERT(!hci_ev_next); break; case BLE_HCI_ADV_DATA_STATUS_INCOMPLETE: BLE_LL_ASSERT(hci_ev_next); break; default: BLE_LL_ASSERT(0); } ble_ll_hci_event_send(*hci_ev); *hci_ev = hci_ev_next; hci_ev_next = NULL; } while ((offset < data_len) && *hci_ev); return truncated ? -1 : 0; } static int ble_ll_hci_ev_send_ext_adv_report_for_aux(struct os_mbuf *rxpdu, struct ble_mbuf_hdr *rxhdr, struct ble_ll_scan_aux_data *aux, struct ble_ll_scan_addr_data *addrd) { struct ble_hci_ev *hci_ev; int rc; if (!ble_ll_hci_is_le_event_enabled(BLE_HCI_LE_SUBEV_EXT_ADV_RPT)) { return -1; } /* * We need to always keep one event allocated in aux to be able to truncate * data properly in case of an error. If there is no event in aux it means * this is first event and we can silently ignore in case of an error. */ if (aux->hci_ev) { hci_ev = aux->hci_ev; aux->hci_ev = NULL; } else { hci_ev = ble_ll_hci_ev_alloc_ext_adv_report_for_aux(addrd, aux); if (!hci_ev) { return -1; } } ble_ll_hci_ev_update_ext_adv_report_from_aux(hci_ev, rxpdu, rxhdr); rc = ble_ll_hci_ev_send_ext_adv_report(rxpdu, rxhdr, &hci_ev); if (rc < 0) { BLE_LL_ASSERT(!hci_ev); aux->hci_state = BLE_LL_SCAN_AUX_H_DONE | BLE_LL_SCAN_AUX_H_TRUNCATED; } else if (hci_ev) { aux->hci_state = BLE_LL_SCAN_AUX_H_SENT_ANY; } else { aux->hci_state = BLE_LL_SCAN_AUX_H_DONE; } aux->hci_ev = hci_ev; return rc; } static void ble_ll_hci_ev_send_ext_adv_report_for_ext(struct os_mbuf *rxpdu, struct ble_mbuf_hdr *rxhdr) { struct ble_hci_ev *hci_ev; if (!ble_ll_hci_is_le_event_enabled(BLE_HCI_LE_SUBEV_EXT_ADV_RPT)) { return; } hci_ev = ble_transport_alloc_evt(1); if (!hci_ev) { return; } ble_ll_hci_ev_update_ext_adv_report_from_ext(hci_ev, rxpdu, rxhdr); ble_ll_hci_ev_send_ext_adv_report(rxpdu, rxhdr, &hci_ev); BLE_LL_ASSERT(!hci_ev); } static void ble_ll_scan_aux_break_ev(struct ble_npl_event *ev) { struct ble_ll_scan_aux_data *aux = ble_npl_event_get_arg(ev); BLE_LL_ASSERT(aux); if (ble_ll_scan_aux_need_truncation(aux)) { ble_ll_hci_ev_send_ext_adv_truncated_report(aux); } ble_ll_scan_aux_update_scan_backoff(aux); ble_ll_scan_aux_free(aux); ble_ll_scan_chk_resume(); } void ble_ll_scan_aux_break(struct ble_ll_scan_aux_data *aux) { #if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL) if (aux->flags & BLE_LL_SCAN_AUX_F_W4_CONNECT_RSP) { ble_ll_conn_send_connect_req_cancel(); } #endif ble_npl_event_init(&aux->break_ev, ble_ll_scan_aux_break_ev, aux); ble_ll_event_send(&aux->break_ev); } static int ble_ll_scan_aux_parse_aux_ptr(struct ble_ll_scan_aux_data *aux, uint32_t aux_ptr, uint32_t *offset_us) { uint8_t offset_unit; uint32_t offset; uint8_t chan; uint8_t phy; phy = (aux_ptr >> 21) & 0x07; switch (phy) { case 0: phy = BLE_PHY_1M; break; #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_2M_PHY) case 1: phy = BLE_PHY_2M; break; #endif #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY) case 2: phy = BLE_PHY_CODED; break; #endif default: return -1; } chan = aux_ptr & 0x3f; if (chan >= BLE_PHY_NUM_DATA_CHANS) { return -1; } offset = 30 * ((aux_ptr >> 8) & 0x1fff); if ((aux_ptr >> 7) & 0x01) { offset *= 10; offset_unit = 1; } else { offset_unit = 0; } if (offset < BLE_LL_MAFS) { return -1; } aux->sec_phy = phy; aux->chan = chan; aux->offset_unit = offset_unit; *offset_us = offset; return 0; } int ble_ll_scan_aux_sched(struct ble_ll_scan_aux_data *aux, uint32_t pdu_time, uint8_t pdu_time_rem, uint32_t aux_ptr) { uint32_t offset_us; int rc; rc = ble_ll_scan_aux_parse_aux_ptr(aux, aux_ptr, &offset_us); if (rc < 0) { return -1; } rc = ble_ll_sched_scan_aux(&aux->sch, pdu_time, pdu_time_rem, offset_us); if (rc < 0) { return -1; } return 0; } int ble_ll_scan_aux_rx_isr_start(uint8_t pdu_type, struct ble_mbuf_hdr *rxhdr) { struct ble_ll_scan_aux_data *aux; BLE_LL_ASSERT(aux_data_current); aux = aux_data_current; if (aux->flags & BLE_LL_SCAN_AUX_F_W4_CONNECT_RSP) { if (pdu_type != BLE_ADV_PDU_TYPE_AUX_CONNECT_RSP) { aux_data_current = NULL; ble_ll_scan_aux_break(aux); ble_ll_state_set(BLE_LL_STATE_STANDBY); return -1; } return 0; } if (pdu_type != BLE_ADV_PDU_TYPE_ADV_EXT_IND) { aux_data_current = NULL; ble_ll_scan_aux_break(aux); ble_ll_state_set(BLE_LL_STATE_STANDBY); return -1; } /* * Prepare TX transition when doing active scanning and receiving 1st PDU * since we may want to send AUX_SCAN_REQ. */ if ((aux->scan_type != BLE_SCAN_TYPE_PASSIVE) && !(aux->flags & BLE_LL_SCAN_AUX_F_AUX_ADV)) { return 1; } return 0; } static int ble_ll_scan_aux_parse_to_aux_data(struct ble_ll_scan_aux_data *aux, uint8_t *rxbuf, struct ble_mbuf_hdr *rxhdr) { uint8_t pdu_hdr; uint8_t pdu_len; uint8_t adv_mode; uint8_t eh_len; uint8_t eh_flags; uint8_t *eh_data; pdu_hdr = rxbuf[0]; pdu_len = rxbuf[1]; /* PDU without at least Extended Header Length is invalid */ if (pdu_len == 0) { return -1; } /* Mark as AUX received on 1st PDU, then as CHAIN received on subsequent */ if (aux->flags & BLE_LL_SCAN_AUX_F_AUX_ADV) { aux->flags |= BLE_LL_SCAN_AUX_F_AUX_CHAIN; } else { aux->flags |= BLE_LL_SCAN_AUX_F_AUX_ADV; } adv_mode = rxbuf[2] >> 6; eh_len = rxbuf[2] & 0x3f; /* Only AUX_CHAIN_IND is valid without Extended Header */ if (eh_len == 0) { if (!(aux->flags & BLE_LL_SCAN_AUX_F_AUX_CHAIN) || adv_mode) { return -1; } return 0; } eh_flags = rxbuf[3]; eh_data = &rxbuf[4]; /* Now parse extended header... */ /* AdvA is only valid in 1st PDU, ignore in AUX_CHAIN_IND */ if (eh_flags & (1 << BLE_LL_EXT_ADV_ADVA_BIT)) { if (!(aux->flags & BLE_LL_SCAN_AUX_F_AUX_CHAIN)) { memcpy(aux->adva, eh_data, 6); aux->adva_type = !!(pdu_hdr & BLE_ADV_PDU_HDR_TXADD_MASK); aux->flags |= BLE_LL_SCAN_AUX_F_HAS_ADVA; #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) aux->rpa_index = ble_hw_resolv_list_match(); #endif } eh_data += BLE_LL_EXT_ADV_ADVA_SIZE; } /* TargetA is only valid in 1st PDU, ignore in AUX_CHAIN_IND */ if (eh_flags & (1 << BLE_LL_EXT_ADV_TARGETA_BIT)) { if (!(aux->flags & BLE_LL_SCAN_AUX_F_AUX_CHAIN)) { memcpy(aux->targeta, eh_data, 6); aux->targeta_type = !!(pdu_hdr & BLE_ADV_PDU_HDR_RXADD_MASK); aux->flags |= BLE_LL_SCAN_AUX_F_HAS_TARGETA; } eh_data += BLE_LL_EXT_ADV_TARGETA_SIZE; } if (eh_flags & (1 << BLE_LL_EXT_ADV_CTE_INFO_BIT)) { eh_data += BLE_LL_EXT_ADV_CTE_INFO_SIZE; } /* * ADI handling is a bit convoluted.... * ADI is mandatory in ADV_EXT_IND with AuxPtr and is also mandatory in PDU * if included in superior PDU. This implies that each AUX_CHAIN shall have * ADI. However... AUX_SCAN_RSP does not need to have ADI, so if there's no * ADI in AUX_SCAN_RSP we allow it and clear corresponding flag to skip ADI * checks on subsequent PDUs. */ if (eh_flags & (1 << BLE_LL_EXT_ADV_DATA_INFO_BIT)) { if (aux->flags & BLE_LL_SCAN_AUX_F_HAS_ADI) { if (get_le16(eh_data) != aux->adi) { return -1; } } eh_data += BLE_LL_EXT_ADV_DATA_INFO_SIZE; } else if (aux->flags & BLE_LL_SCAN_AUX_F_HAS_ADI) { if (rxhdr->rxinfo.flags & BLE_MBUF_HDR_F_SCAN_RSP_RXD) { aux->flags &= ~BLE_LL_SCAN_AUX_F_HAS_ADI; } else { return -1; } } if (eh_flags & (1 << BLE_LL_EXT_ADV_AUX_PTR_BIT)) { aux->aux_ptr = get_le24(eh_data); rxhdr->rxinfo.flags |= BLE_MBUF_HDR_F_AUX_PTR_WAIT; } return 0; } static int ble_ll_scan_aux_ext_parse(uint8_t *rxbuf, struct ble_ll_scan_addr_data *addrd, uint16_t *adi, uint32_t *aux_ptr) { uint8_t pdu_hdr; uint8_t pdu_len; uint8_t adv_mode; uint8_t eh_len; uint8_t eh_flags; uint8_t *eh_data; pdu_hdr = rxbuf[0]; pdu_len = rxbuf[1]; if (pdu_len == 0) { return -1; } adv_mode = rxbuf[2] >> 6; eh_len = rxbuf[2] & 0x3f; if ((adv_mode == 3) || (eh_len == 0)) { return -1; } eh_flags = rxbuf[3]; eh_data = &rxbuf[4]; /* ADV_EXT_IND with AuxPtr but without ADI is invalid */ if ((eh_flags & (1 << BLE_LL_EXT_ADV_AUX_PTR_BIT)) && !(eh_flags & (1 << BLE_LL_EXT_ADV_DATA_INFO_BIT))) { return -1; } /* ADV_EXT_IND without either AdvA or AuxPtr is not valid */ if (!(eh_flags & ((1 << BLE_LL_EXT_ADV_ADVA_BIT) | (1 << BLE_LL_EXT_ADV_AUX_PTR_BIT)))) { return -1; } if (eh_flags & (1 << BLE_LL_EXT_ADV_ADVA_BIT)) { addrd->adva = eh_data; addrd->adva_type = !!(pdu_hdr & BLE_ADV_PDU_HDR_TXADD_RAND); eh_data += BLE_LL_EXT_ADV_ADVA_SIZE; } else { addrd->adva = NULL; } if (eh_flags & (1 << BLE_LL_EXT_ADV_TARGETA_BIT)) { addrd->targeta = eh_data; addrd->targeta_type = !!(pdu_hdr & BLE_ADV_PDU_HDR_RXADD_RAND); eh_data += BLE_LL_EXT_ADV_TARGETA_SIZE; } else { addrd->targeta = NULL; } if (eh_flags & (1 << BLE_LL_EXT_ADV_CTE_INFO_BIT)) { eh_data += BLE_LL_EXT_ADV_CTE_INFO_SIZE; } if (eh_flags & (1 << BLE_LL_EXT_ADV_DATA_INFO_BIT)) { *adi = get_le16(eh_data); eh_data += BLE_LL_EXT_ADV_DATA_INFO_SIZE; } else { *adi = 0; } if (eh_flags & (1 << BLE_LL_EXT_ADV_AUX_PTR_BIT)) { *aux_ptr = get_le24(eh_data); } else { *aux_ptr = 0; } return 0; } static void ble_ll_scan_aux_update_rxinfo_from_addrd(struct ble_ll_scan_addr_data *addrd, struct ble_mbuf_hdr_rxinfo *rxinfo) { #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 } static void ble_ll_scan_aux_update_aux_data_from_addrd(struct ble_ll_scan_addr_data *addrd, struct ble_ll_scan_aux_data *aux) { #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) aux->rpa_index = addrd->rpa_index; if (addrd->adva_resolved) { aux->flags |= BLE_LL_SCAN_AUX_F_RESOLVED_ADVA; } if (addrd->targeta_resolved) { aux->flags |= BLE_LL_SCAN_AUX_F_RESOLVED_TARGETA; } #endif } int ble_ll_scan_aux_rx_isr_end_on_ext(struct ble_ll_scan_sm *scansm, struct os_mbuf *rxpdu) { struct ble_mbuf_hdr *rxhdr = BLE_MBUF_HDR_PTR(rxpdu); struct ble_mbuf_hdr_rxinfo *rxinfo = &rxhdr->rxinfo; struct ble_ll_scan_addr_data addrd; struct ble_ll_scan_aux_data *aux; uint8_t *rxbuf; uint32_t aux_ptr; uint16_t adi; bool do_match; int rc; rxbuf = rxpdu->om_data; rc = ble_ll_scan_aux_ext_parse(rxbuf, &addrd, &adi, &aux_ptr); if (rc < 0) { return -1; } /* We can filter based on ext PDU alone if both AdvA and TargetA are present * or there's no AuxPtr. Otherwise, we need to wait for aux PDU to filter. */ do_match = !aux_ptr || (addrd.adva && addrd.targeta); if (do_match) { #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) addrd.rpa_index = ble_hw_resolv_list_match(); #endif rc = ble_ll_scan_rx_filter(ble_ll_scan_get_own_addr_type(), ble_ll_scan_get_filt_policy(), &addrd, NULL); if (rc < 0) { return -1; } /* Don't care about initiator here, there are no AdvA and TargetA * in connectable ADV_EXT_IND so no filtering to do. */ if (!aux_ptr) { /* We do not allocate aux_data for ADV_EXT_IND without AuxPtr so * need to pass match data in rxinfo. */ ble_ll_scan_aux_update_rxinfo_from_addrd(&addrd, rxinfo); } } if (aux_ptr) { aux = ble_ll_scan_aux_alloc(); if (!aux) { return -1; } aux->scan_type = scansm->scanp->scan_type; aux->pri_phy = rxinfo->phy; aux->aux_ptr = aux_ptr; if (addrd.adva) { memcpy(aux->adva, addrd.adva, 6); aux->adva_type = addrd.adva_type; aux->flags |= BLE_LL_SCAN_AUX_F_HAS_ADVA; } if (addrd.targeta) { memcpy(aux->targeta, addrd.targeta, 6); aux->targeta_type = addrd.targeta_type; aux->flags |= BLE_LL_SCAN_AUX_F_HAS_TARGETA; } aux->adi = adi; aux->flags |= BLE_LL_SCAN_AUX_F_HAS_ADI; #if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL) if (aux->scan_type == BLE_SCAN_TYPE_INITIATE) { aux->flags |= BLE_LL_SCAN_AUX_F_CONNECTABLE; } #endif if (do_match) { aux->flags |= BLE_LL_SCAN_AUX_F_MATCHED; ble_ll_scan_aux_update_aux_data_from_addrd(&addrd, aux); } else if (addrd.adva) { #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) /* If ext PDU has AdvA, we need to store rpa_index to be able to * reuse it for filtering when done on aux PDU. */ aux->rpa_index = ble_hw_resolv_list_match(); #endif } rxinfo->user_data = aux; } return 0; } void ble_ll_scan_aux_pkt_in_on_ext(struct os_mbuf *rxpdu, struct ble_mbuf_hdr *rxhdr) { struct ble_mbuf_hdr_rxinfo *rxinfo = &rxhdr->rxinfo; struct ble_ll_scan_aux_data *aux = rxinfo->user_data; int rc; if (rxinfo->flags & BLE_MBUF_HDR_F_IGNORED) { BLE_LL_ASSERT(!aux); return; } if (!aux) { ble_ll_hci_ev_send_ext_adv_report_for_ext(rxpdu, rxhdr); return; } BLE_LL_ASSERT(aux->aux_ptr); rc = ble_ll_scan_aux_sched(aux, rxhdr->beg_cputime, rxhdr->rem_usecs, aux->aux_ptr); if (rc < 0) { ble_ll_scan_aux_free(aux); } } static uint8_t ble_ll_scan_aux_scan_req_tx_pdu_cb(uint8_t *dptr, void *arg, uint8_t *hdr_byte) { struct ble_ll_scan_aux_data *aux = arg; 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 uint8_t hb; hb = BLE_ADV_PDU_TYPE_SCAN_REQ; /* ScanA */ if (ble_ll_scan_get_own_addr_type() & 0x01) { hb |= 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 (ble_ll_scan_get_own_addr_type() & 0x02) { if (aux->rpa_index >=0) { rl = &g_ble_ll_resolv_list[aux->rpa_index]; } else { rl = NULL; } /* * 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 * ref: Core 5.2, 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 { scana = ble_ll_get_scan_nrpa(); } hb |= BLE_ADV_PDU_HDR_TXADD_RAND; } #endif memcpy(dptr, scana, BLE_DEV_ADDR_LEN); /* AdvA */ if (aux->adva_type) { hb |= BLE_ADV_PDU_HDR_RXADD_RAND; } memcpy(dptr + BLE_DEV_ADDR_LEN, aux->adva, BLE_DEV_ADDR_LEN); *hdr_byte = hb; return BLE_DEV_ADDR_LEN * 2; } static bool ble_ll_scan_aux_send_scan_req(struct ble_ll_scan_aux_data *aux, struct ble_ll_scan_addr_data *addrd) { 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, true, aux->adi)) { return false; } /* We want to send a request, see if backoff allows us */ if (ble_ll_scan_backoff_kick() != 0) { return false; } /* TODO perhaps we should check if scan req+rsp won't overlap with scheduler * item (old code did it), but for now let's just scan and we will be * interrupted if scheduler kicks in. */ rc = ble_phy_tx(ble_ll_scan_aux_scan_req_tx_pdu_cb, aux, BLE_PHY_TRANSITION_TX_RX); if (rc) { return false; } return true; } int ble_ll_scan_aux_rx_isr_end(struct os_mbuf *rxpdu, uint8_t crcok) { struct ble_ll_scan_addr_data addrd; struct ble_mbuf_hdr_rxinfo *rxinfo; struct ble_ll_scan_aux_data *aux; struct ble_mbuf_hdr *rxhdr; uint8_t scan_filt_policy; uint8_t scan_ok; uint8_t adv_mode; uint8_t *rxbuf; int rc; BLE_LL_ASSERT(aux_data_current); aux = aux_data_current; aux_data_current = NULL; if (rxpdu == NULL) { ble_ll_scan_aux_break(aux); goto done; } rxhdr = BLE_MBUF_HDR_PTR(rxpdu); rxinfo = &rxhdr->rxinfo; rxinfo->user_data = aux; if (!crcok) { rxinfo->flags |= BLE_MBUF_HDR_F_IGNORED; goto done; } rxbuf = rxpdu->om_data; if (aux->flags & BLE_LL_SCAN_AUX_F_W4_CONNECT_RSP) { aux->flags &= ~BLE_LL_SCAN_AUX_F_W4_CONNECT_RSP; rxinfo->flags |= BLE_MBUF_HDR_F_CONNECT_RSP_RXD; goto done; } if (aux->flags & BLE_LL_SCAN_AUX_F_W4_SCAN_RSP) { aux->flags &= ~BLE_LL_SCAN_AUX_F_W4_SCAN_RSP; aux->flags |= BLE_LL_SCAN_AUX_F_SCANNED; rxinfo->flags |= BLE_MBUF_HDR_F_SCAN_RSP_RXD; } rc = ble_ll_scan_aux_parse_to_aux_data(aux, rxbuf, rxhdr); if (rc < 0) { rxinfo->flags |= BLE_MBUF_HDR_F_IGNORED; goto done; } if (aux->flags & BLE_LL_SCAN_AUX_F_MATCHED) { goto done; } /* We do filtering on either ADV_EXT_IND or AUX_ADV_IND so we should not be * here when processing AUX_CHAIN_IND. */ BLE_LL_ASSERT(!(aux->flags & BLE_LL_SCAN_AUX_F_AUX_CHAIN)); if (aux->flags & BLE_LL_SCAN_AUX_F_HAS_ADVA) { addrd.adva = aux->adva; addrd.adva_type = aux->adva_type; } else { addrd.adva = NULL; } if (aux->flags & BLE_LL_SCAN_AUX_F_HAS_TARGETA) { addrd.targeta = aux->targeta; addrd.targeta_type = aux->targeta_type; } else { addrd.targeta = NULL; } #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) addrd.rpa_index = aux->rpa_index; #endif scan_filt_policy = ble_ll_scan_get_filt_policy(); rc = ble_ll_scan_rx_filter(ble_ll_scan_get_own_addr_type(), scan_filt_policy, &addrd, &scan_ok); if (rc < 0) { rxinfo->flags |= BLE_MBUF_HDR_F_IGNORED; /* * XXX hack warning * Even if PDU was not allowed by current scan filter policy, we should * still allow it to sync if SyncInfo is present. Since we do not use * F_DEVMATCH in aux code for its intended purpose, let's use it here to * indicate no match due to scan filter policy. */ if (rc == -2) { rxinfo->flags |= BLE_MBUF_HDR_F_DEVMATCH; } goto done; } #if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL) if ((aux->scan_type == BLE_SCAN_TYPE_INITIATE) && !(scan_filt_policy & 0x01)) { rc = ble_ll_scan_rx_check_init(&addrd); if (rc < 0) { rxinfo->flags |= BLE_MBUF_HDR_F_IGNORED; goto done; } } #endif aux->flags |= BLE_LL_SCAN_AUX_F_MATCHED; ble_ll_scan_aux_update_aux_data_from_addrd(&addrd, aux); adv_mode = rxbuf[2] >> 6; switch (aux->scan_type) { case BLE_SCAN_TYPE_ACTIVE: if ((adv_mode == BLE_LL_EXT_ADV_MODE_SCAN) && scan_ok && ble_ll_scan_aux_send_scan_req(aux, &addrd)) { /* AUX_SCAN_REQ sent, keep PHY enabled to continue */ aux->flags |= BLE_LL_SCAN_AUX_F_W4_SCAN_RSP; rxinfo->flags |= BLE_MBUF_HDR_F_SCAN_REQ_TXD; aux_data_current = aux; return 0; } break; #if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL) case BLE_SCAN_TYPE_INITIATE: if (ble_ll_conn_send_connect_req(rxpdu, &addrd, 1) == 0) { /* AUX_CONNECT_REQ sent, keep PHY enabled to continue */ aux->flags |= BLE_LL_SCAN_AUX_F_W4_CONNECT_RSP; rxinfo->flags |= BLE_MBUF_HDR_F_CONNECT_REQ_TXD; aux_data_current = aux; return 0; } else { rxinfo->flags |= BLE_MBUF_HDR_F_IGNORED; } break; #endif default: break; } done: /* 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; } static void ble_ll_scan_aux_init_addrd_from_aux_data(struct ble_ll_scan_aux_data *aux, struct ble_ll_scan_addr_data *addrd) { #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) struct ble_ll_resolv_entry *rl; #endif if (aux->flags & BLE_LL_SCAN_AUX_F_HAS_ADVA) { addrd->adva = aux->adva; addrd->adva_type = aux->adva_type; } else { addrd->adva = NULL; addrd->adva_type = 0; } if (aux->flags & BLE_LL_SCAN_AUX_F_HAS_TARGETA) { addrd->targeta = aux->targeta; addrd->targeta_type = aux->targeta_type; } else { addrd->targeta = NULL; addrd->targeta_type = 0; } addrd->adv_addr = addrd->adva; addrd->adv_addr_type = addrd->adva_type; #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) addrd->rpa_index = aux->rpa_index; if (aux->flags & BLE_LL_SCAN_AUX_F_RESOLVED_ADVA) { BLE_LL_ASSERT(aux->rpa_index >= 0); rl = &g_ble_ll_resolv_list[aux->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 (aux->flags & BLE_LL_SCAN_AUX_F_RESOLVED_TARGETA) { addrd->targeta = ble_ll_scan_aux_get_own_addr(); addrd->targeta_type = ble_ll_scan_get_own_addr_type() & 1; addrd->targeta_resolved = 1; } else { addrd->targeta_resolved = 0; } #endif } #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV) static void ble_ll_scan_aux_sync_check(struct os_mbuf *rxpdu, struct ble_ll_scan_addr_data *addrd) { struct ble_mbuf_hdr *rxhdr = BLE_MBUF_HDR_PTR(rxpdu); uint8_t *rxbuf = rxpdu->om_data; uint8_t adv_mode; uint8_t eh_len; uint8_t eh_flags; uint8_t *eh_data; uint8_t sid; adv_mode = rxbuf[2] >> 6; if (adv_mode != BLE_LL_EXT_ADV_MODE_NON_CONN) { return; } eh_len = rxbuf[2] & 0x3f; if (eh_len == 0) { return; } eh_flags = rxbuf[3]; eh_data = &rxbuf[4]; /* Need ADI and SyncInfo */ if (!(eh_flags & ((1 << BLE_LL_EXT_ADV_SYNC_INFO_BIT) | (1 << BLE_LL_EXT_ADV_DATA_INFO_BIT)))) { return; } if (eh_flags & (1 << BLE_LL_EXT_ADV_ADVA_BIT)) { eh_data += BLE_LL_EXT_ADV_ADVA_SIZE; } if (eh_flags & (1 << BLE_LL_EXT_ADV_TARGETA_BIT)) { eh_data += BLE_LL_EXT_ADV_TARGETA_SIZE; } if (eh_flags & (1 << BLE_LL_EXT_ADV_CTE_INFO_BIT)) { eh_data += 1; } sid = get_le16(eh_data) >> 12; eh_data += BLE_LL_EXT_ADV_DATA_INFO_SIZE; if (eh_flags & (1 << BLE_LL_EXT_ADV_AUX_PTR_BIT)) { eh_data += BLE_LL_EXT_ADV_AUX_PTR_SIZE; } ble_ll_sync_info_event(addrd, sid, rxhdr, eh_data); } #endif #if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL) static int ble_ll_scan_aux_check_connect_rsp(uint8_t *rxbuf, struct ble_ll_scan_pdu_data *pdu_data, struct ble_ll_scan_addr_data *addrd) { uint8_t pdu_hdr; uint8_t pdu_len; uint8_t adv_mode; uint8_t eh_len; uint8_t eh_flags; uint8_t *eh_data; uint8_t adva_type; uint8_t *adva; uint8_t targeta_type; uint8_t *targeta; #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) struct ble_ll_resolv_entry *rl = NULL; #endif pdu_hdr = rxbuf[0]; pdu_len = rxbuf[1]; if (pdu_len == 0) { return -1; } adv_mode = rxbuf[2] >> 6; eh_len = rxbuf[2] & 0x3f; if ((adv_mode != 0) || (eh_len == 0)) { return -1; } eh_flags = rxbuf[3]; eh_data = &rxbuf[4]; /* AUX_CONNECT_RSP without AdvA or TargetA is not valid */ if (!(eh_flags & ((1 << BLE_LL_EXT_ADV_ADVA_BIT) | (1 << BLE_LL_EXT_ADV_TARGETA_BIT)))) { return -1; } adva = eh_data; adva_type = !!(pdu_hdr & BLE_ADV_PDU_HDR_TXADD_RAND); eh_data += BLE_LL_EXT_ADV_ADVA_SIZE; targeta = eh_data; targeta_type = !!(pdu_hdr & BLE_ADV_PDU_HDR_RXADD_RAND); #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) /* If we have IRK for peer and AdvA is an RPA, we need to check if current * RPA resolves using that IRK. This is to verify AdvA in case RPS changed * due to timeout or AdvA in advertising was an identity address but is an * RPA in AUX_CONNECT_RSP. * Otherwise, it shall be the same as in AUX_CONNECT_REQ. */ if ((addrd->rpa_index >= 0) && ble_ll_is_rpa(adva, adva_type)) { rl = &g_ble_ll_resolv_list[addrd->rpa_index]; if (!ble_ll_resolv_rpa(adva, rl->rl_peer_irk)) { return -1; } addrd->adva_resolved = 1; addrd->adva = adva; addrd->adva_type = adva_type; } else if ((adva_type != !!(pdu_data->hdr_byte & BLE_ADV_PDU_HDR_RXADD_MASK)) || (memcmp(adva, pdu_data->adva, 6) != 0)) { return -1; } #else if ((adva_type != !!(pdu_data->hdr_byte & BLE_ADV_PDU_HDR_RXADD_MASK)) || (memcmp(adva, pdu_data->adva, 6) != 0)) { return -1; } #endif /* BLE_LL_CFG_FEAT_LL_PRIVACY */ if ((targeta_type != !!(pdu_data->hdr_byte & BLE_ADV_PDU_HDR_TXADD_MASK)) || (memcmp(targeta, pdu_data->inita, 6) != 0)) { return -1; } return 0; } static void ble_ll_scan_aux_rx_pkt_in_for_initiator(struct os_mbuf *rxpdu, struct ble_mbuf_hdr *rxhdr) { struct ble_ll_scan_addr_data addrd; struct ble_mbuf_hdr_rxinfo *rxinfo; struct ble_ll_scan_aux_data *aux; rxinfo = &rxhdr->rxinfo; aux = rxinfo->user_data; if (rxinfo->flags & BLE_MBUF_HDR_F_IGNORED) { ble_ll_scan_aux_free(aux); ble_ll_scan_chk_resume(); return; } if (!(rxinfo->flags & BLE_MBUF_HDR_F_CONNECT_RSP_RXD)) { BLE_LL_ASSERT(rxinfo->flags & BLE_MBUF_HDR_F_CONNECT_REQ_TXD); /* Waiting for AUX_CONNECT_RSP, do nothing */ return; } ble_ll_scan_aux_init_addrd_from_aux_data(aux, &addrd); if (ble_ll_scan_aux_check_connect_rsp(rxpdu->om_data, ble_ll_scan_get_pdu_data(), &addrd) < 0) { ble_ll_conn_send_connect_req_cancel(); ble_ll_scan_aux_free(aux); ble_ll_scan_chk_resume(); return; } ble_ll_scan_sm_stop(0); ble_ll_conn_created_on_aux(rxpdu, &addrd, aux->targeta); ble_ll_scan_aux_free(aux); } #endif void ble_ll_scan_aux_rx_pkt_in(struct os_mbuf *rxpdu, struct ble_mbuf_hdr *rxhdr) { struct ble_ll_scan_addr_data addrd; struct ble_mbuf_hdr_rxinfo *rxinfo; struct ble_ll_scan_aux_data *aux; bool scan_duplicate = false; #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV) bool sync_check = false; #endif int rc; rxinfo = &rxhdr->rxinfo; aux = rxinfo->user_data; BLE_LL_ASSERT(aux); #if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL) if (aux->scan_type == BLE_SCAN_TYPE_INITIATE) { ble_ll_scan_aux_rx_pkt_in_for_initiator(rxpdu, rxhdr); return; } #endif #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV) sync_check = ble_ll_sync_enabled() && !(aux->flags & BLE_LL_SCAN_AUX_F_AUX_CHAIN); /* PDU was not allowed due to scan filter policy, but we can still try to * sync since separate filter policy is used for this purpose. */ if ((rxinfo->flags & BLE_MBUF_HDR_F_DEVMATCH) && sync_check) { ble_ll_scan_aux_init_addrd_from_aux_data(aux, &addrd); ble_ll_scan_aux_sync_check(rxpdu, &addrd); } #endif if (rxinfo->flags & BLE_MBUF_HDR_F_IGNORED) { if (ble_ll_scan_aux_need_truncation(aux)) { ble_ll_hci_ev_send_ext_adv_truncated_report(aux); } else { aux->hci_state |= BLE_LL_SCAN_AUX_H_DONE; } ble_ll_scan_aux_update_scan_backoff(aux); } if (aux->hci_state & BLE_LL_SCAN_AUX_H_DONE) { ble_ll_scan_aux_free(aux); ble_ll_scan_chk_resume(); return; } /* Try to schedule scan for subsequent aux asap, if needed */ if (rxinfo->flags & BLE_MBUF_HDR_F_AUX_PTR_WAIT) { rc = ble_ll_scan_aux_sched(aux, rxhdr->beg_cputime, rxhdr->rem_usecs, aux->aux_ptr); if (rc < 0) { rxinfo->flags &= ~BLE_MBUF_HDR_F_AUX_PTR_WAIT; } } ble_ll_scan_aux_init_addrd_from_aux_data(aux, &addrd); #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV) if (sync_check) { ble_ll_scan_aux_sync_check(rxpdu, &addrd); } #endif #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 scan_duplicate = ble_ll_scan_get_filt_dups() && ble_ll_scan_dup_check_ext(addrd.adv_addr_type, addrd.adv_addr, true, aux->adi); if (!scan_duplicate) { rc = ble_ll_hci_ev_send_ext_adv_report_for_aux(rxpdu, rxhdr, aux, &addrd); /* * Update duplicates list if report was sent successfully and we are * done with this chain. On error, status is already set to 'done' so * we will cancel aux scan (if any) and stop further processing. * However, if we send AUX_SCAN_REQ for this PDU then need to remove * 'done' as we should continue with scanning after AUX_SCAN_RSP. */ if (rc == 0) { if (rxinfo->flags & BLE_MBUF_HDR_F_SCAN_REQ_TXD) { aux->hci_state &= ~BLE_LL_SCAN_AUX_H_DONE; } else if (aux->hci_state & BLE_LL_SCAN_AUX_H_DONE) { BLE_LL_ASSERT(!(rxinfo->flags & BLE_MBUF_HDR_F_AUX_PTR_WAIT)); if (ble_ll_scan_get_filt_dups()) { ble_ll_scan_dup_update_ext(addrd.adv_addr_type, addrd.adv_addr, true, aux->adi); } if (aux->flags & BLE_LL_SCAN_AUX_F_SCANNED) { ble_ll_scan_add_scan_rsp_adv(addrd.adv_addr, addrd.adv_addr_type, 1, aux->adi); } } } } else { /* This is a duplicate, we don't want to scan it anymore */ aux->hci_state |= BLE_LL_SCAN_AUX_H_DONE; } /* * If we are done processing this chain and aux scan was not scheduled or * we removed it from scheduler, we can remove aux_data now. Otherwise we * will remove on next pkt_in. */ if ((aux->hci_state & BLE_LL_SCAN_AUX_H_DONE) && (!(rxinfo->flags & BLE_MBUF_HDR_F_AUX_PTR_WAIT) || (ble_ll_sched_rmv_elem(&aux->sch) == 0))) { ble_ll_scan_aux_update_scan_backoff(aux); ble_ll_scan_aux_free(aux); } ble_ll_scan_chk_resume(); } void ble_ll_scan_aux_wfr_timer_exp(void) { ble_phy_disable(); ble_ll_scan_aux_halt(); } void ble_ll_scan_aux_halt(void) { struct ble_ll_scan_aux_data *aux = aux_data_current; BLE_LL_ASSERT(aux); aux_data_current = NULL; ble_ll_state_set(BLE_LL_STATE_STANDBY); ble_ll_scan_aux_break(aux); } void ble_ll_scan_aux_sched_remove(struct ble_ll_sched_item *sch) { ble_ll_scan_aux_break(sch->cb_arg); } void ble_ll_scan_aux_init(void) { os_error_t err; err = os_mempool_init(&aux_data_pool, MYNEWT_VAL(BLE_LL_SCAN_AUX_SEGMENT_CNT), sizeof(struct ble_ll_scan_aux_data), aux_data_mem, "ble_ll_scan_aux_data_pool"); BLE_LL_ASSERT(err == 0); } #endif /* BLE_LL_CFG_FEAT_LL_EXT_ADV */