blob: 3e3f0a1617d9e412d33d325de7a48a60a962d3d6 [file] [log] [blame]
/*
* 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 "os/os_cputime.h"
#include "nimble/ble.h"
#include "nimble/nimble_opt.h"
#include "nimble/hci_common.h"
#include "nimble/ble_hci_trans.h"
#include "controller/ble_phy.h"
#include "controller/ble_hw.h"
#include "controller/ble_ll.h"
#include "controller/ble_ll_sched.h"
#include "controller/ble_ll_adv.h"
#include "controller/ble_ll_scan.h"
#include "controller/ble_ll_hci.h"
#include "controller/ble_ll_whitelist.h"
#include "controller/ble_ll_resolv.h"
#include "controller/ble_ll_xcvr.h"
#include "controller/ble_ll_trace.h"
#include "controller/ble_ll_sync.h"
#include "ble_ll_conn_priv.h"
/*
* XXX:
* 1) I think I can guarantee that we dont process things out of order if
* I send an event when a scan request is sent. The scan_rsp_pending flag
* code might be made simpler.
*
* 2) Interleave sending scan requests to different advertisers? I guess I need
* a list of advertisers to which I sent a scan request and have yet to
* receive a scan response from? Implement this.
*/
/* Dont allow more than 255 of these entries */
#if MYNEWT_VAL(BLE_LL_NUM_SCAN_RSP_ADVS) > 255
#error "Cannot have more than 255 scan response entries!"
#endif
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
static const uint8_t ble_ll_valid_scan_phy_mask = (BLE_HCI_LE_PHY_1M_PREF_MASK
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY)
| BLE_HCI_LE_PHY_CODED_PREF_MASK
#endif
);
#endif
/* The scanning parameters set by host */
static struct ble_ll_scan_params g_ble_ll_scan_params[BLE_LL_SCAN_PHY_NUMBER];
/* The scanning state machine global object */
static struct ble_ll_scan_sm g_ble_ll_scan_sm;
#define BLE_LL_EXT_ADV_ADVA_BIT (0)
#define BLE_LL_EXT_ADV_TARGETA_BIT (1)
#define BLE_LL_EXT_ADV_RFU_BIT (2)
#define BLE_LL_EXT_ADV_DATA_INFO_BIT (3)
#define BLE_LL_EXT_ADV_AUX_PTR_BIT (4)
#define BLE_LL_EXT_ADV_SYNC_INFO_BIT (5)
#define BLE_LL_EXT_ADV_TX_POWER_BIT (6)
#define BLE_LL_EXT_ADV_ADVA_SIZE (6)
#define BLE_LL_EXT_ADV_TARGETA_SIZE (6)
#define BLE_LL_EXT_ADV_DATA_INFO_SIZE (2)
#define BLE_LL_EXT_ADV_AUX_PTR_SIZE (3)
#define BLE_LL_EXT_ADV_SYNC_INFO_SIZE (18)
#define BLE_LL_EXT_ADV_TX_POWER_SIZE (1)
struct ble_ll_ext_adv_hdr
{
uint8_t mode;
uint8_t hdr_len;
uint8_t hdr[0];
};
struct ble_ll_ext_adv_report {
/* We support one report per event for now */
uint8_t event_meta; /* BLE_HCI_EVCODE_LE_META */
uint8_t event_len;
uint8_t subevt;
uint8_t num_reports;
uint16_t evt_type;
uint8_t addr_type;
uint8_t addr[6];
uint8_t prim_phy;
uint8_t sec_phy;
uint8_t sid;
uint8_t tx_power;
int8_t rssi;
uint16_t per_adv_itvl;
uint8_t dir_addr_type;
uint8_t dir_addr[6];
uint8_t adv_data_len;
uint8_t adv_data[0];
} __attribute__((packed));
/*
* Structure used to store advertisers. This is used to limit sending scan
* requests to the same advertiser and also to filter duplicate events sent
* to the host.
*/
struct ble_ll_scan_advertisers
{
uint16_t sc_adv_flags;
uint16_t adi;
struct ble_dev_addr adv_addr;
};
#define BLE_LL_SC_ADV_F_RANDOM_ADDR (0x01)
#define BLE_LL_SC_ADV_F_SCAN_RSP_RXD (0x02)
#define BLE_LL_SC_ADV_F_DIRECT_RPT_SENT (0x04)
#define BLE_LL_SC_ADV_F_ADV_RPT_SENT (0x08)
#define BLE_LL_SC_ADV_F_SCAN_RSP_SENT (0x10)
/* Contains list of advertisers that we have heard scan responses from */
static uint8_t g_ble_ll_scan_num_rsp_advs;
struct ble_ll_scan_advertisers
g_ble_ll_scan_rsp_advs[MYNEWT_VAL(BLE_LL_NUM_SCAN_RSP_ADVS)];
/* Duplicates filtering data */
#define BLE_LL_SCAN_ENTRY_TYPE_LEGACY(addr_type) \
((addr_type) & 1)
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
#define BLE_LL_SCAN_ENTRY_TYPE_EXT(addr_type, has_aux, is_anon, adi) \
(((adi >> 8) & 0xF0) | (1 << 3) | (is_anon << 2) | (has_aux << 1) | ((addr_type) & 1))
#endif
#define BLE_LL_SCAN_DUP_F_ADV_REPORT_SENT (0x01)
#define BLE_LL_SCAN_DUP_F_DIR_ADV_REPORT_SENT (0x02)
#define BLE_LL_SCAN_DUP_F_SCAN_RSP_SENT (0x04)
struct ble_ll_scan_dup_entry {
uint8_t type; /* entry type, see BLE_LL_SCAN_ENTRY_TYPE_* */
uint8_t addr[6];
uint8_t flags; /* use BLE_LL_SCAN_DUP_F_xxx */
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
uint16_t adi;
#endif
TAILQ_ENTRY(ble_ll_scan_dup_entry) link;
};
static os_membuf_t g_scan_dup_mem[ OS_MEMPOOL_SIZE(
MYNEWT_VAL(BLE_LL_NUM_SCAN_DUP_ADVS),
sizeof(struct ble_ll_scan_dup_entry)) ];
static struct os_mempool g_scan_dup_pool;
static TAILQ_HEAD(ble_ll_scan_dup_list, ble_ll_scan_dup_entry) g_scan_dup_list;
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
#if MYNEWT_VAL(BLE_LL_EXT_ADV_AUX_PTR_CNT) != 0
static os_membuf_t ext_adv_mem[ OS_MEMPOOL_SIZE(
MYNEWT_VAL(BLE_LL_EXT_ADV_AUX_PTR_CNT),
sizeof (struct ble_ll_aux_data))
];
#else
#define ext_adv_mem NULL
#endif
static struct os_mempool ext_adv_pool;
static int ble_ll_scan_start(struct ble_ll_scan_sm *scansm,
struct ble_ll_sched_item *sch);
static int
ble_ll_aux_scan_cb(struct ble_ll_sched_item *sch)
{
struct ble_ll_scan_sm *scansm = &g_ble_ll_scan_sm;
uint8_t lls = ble_ll_state_get();
uint32_t wfr_usec;
STATS_INC(ble_ll_stats, aux_sched_cb);
/* In case scan has been disabled or there is other aux ptr in progress
* just drop the scheduled item
*/
if (!scansm->scan_enabled || scansm->cur_aux_data) {
ble_ll_scan_aux_data_unref(sch->cb_arg);
sch->cb_arg = NULL;
goto done;
}
/* Check if there is no aux connect sent. If so drop the sched item */
if (lls == BLE_LL_STATE_INITIATING && ble_ll_conn_init_pending_aux_conn_rsp()) {
ble_ll_scan_aux_data_unref(sch->cb_arg);
sch->cb_arg = NULL;
goto done;
}
/* This function is called only when scanner is running. This can happen
* in 3 states:
* BLE_LL_STATE_SCANNING
* BLE_LL_STATE_INITIATING
* BLE_LL_STATE_STANDBY
*/
if (lls != BLE_LL_STATE_STANDBY) {
ble_phy_disable();
ble_ll_wfr_disable();
ble_ll_state_set(BLE_LL_STATE_STANDBY);
}
/* When doing RX for AUX pkt, cur_aux_data keeps valid aux data */
scansm->cur_aux_data = sch->cb_arg;
sch->cb_arg = NULL;
BLE_LL_ASSERT(scansm->cur_aux_data != NULL);
scansm->cur_aux_data->scanning = 1;
if (ble_ll_scan_start(scansm, sch)) {
ble_ll_scan_interrupted(scansm);
goto done;
}
STATS_INC(ble_ll_stats, aux_fired_for_read);
wfr_usec = scansm->cur_aux_data->offset_units ? 300 : 30;
ble_phy_wfr_enable(BLE_PHY_WFR_ENABLE_RX, 0, wfr_usec);
done:
return BLE_LL_SCHED_STATE_DONE;
}
static int
ble_ll_scan_ext_adv_init(struct ble_ll_aux_data **aux_data)
{
struct ble_ll_aux_data *e;
e = os_memblock_get(&ext_adv_pool);
if (!e) {
return -1;
}
memset(e, 0, sizeof(*e));
e->sch.sched_cb = ble_ll_aux_scan_cb;
e->sch.sched_type = BLE_LL_SCHED_TYPE_AUX_SCAN;
e->ref_cnt = 1;
ble_ll_trace_u32x2(BLE_LL_TRACE_ID_AUX_REF, (uint32_t)e, e->ref_cnt);
*aux_data = e;
STATS_INC(ble_ll_stats, aux_allocated);
return 0;
}
#endif
/* 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)
{
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 = rand() & (scansm->upper_limit - 1);
++scansm->backoff_count;
BLE_LL_ASSERT(scansm->backoff_count <= 256);
}
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY)
static void
ble_ll_scan_refresh_nrpa(struct ble_ll_scan_sm *scansm)
{
ble_npl_time_t now;
now = ble_npl_time_get();
if ((ble_npl_stime_t)(now - scansm->scan_nrpa_timer) >= 0) {
/* 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
/**
* ble ll scan req pdu make
*
* Construct a SCAN_REQ PDU.
*
* @param scansm Pointer to scanning state machine
* @param adv_addr Pointer to device address of advertiser
* @param addr_type 0 if public; non-zero if random. This is the addr type of
* the advertiser; not our "own address type"
*/
static void
ble_ll_scan_req_pdu_prepare(struct ble_ll_scan_sm *scansm, uint8_t *adv_addr,
uint8_t adv_addr_type)
{
uint8_t hdr_byte;
uint8_t *scana;
struct ble_ll_scan_pdu_data *pdu_data;
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY)
uint8_t rpa[BLE_DEV_ADDR_LEN];
struct ble_ll_resolv_entry *rl;
#endif
pdu_data = &scansm->pdu_data;
/* Construct first PDU header byte */
hdr_byte = BLE_ADV_PDU_TYPE_SCAN_REQ;
if (adv_addr_type) {
hdr_byte |= BLE_ADV_PDU_HDR_RXADD_RAND;
}
/* Get pointer to our device address */
if ((scansm->own_addr_type & 1) == 0) {
scana = g_dev_addr;
} else {
hdr_byte |= BLE_ADV_PDU_HDR_TXADD_RAND;
scana = g_random_addr;
}
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY)
if (scansm->own_addr_type > BLE_HCI_ADV_OWN_ADDR_RANDOM) {
rl = NULL;
if (ble_ll_is_rpa(adv_addr, adv_addr_type)) {
if (scansm->scan_rpa_index >= 0) {
/* Generate a RPA to use for scana */
rl = &g_ble_ll_resolv_list[scansm->scan_rpa_index];
}
} else {
if (ble_ll_resolv_enabled()) {
rl = ble_ll_resolv_list_find(adv_addr, adv_addr_type);
}
}
/*
* If advertising device is on our resolving list, we use RPA generated
* using Local IRK from resolving list entry as ScanA. In other case,
* we use NRPA as ScanA as allowed by spec to prevent our device from
* being tracked when doing an active scan (see Core 5.0, Vol 6, Part B,
* section 6.3).
*/
if (rl) {
ble_ll_resolv_get_priv_addr(rl, 1, rpa);
scana = rpa;
} else {
ble_ll_scan_refresh_nrpa(scansm);
scana = scansm->scan_nrpa;
}
hdr_byte |= BLE_ADV_PDU_HDR_TXADD_RAND;
}
#endif
/* Save scan request data */
pdu_data->hdr_byte = hdr_byte;
memcpy(pdu_data->scana, scana, BLE_DEV_ADDR_LEN);
memcpy(pdu_data->adva, adv_addr, BLE_DEV_ADDR_LEN);
}
static uint8_t
ble_ll_scan_req_tx_pducb(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)
static struct ble_ll_ext_adv_report *
ble_ll_scan_init_ext_adv_report(struct ble_ll_ext_adv_report *copy_from)
{
struct ble_ll_ext_adv_report *evt;
evt = (struct ble_ll_ext_adv_report *) ble_hci_trans_buf_alloc(
BLE_HCI_TRANS_BUF_EVT_LO);
if (!evt) {
return NULL;
}
if (copy_from) {
memcpy(evt, copy_from, sizeof(*evt));
} else {
memset(evt, 0, sizeof(*evt));
evt->event_meta = BLE_HCI_EVCODE_LE_META;
evt->subevt = BLE_HCI_LE_SUBEV_EXT_ADV_RPT;
/* We support only one report per event now */
evt->num_reports = 1;
/* Init TX Power with "Not available" which is 127 */
evt->tx_power = 127;
/* Init RSSI with "Not available" which is 127 */
evt->rssi = 127;
/* Init SID with "Not available" which is 0xFF */
evt->sid = 0xFF;
/* Init address type with "anonymous" which is 0xFF */
evt->addr_type = 0xFF;
}
return evt;
}
static void
ble_ll_scan_send_truncated_if_chained(struct ble_ll_aux_data *aux_data)
{
struct ble_ll_ext_adv_report *evt;
if (!ble_ll_hci_is_le_event_enabled(BLE_HCI_LE_SUBEV_EXT_ADV_RPT)) {
return;
}
BLE_LL_ASSERT(aux_data);
if (!BLE_LL_AUX_CHECK_FLAG(aux_data, BLE_LL_AUX_CHAIN_BIT)) {
/* if not chained, there is nothing to do here */
return;
}
BLE_LL_ASSERT(aux_data->evt);
evt = aux_data->evt;
aux_data->evt = NULL;
evt->event_len = sizeof(*evt) - BLE_HCI_EVENT_HDR_LEN;
evt->evt_type = aux_data->evt_type;
evt->evt_type |= (BLE_HCI_ADV_DATA_STATUS_TRUNCATED);
BLE_LL_AUX_SET_FLAG(aux_data, BLE_LL_AUX_TRUNCATED_SENT);
if (BLE_LL_AUX_CHECK_FLAG(aux_data, BLE_LL_AUX_HAS_ADDRA)) {
memcpy(evt->addr, aux_data->addr, 6);
evt->addr_type = aux_data->addr_type;
}
if (BLE_LL_AUX_CHECK_FLAG(aux_data, BLE_LL_AUX_HAS_DIR_ADDRA)) {
memcpy(evt->dir_addr, aux_data->dir_addr, 6);
evt->dir_addr_type = aux_data->dir_addr_type;
}
evt->sid = (aux_data->adi >> 12);
ble_ll_hci_event_send((uint8_t *)evt);
}
static int
ble_ll_scan_get_adi(struct ble_ll_aux_data *aux_data, uint16_t *adi)
{
if (!aux_data) {
return -1;
}
if (!BLE_LL_AUX_CHECK_FLAG(aux_data, BLE_LL_AUX_HAS_ADI)) {
return -1;
}
*adi = aux_data->adi;
return 0;
}
#endif
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
void
ble_ll_scan_end_adv_evt(struct ble_ll_aux_data *aux_data)
{
/* If part of the event has been sent to the host and truncated
* has not been sent, do it now
*/
if (BLE_LL_AUX_CHECK_FLAG(aux_data, BLE_LL_SENT_EVENT_TO_HOST) &&
!BLE_LL_AUX_CHECK_FLAG(aux_data, BLE_LL_AUX_TRUNCATED_SENT)) {
ble_ll_scan_send_truncated_if_chained(aux_data);
}
}
#endif
/**
* Do scan machine clean up on PHY disabled
*
*/
void
ble_ll_scan_clean_cur_aux_data(void)
{
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
struct ble_ll_scan_sm *scansm = &g_ble_ll_scan_sm;
/* If scanner was reading aux ptr, we need to clean it up */
if (scansm && scansm->cur_aux_data) {
ble_ll_scan_end_adv_evt(scansm->cur_aux_data);
ble_ll_scan_aux_data_unref(scansm->cur_aux_data);
scansm->cur_aux_data = NULL;
}
#endif
}
/**
* Checks to see if we have received a scan response from this advertiser.
*
* @param adv_addr Address of advertiser
* @param txadd TxAdd bit (0: public; random otherwise)
*
* @return int 0: have not received a scan response; 1 otherwise.
*/
static int
ble_ll_scan_have_rxd_scan_rsp(uint8_t *addr, uint8_t txadd,
uint8_t ext_adv, uint16_t adi)
{
uint8_t num_advs;
struct ble_ll_scan_advertisers *adv;
/* Do we have an address match? Must match address type */
adv = &g_ble_ll_scan_rsp_advs[0];
num_advs = g_ble_ll_scan_num_rsp_advs;
while (num_advs) {
if (!memcmp(&adv->adv_addr, addr, BLE_DEV_ADDR_LEN)) {
/* Address type must match */
if (txadd) {
if (adv->sc_adv_flags & BLE_LL_SC_ADV_F_RANDOM_ADDR) {
if (ext_adv) {
if (adi == adv->adi) {
return 1;
}
goto next;
}
return 1;
}
} else {
if ((adv->sc_adv_flags & BLE_LL_SC_ADV_F_RANDOM_ADDR) == 0) {
if (ext_adv) {
if (adi == adv->adi) {
return 1;
}
goto next;
}
return 1;
}
}
}
next:
++adv;
--num_advs;
}
return 0;
}
static void
ble_ll_scan_add_scan_rsp_adv(uint8_t *addr, uint8_t txadd,
uint8_t ext_adv, uint16_t adi)
{
uint8_t num_advs;
struct ble_ll_scan_advertisers *adv;
/* XXX: for now, if we dont have room, just leave */
num_advs = g_ble_ll_scan_num_rsp_advs;
if (num_advs == MYNEWT_VAL(BLE_LL_NUM_SCAN_RSP_ADVS)) {
return;
}
/* Check if address is already on the list */
if (ble_ll_scan_have_rxd_scan_rsp(addr, txadd, ext_adv, adi)) {
return;
}
/* Add the advertiser to the array */
adv = &g_ble_ll_scan_rsp_advs[num_advs];
memcpy(&adv->adv_addr, addr, BLE_DEV_ADDR_LEN);
adv->sc_adv_flags = BLE_LL_SC_ADV_F_SCAN_RSP_RXD;
if (txadd) {
adv->sc_adv_flags |= BLE_LL_SC_ADV_F_RANDOM_ADDR;
}
adv->adi = adi;
++g_ble_ll_scan_num_rsp_advs;
return;
}
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
static int
ble_ll_hci_send_legacy_ext_adv_report(uint8_t evtype,
uint8_t addr_type, uint8_t *addr,
uint8_t rssi,
uint8_t adv_data_len, struct os_mbuf *adv_data,
uint8_t *inita, uint8_t inita_type)
{
struct ble_ll_ext_adv_report *evt;
if (!ble_ll_hci_is_le_event_enabled(BLE_HCI_LE_SUBEV_EXT_ADV_RPT)) {
return -1;
}
/* Drop packet if it does not fit into event buffer */
if ((sizeof(*evt) + adv_data_len) + 1 > MYNEWT_VAL(BLE_HCI_EVT_BUF_SIZE)) {
STATS_INC(ble_ll_stats, adv_evt_dropped);
return -1;
}
evt = ble_ll_scan_init_ext_adv_report(NULL);
if (!evt) {
return 0;
}
switch (evtype) {
case BLE_HCI_ADV_RPT_EVTYPE_ADV_IND:
evt->evt_type = BLE_HCI_LEGACY_ADV_EVTYPE_ADV_IND;
break;
case BLE_HCI_ADV_RPT_EVTYPE_DIR_IND:
evt->evt_type = BLE_HCI_LEGACY_ADV_EVTYPE_ADV_DIRECT_IND;
break;
case BLE_HCI_ADV_RPT_EVTYPE_NONCONN_IND:
evt->evt_type = BLE_HCI_LEGACY_ADV_EVTYPE_ADV_NONCON_IND;
break;
case BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP:
evt->evt_type = BLE_HCI_LEGACY_ADV_EVTYPE_SCAN_RSP_ADV_IND;
break;
case BLE_HCI_ADV_RPT_EVTYPE_SCAN_IND:
evt->evt_type = BLE_HCI_LEGACY_ADV_EVTYPE_ADV_SCAN_IND;
break;
default:
BLE_LL_ASSERT(0);
break;
}
evt->addr_type = addr_type;
memcpy(evt->addr, addr, BLE_DEV_ADDR_LEN);
evt->event_len = sizeof(*evt) - BLE_HCI_EVENT_HDR_LEN;
if (inita) {
evt->dir_addr_type = inita_type;
memcpy(evt->dir_addr, inita, BLE_DEV_ADDR_LEN);
} else if (adv_data_len <= (MYNEWT_VAL(BLE_HCI_EVT_BUF_SIZE) - sizeof(*evt))) {
evt->adv_data_len = adv_data_len;
os_mbuf_copydata(adv_data, 0, adv_data_len, evt->adv_data);
evt->event_len += adv_data_len;
}
evt->rssi = rssi;
evt->prim_phy = BLE_PHY_1M;
return ble_ll_hci_event_send((uint8_t *) evt);
}
#endif
static int
ble_ll_hci_send_adv_report(uint8_t subev, uint8_t evtype,uint8_t event_len,
uint8_t addr_type, uint8_t *addr, uint8_t rssi,
uint8_t adv_data_len, struct os_mbuf *adv_data,
uint8_t *inita, uint8_t inita_type)
{
uint8_t *evbuf;
uint8_t *tmp;
if (!ble_ll_hci_is_le_event_enabled(subev)) {
return -1;
}
/* Drop packet if it does not fit into event buffer */
if (event_len + 1 > MYNEWT_VAL(BLE_HCI_EVT_BUF_SIZE)) {
STATS_INC(ble_ll_stats, adv_evt_dropped);
return -1;
}
evbuf = ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_LO);
if (!evbuf) {
return -1;
}
evbuf[0] = BLE_HCI_EVCODE_LE_META;
evbuf[1] = event_len;
evbuf[2] = subev;
evbuf[3] = 1; /* number of reports */
evbuf[4] = evtype;
evbuf[5] = addr_type;
memcpy(&evbuf[6], addr, BLE_DEV_ADDR_LEN);
tmp = &evbuf[12];
if (subev == BLE_HCI_LE_SUBEV_DIRECT_ADV_RPT) {
BLE_LL_ASSERT(inita);
tmp[0] = inita_type;
memcpy(tmp + 1, inita, BLE_DEV_ADDR_LEN);
tmp += BLE_DEV_ADDR_LEN + 1;
} else if (subev == BLE_HCI_LE_SUBEV_ADV_RPT) {
tmp[0] = adv_data_len;
os_mbuf_copydata(adv_data, 0, adv_data_len, tmp + 1);
tmp += adv_data_len + 1;
} else {
BLE_LL_ASSERT(0);
return -1;
}
tmp[0] = rssi;
return ble_ll_hci_event_send(evbuf);
}
static int ble_ll_scan_dup_update_legacy(uint8_t addr_type, uint8_t *addr, uint8_t subev,
uint8_t evtype);
/**
* 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, uint8_t *adva, uint8_t adva_type,
uint8_t *inita, uint8_t inita_type,
struct os_mbuf *om,
struct ble_mbuf_hdr *hdr,
struct ble_ll_scan_sm *scansm)
{
int rc;
uint8_t *rxbuf = om->om_data;
uint8_t evtype;
uint8_t subev;
uint8_t adv_data_len;
uint8_t event_len;
if (pdu_type == BLE_ADV_PDU_TYPE_ADV_DIRECT_IND) {
if (ble_ll_is_rpa(inita, inita_type)) {
/* For resolvable we send separate event */
subev = BLE_HCI_LE_SUBEV_DIRECT_ADV_RPT;
event_len = BLE_HCI_LE_ADV_DIRECT_RPT_LEN;
} else {
subev = BLE_HCI_LE_SUBEV_ADV_RPT;
event_len = BLE_HCI_LE_ADV_RPT_MIN_LEN;
}
evtype = BLE_HCI_ADV_RPT_EVTYPE_DIR_IND;
adv_data_len = 0;
} else {
subev = BLE_HCI_LE_SUBEV_ADV_RPT;
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 = rxbuf[1];
adv_data_len -= BLE_DEV_ADDR_LEN;
event_len = BLE_HCI_LE_ADV_RPT_MIN_LEN + adv_data_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_INITA_RESOLVED(hdr)) {
inita_type += 2;
}
#endif
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
if (scansm->ext_scanning) {
rc = ble_ll_hci_send_legacy_ext_adv_report(evtype,
adva_type, adva,
hdr->rxinfo.rssi,
adv_data_len, om,
inita, inita_type);
} else {
rc = ble_ll_hci_send_adv_report(subev, evtype, event_len,
adva_type, adva,
hdr->rxinfo.rssi,
adv_data_len, om,
inita, inita_type);
}
#else
rc = ble_ll_hci_send_adv_report(subev, evtype, event_len,
adva_type, adva,
hdr->rxinfo.rssi,
adv_data_len, om,
inita, inita_type);
#endif
if (!rc && scansm->scan_filt_dups) {
ble_ll_scan_dup_update_legacy(adva_type, adva, subev, evtype);
}
}
/**
* Checks the scanner filter policy to determine if we should allow or discard
* the received PDU.
*
* NOTE: connect requests and scan requests are not passed here
*
* @param pdu_type
* @param adv_addr
* @param adv_addr_type
* @param init_addr
* @param init_addr_type
* @param flags
*
* @return int 0: pdu allowed by filter policy. 1: pdu not allowed
*/
static int
ble_ll_scan_chk_filter_policy(uint8_t pdu_type, uint8_t *adv_addr,
uint8_t adv_addr_type, uint8_t *init_addr,
uint8_t init_addr_type, uint8_t devmatch)
{
int use_whitelist;
int chk_inita;
struct ble_ll_scan_params *params =
&g_ble_ll_scan_sm.phy_data[g_ble_ll_scan_sm.cur_phy];
use_whitelist = 0;
chk_inita = 0;
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
if (pdu_type == BLE_ADV_PDU_TYPE_ADV_EXT_IND && adv_addr == NULL) {
/* Note: adv_addr can be NULL (but don't have to) for ext adv. If NULL
* that means it is beacon and skip filter policy for now */
return 0;
}
#endif
switch (params->scan_filt_policy) {
case BLE_HCI_SCAN_FILT_NO_WL:
break;
case BLE_HCI_SCAN_FILT_USE_WL:
use_whitelist = 1;
break;
case BLE_HCI_SCAN_FILT_NO_WL_INITA:
chk_inita = 1;
break;
case BLE_HCI_SCAN_FILT_USE_WL_INITA:
chk_inita = 1;
use_whitelist = 1;
break;
default:
BLE_LL_ASSERT(0);
break;
}
/* If we are using the whitelist, check that first */
if (use_whitelist && (pdu_type != BLE_ADV_PDU_TYPE_SCAN_RSP)) {
/* If device does not match let us skip this PDU.
* If device matches, lets check for InitA further in the code
*/
if (!devmatch) {
return 1;
}
}
/* If this is a directed advertisement, init_addr is not NULL.
* Check that it is for us */
if (init_addr) {
/* Is this for us? If not, is it resolvable */
if (!ble_ll_is_our_devaddr(init_addr, init_addr_type)) {
if (!chk_inita || !ble_ll_is_rpa(adv_addr, adv_addr_type)) {
return 1;
}
}
}
return 0;
}
static void
ble_ll_get_chan_to_scan(struct ble_ll_scan_sm *scansm, uint8_t *chan,
int *phy)
{
struct ble_ll_scan_params *scanphy = &scansm->phy_data[scansm->cur_phy];
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
struct ble_ll_aux_data *aux_data = scansm->cur_aux_data;
if (!scansm->ext_scanning || !aux_data || !aux_data->scanning) {
*chan = scanphy->scan_chan;
*phy = scanphy->phy;
return;
}
*chan = aux_data->chan;
*phy = aux_data->aux_phy;
#else
*chan = scanphy->scan_chan;
*phy = scanphy->phy;
#endif
}
/**
* Called to enable the receiver for scanning.
*
* Context: Link Layer task
*
* @param sch
*
* @return int
*/
static int
ble_ll_scan_start(struct ble_ll_scan_sm *scansm, struct ble_ll_sched_item *sch)
{
int rc;
struct ble_ll_scan_params *scanphy = &scansm->phy_data[scansm->cur_phy];
uint8_t scan_chan;
#if (BLE_LL_BT5_PHY_SUPPORTED == 1)
uint8_t phy_mode;
#endif
int phy;
ble_ll_get_chan_to_scan(scansm, &scan_chan, &phy);
/* XXX: right now scheduled item is only present if we schedule for aux
* scan just make sanity check that we have proper combination of
* sch and resulting scan_chan
*/
BLE_LL_ASSERT(!sch || scan_chan < BLE_PHY_ADV_CHAN_START);
BLE_LL_ASSERT(sch || scan_chan >= BLE_PHY_ADV_CHAN_START);
/* Set channel */
rc = ble_phy_setchan(scan_chan, BLE_ACCESS_ADDR_ADV, BLE_LL_CRCINIT_ADV);
BLE_LL_ASSERT(rc == 0);
/*
* Set transmit end callback to NULL in case we transmit a scan request.
* There is a callback for the connect request.
*/
ble_phy_set_txend_cb(NULL, NULL);
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION)
ble_phy_encrypt_disable();
#endif
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY)
if (ble_ll_resolv_enabled()) {
ble_phy_resolv_list_enable();
} else {
ble_phy_resolv_list_disable();
}
#endif
#if (BLE_LL_BT5_PHY_SUPPORTED == 1)
phy_mode = ble_ll_phy_to_phy_mode(phy, BLE_HCI_LE_PHY_CODED_ANY);
ble_phy_mode_set(phy_mode, phy_mode);
#endif
/* XXX: probably need to make sure hfxo is running too */
/* XXX: can make this better; want to just start asap. */
if (sch) {
rc = ble_phy_rx_set_start_time(sch->start_time +
g_ble_ll_sched_offset_ticks,
sch->remainder);
} else {
rc = ble_phy_rx_set_start_time(os_cputime_get32() +
g_ble_ll_sched_offset_ticks, 0);
}
if (!rc || rc == BLE_PHY_ERR_RX_LATE) {
/* If we are late here, it is still OK because we keep scanning.
* Clear error
*/
rc = 0;
/* Enable/disable whitelisting */
if (scanphy->scan_filt_policy & 1) {
ble_ll_whitelist_enable();
} else {
ble_ll_whitelist_disable();
}
/* Set link layer state to scanning */
if (scanphy->scan_type == BLE_SCAN_TYPE_INITIATE) {
ble_ll_state_set(BLE_LL_STATE_INITIATING);
} else {
ble_ll_state_set(BLE_LL_STATE_SCANNING);
}
}
/* If there is a still a scan response pending, we have failed! */
if (scansm->scan_rsp_pending) {
ble_ll_scan_req_backoff(scansm, 0);
}
return rc;
}
#ifdef BLE_XCVR_RFCLK
static void
ble_ll_scan_rfclk_chk_stop(void)
{
int stop;
int32_t time_till_next;
os_sr_t sr;
uint32_t next_time;
stop = 0;
OS_ENTER_CRITICAL(sr);
if (ble_ll_sched_next_time(&next_time)) {
/*
* If the time until the next event is too close, dont bother to turn
* off the clock
*/
time_till_next = (int32_t)(next_time - os_cputime_get32());
if (time_till_next > g_ble_ll_data.ll_xtal_ticks) {
stop = 1;
}
} else {
stop = 1;
}
if (stop) {
ble_ll_xcvr_rfclk_disable();
}
OS_EXIT_CRITICAL(sr);
}
#endif
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_get_current_scan_win(struct ble_ll_scan_sm *scansm, uint32_t cputime)
{
uint32_t itvl;
struct ble_ll_scan_params *scanphy = &scansm->phy_data[scansm->cur_phy];
/* Well, in case we missed to schedule scan, lets move to next stan interval.
*/
itvl = os_cputime_usecs_to_ticks(scanphy->scan_itvl * BLE_HCI_SCAN_ITVL);
while ((int32_t)(cputime - scanphy->scan_win_start_time) >= itvl) {
scanphy->scan_win_start_time += itvl;
/* If we missed scan window, make sure we update scan channel */
scanphy->scan_chan =
ble_ll_scan_get_next_adv_prim_chan(scanphy->scan_chan);
}
return scanphy->scan_win_start_time;
}
/**
* Called to determine if we are inside or outside the scan window. If we
* are inside the scan window it means that the device should be receiving
* on the scan channel.
*
* Context: Link Layer
*
* @param scansm
*
* @return int 0: inside scan window 1: outside scan window
*/
static int
ble_ll_scan_window_chk(struct ble_ll_scan_sm *scansm, uint32_t cputime)
{
uint32_t win;
uint32_t dt;
uint32_t win_start;
struct ble_ll_scan_params *scanphy = &scansm->phy_data[scansm->cur_phy];
win_start = ble_ll_scan_get_current_scan_win(scansm, cputime);
if (scanphy->scan_window != scanphy->scan_itvl) {
win = os_cputime_usecs_to_ticks(scanphy->scan_window * BLE_HCI_SCAN_ITVL);
dt = cputime - win_start;
if (dt >= win) {
#ifdef BLE_XCVR_RFCLK
if (dt < (scanphy->scan_itvl - g_ble_ll_data.ll_xtal_ticks)) {
ble_ll_scan_rfclk_chk_stop();
}
#endif
return 1;
}
}
return 0;
}
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
static void
ble_ll_scan_aux_data_free(struct ble_ll_aux_data *aux_data)
{
if (aux_data) {
if (aux_data->evt) {
ble_hci_trans_buf_free((uint8_t *)aux_data->evt);
aux_data->evt = NULL;
}
os_memblock_put(&ext_adv_pool, aux_data);
STATS_INC(ble_ll_stats, aux_freed);
}
}
struct ble_ll_aux_data *
ble_ll_scan_aux_data_ref(struct ble_ll_aux_data *aux_data)
{
os_sr_t sr;
BLE_LL_ASSERT(aux_data);
OS_ENTER_CRITICAL(sr);
aux_data->ref_cnt++;
ble_ll_trace_u32x2(BLE_LL_TRACE_ID_AUX_REF, (uint32_t) aux_data, aux_data->ref_cnt);
OS_EXIT_CRITICAL(sr);
return aux_data;
}
void
ble_ll_scan_aux_data_unref(struct ble_ll_aux_data *aux_data)
{
os_sr_t sr;
BLE_LL_ASSERT(aux_data);
OS_ENTER_CRITICAL(sr);
aux_data->ref_cnt--;
ble_ll_trace_u32x2(BLE_LL_TRACE_ID_AUX_UNREF, (uint32_t) aux_data, aux_data->ref_cnt);
if (aux_data->ref_cnt == 0) {
/* Below assert is to detect missing complete or truncated event in case of chaining */
BLE_LL_ASSERT(!((BLE_LL_AUX_CHECK_FLAG(aux_data, BLE_LL_AUX_CHAIN_BIT) &&
BLE_LL_AUX_CHECK_FLAG(aux_data, BLE_LL_SENT_EVENT_TO_HOST)) &&
!(BLE_LL_AUX_CHECK_FLAG(aux_data, BLE_LL_AUX_TRUNCATED_SENT) ||
!BLE_LL_AUX_CHECK_FLAG(aux_data, BLE_LL_AUX_INCOMPLETE_BIT))));
ble_ll_scan_aux_data_free(aux_data);
}
OS_EXIT_CRITICAL(sr);
}
static void
ble_ll_scan_sched_remove(struct ble_ll_sched_item *sch)
{
ble_ll_scan_aux_data_unref(sch->cb_arg);
sch->cb_arg = NULL;
}
#endif
/**
* Stop the scanning state machine
*/
void
ble_ll_scan_sm_stop(int chk_disable)
{
os_sr_t sr;
uint8_t lls;
struct ble_ll_scan_sm *scansm;
/* Stop the scanning timer */
scansm = &g_ble_ll_scan_sm;
os_cputime_timer_stop(&scansm->scan_timer);
OS_ENTER_CRITICAL(sr);
/* Disable scanning state machine */
scansm->scan_enabled = 0;
scansm->restart_timer_needed = 0;
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
if (scansm->ext_scanning) {
ble_ll_scan_clean_cur_aux_data();
ble_ll_sched_rmv_elem_type(BLE_LL_SCHED_TYPE_AUX_SCAN, ble_ll_scan_sched_remove);
scansm->ext_scanning = 0;
}
#endif
OS_EXIT_CRITICAL(sr);
/* Count # of times stopped */
STATS_INC(ble_ll_stats, scan_stops);
/* Only set state if we are currently in a scan window */
if (chk_disable) {
OS_ENTER_CRITICAL(sr);
lls = ble_ll_state_get();
if ((lls == BLE_LL_STATE_SCANNING) ||
(lls == BLE_LL_STATE_INITIATING && chk_disable == 1)) {
/* Disable phy */
ble_phy_disable();
/* Set LL state to standby */
ble_ll_state_set(BLE_LL_STATE_STANDBY);
/* May need to stop the rfclk */
#ifdef BLE_XCVR_RFCLK
ble_ll_scan_rfclk_chk_stop();
#endif
}
OS_EXIT_CRITICAL(sr);
}
}
static int
ble_ll_scan_sm_start(struct ble_ll_scan_sm *scansm)
{
/*
* This is not in the specification. I will reject the command with a
* command disallowed error if no random address has been sent by the
* host. All the parameter errors refer to the command parameter
* (which in this case is just enable or disable) so that is why I chose
* command disallowed.
*/
if (scansm->own_addr_type == BLE_HCI_ADV_OWN_ADDR_RANDOM) {
if (!ble_ll_is_valid_random_addr(g_random_addr)) {
return BLE_ERR_CMD_DISALLOWED;
}
}
/* 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 */
BLE_LL_ASSERT(scansm->cur_phy != PHY_NOT_CONFIGURED);
scansm->phy_data[scansm->cur_phy].scan_chan = BLE_PHY_ADV_CHAN_START;
if (scansm->next_phy != PHY_NOT_CONFIGURED &&
scansm->next_phy != scansm->cur_phy) {
scansm->phy_data[scansm->next_phy].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);
/* XXX: align to current or next slot???. */
/* Schedule start time now */
scansm->phy_data[scansm->cur_phy].scan_win_start_time = os_cputime_get32();
/* Post scanning event to start off the scanning process */
ble_ll_event_send(&scansm->scan_sched_ev);
return BLE_ERR_SUCCESS;
}
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
static void
ble_ll_scan_switch_phy(struct ble_ll_scan_sm *scansm)
{
uint8_t tmp;
if (scansm->next_phy == PHY_NOT_CONFIGURED) {
return;
}
tmp = scansm->next_phy;
scansm->next_phy = scansm->cur_phy;
scansm->cur_phy = tmp;
/* PHY is changing in ble_ll_scan_start() */
}
/**
* Called to change PHY if needed and set new event time
*
* Context: Link Layer task.
*
* @param arg
*/
static bool
ble_ll_scan_start_next_phy(struct ble_ll_scan_sm *scansm,
uint32_t *next_event_time)
{
struct ble_ll_scan_params *cur_phy;
struct ble_ll_scan_params *next_phy;
uint32_t now;
uint32_t win;
/* Lets check if we want to switch to other PHY */
if (scansm->cur_phy == scansm->next_phy ||
scansm->next_phy == PHY_NOT_CONFIGURED) {
return false;
}
cur_phy = &scansm->phy_data[scansm->cur_phy];
next_phy = &scansm->phy_data[scansm->next_phy];
/* Store next event for current phy */
cur_phy->next_event_start = *next_event_time;
/* Other PHY already wanted to scan. Allow it */
ble_ll_scan_switch_phy(scansm);
now = os_cputime_get32();
/* Start with new channel only if PHY was scanning already */
if (next_phy->next_event_start != 0) {
next_phy->scan_chan =
ble_ll_scan_get_next_adv_prim_chan(next_phy->scan_chan);
}
next_phy->scan_win_start_time = now;
win = os_cputime_usecs_to_ticks(next_phy->scan_window *
BLE_HCI_SCAN_ITVL);
next_phy->next_event_start = now + win;
*next_event_time = next_phy->next_event_start;
return true;
}
static void
ble_ll_aux_scan_rsp_failed(struct ble_ll_scan_sm *scansm)
{
if (!scansm->cur_aux_data) {
return;
}
STATS_INC(ble_ll_stats, aux_scan_rsp_err);
ble_ll_scan_interrupted(scansm);
}
#endif
static void
ble_ll_scan_interrupted_event_cb(struct ble_npl_event *ev)
{
struct ble_ll_scan_sm *scansm = &g_ble_ll_scan_sm;
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
struct ble_ll_aux_data *aux_data;
#endif
if (!scansm->scan_enabled) {
return;
}
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
aux_data = ble_npl_event_get_arg(ev);
if (aux_data) {
if (scansm->scan_rsp_pending) {
STATS_INC(ble_ll_stats, aux_scan_rsp_err);
}
ble_ll_scan_end_adv_evt(aux_data);
ble_ll_scan_aux_data_unref(aux_data);
ble_npl_event_set_arg(ev, NULL);
STATS_INC(ble_ll_stats, aux_missed_adv);
}
#endif
/*
* If we timed out waiting for a response, the scan response pending
* flag should be set. Deal with scan backoff. Put device back into rx.
*/
if (scansm->scan_rsp_pending) {
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)
{
os_sr_t sr;
int inside_window;
int start_scan;
uint32_t now;
uint32_t dt;
uint32_t win;
uint32_t win_start;
uint32_t scan_itvl;
uint32_t next_event_time;
#ifdef BLE_XCVR_RFCLK
uint32_t xtal_ticks;
int xtal_state;
#endif
struct ble_ll_scan_sm *scansm;
struct ble_ll_scan_params *scanphy;
/*
* 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);
scanphy = &scansm->phy_data[scansm->cur_phy];
OS_ENTER_CRITICAL(sr);
if (!scansm->scan_enabled) {
os_cputime_timer_stop(&scansm->scan_timer);
OS_EXIT_CRITICAL(sr);
return;
}
if (scansm->cur_aux_data) {
/* Aux scan in progress. Wait */
STATS_INC(ble_ll_stats, scan_timer_stopped);
scansm->restart_timer_needed = 1;
OS_EXIT_CRITICAL(sr);
return;
}
/* Make sure the scan window start time and channel are up to date. */
now = os_cputime_get32();
win_start = ble_ll_scan_get_current_scan_win(scansm, now);
/* Check if we are in scan window */
dt = now - win_start;
if (scanphy->scan_window != scanphy->scan_itvl) {
win = os_cputime_usecs_to_ticks(scanphy->scan_window * BLE_HCI_SCAN_ITVL);
inside_window = dt < win ? 1 : 0;
} else {
win = 0;
/* In case continous scan lets assume we area always in the window*/
inside_window = 1;
}
/* Determine on/off state based on scan window */
scan_itvl = os_cputime_usecs_to_ticks(scanphy->scan_itvl *
BLE_HCI_SCAN_ITVL);
if (win != 0 && inside_window) {
next_event_time = win_start + win;
} else {
next_event_time = win_start + scan_itvl;
}
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
if (inside_window == 0 &&
ble_ll_scan_start_next_phy(scansm, &next_event_time)) {
/* Check if we should start next phy. If so let's say we are inside
* the window
*/
inside_window = 1;
}
#endif
/*
* 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 = 1;
switch (ble_ll_state_get()) {
case BLE_LL_STATE_ADV:
case BLE_LL_STATE_CONNECTION:
case BLE_LL_STATE_SYNC:
start_scan = 0;
break;
case BLE_LL_STATE_INITIATING:
/* Must disable PHY since we will move to a new channel */
ble_phy_disable();
if (!inside_window) {
ble_ll_state_set(BLE_LL_STATE_STANDBY);
}
/* PHY is disabled - make sure we do not wait for AUX_CONNECT_RSP */
ble_ll_conn_reset_pending_aux_conn_rsp();
break;
case BLE_LL_STATE_SCANNING:
/* Must disable PHY since we will move to a new channel */
ble_phy_disable();
if (!inside_window) {
ble_ll_state_set(BLE_LL_STATE_STANDBY);
}
break;
case BLE_LL_STATE_STANDBY:
break;
default:
BLE_LL_ASSERT(0);
break;
}
if (start_scan && inside_window) {
#ifdef BLE_XCVR_RFCLK
xtal_state = ble_ll_xcvr_rfclk_state();
if (xtal_state != BLE_RFCLK_STATE_SETTLED) {
if (xtal_state == BLE_RFCLK_STATE_OFF) {
xtal_ticks = g_ble_ll_data.ll_xtal_ticks;
} else {
xtal_ticks = ble_ll_xcvr_rfclk_time_till_settled();
}
/*
* Only bother if we have enough time to receive anything
* here. The next event time will turn off the clock.
*/
if (win != 0) {
if ((win - dt) <= xtal_ticks) {
goto done;
}
}
/*
* If clock off, start clock. Set next event time to now plus
* the clock setting time.
*/
if (xtal_state == BLE_RFCLK_STATE_OFF) {
ble_ll_xcvr_rfclk_start_now(now);
}
next_event_time = now + xtal_ticks;
goto done;
}
#endif
ble_ll_scan_start(scansm, NULL);
goto done;
}
#ifdef BLE_XCVR_RFCLK
if (inside_window == 0) {
/*
* We need to wake up before we need to start scanning in order
* to make sure the rfclock is on. If we are close to being on,
* enable the rfclock. If not, set wakeup time.
*/
if (dt >= (scan_itvl - g_ble_ll_data.ll_xtal_ticks)) {
/* Start the clock if necessary */
if (start_scan) {
if (ble_ll_xcvr_rfclk_state() == BLE_RFCLK_STATE_OFF) {
ble_ll_xcvr_rfclk_start_now(now);
next_event_time = now + g_ble_ll_data.ll_xtal_ticks;
}
}
} else {
next_event_time -= g_ble_ll_data.ll_xtal_ticks;
if (start_scan) {
ble_ll_scan_rfclk_chk_stop();
}
}
}
#endif
done:
OS_EXIT_CRITICAL(sr);
os_cputime_timer_start(&scansm->scan_timer, next_event_time);
}
/**
* ble ll scan rx pdu start
*
* Called when a PDU reception has started and the Link Layer is in the
* scanning state.
*
* Context: Interrupt
*
* @param pdu_type
* @param rxflags
*
* @return int
* 0: we will not attempt to reply to this frame
* 1: we may send a response to this frame.
*/
int
ble_ll_scan_rx_isr_start(uint8_t pdu_type, uint16_t *rxflags)
{
int rc;
struct ble_ll_scan_sm *scansm;
struct ble_ll_scan_params *scanphy;
rc = 0;
scansm = &g_ble_ll_scan_sm;
scanphy = &scansm->phy_data[scansm->cur_phy];
switch (scanphy->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) {
if (pdu_type == BLE_ADV_PDU_TYPE_SCAN_RSP) {
*rxflags |= BLE_MBUF_HDR_F_SCAN_RSP_CHK;
} else if (pdu_type == BLE_ADV_PDU_TYPE_AUX_SCAN_RSP) {
*rxflags |= BLE_MBUF_HDR_F_SCAN_RSP_CHK;
} else {
ble_ll_scan_req_backoff(scansm, 0);
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
ble_ll_aux_scan_rsp_failed(scansm);
#endif
}
}
if (scansm->cur_aux_data && !scansm->scan_rsp_pending ) {
STATS_INC(ble_ll_stats, aux_received);
}
/* Disable wfr if running */
ble_ll_wfr_disable();
break;
case BLE_SCAN_TYPE_PASSIVE:
default:
break;
}
return rc;
}
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
static uint8_t
ble_ll_ext_adv_phy_mode_to_local_phy(uint8_t adv_phy_mode)
{
switch (adv_phy_mode) {
case 0x00:
return BLE_PHY_1M;
case 0x01:
return BLE_PHY_2M;
case 0x02:
return BLE_PHY_CODED;
}
return 0;
}
static int
ble_ll_ext_scan_parse_aux_ptr(struct ble_ll_aux_data *aux_data, uint8_t *buf)
{
uint32_t aux_ptr_field = get_le32(buf) & 0x00FFFFFF;
aux_data->chan = (aux_ptr_field) & 0x3F;
if (aux_data->chan >= BLE_PHY_NUM_DATA_CHANS) {
return -1;
}
/* TODO use CA aux_ptr_field >> 6 */
aux_data->offset = 30 * ((aux_ptr_field >> 8) & 0x1FFF);
if ((aux_ptr_field >> 7) & 0x01) {
aux_data->offset *= 10;
aux_data->offset_units = 1;
}
if (aux_data->offset < BLE_LL_MAFS) {
return -1;
}
aux_data->aux_phy =
ble_ll_ext_adv_phy_mode_to_local_phy((aux_ptr_field >> 21) & 0x07);
if (aux_data->aux_phy == 0) {
return -1;
}
return 0;
}
static void
ble_ll_ext_scan_parse_adv_info(struct ble_ll_scan_sm *scansm,
struct ble_ll_ext_adv_report *evt, uint8_t *buf)
{
uint16_t adv_info = get_le16(buf);
/* TODO Use DID */
evt->sid = (adv_info >> 12);
}
/**
* ble_ll_scan_get_aux_data
*
* Get aux data pointer. It is new allocated data for beacon or currently
* processing aux data pointer. Aux data pointer will be attached to ble_hdr.rxinfo.user_data
*
* Context: Interrupt
*
* @param scansm
* @param ble_hdr
* @param rxbuf
* @param aux_data
*
* @return int
* 0: new allocated aux data
* 1: current processing aux data
* -1: error
*/
int
ble_ll_scan_get_aux_data(struct ble_mbuf_hdr *ble_hdr, uint8_t *rxbuf)
{
uint8_t ext_hdr_len;
uint8_t pdu_len;
uint8_t ext_hdr_flags;
uint8_t *ext_hdr;
uint8_t has_addr = 0;
uint8_t has_dir_addr = 0;
uint8_t has_adi = 0;
int i;
struct ble_ll_aux_data *current_aux = ble_hdr->rxinfo.user_data;
struct ble_ll_aux_data *new_aux = NULL;
struct ble_ll_aux_data tmp_aux_data = { 0 };
int rc;
pdu_len = rxbuf[1];
if (pdu_len == 0) {
return -1;
}
tmp_aux_data.mode = rxbuf[2] >> 6;
ext_hdr_len = rxbuf[2] & 0x3F;
if (ext_hdr_len < BLE_LL_EXT_ADV_AUX_PTR_SIZE && !current_aux) {
return -1;
}
if (ext_hdr_len == 0) {
if (current_aux) {
/* This is last element in the chain.
* Clear incomplete flag
*/
BLE_LL_AUX_CLEAR_FLAG(current_aux, BLE_LL_AUX_INCOMPLETE_BIT);
return 1;
}
return -1;
}
ext_hdr_flags = rxbuf[3];
ext_hdr = &rxbuf[4];
i = 0;
if (ext_hdr_flags & (1 << BLE_LL_EXT_ADV_ADVA_BIT)) {
memcpy(tmp_aux_data.addr, ext_hdr + i, 6);
tmp_aux_data.addr_type =
ble_ll_get_addr_type(rxbuf[0] & BLE_ADV_PDU_HDR_TXADD_MASK);
i += BLE_LL_EXT_ADV_ADVA_SIZE;
has_addr = 1;
}
if (ext_hdr_flags & (1 << BLE_LL_EXT_ADV_TARGETA_BIT)) {
memcpy(tmp_aux_data.dir_addr, ext_hdr + i, 6);
tmp_aux_data.dir_addr_type =
ble_ll_get_addr_type(rxbuf[0] & BLE_ADV_PDU_HDR_RXADD_MASK);
has_dir_addr = 1;
i += BLE_LL_EXT_ADV_TARGETA_SIZE;
}
if (ext_hdr_flags & (1 << BLE_LL_EXT_ADV_RFU_BIT)) {
i += 1;
}
if (ext_hdr_flags & (1 << BLE_LL_EXT_ADV_DATA_INFO_BIT)) {
tmp_aux_data.adi = get_le16(ext_hdr + i);
i += BLE_LL_EXT_ADV_DATA_INFO_SIZE;
has_adi = 1;
}
if (ext_hdr_flags & (1 << BLE_LL_EXT_ADV_AUX_PTR_BIT)) {
if (ble_ll_ext_scan_parse_aux_ptr(&tmp_aux_data, ext_hdr + i) < 0) {
return -1;
}
if (current_aux) {
/* If we are here that means there is chain advertising. */
/* Lets reuse old aux_data */
new_aux = current_aux;
/* TODO Collision; Do smth smart when did does not match */
if (!(new_aux->evt_type & (BLE_HCI_ADV_SCAN_MASK))
&& (tmp_aux_data.adi != new_aux->adi)) {
STATS_INC(ble_ll_stats, aux_chain_err);
BLE_LL_AUX_SET_FLAG(new_aux, BLE_LL_AUX_INCOMPLETE_ERR_BIT);
}
BLE_LL_AUX_SET_FLAG(new_aux, BLE_LL_AUX_CHAIN_BIT);
BLE_LL_AUX_SET_FLAG(new_aux, BLE_LL_AUX_INCOMPLETE_BIT);
} else {
if (ble_ll_scan_ext_adv_init(&new_aux) < 0) {
/* Out of memory */
return -1;
}
}
new_aux->aux_phy = tmp_aux_data.aux_phy;
if (!current_aux) {
/* Only for first ext adv we want to keep primary phy.*/
new_aux->aux_primary_phy = ble_hdr->rxinfo.phy;
}
new_aux->chan = tmp_aux_data.chan;
new_aux->offset = tmp_aux_data.offset;
new_aux->mode = tmp_aux_data.mode;
/* New aux_data or chaining */
rc = 0;
} else {
/* So ext packet does not have aux ptr. It can be because
* a) it is empty beacon (no aux ptr at all)
* b) there is no chaining or chaining has just stopped. In this case we do hava aux_data */
if (!current_aux) {
new_aux = NULL;
return 1;
}
/*If there is no new aux ptr, just get current one */
new_aux = ble_hdr->rxinfo.user_data;
/* Clear incomplete flag */
BLE_LL_AUX_CLEAR_FLAG(new_aux, BLE_LL_AUX_INCOMPLETE_BIT);
/* Current processing aux_ptr */
rc = 1;
}
if (has_adi) {
new_aux->adi = tmp_aux_data.adi;
BLE_LL_AUX_SET_FLAG(new_aux, BLE_LL_AUX_HAS_ADI);
}
if (has_addr) {
memcpy(new_aux->addr, tmp_aux_data.addr, 6);
new_aux->addr_type = tmp_aux_data.addr_type;
BLE_LL_AUX_SET_FLAG(new_aux, BLE_LL_AUX_HAS_ADDRA);
}
if (has_dir_addr) {
memcpy(new_aux->dir_addr, tmp_aux_data.dir_addr, 6);
new_aux->dir_addr_type = tmp_aux_data.dir_addr_type;
BLE_LL_AUX_SET_FLAG(new_aux, BLE_LL_AUX_HAS_DIR_ADDRA);
}
ble_hdr->rxinfo.user_data = new_aux;
return rc;
}
/**
* Called when a receive ADV_EXT PDU has ended.
*
* Context: Interrupt
*
* @return int
* < 0 Error
* >= 0: Success (number of bytes left in PDU)
*
*/
int
ble_ll_scan_parse_ext_hdr(struct os_mbuf *om,
uint8_t *adva, uint8_t adva_type,
uint8_t *inita, uint8_t inita_type,
struct ble_mbuf_hdr *ble_hdr,
struct ble_ll_ext_adv_report *out_evt)
{
uint8_t pdu_len;
uint8_t ext_hdr_len;
uint8_t ext_hdr_flags;
uint8_t *ext_hdr;
uint8_t *rxbuf = om->om_data;
int i = 1;
struct ble_ll_scan_sm *scansm;
struct ble_ll_aux_data *aux_data = ble_hdr->rxinfo.user_data;
if (!out_evt) {
return -1;
}
scansm = &g_ble_ll_scan_sm;
if (!scansm->ext_scanning) {
/* Ignore ext adv if host does not want it*/
return -1;
}
pdu_len = rxbuf[1];
if (pdu_len == 0) {
return -1;
}
out_evt->evt_type = rxbuf[2] >> 6;
if (out_evt->evt_type > BLE_LL_EXT_ADV_MODE_SCAN) {
return -1;
}
if (BLE_MBUF_HDR_SCAN_RSP_RCV(ble_hdr)) {
out_evt->evt_type |= BLE_HCI_ADV_SCAN_RSP_MASK;
}
ext_hdr_len = rxbuf[2] & 0x3F;
os_mbuf_adj(om, 3);
ext_hdr_flags = rxbuf[3];
ext_hdr = &rxbuf[4];
if (ext_hdr_len) {
i = 0;
if (ext_hdr_flags & (1 << BLE_LL_EXT_ADV_ADVA_BIT)) {
i += BLE_LL_EXT_ADV_ADVA_SIZE;
}
if (adva) {
memcpy(out_evt->addr, adva, 6);
out_evt->addr_type = adva_type;
}
if (ext_hdr_flags & (1 << BLE_LL_EXT_ADV_TARGETA_BIT)) {
i += BLE_LL_EXT_ADV_TARGETA_SIZE;
}
if (inita) {
memcpy(out_evt->dir_addr, inita, 6);
out_evt->dir_addr_type = inita_type;
out_evt->evt_type |= BLE_HCI_ADV_DIRECT_MASK;
}
if (ext_hdr_flags & (1 << BLE_LL_EXT_ADV_RFU_BIT)) {
/* Just skip it for now*/
i += 1;
}
if (ext_hdr_flags & (1 << BLE_LL_EXT_ADV_DATA_INFO_BIT)) {
ble_ll_ext_scan_parse_adv_info(scansm, out_evt, (ext_hdr + i));
i += BLE_LL_EXT_ADV_DATA_INFO_SIZE;
} else if (out_evt->evt_type & BLE_HCI_ADV_SCAN_RSP_MASK) {
out_evt->sid = (aux_data->adi >> 12);
}
/* In this point of time we don't want to care about aux ptr */
if (ext_hdr_flags & (1 << BLE_LL_EXT_ADV_AUX_PTR_BIT)) {
i += BLE_LL_EXT_ADV_AUX_PTR_SIZE;
}
if (ext_hdr_flags & (1 << BLE_LL_EXT_ADV_SYNC_INFO_BIT)) {
out_evt->per_adv_itvl = get_le16(ext_hdr + i + 2);
i += BLE_LL_EXT_ADV_SYNC_INFO_SIZE;
}
if (ext_hdr_flags & (1 << BLE_LL_EXT_ADV_TX_POWER_BIT)) {
out_evt->tx_power = *(ext_hdr + i);
i += BLE_LL_EXT_ADV_TX_POWER_SIZE;
}
/* TODO Handle ACAD if needed */
}
/* In the event we need information on primary and secondary PHY used during
* advertising.
*/
if (!aux_data) {
out_evt->prim_phy = ble_hdr->rxinfo.phy;
goto done;
}
out_evt->sec_phy = aux_data->aux_phy;
out_evt->prim_phy = aux_data->aux_primary_phy;
if (ext_hdr_len) {
/* Adjust mbuf to contain advertising data only */
os_mbuf_adj(om, ext_hdr_len);
}
/* Let us first keep update event type in aux data.
* Note that in aux chain and aux scan response packets
* we do miss original event type, which we need for advertising report.
*/
aux_data->evt_type |= out_evt->evt_type;
out_evt->evt_type = aux_data->evt_type;
done:
return pdu_len - ext_hdr_len - 1;
}
static int
ble_ll_scan_get_addr_from_ext_adv(uint8_t *rxbuf, struct ble_mbuf_hdr *ble_hdr,
uint8_t **addr, uint8_t *addr_type,
uint8_t **inita, uint8_t *inita_type,
int *ext_mode)
{
uint8_t pdu_len;
uint8_t ext_hdr_len;
uint8_t ext_hdr_flags;
uint8_t *ext_hdr;
bool has_adva = false;
bool has_inita = false;
int i;
struct ble_ll_aux_data *aux_data = ble_hdr->rxinfo.user_data;
*addr = NULL;
*inita = NULL;
pdu_len = rxbuf[1];
if (pdu_len == 0) {
return -1;
}
*ext_mode = rxbuf[2] >> 6;
if (*ext_mode > BLE_LL_EXT_ADV_MODE_SCAN) {
return -1;
}
ext_hdr_len = rxbuf[2] & 0x3F;
if (ext_hdr_len == 0) {
goto done;
}
ext_hdr_flags = rxbuf[3];
ext_hdr = &rxbuf[4];
i = 0;
if (ext_hdr_flags & (1 << BLE_LL_EXT_ADV_ADVA_BIT)) {
if (ext_hdr_len < BLE_LL_EXT_ADV_ADVA_SIZE) {
return -1;
}
*addr = ext_hdr + i;
*addr_type =
ble_ll_get_addr_type(rxbuf[0] & BLE_ADV_PDU_HDR_TXADD_MASK);
i += BLE_LL_EXT_ADV_ADVA_SIZE;
has_adva = true;
}
if (!inita) {
goto done;
}
if (ext_hdr_flags & (1 << BLE_LL_EXT_ADV_TARGETA_BIT)) {
*inita = ext_hdr + i;
*inita_type =
ble_ll_get_addr_type(rxbuf[0] & BLE_ADV_PDU_HDR_RXADD_MASK);
i += BLE_LL_EXT_ADV_TARGETA_SIZE;
has_inita = true;
}
done:
/* Check if we had address already. If yes, replace it with new one */
if (aux_data) {
/* If address has been provided, we do have it already in aux_data.*/
if (BLE_LL_AUX_CHECK_FLAG(aux_data, BLE_LL_AUX_HAS_ADDRA)) {
if (!has_adva) {
*addr = aux_data->addr;
*addr_type = aux_data->addr_type;
} else {
memcpy(aux_data->addr, *addr, 6);
aux_data->addr_type = *addr_type;
}
}
if (!inita) {
return 0;
}
if (BLE_LL_AUX_CHECK_FLAG(aux_data, BLE_LL_AUX_HAS_DIR_ADDRA)) {
if (!has_inita) {
*inita = aux_data->dir_addr;
*inita_type = aux_data->dir_addr_type;
} else {
memcpy(aux_data->dir_addr, *inita, 6);
aux_data->dir_addr_type = *inita_type;
}
}
}
return 0;
}
#endif
int
ble_ll_scan_adv_decode_addr(uint8_t pdu_type, uint8_t *rxbuf,
struct ble_mbuf_hdr *ble_hdr,
uint8_t **addr, uint8_t *addr_type,
uint8_t **inita, uint8_t *inita_type,
int *ext_mode)
{
if (pdu_type != BLE_ADV_PDU_TYPE_ADV_EXT_IND &&
pdu_type != BLE_ADV_PDU_TYPE_AUX_CONNECT_RSP) {
/* Legacy advertising */
*addr_type = ble_ll_get_addr_type(rxbuf[0] & BLE_ADV_PDU_HDR_TXADD_MASK);
*addr = rxbuf + BLE_LL_PDU_HDR_LEN;
if (!inita) {
return 0;
}
if (pdu_type != BLE_ADV_PDU_TYPE_ADV_DIRECT_IND) {
*inita = NULL;
*inita_type = 0;
return 0;
}
*inita = rxbuf + BLE_LL_PDU_HDR_LEN + BLE_DEV_ADDR_LEN;
*inita_type = ble_ll_get_addr_type(rxbuf[0] & BLE_ADV_PDU_HDR_RXADD_MASK);
return 0;
}
/* Extended advertising */
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
return ble_ll_scan_get_addr_from_ext_adv(rxbuf, ble_hdr, addr, addr_type,
inita, inita_type, ext_mode);
#else
return -1;
#endif
return 0;
}
/**
* 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)
{
int rc;
int chk_send_req;
int chk_wl;
int index;
int resolved;
uint8_t pdu_type;
uint8_t addr_type;
uint8_t peer_addr_type = 0;
uint8_t *adv_addr = NULL;
uint8_t *peer = NULL;
uint8_t *inita = NULL;
uint8_t inita_type = 0;
uint8_t *rxbuf;
struct ble_mbuf_hdr *ble_hdr;
struct ble_ll_scan_sm *scansm;
struct ble_ll_scan_params *scanphy;
int ext_adv_mode = -1;
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
uint8_t phy_mode;
uint16_t adi;
#endif
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY)
struct ble_ll_resolv_entry *rl = NULL;
#endif
/* Get scanning state machine */
scansm = &g_ble_ll_scan_sm;
scanphy = &scansm->phy_data[scansm->cur_phy];
/*
* The reason we do something different here (as opposed to failed CRC) is
* that the received PDU will not be handed up in this case. So we have
* to restart scanning and handle a failed scan request. Note that we
* return 0 in this case because we dont want the phy disabled.
*/
if (rxpdu == NULL) {
ble_ll_scan_interrupted(scansm);
return 0;
}
rc = -1;
ble_hdr = BLE_MBUF_HDR_PTR(rxpdu);
/* Get pdu type, pointer to address and address "type" */
rxbuf = rxpdu->om_data;
pdu_type = rxbuf[0] & BLE_ADV_PDU_HDR_TYPE_MASK;
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
if (scansm->cur_aux_data) {
ble_hdr->rxinfo.user_data = scansm->cur_aux_data;
scansm->cur_aux_data = NULL;
/* If we were expecting aux/chain and it not arrived,
* lets just exit here.
*/
if (pdu_type != BLE_ADV_PDU_TYPE_ADV_EXT_IND) {
goto scan_rx_isr_exit;
}
}
#endif
/* Just leave if the CRC is not OK. */
if (!crcok) {
goto scan_rx_isr_exit;
}
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
if (pdu_type == BLE_ADV_PDU_TYPE_ADV_EXT_IND) {
if (!scansm->ext_scanning) {
goto scan_rx_isr_exit;
}
/* Create new aux data for beacon or get current processing aux ptr */
rc = ble_ll_scan_get_aux_data(ble_hdr, rxbuf);
if (rc < 0) {
/* No memory or broken packet */
ble_hdr->rxinfo.flags |= BLE_MBUF_HDR_F_AUX_INVALID;
goto scan_rx_isr_exit;
}
if (rc == 0) {
/* Let's tell LL to schedule aux */
ble_hdr->rxinfo.flags |= BLE_MBUF_HDR_F_AUX_PTR_WAIT;
}
rc = -1;
}
#endif
/* Lets get addresses from advertising report*/
if (ble_ll_scan_adv_decode_addr(pdu_type, rxbuf, ble_hdr,
&peer, &peer_addr_type,
&inita, &inita_type, &ext_adv_mode)) {
goto scan_rx_isr_exit;
}
/* Determine if request may be sent and if whitelist needs to be checked */
chk_send_req = 0;
switch (pdu_type) {
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
case BLE_ADV_PDU_TYPE_ADV_EXT_IND:
if (!peer) {
/*Wait for AUX ptr */
goto scan_rx_isr_exit;
}
if ((ext_adv_mode == BLE_LL_EXT_ADV_MODE_SCAN) &&
(scanphy->scan_type == BLE_SCAN_TYPE_ACTIVE)) {
chk_send_req = 1;
}
chk_wl = 1;
break;
#endif
case BLE_ADV_PDU_TYPE_ADV_IND:
case BLE_ADV_PDU_TYPE_ADV_SCAN_IND:
if (scanphy->scan_type == BLE_SCAN_TYPE_ACTIVE) {
chk_send_req = 1;
}
chk_wl = 1;
break;
case BLE_ADV_PDU_TYPE_ADV_NONCONN_IND:
case BLE_ADV_PDU_TYPE_ADV_DIRECT_IND:
chk_wl = 1;
break;
default:
chk_wl = 0;
break;
}
/* Since peer might point to different address e.g. resolved one
* lets do a copy of pointers for scan request
*/
adv_addr = peer;
addr_type = peer_addr_type;
if ((scanphy->scan_filt_policy & 1) == 0) {
chk_wl = 0;
}
resolved = 0;
index = -1;
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY)
if (ble_ll_resolv_enabled()) {
if (ble_ll_is_rpa(peer, peer_addr_type)) {
index = ble_hw_resolv_list_match();
if (index >= 0) {
ble_hdr->rxinfo.flags |= BLE_MBUF_HDR_F_RESOLVED;
peer = g_ble_ll_resolv_list[index].rl_identity_addr;
peer_addr_type = g_ble_ll_resolv_list[index].rl_addr_type;
resolved = 1;
if (ble_ll_is_rpa(inita, inita_type)) {
if (!ble_ll_resolv_rpa(inita, g_ble_ll_resolv_list[index].rl_local_irk)) {
goto scan_rx_isr_exit;
}
ble_hdr->rxinfo.flags |= BLE_MBUF_HDR_F_INITA_RESOLVED;
}
} else {
if (chk_wl) {
goto scan_rx_isr_exit;
}
/* We don't know peer and InitA is RPA so nothing to do more here */
if (chk_send_req && inita && ble_ll_is_rpa(inita, inita_type)) {
goto scan_rx_isr_exit;
}
}
} else if (chk_send_req && inita && ble_ll_is_rpa(inita, inita_type)) {
/* If remote is identity address but InitA is RPA, make sure we can resolve it.
* If not, nothing more to do here
*/
rl = ble_ll_resolv_list_find(peer, peer_addr_type);
if (!rl) {
goto scan_rx_isr_exit;
}
if (!ble_ll_resolv_rpa(inita, rl->rl_local_irk)) {
goto scan_rx_isr_exit;
}
ble_hdr->rxinfo.flags |= BLE_MBUF_HDR_F_INITA_RESOLVED;
}
}
#else
if (chk_send_req && inita && ble_ll_is_rpa(inita, inita_type)) {
goto scan_rx_isr_exit;
}
#endif
scansm->scan_rpa_index = index;
/* If whitelist enabled, check to see if device is in the white list */
if (chk_wl && !ble_ll_whitelist_match(peer, peer_addr_type, resolved)) {
goto scan_rx_isr_exit;
}
ble_hdr->rxinfo.flags |= BLE_MBUF_HDR_F_DEVMATCH;
/* Should we send a scan request? */
if (chk_send_req) {
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
if (pdu_type == BLE_ADV_PDU_TYPE_ADV_EXT_IND) {
/* Let us check if InitA is our device.
* Note that InitA RPA is handled above where privacy is handled
*/
if (inita && !ble_ll_is_rpa(inita, inita_type) &&
!ble_ll_is_our_devaddr(inita, inita_type)) {
goto scan_rx_isr_exit;
}
if (ble_ll_scan_get_adi(ble_hdr->rxinfo.user_data, &adi) < 0) {
/* There is not ADI in scannable packet? This must be some trash,
* ignore it
*/
goto scan_rx_isr_exit;
}
if (ble_ll_scan_have_rxd_scan_rsp(peer, peer_addr_type, 1, adi)) {
goto scan_rx_isr_exit;
}
} else {
/* Dont send scan request if we have sent one to this advertiser */
if (ble_ll_scan_have_rxd_scan_rsp(peer, peer_addr_type, 0, 0)) {
goto scan_rx_isr_exit;
}
}
#else
/* Dont send scan request if we have sent one to this advertiser */
if (ble_ll_scan_have_rxd_scan_rsp(peer, peer_addr_type, 0, 0)) {
goto scan_rx_isr_exit;
}
#endif
/* 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 */
--scansm->backoff_count;
if (scansm->backoff_count == 0) {
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
phy_mode = ble_ll_phy_to_phy_mode(ble_hdr->rxinfo.phy,
BLE_HCI_LE_PHY_CODED_ANY);
if (ble_ll_sched_scan_req_over_aux_ptr(ble_hdr->rxinfo.channel,
phy_mode)) {
goto scan_rx_isr_exit;
}
#endif
/* XXX: TODO assume we are on correct phy */
ble_ll_scan_req_pdu_prepare(scansm, adv_addr, addr_type);
rc = ble_phy_tx(ble_ll_scan_req_tx_pducb, scansm,
BLE_PHY_TRANSITION_TX_RX);
if (rc == 0) {
/* Set "waiting for scan response" flag */
scansm->scan_rsp_pending = 1;
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
if (ble_hdr->rxinfo.channel < BLE_PHY_NUM_DATA_CHANS) {
/* Let's keep the aux ptr as a reference to scan rsp */
scansm->cur_aux_data =
ble_ll_scan_aux_data_ref(ble_hdr->rxinfo.user_data);
STATS_INC(ble_ll_stats, aux_scan_req_tx);
}
#endif
}
}
}
scan_rx_isr_exit:
if (rc) {
ble_ll_state_set(BLE_LL_STATE_STANDBY);
}
return rc;
}
/**
* 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;
scansm = &g_ble_ll_scan_sm;
if (scansm->scan_enabled) {
OS_ENTER_CRITICAL(sr);
if (scansm->restart_timer_needed) {
scansm->restart_timer_needed = 0;
ble_ll_event_send(&scansm->scan_sched_ev);
STATS_INC(ble_ll_stats, scan_timer_restarted);
OS_EXIT_CRITICAL(sr);
return;
}
if (ble_ll_state_get() == BLE_LL_STATE_STANDBY &&
ble_ll_scan_window_chk(scansm, os_cputime_get32()) == 0) {
/* Turn on the receiver and set state */
ble_ll_scan_start(scansm, NULL);
}
OS_EXIT_CRITICAL(sr);
}
}
/**
* Scan timer callback; means that the scan window timeout has been reached
* and we should perform the appropriate actions.
*
* Context: Interrupt (cputimer)
*
* @param arg Pointer to scan state machine.
*/
void
ble_ll_scan_timer_cb(void *arg)
{
struct ble_ll_scan_sm *scansm;
scansm = (struct ble_ll_scan_sm *)arg;
ble_ll_event_send(&scansm->scan_sched_ev);
}
void
ble_ll_scan_interrupted(struct ble_ll_scan_sm *scansm)
{
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
ble_npl_event_set_arg(&scansm->scan_interrupted_ev, scansm->cur_aux_data);
scansm->cur_aux_data = NULL;
#endif
ble_ll_event_send(&scansm->scan_interrupted_ev);
}
/**
* Called when the wait for response timer expires while in the scanning
* state.
*
* Context: Interrupt.
*/
void
ble_ll_scan_wfr_timer_exp(void)
{
struct ble_ll_scan_sm *scansm;
uint8_t chan;
int phy;
int rc;
#if (BLE_LL_BT5_PHY_SUPPORTED == 1)
uint8_t phy_mode;
#endif
scansm = &g_ble_ll_scan_sm;
if (scansm->cur_aux_data) {
/* We actually care about interrupted scan only for EXT ADV because only
* then we might consider to send truncated event to the host.
*/
ble_ll_scan_interrupted(scansm);
/* Need to disable phy since we are going to move to BLE_LL_STATE_STANDBY
* or we will need to change channel to primary one
*/
ble_phy_disable();
if (ble_ll_scan_window_chk(scansm, os_cputime_get32()) == 1) {
/* Outside the window scan */
ble_ll_state_set(BLE_LL_STATE_STANDBY);
return;
}
ble_ll_get_chan_to_scan(scansm, &chan, &phy);
#if (BLE_LL_BT5_PHY_SUPPORTED == 1)
phy_mode = ble_ll_phy_to_phy_mode(phy, BLE_HCI_LE_PHY_CODED_ANY);
ble_phy_mode_set(phy_mode, phy_mode);
#endif
rc = ble_phy_setchan(chan, BLE_ACCESS_ADDR_ADV, BLE_LL_CRCINIT_ADV);
BLE_LL_ASSERT(rc == 0);
}
if (scansm->scan_rsp_pending) {
ble_ll_scan_req_backoff(scansm, 0);
}
ble_phy_restart_rx();
}
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
/*
* Send extended advertising report
*
* @return -1 on error (data truncated or other error)
* 0 on success (data status is "completed")
* 1 on success (data status is not "completed")
*/
static int
ble_ll_hci_send_ext_adv_report(uint8_t ptype, uint8_t *adva, uint8_t adva_type,
uint8_t *inita, uint8_t inita_type,
struct os_mbuf *om,
struct ble_mbuf_hdr *hdr)
{
struct ble_ll_aux_data *aux_data = hdr->rxinfo.user_data;
struct ble_ll_ext_adv_report *evt;
struct ble_ll_ext_adv_report *next_evt;
int offset;
int datalen;
int rc;
bool need_event;
uint16_t max_event_len;
if (!ble_ll_hci_is_le_event_enabled(BLE_HCI_LE_SUBEV_EXT_ADV_RPT)) {
rc = -1;
goto done;
}
/*
* We keep one allocated event in aux_data to be able to truncate chain
* properly in case of error. If there is no event in aux_data it means this
* is the first event for this chain.
*/
if (aux_data && aux_data->evt) {
evt = aux_data->evt;
aux_data->evt = NULL;
} else {
evt = ble_ll_scan_init_ext_adv_report(NULL);
if (!evt) {
rc = -1;
goto done;
}
}
#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_INITA_RESOLVED(hdr)) {
inita_type += 2;
}
#endif
datalen = ble_ll_scan_parse_ext_hdr(om, adva, adva_type, inita, inita_type, hdr, evt);
if (datalen < 0) {
rc = -1;
/* If we were in the middle of advertising report, let us send truncated report. */
if (aux_data &&
BLE_LL_AUX_CHECK_FLAG(aux_data, BLE_LL_SENT_EVENT_TO_HOST) &&
BLE_LL_AUX_CHECK_FLAG(aux_data, BLE_LL_AUX_CHAIN_BIT)) {
evt->evt_type |= (BLE_HCI_ADV_DATA_STATUS_TRUNCATED);
BLE_LL_AUX_SET_FLAG(aux_data, BLE_LL_AUX_TRUNCATED_SENT);
ble_ll_hci_event_send((uint8_t *)evt);
goto done;
}
ble_hci_trans_buf_free((uint8_t *)evt);
goto done;
}
/* Max_event_len contains advertising data and BLE_HCI_EVENT_HDR_LEN as this is related
* to the buffer available for the event. The maximum is 255 + 2
*/
offset = 0;
max_event_len = min(UINT8_MAX + BLE_HCI_EVENT_HDR_LEN, BLE_LL_MAX_EVT_LEN);
do {
need_event = false;
next_evt = NULL;
evt->adv_data_len = min(max_event_len - sizeof(*evt),
datalen - offset);
/* Event len, should not contain event meta code and let itself */
evt->event_len = (sizeof(*evt) - BLE_HCI_EVENT_HDR_LEN) + evt->adv_data_len;
evt->rssi = hdr->rxinfo.rssi;
os_mbuf_copydata(om, offset, evt->adv_data_len, evt->adv_data);
offset += evt->adv_data_len;
if (offset < datalen) {
/* Need another event for next fragment of this PDU */
need_event = true;
} else if (aux_data && BLE_LL_AUX_CHECK_FLAG(aux_data, BLE_LL_AUX_INCOMPLETE_ERR_BIT)) {
need_event = false;
} else if (aux_data && BLE_LL_AUX_CHECK_FLAG(aux_data, BLE_LL_AUX_INCOMPLETE_BIT)) {
/* Need another event for next PDU in chain */
need_event = true;
}
/* Assume data status is not "completed" */
rc = 1;
if (need_event) {
/*
* We will need another event so let's try to allocate one now. If
* we cannot do this, need to mark event as truncated.
*/
next_evt = ble_ll_scan_init_ext_adv_report(evt);
if (next_evt) {
evt->evt_type |= (BLE_HCI_ADV_DATA_STATUS_INCOMPLETE);
} else {
evt->evt_type |= (BLE_HCI_ADV_DATA_STATUS_TRUNCATED);
rc = -1;
if (aux_data) {
BLE_LL_AUX_SET_FLAG(aux_data, BLE_LL_AUX_TRUNCATED_SENT);
if (!BLE_LL_AUX_CHECK_FLAG(aux_data, BLE_LL_SENT_EVENT_TO_HOST)) {
ble_hci_trans_buf_free((uint8_t *)evt);
goto done;
}
}
}
} else if (aux_data) {
if (BLE_LL_AUX_CHECK_FLAG(aux_data, BLE_LL_AUX_INCOMPLETE_ERR_BIT)) {
evt->evt_type |= (BLE_HCI_ADV_DATA_STATUS_TRUNCATED);
BLE_LL_AUX_SET_FLAG(aux_data, BLE_LL_AUX_TRUNCATED_SENT);
rc = -1;
} else if (BLE_LL_AUX_CHECK_FLAG(aux_data, BLE_LL_AUX_INCOMPLETE_BIT)) {
evt->evt_type |= (BLE_HCI_ADV_DATA_STATUS_INCOMPLETE);
} else {
rc = 0;
}
} else {
rc = 0;
}
ble_ll_hci_event_send((uint8_t *)evt);
if (aux_data) {
BLE_LL_AUX_SET_FLAG(aux_data, BLE_LL_SENT_EVENT_TO_HOST);
/* In case it is scannable AUX and we are waiting for scan response,
* let us clear BLE_LL_SENT_EVENT_TO_HOST flag as we consider scan response
* as separate report even aux_data is reused.
* This is needed to proper detect of not completed advertising
* reports.
*/
if ((aux_data->evt_type & BLE_HCI_ADV_SCAN_MASK) &&
!(aux_data->evt_type & BLE_HCI_ADV_SCAN_RSP_MASK)) {
BLE_LL_AUX_CLEAR_FLAG(aux_data, BLE_LL_SENT_EVENT_TO_HOST);
}
}
evt = next_evt;
} while ((offset < datalen) && evt);
BLE_LL_ASSERT(offset <= datalen);
if (aux_data) {
/* Store any event left for later use */
aux_data->evt = evt;
} else {
/* If it is empty beacon, evt shall be NULL */
BLE_LL_ASSERT(!evt);
}
done:
/* Incomplete event. Can leave now.*/
if (rc == 1) {
return rc;
}
if (aux_data){
if ((rc == 0) && (aux_data->evt_type & BLE_HCI_ADV_SCAN_RSP_MASK)) {
/* Scan response completed successfully, add to duplicate list
* if possible
*/
ble_ll_scan_add_scan_rsp_adv(aux_data->addr, aux_data->addr_type,
1, aux_data->adi);
}
/* Error during sending event or even before.*/
if (rc < 0) {
BLE_LL_AUX_SET_FLAG(aux_data, BLE_LL_AUX_INCOMPLETE_ERR_BIT);
}
}
return rc;
}
#endif
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV)
static void
check_periodic_sync(const struct os_mbuf *om, struct ble_mbuf_hdr *rxhdr,
uint8_t *adva, uint8_t adva_type)
{
uint8_t pdu_len;
uint8_t ext_hdr_len;
uint8_t ext_hdr_flags;
uint8_t *ext_hdr;
uint8_t *rxbuf = om->om_data;
uint8_t sid;
int i;
pdu_len = rxbuf[1];
if (pdu_len == 0) {
return;
}
ext_hdr_len = rxbuf[2] & 0x3F;
if (ext_hdr_len) {
ext_hdr_flags = rxbuf[3];
ext_hdr = &rxbuf[4];
i = 0;
if (ext_hdr_flags & (1 << BLE_LL_EXT_ADV_ADVA_BIT)) {
i += BLE_LL_EXT_ADV_ADVA_SIZE;
}
if (ext_hdr_flags & (1 << BLE_LL_EXT_ADV_TARGETA_BIT)) {
i += BLE_LL_EXT_ADV_TARGETA_SIZE;
}
if (ext_hdr_flags & (1 << BLE_LL_EXT_ADV_RFU_BIT)) {
i += 1;
}
if (ext_hdr_flags & (1 << BLE_LL_EXT_ADV_DATA_INFO_BIT)) {
sid = (get_le16(ext_hdr + i) >> 12);
i += BLE_LL_EXT_ADV_DATA_INFO_SIZE;
} else {
/* ADI is mandatory */
return;
}
if (ext_hdr_flags & (1 << BLE_LL_EXT_ADV_AUX_PTR_BIT)) {
i += BLE_LL_EXT_ADV_AUX_PTR_SIZE;
}
if (ext_hdr_flags & (1 << BLE_LL_EXT_ADV_SYNC_INFO_BIT)) {
ble_ll_sync_info_event(adva, adva_type, sid, rxhdr, ext_hdr + i);
}
}
}
#endif
static inline void
ble_ll_scan_dup_move_to_head(struct ble_ll_scan_dup_entry *e)
{
if (e != TAILQ_FIRST(&g_scan_dup_list)) {
TAILQ_REMOVE(&g_scan_dup_list, e, link);
TAILQ_INSERT_HEAD(&g_scan_dup_list, e, link);
}
}
static inline struct ble_ll_scan_dup_entry *
ble_ll_scan_dup_new(void)
{
struct ble_ll_scan_dup_entry *e;
e = os_memblock_get(&g_scan_dup_pool);
if (!e) {
e = TAILQ_LAST(&g_scan_dup_list, ble_ll_scan_dup_list);
TAILQ_REMOVE(&g_scan_dup_list, e, link);
}
memset(e, 0, sizeof(*e));
return e;
}
static int
ble_ll_scan_dup_check_legacy(uint8_t addr_type, uint8_t *addr, uint8_t pdu_type)
{
struct ble_ll_scan_dup_entry *e;
uint8_t type;
int rc;
type = BLE_LL_SCAN_ENTRY_TYPE_LEGACY(addr_type);
TAILQ_FOREACH(e, &g_scan_dup_list, link) {
if ((e->type == type) && !memcmp(e->addr, addr, 6)) {
break;
}
}
if (e) {
if (pdu_type == BLE_ADV_PDU_TYPE_ADV_DIRECT_IND) {
rc = e->flags & BLE_LL_SCAN_DUP_F_DIR_ADV_REPORT_SENT;
} else if (pdu_type == BLE_ADV_PDU_TYPE_SCAN_RSP) {
rc = e->flags & BLE_LL_SCAN_DUP_F_SCAN_RSP_SENT;
} else {
rc = e->flags & BLE_LL_SCAN_DUP_F_ADV_REPORT_SENT;
}
ble_ll_scan_dup_move_to_head(e);
} else {
rc = 0;
e = ble_ll_scan_dup_new();
e->flags = 0;
e->type = type;
memcpy(e->addr, addr, 6);
TAILQ_INSERT_HEAD(&g_scan_dup_list, e, link);
}
return rc;
}
static int
ble_ll_scan_dup_update_legacy(uint8_t addr_type, 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;
}
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
static int
ble_ll_scan_dup_check_ext(uint8_t addr_type, uint8_t *addr,
struct ble_ll_aux_data *aux_data)
{
struct ble_ll_scan_dup_entry *e;
bool has_aux;
bool is_anon;
uint16_t adi;
uint8_t type;
int rc;
has_aux = aux_data != NULL;
is_anon = addr == NULL;
adi = has_aux ? aux_data->adi : 0;
type = BLE_LL_SCAN_ENTRY_TYPE_EXT(addr_type, has_aux, is_anon, adi);
TAILQ_FOREACH(e, &g_scan_dup_list, link) {
if ((e->type == type) &&
(is_anon || !memcmp(e->addr, addr, BLE_DEV_ADDR_LEN))) {
break;
}
}
if (e) {
if (e->adi != adi) {
rc = 0;
e->flags = 0;
e->adi = adi;
} else {
rc = e->flags & BLE_LL_SCAN_DUP_F_ADV_REPORT_SENT;
}
ble_ll_scan_dup_move_to_head(e);
} else {
rc = 0;
e = ble_ll_scan_dup_new();
e->flags = 0;
e->type = type;
e->adi = adi;
if (!is_anon) {
memcpy(e->addr, addr, 6);
}
TAILQ_INSERT_HEAD(&g_scan_dup_list, e, link);
}
return rc;
}
static int
ble_ll_scan_dup_update_ext(uint8_t addr_type, uint8_t *addr,
struct ble_ll_aux_data *aux_data)
{
struct ble_ll_scan_dup_entry *e;
bool has_aux;
bool is_anon;
uint16_t adi;
uint8_t type;
has_aux = aux_data != NULL;
is_anon = addr == NULL;
adi = has_aux ? aux_data->adi : 0;
type = BLE_LL_SCAN_ENTRY_TYPE_EXT(addr_type, has_aux, is_anon, adi);
/*
* We assume ble_ll_scan_dup_check() was called before which either matched
* some entry or allocated new one and placed in on the top of queue.
*/
e = TAILQ_FIRST(&g_scan_dup_list);
BLE_LL_ASSERT(e && e->type == type && (is_anon || !memcmp(e->addr, addr, 6)));
e->flags |= BLE_LL_SCAN_DUP_F_ADV_REPORT_SENT;
return 0;
}
#endif
/**
* Process a received PDU while in the scanning state.
*
* Context: Link Layer task.
*
* @param pdu_type
* @param rxbuf
*/
void
ble_ll_scan_rx_pkt_in(uint8_t ptype, struct os_mbuf *om, struct ble_mbuf_hdr *hdr)
{
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY)
int index;
struct ble_ll_resolv_entry *rl;
#endif
uint8_t *rxbuf = om->om_data;
uint8_t *adv_addr = NULL;
uint8_t *adva;
uint8_t *ident_addr;
uint8_t ident_addr_type;
uint8_t *init_addr = NULL;
uint8_t init_addr_type = 0;
uint8_t txadd = 0;
uint8_t rxadd;
uint8_t scan_rsp_chk;
struct ble_ll_scan_sm *scansm = &g_ble_ll_scan_sm;
int ext_adv_mode = -1;
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
struct ble_ll_aux_data *aux_data;
int rc;
uint8_t evt_possibly_truncated = 0;
/* No need to ref as this is only local helper
* and unref on hdr->rxinfo.user_data is done in the end of this function
*/
aux_data = hdr->rxinfo.user_data;
#endif
/* Set scan response check flag */
scan_rsp_chk = BLE_MBUF_HDR_SCAN_RSP_RCV(hdr);
if (!BLE_MBUF_HDR_CRC_OK(hdr)) {
goto scan_continue;
}
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
if (BLE_MBUF_HDR_AUX_INVALID(hdr)) {
evt_possibly_truncated = 1;
goto scan_continue;
}
if (aux_data && (ptype != BLE_ADV_PDU_TYPE_ADV_EXT_IND ||
BLE_LL_AUX_CHECK_FLAG(aux_data, BLE_LL_AUX_INCOMPLETE_ERR_BIT))) {
/* LL was scheduled for aux but received something different.
* Let's just ignore received event and send truncated for
* previous report if needed.
*/
evt_possibly_truncated = 1;
goto scan_continue;
}
#endif
/* We dont care about scan requests or connect requests */
if ((ptype == BLE_ADV_PDU_TYPE_SCAN_REQ) || (ptype == BLE_ADV_PDU_TYPE_CONNECT_IND)) {
goto scan_continue;
}
if (ble_ll_scan_adv_decode_addr(ptype, rxbuf, hdr,
&adv_addr, &txadd,
&init_addr, &init_addr_type,
&ext_adv_mode)) {
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
evt_possibly_truncated = 1;
#endif
goto scan_continue;
}
ident_addr = adv_addr;
ident_addr_type = txadd;
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY)
index = scansm->scan_rpa_index;
if (index >= 0) {
ident_addr = g_ble_ll_resolv_list[index].rl_identity_addr;
ident_addr_type = g_ble_ll_resolv_list[index].rl_addr_type;
if (ble_ll_is_rpa(init_addr, init_addr_type)) {
/* Let's try resolve InitA. */
if (ble_ll_resolv_rpa(init_addr, g_ble_ll_resolv_list[index].rl_local_irk)) {
init_addr = ble_ll_get_our_devaddr(scansm->own_addr_type & 1);
init_addr_type = scansm->own_addr_type & 1;
}
}
} else if (init_addr && ble_ll_resolv_enabled() && ble_ll_is_rpa(init_addr, init_addr_type)) {
/* If we are here it means AdvA is identity. Check if initA is RPA */
rl = ble_ll_resolv_list_find(ident_addr, ident_addr_type);
if (rl && ble_ll_resolv_rpa(init_addr, rl->rl_local_irk)) {
init_addr = ble_ll_get_our_devaddr(scansm->own_addr_type & 1);
init_addr_type = scansm->own_addr_type & 1;
}
}
#endif
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV)
/* periodic scan is using own filter list so we need to bypass scan filter
* policy if PDU and adv type match and advertiser address is present
*/
if ((ptype == BLE_ADV_PDU_TYPE_ADV_EXT_IND) &&
(ext_adv_mode == BLE_LL_EXT_ADV_MODE_NON_CONN) && ident_addr) {
check_periodic_sync(om, hdr, ident_addr, ident_addr_type);
}
#endif
/* Check the scanner filter policy */
if (ble_ll_scan_chk_filter_policy(ptype, adv_addr, txadd, init_addr,
init_addr_type,
BLE_MBUF_HDR_DEVMATCH(hdr))) {
goto scan_continue;
}
/*
* XXX: The BLE spec is a bit unclear here. What if we get a scan
* response from an advertiser that we did not send a request to?
* Do we send an advertising report? Do we add it to list of devices
* that we have heard a scan response from?
*/
if (ptype == BLE_ADV_PDU_TYPE_SCAN_RSP) {
/*
* If this is a scan response in reply to a request we sent we need
* to store this advertiser's address so we dont send a request to it.
*/
if (scansm->scan_rsp_pending && scan_rsp_chk) {
/*
* We could also check the timing of the scan reponse; make sure
* that it is relatively close to the end of the scan request but
* we wont for now.
*/
rxadd = scansm->pdu_data.hdr_byte & BLE_ADV_PDU_HDR_RXADD_MASK;
adva = scansm->pdu_data.adva;
if (((txadd && rxadd) || ((txadd + rxadd) == 0)) &&
!memcmp(adv_addr, adva, BLE_DEV_ADDR_LEN)) {
/* We have received a scan response. Add to list */
ble_ll_scan_add_scan_rsp_adv(ident_addr, ident_addr_type, 0, 0);
/* Perform scan request backoff procedure */
ble_ll_scan_req_backoff(scansm, 1);
}
} else {
/* Ignore if this is not ours */
goto scan_continue;
}
}
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
if (ptype == BLE_ADV_PDU_TYPE_ADV_EXT_IND) {
if (!scansm->ext_scanning) {
goto scan_continue;
}
/* Let's see if that packet contains aux ptr*/
if (BLE_MBUF_HDR_WAIT_AUX(hdr)) {
BLE_LL_ASSERT(aux_data);
/* ble_ll_sched_aux_scan will ref aux_data */
if (ble_ll_sched_aux_scan(hdr, scansm, aux_data)) {
/* We are here when could not schedule the aux ptr */
hdr->rxinfo.flags &= ~BLE_MBUF_HDR_F_AUX_PTR_WAIT;
/* Mark that chain is trimmed */
BLE_LL_AUX_SET_FLAG(aux_data, BLE_LL_AUX_INCOMPLETE_ERR_BIT);
/* Note: aux_data unref will be done when truncated is sent to the host or
* below if we failed to schedule for the very first aux packet.
*/
if (!BLE_LL_AUX_CHECK_FLAG(aux_data, BLE_LL_SENT_EVENT_TO_HOST)) {
goto scan_continue;
}
}
/*
* If this is ext adv, there is nothing to do here but just leave and wait
* for aux packet.
*/
if (!BLE_LL_AUX_CHECK_FLAG(aux_data, BLE_LL_AUX_CHAIN_BIT)) {
goto scan_continue;
}
STATS_INC(ble_ll_stats, aux_chain_cnt);
}
/* Filter duplicates */
if (!scansm->scan_filt_dups ||
!ble_ll_scan_dup_check_ext(ident_addr_type, ident_addr, aux_data)) {
rc = ble_ll_hci_send_ext_adv_report(ptype, ident_addr, ident_addr_type,
init_addr, init_addr_type, om, hdr);
if (rc < 0) {
/*
* Data were trimmed so no need to scan this chain anymore. Also
* make sure we do not send any more events for this chain, just in
* case we managed to scan something before events were processed.
*/
if (BLE_MBUF_HDR_WAIT_AUX(hdr)) {
hdr->rxinfo.flags &= ~BLE_MBUF_HDR_F_AUX_PTR_WAIT;
if (ble_ll_sched_rmv_elem(&aux_data->sch) == 0) {
/* AUX PTR removed from the scheduler.
* Unref aux_data which are stored in the scheduler item
* as a cb_arg
*/
ble_ll_scan_aux_data_unref(aux_data->sch.cb_arg);
aux_data->sch.cb_arg = NULL;
}
evt_possibly_truncated = 1;
}
} else if (!rc && scansm->scan_filt_dups) {
ble_ll_scan_dup_update_ext(ident_addr_type, ident_addr, aux_data);
}
}
ble_ll_scan_switch_phy(scansm);
if (scansm->scan_rsp_pending) {
if (!scan_rsp_chk) {
/* We are here because we sent SCAN_REQ and wait for SCAN_RSP. */
ble_ll_scan_aux_data_unref(hdr->rxinfo.user_data);
hdr->rxinfo.user_data = NULL;
return;
}
/* XXX: For now let us consider scan response as succeed in the backoff context,
* after first scan response packet is received.
* I guess we should marked it succeed after complete scan response is received,
* and failed when truncated, but then we need to analyze reason of truncation as it
* also might be issue of the resources on our side
*/
ble_ll_scan_req_backoff(scansm, 1);
}
goto scan_continue;
}
#endif
/* Filter duplicates */
if (!scansm->scan_filt_dups ||
!ble_ll_scan_dup_check_legacy(ident_addr_type, ident_addr, ptype)) {
/* Send the advertising report, also updates scan duplicates list */
ble_ll_scan_send_adv_report(ptype, ident_addr, ident_addr_type,
init_addr, init_addr_type, om, hdr, scansm);
}
scan_continue:
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
if (aux_data) {
if (evt_possibly_truncated || !BLE_MBUF_HDR_CRC_OK(hdr)) {
ble_ll_scan_end_adv_evt(aux_data);
}
ble_ll_scan_aux_data_unref(hdr->rxinfo.user_data);
hdr->rxinfo.user_data = NULL;
}
#endif
/*
* If the scan response check bit is set and we are pending a response,
* we have failed the scan request (as we would have reset the scan rsp
* pending flag if we received a valid response
*/
if (scansm->scan_rsp_pending && scan_rsp_chk) {
ble_ll_scan_req_backoff(scansm, 0);
}
ble_ll_scan_chk_resume();
}
int
ble_ll_scan_set_scan_params(uint8_t *cmd)
{
uint8_t scan_type;
uint8_t own_addr_type;
uint8_t filter_policy;
uint16_t scan_itvl;
uint16_t scan_window;
struct ble_ll_scan_sm *scansm;
struct ble_ll_scan_params *scanp;
/* 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_type = cmd[0];
scan_itvl = get_le16(cmd + 1);
scan_window = get_le16(cmd + 3);
own_addr_type = cmd[5];
filter_policy = cmd[6];
/* Check scan type */
if ((scan_type != BLE_HCI_SCAN_TYPE_PASSIVE) &&
(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 (own_addr_type > BLE_HCI_ADV_OWN_ADDR_MAX) {
return BLE_ERR_INV_HCI_CMD_PARMS;
}
/* Check scanner filter policy */
if (filter_policy > BLE_HCI_SCAN_FILT_MAX) {
return BLE_ERR_INV_HCI_CMD_PARMS;
}
/* Store scan parameters */
scanp = &g_ble_ll_scan_params[PHY_UNCODED];
scanp->configured = 1;
scanp->scan_type = scan_type;
scanp->scan_itvl = scan_itvl;
scanp->scan_window = scan_window;
scanp->scan_filt_policy = filter_policy;
scanp->own_addr_type = own_addr_type;
#if (BLE_LL_SCAN_PHY_NUMBER == 2)
g_ble_ll_scan_params[PHY_CODED].configured = 0;
#endif
return 0;
}
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
static int
ble_ll_check_scan_params(uint8_t type, uint16_t itvl, uint16_t window)
{
/* Check scan type */
if ((type != BLE_HCI_SCAN_TYPE_PASSIVE) &&
(type != BLE_HCI_SCAN_TYPE_ACTIVE)) {
return BLE_ERR_INV_HCI_CMD_PARMS;
}
/* Check interval and window */
if ((itvl < BLE_HCI_SCAN_ITVL_MIN) ||
(itvl > BLE_HCI_SCAN_ITVL_MAX) ||
(window < BLE_HCI_SCAN_WINDOW_MIN) ||
(window > BLE_HCI_SCAN_WINDOW_MAX) ||
(itvl < window)) {
return BLE_ERR_INV_HCI_CMD_PARMS;
}
return 0;
}
int
ble_ll_set_ext_scan_params(uint8_t *cmd, uint8_t cmdlen)
{
struct ble_ll_scan_params new_params[BLE_LL_SCAN_PHY_NUMBER] = { };
struct ble_ll_scan_params *uncoded = &new_params[PHY_UNCODED];
struct ble_ll_scan_params *coded = &new_params[PHY_CODED];
uint8_t idx;
int rc;
if (cmdlen < 3) {
return BLE_ERR_INV_HCI_CMD_PARMS;
}
/* 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[0] > BLE_HCI_ADV_OWN_ADDR_MAX) {
return BLE_ERR_INV_HCI_CMD_PARMS;
}
coded->own_addr_type = cmd[0];
uncoded->own_addr_type = cmd[0];
/* Check scanner filter policy */
if (cmd[1] > BLE_HCI_SCAN_FILT_MAX) {
return BLE_ERR_INV_HCI_CMD_PARMS;
}
coded->scan_filt_policy = cmd[1];
uncoded->scan_filt_policy = cmd[1];
/* Check if no reserved bits in PHYS are set and that at least one valid PHY
* is set.
*/
if (!(cmd[2] & ble_ll_valid_scan_phy_mask) ||
(cmd[2] & ~ble_ll_valid_scan_phy_mask)) {
return BLE_ERR_INV_HCI_CMD_PARMS;
}
idx = 3;
if (cmd[2] & BLE_HCI_LE_PHY_1M_PREF_MASK) {
if (cmdlen < idx + 5) {
return BLE_ERR_INV_HCI_CMD_PARMS;
}
uncoded->scan_type = cmd[idx];
idx++;
uncoded->scan_itvl = get_le16(cmd + idx);
idx += 2;
uncoded->scan_window = get_le16(cmd + idx);
idx += 2;
rc = ble_ll_check_scan_params(uncoded->scan_type,
uncoded->scan_itvl,
uncoded->scan_window);
if (rc) {
return rc;
}
/* That means user whats to use this PHY for scanning */
uncoded->configured = 1;
}
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY)
if (cmd[2] & BLE_HCI_LE_PHY_CODED_PREF_MASK) {
if (cmdlen < idx + 5) {
return BLE_ERR_INV_HCI_CMD_PARMS;
}
coded->scan_type = cmd[idx];
idx++;
coded->scan_itvl = get_le16(cmd + idx);
idx += 2;
coded->scan_window = get_le16(cmd + idx);
idx += 2;
rc = ble_ll_check_scan_params(coded->scan_type,
coded->scan_itvl,
coded->scan_window);
if (rc) {
return rc;
}
/* That means user whats to use this PHY for scanning */
coded->configured = 1;
}
#endif
/* If host requests continuous scan for 2 PHYs, we double scan interval
* and split it for two equal scan windows between 2 PHYs
*/
if ((coded->configured && uncoded->configured) &&
((uncoded->scan_itvl == uncoded->scan_window) ||
(coded->scan_itvl == coded-> scan_window))) {
uncoded->scan_itvl *= 2;
coded-> scan_itvl = uncoded->scan_itvl;
coded->scan_window = uncoded->scan_window;
}
memcpy(g_ble_ll_scan_params, new_params, sizeof(new_params));
return 0;
}
#endif
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
static void
ble_ll_scan_duration_period_timers_restart(struct ble_ll_scan_sm *scansm)
{
uint32_t now;
now = os_cputime_get32();
os_cputime_timer_stop(&scansm->duration_timer);
os_cputime_timer_stop(&scansm->period_timer);
if (scansm->duration_ticks) {
os_cputime_timer_start(&scansm->duration_timer,
now + scansm->duration_ticks);
if (scansm->period_ticks) {
os_cputime_timer_start(&scansm->period_timer,
now + scansm->period_ticks);
}
}
}
static void
ble_ll_scan_duration_timer_cb(void *arg)
{
struct ble_ll_scan_sm *scansm;
scansm = (struct ble_ll_scan_sm *)arg;
ble_ll_scan_sm_stop(2);
/* if period is set both timers get started from period cb */
if (!scansm->period_ticks) {
ble_ll_hci_ev_send_scan_timeout();
}
}
static void
ble_ll_scan_period_timer_cb(void *arg)
{
struct ble_ll_scan_sm *scansm = arg;
ble_ll_scan_sm_start(scansm);
/* always start timer regardless of ble_ll_scan_sm_start result
* if it failed will restart in next period
*/
ble_ll_scan_duration_period_timers_restart(scansm);
}
#endif
/**
* ble ll scan set enable
*
* HCI scan set enable command processing function
*
* Context: Link Layer task (HCI Command parser).
*
* @param cmd Pointer to command buffer
*
* @return int BLE error code.
*/
int
ble_ll_scan_set_enable(uint8_t *cmd, uint8_t ext)
{
int rc;
uint8_t filter_dups;
uint8_t enable;
struct ble_ll_scan_sm *scansm;
struct ble_ll_scan_params *scanp;
struct ble_ll_scan_params *scanphy;
int i;
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
uint32_t period_ticks = 0;
uint32_t dur_ticks = 0;
uint16_t period;
uint16_t dur;
#endif
/* Check for valid parameters */
enable = cmd[0];
filter_dups = cmd[1];
if ((filter_dups > 1) || (enable > 1)) {
return BLE_ERR_INV_HCI_CMD_PARMS;
}
scansm = &g_ble_ll_scan_sm;
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
/* we can do that here since value will never change until reset */
scansm->ext_scanning = ext;
if (ext) {
dur = get_le16(cmd + 2);
/* Period parameter is ignored when the Duration parameter is zero */
if (dur) {
period = get_le16(cmd + 4);
} else {
period = 0;
}
/* period is in 1.28 sec units
* TODO support full range, would require os_cputime milliseconds API
*/
if (period > 3355) {
return BLE_ERR_INV_HCI_CMD_PARMS;
}
period_ticks = os_cputime_usecs_to_ticks(period * 1280000);
/* duration is in 10ms units */
dur_ticks = os_cputime_usecs_to_ticks(dur * 10000);
if (dur_ticks && period_ticks && (dur_ticks >= period_ticks)) {
return BLE_ERR_INV_HCI_CMD_PARMS;
}
}
#endif
/* disable*/
if (!enable) {
if (scansm->scan_enabled) {
ble_ll_scan_sm_stop(1);
}
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
os_cputime_timer_stop(&scansm->duration_timer);
os_cputime_timer_stop(&scansm->period_timer);
#endif
return BLE_ERR_SUCCESS;
}
/* if already enable we just need to update parameters */
if (scansm->scan_enabled) {
/* Controller does not allow initiating and scanning.*/
for (i = 0; i < BLE_LL_SCAN_PHY_NUMBER; i++) {
scanphy = &scansm->phy_data[i];
if (scanphy->configured &&
scanphy->scan_type == BLE_SCAN_TYPE_INITIATE) {
return BLE_ERR_CMD_DISALLOWED;
}
}
/* update filter policy */
scansm->scan_filt_dups = filter_dups;
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
/* restart timers according to new settings */
scansm->duration_ticks = dur_ticks;
scansm->period_ticks = period_ticks;
ble_ll_scan_duration_period_timers_restart(scansm);
#endif
return BLE_ERR_SUCCESS;
}
/* we can store those upfront regardless of start scan result since scan is
* disabled now
*/
scansm->scan_filt_dups = filter_dups;
scansm->cur_phy = PHY_NOT_CONFIGURED;
scansm->next_phy = PHY_NOT_CONFIGURED;
for (i = 0; i < BLE_LL_SCAN_PHY_NUMBER; i++) {
scanphy = &scansm->phy_data[i];
scanp = &g_ble_ll_scan_params[i];
if (!scanp->configured) {
continue;
}
scanphy->configured = scanp->configured;
scanphy->scan_type = scanp->scan_type;
scanphy->scan_itvl = scanp->scan_itvl;
scanphy->scan_window = scanp->scan_window;
scanphy->scan_filt_policy = scanp->scan_filt_policy;
scanphy->own_addr_type = scanp->own_addr_type;
if (scansm->cur_phy == PHY_NOT_CONFIGURED) {
scansm->cur_phy = i;
/* Take own_addr_type from the first configured PHY.
* Note: All configured PHYs shall have the same own_addr_type
*/
scansm->own_addr_type = scanphy->own_addr_type;
} else {
scansm->next_phy = i;
}
}
rc = ble_ll_scan_sm_start(scansm);
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
if (rc == BLE_ERR_SUCCESS) {
scansm->duration_ticks = dur_ticks;
scansm->period_ticks = period_ticks;
ble_ll_scan_duration_period_timers_restart(scansm);
}
#endif
return rc;
}
/**
* Checks if controller can change the whitelist. If scanning is enabled and
* using the whitelist the controller is not allowed to change the whitelist.
*
* @return int 0: not allowed to change whitelist; 1: change allowed.
*/
int
ble_ll_scan_can_chg_whitelist(void)
{
int rc;
struct ble_ll_scan_sm *scansm;
struct ble_ll_scan_params *params;
scansm = &g_ble_ll_scan_sm;
params = &scansm->phy_data[scansm->cur_phy];
if (scansm->scan_enabled && (params->scan_filt_policy & 1)) {
rc = 0;
} else {
rc = 1;
}
return rc;
}
int
ble_ll_scan_initiator_start(struct hci_create_conn *hcc,
struct ble_ll_scan_sm **sm)
{
struct ble_ll_scan_sm *scansm;
struct ble_ll_scan_params *scanphy;
int rc;
scansm = &g_ble_ll_scan_sm;
scansm->own_addr_type = hcc->own_addr_type;
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
scansm->ext_scanning = 0;
#endif
scansm->cur_phy = PHY_UNCODED;
scansm->next_phy = PHY_NOT_CONFIGURED;
scanphy = &scansm->phy_data[scansm->cur_phy];
scanphy->scan_filt_policy = hcc->filter_policy;
scanphy->scan_itvl = hcc->scan_itvl;
scanphy->scan_window = hcc->scan_window;
scanphy->scan_type = BLE_SCAN_TYPE_INITIATE;
rc = ble_ll_scan_sm_start(scansm);
if (sm == NULL) {
return rc;
}
if (rc == BLE_ERR_SUCCESS) {
*sm = scansm;
} else {
*sm = NULL;
}
return rc;
}
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
int
ble_ll_scan_ext_initiator_start(struct hci_ext_create_conn *hcc,
struct ble_ll_scan_sm **sm)
{
struct ble_ll_scan_sm *scansm;
struct ble_ll_scan_params *uncoded;
struct ble_ll_scan_params *coded;
struct hci_ext_conn_params *params;
int rc;
scansm = &g_ble_ll_scan_sm;
scansm->own_addr_type = hcc->own_addr_type;
scansm->cur_phy = PHY_NOT_CONFIGURED;
scansm->next_phy = PHY_NOT_CONFIGURED;
scansm->ext_scanning = 1;
if (hcc->init_phy_mask & BLE_PHY_MASK_1M) {
params = &hcc->params[0];
uncoded = &scansm->phy_data[PHY_UNCODED];
uncoded->scan_itvl = params->scan_itvl;
uncoded->scan_window = params->scan_window;
uncoded->scan_type = BLE_SCAN_TYPE_INITIATE;
uncoded->scan_filt_policy = hcc->filter_policy;
scansm->cur_phy = PHY_UNCODED;
}
if (hcc->init_phy_mask & BLE_PHY_MASK_CODED) {
params = &hcc->params[2];
coded = &scansm->phy_data[PHY_CODED];
coded->scan_itvl = params->scan_itvl;
coded->scan_window = params->scan_window;
coded->scan_type = BLE_SCAN_TYPE_INITIATE;
coded->scan_filt_policy = hcc->filter_policy;
if (scansm->cur_phy == PHY_NOT_CONFIGURED) {
scansm->cur_phy = PHY_CODED;
} else {
scansm->next_phy = PHY_CODED;
}
}
/* If host request for continuous scan if 2 PHYs are requested, we split
* time on two
*/
if ((scansm->next_phy != PHY_NOT_CONFIGURED) &&
((uncoded->scan_itvl == uncoded->scan_window) ||
(coded->scan_itvl == coded-> scan_window))) {
uncoded->scan_itvl *= 2;
coded-> scan_itvl = uncoded->scan_itvl;
coded->scan_window = uncoded->scan_window;
}
rc = ble_ll_scan_sm_start(scansm);
if (sm == NULL) {
return rc;
}
if (rc == BLE_ERR_SUCCESS) {
*sm = scansm;
} else {
*sm = NULL;
}
return rc;
}
#endif
/**
* Checks to see if the scanner is enabled.
*
* @return int 0: not enabled; enabled otherwise
*/
int
ble_ll_scan_enabled(void)
{
return (int)g_ble_ll_scan_sm.scan_enabled;
}
/**
* Returns the peer resolvable private address of last device connecting to us
*
* @return uint8_t*
*/
uint8_t *
ble_ll_scan_get_peer_rpa(void)
{
struct ble_ll_scan_sm *scansm;
/* XXX: should this go into IRK list or connection? */
scansm = &g_ble_ll_scan_sm;
return scansm->scan_peer_rpa;
}
/**
* Returns the local resolvable private address currently being using by
* the scanner/initiator
*
* @return uint8_t*
*/
uint8_t *
ble_ll_scan_get_local_rpa(void)
{
return g_ble_ll_scan_sm.pdu_data.scana;
}
/**
* Set the Resolvable Private Address in the scanning (or initiating) state
* machine.
*
* XXX: should this go into IRK list or connection?
*
* @param rpa
*/
void
ble_ll_scan_set_peer_rpa(uint8_t *rpa)
{
struct ble_ll_scan_sm *scansm;
scansm = &g_ble_ll_scan_sm;
memcpy(scansm->scan_peer_rpa, rpa, BLE_DEV_ADDR_LEN);
}
struct ble_ll_scan_pdu_data *
ble_ll_scan_get_pdu_data(void)
{
return &g_ble_ll_scan_sm.pdu_data;
}
/* Returns true if whitelist is enabled for scanning */
int
ble_ll_scan_whitelist_enabled(void)
{
struct ble_ll_scan_params *params;
params = &g_ble_ll_scan_sm.phy_data[g_ble_ll_scan_sm.cur_phy];
return params->scan_filt_policy & 1;
}
static void
ble_ll_scan_common_init(void)
{
struct ble_ll_scan_sm *scansm;
struct ble_ll_scan_params *scanp;
int i;
/* Clear state machine in case re-initialized */
scansm = &g_ble_ll_scan_sm;
memset(scansm, 0, sizeof(struct ble_ll_scan_sm));
/* Clear scan parameters in case re-initialized */
memset(g_ble_ll_scan_params, 0, sizeof(g_ble_ll_scan_params));
/* Initialize scanning window end event */
ble_npl_event_init(&scansm->scan_sched_ev, ble_ll_scan_event_proc, scansm);
for (i = 0; i < BLE_LL_SCAN_PHY_NUMBER; i++) {
/* Set all non-zero default parameters */
scanp = &g_ble_ll_scan_params[i];
scanp->scan_itvl = BLE_HCI_SCAN_ITVL_DEF;
scanp->scan_window = BLE_HCI_SCAN_WINDOW_DEF;
}
scansm->phy_data[PHY_UNCODED].phy = BLE_PHY_1M;
#if (BLE_LL_SCAN_PHY_NUMBER == 2)
scansm->phy_data[PHY_CODED].phy = BLE_PHY_CODED;
#endif
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY)
/* Make sure we'll generate new NRPA if necessary */
scansm->scan_nrpa_timer = ble_npl_time_get();
#endif
/* Initialize scanning timer */
os_cputime_timer_init(&scansm->scan_timer, ble_ll_scan_timer_cb, scansm);
/* Initialize extended scan timers */
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
os_cputime_timer_init(&scansm->duration_timer,
ble_ll_scan_duration_timer_cb, scansm);
os_cputime_timer_init(&scansm->period_timer, ble_ll_scan_period_timer_cb,
scansm);
#endif
ble_npl_event_init(&scansm->scan_interrupted_ev, ble_ll_scan_interrupted_event_cb, NULL);
}
/**
* Called when the controller receives the reset command. Resets the
* scanning state machine to its initial state.
*
* @return int
*/
void
ble_ll_scan_reset(void)
{
struct ble_ll_scan_sm *scansm;
scansm = &g_ble_ll_scan_sm;
/* If enabled, stop it. */
if (scansm->scan_enabled) {
ble_ll_scan_sm_stop(0);
}
/* stop extended scan timers */
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
os_cputime_timer_stop(&scansm->duration_timer);
os_cputime_timer_stop(&scansm->period_timer);
#endif
/* Reset duplicate advertisers and those from which we rxd a response */
g_ble_ll_scan_num_rsp_advs = 0;
memset(&g_ble_ll_scan_rsp_advs[0], 0, sizeof(g_ble_ll_scan_rsp_advs));
os_mempool_clear(&g_scan_dup_pool);
TAILQ_INIT(&g_scan_dup_list);
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
/* clear memory pool for AUX scan results */
os_mempool_clear(&ext_adv_pool);
#endif
/* Call the common init function again */
ble_ll_scan_common_init();
}
/**
* ble ll scan init
*
* Initialize a scanner. Must be called before scanning can be started.
* Expected to be called with a un-initialized scanning state machine.
*/
void
ble_ll_scan_init(void)
{
os_error_t err;
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
err = os_mempool_init(&ext_adv_pool,
MYNEWT_VAL(BLE_LL_EXT_ADV_AUX_PTR_CNT),
sizeof (struct ble_ll_aux_data),
ext_adv_mem,
"ble_ll_aux_scan_pool");
BLE_LL_ASSERT(err == 0);
#endif
err = os_mempool_init(&g_scan_dup_pool,
MYNEWT_VAL(BLE_LL_NUM_SCAN_DUP_ADVS),
sizeof(struct ble_ll_scan_dup_entry),
g_scan_dup_mem,
"ble_ll_scan_dup_pool");
BLE_LL_ASSERT(err == 0);
TAILQ_INIT(&g_scan_dup_list);
ble_ll_scan_common_init();
}