blob: 943d666d7febba667adf7a039a691edc2a00cd89 [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 "ble/xcvr.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_hci.h"
#include "controller/ble_ll_adv.h"
#include "controller/ble_ll_sched.h"
#include "controller/ble_ll_scan.h"
#include "controller/ble_ll_whitelist.h"
#include "controller/ble_ll_resolv.h"
#include "controller/ble_ll_trace.h"
#include "ble_ll_conn_priv.h"
/* XXX: TODO
* 1) Need to look at advertising and scan request PDUs. Do I allocate these
* once? Do I use a different pool for smaller ones? Do I statically declare
* them?
* 3) How do features get supported? What happens if device does not support
* advertising? (for example)
* 4) How to determine the advertising interval we will actually use. As of
* now, we set it to max.
* 5) How does the advertising channel tx power get set? I dont implement
* that currently.
*/
/* Scheduling data for secondary channel */
struct ble_ll_adv_aux {
struct ble_ll_sched_item sch;
uint32_t start_time;
uint16_t aux_data_offset;
uint8_t ext_hdr;
uint8_t aux_data_len;
uint8_t payload_len;
};
/*
* Advertising state machine
*
* The advertising state machine data structure.
*
* adv_pdu_len
* The length of the advertising PDU that will be sent. This does not
* include the preamble, access address and CRC.
*
* initiator_addr:
* This is the address that we send in directed advertisements (the
* INITA field). If we are using Privacy this is a RPA that we need to
* generate. We reserve space in the advsm to save time when creating
* the ADV_DIRECT_IND. If own address type is not 2 or 3, this is simply
* the peer address from the set advertising parameters.
*/
struct ble_ll_adv_sm
{
uint8_t adv_enabled;
uint8_t adv_instance;
uint8_t adv_chanmask;
uint8_t adv_filter_policy;
uint8_t own_addr_type;
uint8_t peer_addr_type;
uint8_t adv_chan;
uint8_t adv_pdu_len;
int8_t adv_rpa_index;
int8_t adv_txpwr;
uint16_t flags;
uint16_t props;
uint16_t adv_itvl_min;
uint16_t adv_itvl_max;
uint32_t adv_itvl_usecs;
uint32_t adv_event_start_time;
uint32_t adv_pdu_start_time;
uint32_t adv_end_time;
uint32_t adv_rpa_timer;
uint8_t adva[BLE_DEV_ADDR_LEN];
uint8_t adv_rpa[BLE_DEV_ADDR_LEN];
uint8_t peer_addr[BLE_DEV_ADDR_LEN];
uint8_t initiator_addr[BLE_DEV_ADDR_LEN];
struct os_mbuf *adv_data;
struct os_mbuf *scan_rsp_data;
uint8_t *conn_comp_ev;
struct ble_npl_event adv_txdone_ev;
struct ble_ll_sched_item adv_sch;
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
uint8_t aux_active : 1;
uint8_t aux_index : 1;
uint8_t aux_first_pdu : 1;
uint8_t aux_not_scanned : 1;
struct ble_mbuf_hdr *rx_ble_hdr;
struct os_mbuf **aux_data;
struct ble_ll_adv_aux aux[2];
struct ble_npl_event adv_sec_txdone_ev;
uint16_t duration;
uint16_t adi;
uint8_t adv_secondary_chan;
uint8_t adv_random_addr[BLE_DEV_ADDR_LEN];
uint8_t events_max;
uint8_t events;
uint8_t pri_phy;
uint8_t sec_phy;
#endif
};
#define BLE_LL_ADV_SM_FLAG_TX_ADD 0x0001
#define BLE_LL_ADV_SM_FLAG_RX_ADD 0x0002
#define BLE_LL_ADV_SM_FLAG_SCAN_REQ_NOTIF 0x0004
#define BLE_LL_ADV_SM_FLAG_CONN_RSP_TXD 0x0008
#define BLE_LL_ADV_SM_FLAG_ACTIVE_CHANSET_MASK 0x0030 /* use helpers! */
#define BLE_LL_ADV_SM_FLAG_ADV_DATA_INCOMPLETE 0x0040
#define BLE_LL_ADV_SM_FLAG_CONFIGURED 0x0080
#define BLE_LL_ADV_SM_FLAG_ADV_RPA_TMO 0x0100
#define ADV_DATA_LEN(_advsm) \
((_advsm->adv_data) ? OS_MBUF_PKTLEN(advsm->adv_data) : 0)
#define SCAN_RSP_DATA_LEN(_advsm) \
((_advsm->scan_rsp_data) ? OS_MBUF_PKTLEN(advsm->scan_rsp_data) : 0)
#define AUX_DATA_LEN(_advsm) \
(*(_advsm->aux_data) ? OS_MBUF_PKTLEN(*advsm->aux_data) : 0)
#define AUX_CURRENT(_advsm) (&(_advsm->aux[_advsm->aux_index]))
#define AUX_NEXT(_advsm) (&(_advsm->aux[_advsm->aux_index ^ 1]))
static inline int
ble_ll_adv_active_chanset_is_pri(struct ble_ll_adv_sm *advsm)
{
return (advsm->flags & BLE_LL_ADV_SM_FLAG_ACTIVE_CHANSET_MASK) == 0x10;
}
static inline int
ble_ll_adv_active_chanset_is_sec(struct ble_ll_adv_sm *advsm)
{
return (advsm->flags & BLE_LL_ADV_SM_FLAG_ACTIVE_CHANSET_MASK) == 0x20;
}
static inline void
ble_ll_adv_active_chanset_clear(struct ble_ll_adv_sm *advsm)
{
advsm->flags &= ~BLE_LL_ADV_SM_FLAG_ACTIVE_CHANSET_MASK;
}
static inline void
ble_ll_adv_active_chanset_set_pri(struct ble_ll_adv_sm *advsm)
{
assert((advsm->flags & BLE_LL_ADV_SM_FLAG_ACTIVE_CHANSET_MASK) == 0);
advsm->flags &= ~BLE_LL_ADV_SM_FLAG_ACTIVE_CHANSET_MASK;
advsm->flags |= 0x10;
}
static inline void
ble_ll_adv_active_chanset_set_sec(struct ble_ll_adv_sm *advsm)
{
assert((advsm->flags & BLE_LL_ADV_SM_FLAG_ACTIVE_CHANSET_MASK) == 0);
advsm->flags &= ~BLE_LL_ADV_SM_FLAG_ACTIVE_CHANSET_MASK;
advsm->flags |= 0x20;
}
/* The advertising state machine global object */
struct ble_ll_adv_sm g_ble_ll_adv_sm[BLE_ADV_INSTANCES];
struct ble_ll_adv_sm *g_ble_ll_cur_adv_sm;
static void ble_ll_adv_make_done(struct ble_ll_adv_sm *advsm, struct ble_mbuf_hdr *hdr);
static void ble_ll_adv_sm_init(struct ble_ll_adv_sm *advsm);
static void ble_ll_adv_sm_stop_timeout(struct ble_ll_adv_sm *advsm);
#if (MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) == 1)
static void
ble_ll_adv_rpa_update(struct ble_ll_adv_sm *advsm)
{
if (ble_ll_resolv_gen_rpa(advsm->peer_addr, advsm->peer_addr_type,
advsm->adva, 1)) {
advsm->flags |= BLE_LL_ADV_SM_FLAG_TX_ADD;
} else {
if (advsm->own_addr_type & 1) {
advsm->flags |= BLE_LL_ADV_SM_FLAG_TX_ADD;
} else {
advsm->flags &= ~BLE_LL_ADV_SM_FLAG_TX_ADD;
}
}
if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_DIRECTED) {
if (ble_ll_resolv_gen_rpa(advsm->peer_addr, advsm->peer_addr_type,
advsm->initiator_addr, 0)) {
advsm->flags |= BLE_LL_ADV_SM_FLAG_RX_ADD;
} else {
if (advsm->peer_addr_type & 1) {
advsm->flags |= BLE_LL_ADV_SM_FLAG_RX_ADD;
} else {
advsm->flags &= ~BLE_LL_ADV_SM_FLAG_RX_ADD;
}
}
}
}
/**
* Called to change advertisers ADVA and INITA (for directed advertisements)
* as an advertiser needs to adhere to the resolvable private address generation
* timer.
*
* NOTE: the resolvable private address code uses its own timer to regenerate
* local resolvable private addresses. The advertising code uses its own
* timer to reset the INITA (for directed advertisements). This code also sets
* the appropriate txadd and rxadd bits that will go into the advertisement.
*
* Another thing to note: it is possible that an IRK is all zeroes in the
* resolving list. That is why we need to check if the generated address is
* in fact a RPA as a resolving list entry with all zeroes will use the
* identity address (which may be a private address or public).
*
* @param advsm
*/
void
ble_ll_adv_chk_rpa_timeout(struct ble_ll_adv_sm *advsm)
{
if (advsm->own_addr_type < BLE_HCI_ADV_OWN_ADDR_PRIV_PUB) {
return;
}
if (advsm->flags & BLE_LL_ADV_SM_FLAG_ADV_RPA_TMO) {
ble_ll_adv_rpa_update(advsm);
advsm->flags &= ~BLE_LL_ADV_SM_FLAG_ADV_RPA_TMO;
}
}
void
ble_ll_adv_rpa_timeout(void)
{
struct ble_ll_adv_sm *advsm;
int i;
for (i = 0; i < BLE_ADV_INSTANCES; i++) {
advsm = &g_ble_ll_adv_sm[i];
if (advsm->adv_enabled &&
advsm->own_addr_type > BLE_HCI_ADV_OWN_ADDR_RANDOM) {
/* Mark RPA as timed out so we get a new RPA */
advsm->flags |= BLE_LL_ADV_SM_FLAG_ADV_RPA_TMO;
}
}
}
#endif
/**
* Calculate the first channel that we should advertise upon when we start
* an advertising event.
*
* @param advsm
*
* @return uint8_t The number of the first channel usable for advertising.
*/
static uint8_t
ble_ll_adv_first_chan(struct ble_ll_adv_sm *advsm)
{
uint8_t adv_chan;
/* Set first advertising channel */
if (advsm->adv_chanmask & 0x01) {
adv_chan = BLE_PHY_ADV_CHAN_START;
} else if (advsm->adv_chanmask & 0x02) {
adv_chan = BLE_PHY_ADV_CHAN_START + 1;
} else {
adv_chan = BLE_PHY_ADV_CHAN_START + 2;
}
return adv_chan;
}
/**
* Calculate the final channel that we should advertise upon when we start
* an advertising event.
*
* @param advsm
*
* @return uint8_t The number of the final channel usable for advertising.
*/
static uint8_t
ble_ll_adv_final_chan(struct ble_ll_adv_sm *advsm)
{
uint8_t adv_chan;
if (advsm->adv_chanmask & 0x04) {
adv_chan = BLE_PHY_ADV_CHAN_START + 2;
} else if (advsm->adv_chanmask & 0x02) {
adv_chan = BLE_PHY_ADV_CHAN_START + 1;
} else {
adv_chan = BLE_PHY_ADV_CHAN_START;
}
return adv_chan;
}
/**
* Create the advertising legacy PDU
*
* @param advsm Pointer to advertisement state machine
*/
static uint8_t
ble_ll_adv_legacy_pdu_make(uint8_t *dptr, void *pducb_arg, uint8_t *hdr_byte)
{
struct ble_ll_adv_sm *advsm;
uint8_t adv_data_len;
uint8_t pdulen;
uint8_t pdu_type;
advsm = pducb_arg;
/* assume this is not a direct ind */
adv_data_len = ADV_DATA_LEN(advsm);
pdulen = BLE_DEV_ADDR_LEN + adv_data_len;
if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_DIRECTED) {
pdu_type = BLE_ADV_PDU_TYPE_ADV_DIRECT_IND;
#if (MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CSA2) == 1)
pdu_type |= BLE_ADV_PDU_HDR_CHSEL;
#endif
if (advsm->flags & BLE_LL_ADV_SM_FLAG_RX_ADD) {
pdu_type |= BLE_ADV_PDU_HDR_RXADD_RAND;
}
adv_data_len = 0;
pdulen = BLE_ADV_DIRECT_IND_LEN;
} else if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_CONNECTABLE) {
pdu_type = BLE_ADV_PDU_TYPE_ADV_IND;
#if (MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CSA2) == 1)
pdu_type |= BLE_ADV_PDU_HDR_CHSEL;
#endif
} else if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_SCANNABLE) {
pdu_type = BLE_ADV_PDU_TYPE_ADV_SCAN_IND;
} else {
pdu_type = BLE_ADV_PDU_TYPE_ADV_NONCONN_IND;
}
/* An invalid advertising data length indicates a memory overwrite */
assert(adv_data_len <= BLE_ADV_LEGACY_DATA_MAX_LEN);
/* Set the PDU length in the state machine (includes header) */
advsm->adv_pdu_len = pdulen + BLE_LL_PDU_HDR_LEN;
/* Set TxAdd to random if needed. */
if (advsm->flags & BLE_LL_ADV_SM_FLAG_TX_ADD) {
pdu_type |= BLE_ADV_PDU_HDR_TXADD_RAND;
}
*hdr_byte = pdu_type;
/* Construct advertisement */
memcpy(dptr, advsm->adva, BLE_DEV_ADDR_LEN);
dptr += BLE_DEV_ADDR_LEN;
/* For ADV_DIRECT_IND add inita */
if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_DIRECTED) {
memcpy(dptr, advsm->initiator_addr, BLE_DEV_ADDR_LEN);
}
/* Copy in advertising data, if any */
if (adv_data_len != 0) {
os_mbuf_copydata(advsm->adv_data, 0, adv_data_len, dptr);
}
return pdulen;
}
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
static void
ble_ll_adv_put_aux_ptr(struct ble_ll_adv_sm *advsm, uint32_t offset,
uint8_t *dptr)
{
dptr[0] = advsm->adv_secondary_chan;
if (offset > 245700) {
dptr[0] |= 0x80;
offset = offset / 300;
} else {
offset = offset / 30;
}
dptr[1] = (offset & 0x000000ff);
dptr[2] = ((offset >> 8) & 0x0000001f) | (advsm->sec_phy - 1) << 5; //TODO;
}
/**
* Create the advertising PDU
*/
static uint8_t
ble_ll_adv_pdu_make(uint8_t *dptr, void *pducb_arg, uint8_t *hdr_byte)
{
struct ble_ll_adv_sm *advsm;
uint8_t pdu_type;
uint8_t adv_mode;
uint8_t ext_hdr_len;
uint8_t ext_hdr_flags;
uint32_t offset;
advsm = pducb_arg;
assert(ble_ll_adv_active_chanset_is_pri(advsm));
if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY) {
return ble_ll_adv_legacy_pdu_make(dptr, advsm, hdr_byte);
}
/* only ADV_EXT_IND goes on primary advertising channels */
pdu_type = BLE_ADV_PDU_TYPE_ADV_EXT_IND;
/* Set TxAdd to random if needed. */
if (advsm->flags & BLE_LL_ADV_SM_FLAG_TX_ADD) {
pdu_type |= BLE_ADV_PDU_HDR_TXADD_RAND;
}
*hdr_byte = pdu_type;
adv_mode = 0;
if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_CONNECTABLE) {
adv_mode |= BLE_LL_EXT_ADV_MODE_CONN;
}
if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_SCANNABLE) {
adv_mode |= BLE_LL_EXT_ADV_MODE_SCAN;
}
ext_hdr_len = BLE_LL_EXT_ADV_FLAGS_SIZE + BLE_LL_EXT_ADV_DATA_INFO_SIZE +
BLE_LL_EXT_ADV_AUX_PTR_SIZE;
ext_hdr_flags = (1 << BLE_LL_EXT_ADV_DATA_INFO_BIT) |
(1 << BLE_LL_EXT_ADV_AUX_PTR_BIT);
/* ext hdr len and adv mode */
dptr[0] = ext_hdr_len | (adv_mode << 6);
dptr += 1;
/* ext hdr flags */
dptr[0] = ext_hdr_flags;
dptr += 1;
/* ADI */
dptr[0] = advsm->adi & 0x00ff;
dptr[1] = advsm->adi >> 8;
dptr += BLE_LL_EXT_ADV_DATA_INFO_SIZE;
/* AuxPtr */
if (AUX_CURRENT(advsm)->sch.enqueued) {
offset = os_cputime_ticks_to_usecs(AUX_CURRENT(advsm)->start_time - advsm->adv_pdu_start_time);
} else {
offset = 0;
}
ble_ll_adv_put_aux_ptr(advsm, offset, dptr);
return BLE_LL_EXT_ADV_HDR_LEN + ext_hdr_len;
}
/**
* Create the AUX PDU
*/
static uint8_t
ble_ll_adv_aux_pdu_make(uint8_t *dptr, void *pducb_arg, uint8_t *hdr_byte)
{
struct ble_ll_adv_sm *advsm;
struct ble_ll_adv_aux *aux;
uint8_t adv_mode;
uint8_t pdu_type;
uint8_t ext_hdr_len;
uint32_t offset;
advsm = pducb_arg;
aux = AUX_CURRENT(advsm);
assert(!(advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY));
assert(ble_ll_adv_active_chanset_is_sec(advsm));
/* It's the same for AUX_ADV_IND and AUX_CHAIN_IND */
pdu_type = BLE_ADV_PDU_TYPE_AUX_ADV_IND;
/* Set TxAdd to random if needed. */
if (advsm->flags & BLE_LL_ADV_SM_FLAG_TX_ADD) {
pdu_type |= BLE_ADV_PDU_HDR_TXADD_RAND;
}
/* We do not create scannable PDUs here - this is handled separately */
adv_mode = 0;
if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_CONNECTABLE) {
adv_mode |= BLE_LL_EXT_ADV_MODE_CONN;
}
ext_hdr_len = aux->payload_len - BLE_LL_EXT_ADV_HDR_LEN - aux->aux_data_len;
dptr[0] = (adv_mode << 6) | ext_hdr_len;
dptr += 1;
dptr[0] = aux->ext_hdr;
dptr += 1;
if (aux->ext_hdr & (1 << BLE_LL_EXT_ADV_ADVA_BIT)) {
memcpy(dptr, advsm->adva, BLE_LL_EXT_ADV_ADVA_SIZE);
dptr += BLE_LL_EXT_ADV_ADVA_SIZE;
}
if (aux->ext_hdr & (1 << BLE_LL_EXT_ADV_TARGETA_BIT)) {
memcpy(dptr, advsm->initiator_addr, BLE_LL_EXT_ADV_TARGETA_SIZE);
dptr += BLE_LL_EXT_ADV_TARGETA_SIZE;
/* Set RxAdd to random if needed. */
if (advsm->flags & BLE_LL_ADV_SM_FLAG_RX_ADD) {
pdu_type |= BLE_ADV_PDU_HDR_RXADD_RAND;
}
}
if (aux->ext_hdr & (1 << BLE_LL_EXT_ADV_DATA_INFO_BIT)) {
dptr[0] = advsm->adi & 0x00ff;
dptr[1] = advsm->adi >> 8;
dptr += BLE_LL_EXT_ADV_DATA_INFO_SIZE;
}
if (aux->ext_hdr & (1 << BLE_LL_EXT_ADV_AUX_PTR_BIT)) {
if (!AUX_NEXT(advsm)->sch.enqueued) {
/*
* Trim data here in case we do not have next aux scheduled. This
* can happen if next aux was outside advertising set period and
* was removed from scheduler.
*/
offset = 0;
} else if (advsm->rx_ble_hdr) {
offset = os_cputime_ticks_to_usecs(AUX_NEXT(advsm)->start_time - advsm->rx_ble_hdr->beg_cputime);
offset -= (advsm->rx_ble_hdr->rem_usecs + ble_ll_pdu_tx_time_get(12, advsm->sec_phy) + BLE_LL_IFS);
} else {
offset = os_cputime_ticks_to_usecs(AUX_NEXT(advsm)->start_time - aux->start_time);
}
ble_ll_adv_put_aux_ptr(advsm, offset, dptr);
dptr += BLE_LL_EXT_ADV_AUX_PTR_SIZE;
}
if (aux->ext_hdr & (1 << BLE_LL_EXT_ADV_TX_POWER_BIT)) {
dptr[0] = advsm->adv_txpwr;
dptr += BLE_LL_EXT_ADV_TX_POWER_SIZE;
}
if (aux->aux_data_len) {
os_mbuf_copydata(*advsm->aux_data, aux->aux_data_offset,
aux->aux_data_len, dptr);
}
*hdr_byte = pdu_type;
return aux->payload_len;
}
static uint8_t
ble_ll_adv_aux_scannable_pdu_make(uint8_t *dptr, void *pducb_arg, uint8_t *hdr_byte)
{
struct ble_ll_adv_sm *advsm;
uint8_t pdu_type;
uint8_t *ext_hdr_len;
uint8_t *ext_hdr;
uint8_t pdulen;
advsm = pducb_arg;
assert(!(advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY));
assert(advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_SCANNABLE);
assert(advsm->aux_first_pdu);
assert(ble_ll_adv_active_chanset_is_sec(advsm));
pdu_type = BLE_ADV_PDU_TYPE_AUX_ADV_IND;
/* Set TxAdd to random if needed. */
if (advsm->flags & BLE_LL_ADV_SM_FLAG_TX_ADD) {
pdu_type |= BLE_ADV_PDU_HDR_TXADD_RAND;
}
ext_hdr_len = &dptr[0];
ext_hdr = &dptr[1];
dptr += 2;
/* Flags always */
*ext_hdr_len = BLE_LL_EXT_ADV_FLAGS_SIZE;
*ext_hdr = 0;
/* AdvA always */
*ext_hdr_len += BLE_LL_EXT_ADV_ADVA_SIZE;
*ext_hdr |= (1 << BLE_LL_EXT_ADV_ADVA_BIT);
memcpy(dptr, advsm->adva, BLE_LL_EXT_ADV_ADVA_SIZE);
dptr += BLE_LL_EXT_ADV_ADVA_SIZE;
/* TargetA only for directed */
if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_DIRECTED) {
*ext_hdr_len += BLE_LL_EXT_ADV_TARGETA_SIZE;
*ext_hdr |= (1 << BLE_LL_EXT_ADV_TARGETA_BIT);
memcpy(dptr, advsm->initiator_addr, BLE_LL_EXT_ADV_TARGETA_SIZE);
dptr += BLE_LL_EXT_ADV_TARGETA_SIZE;
/* Set RxAdd to random if needed. */
if (advsm->flags & BLE_LL_ADV_SM_FLAG_RX_ADD) {
pdu_type |= BLE_ADV_PDU_HDR_RXADD_RAND;
}
}
/* ADI always */
*ext_hdr_len += BLE_LL_EXT_ADV_DATA_INFO_SIZE;
*ext_hdr |= (1 << BLE_LL_EXT_ADV_DATA_INFO_BIT);
dptr[0] = advsm->adi & 0x00ff;
dptr[1] = advsm->adi >> 8;
dptr += BLE_LL_EXT_ADV_DATA_INFO_SIZE;
/* TxPower if configured */
if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_INC_TX_PWR) {
*ext_hdr_len += BLE_LL_EXT_ADV_TX_POWER_SIZE;
*ext_hdr |= (1 << BLE_LL_EXT_ADV_TX_POWER_BIT);
dptr[0] = advsm->adv_txpwr;
dptr += BLE_LL_EXT_ADV_TX_POWER_SIZE;
}
pdulen = BLE_LL_EXT_ADV_HDR_LEN + *ext_hdr_len;
*hdr_byte = pdu_type;
*ext_hdr_len |= (BLE_LL_EXT_ADV_MODE_SCAN << 6);
return pdulen;
}
#endif
static uint8_t
ble_ll_adv_scan_rsp_legacy_pdu_make(uint8_t *dptr, void *pducb_arg,
uint8_t *hdr_byte)
{
struct ble_ll_adv_sm *advsm;
uint8_t scan_rsp_len;
uint8_t pdulen;
uint8_t hdr;
advsm = pducb_arg;
/* Make sure that the length is valid */
scan_rsp_len = SCAN_RSP_DATA_LEN(advsm);
assert(scan_rsp_len <= BLE_SCAN_RSP_LEGACY_DATA_MAX_LEN);
/* Set BLE transmit header */
pdulen = BLE_DEV_ADDR_LEN + scan_rsp_len;
hdr = BLE_ADV_PDU_TYPE_SCAN_RSP;
if (advsm->flags & BLE_LL_ADV_SM_FLAG_TX_ADD) {
hdr |= BLE_ADV_PDU_HDR_TXADD_RAND;
}
*hdr_byte = hdr;
/*
* The adva in this packet will be the same one that was being advertised
* and is based on the peer identity address in the set advertising
* parameters. If a different peer sends us a scan request (for some reason)
* we will reply with an adva that was not generated based on the local irk
* of the peer sending the scan request.
*/
/* Construct scan response */
memcpy(dptr, advsm->adva, BLE_DEV_ADDR_LEN);
if (scan_rsp_len != 0) {
os_mbuf_copydata(advsm->scan_rsp_data, 0, scan_rsp_len,
dptr + BLE_DEV_ADDR_LEN);
}
return pdulen;
}
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
/**
* Create a scan response PDU
*
* @param advsm
*/
static uint8_t
ble_ll_adv_scan_rsp_pdu_make(uint8_t *dptr, void *pducb_arg, uint8_t *hdr_byte)
{
struct ble_ll_adv_sm *advsm;
advsm = pducb_arg;
if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY) {
return ble_ll_adv_scan_rsp_legacy_pdu_make(dptr, pducb_arg, hdr_byte);
}
return ble_ll_adv_aux_pdu_make(dptr, pducb_arg, hdr_byte);
}
struct aux_conn_rsp_data {
struct ble_ll_adv_sm *advsm;
uint8_t *peer;
uint8_t rxadd;
};
/**
* Create a AUX connect response PDU
*
* @param advsm
*/
static uint8_t
ble_ll_adv_aux_conn_rsp_pdu_make(uint8_t *dptr, void *pducb_arg,
uint8_t *hdr_byte)
{
struct aux_conn_rsp_data *rsp_data;
uint8_t pdulen;
uint8_t ext_hdr_len;
uint8_t ext_hdr_flags;
uint8_t hdr;
rsp_data = pducb_arg;
/* flags,AdvA and TargetA */
ext_hdr_len = BLE_LL_EXT_ADV_FLAGS_SIZE + BLE_LL_EXT_ADV_ADVA_SIZE +
BLE_LL_EXT_ADV_TARGETA_SIZE;
ext_hdr_flags = (1 << BLE_LL_EXT_ADV_ADVA_BIT);
ext_hdr_flags |= (1 << BLE_LL_EXT_ADV_TARGETA_BIT);
pdulen = BLE_LL_EXT_ADV_HDR_LEN + ext_hdr_len;
/* Set BLE transmit header */
hdr = BLE_ADV_PDU_TYPE_AUX_CONNECT_RSP;
if (rsp_data->rxadd) {
hdr |= BLE_ADV_PDU_HDR_RXADD_MASK;
}
if (rsp_data->advsm->flags & BLE_LL_ADV_SM_FLAG_TX_ADD) {
hdr |= BLE_ADV_PDU_HDR_TXADD_MASK;
}
*hdr_byte = hdr;
/* ext hdr len and adv mode (00b) */
dptr[0] = ext_hdr_len;
dptr += 1;
/* ext hdr flags */
dptr[0] = ext_hdr_flags;
dptr += 1;
memcpy(dptr, rsp_data->advsm->adva, BLE_LL_EXT_ADV_ADVA_SIZE);
dptr += BLE_LL_EXT_ADV_ADVA_SIZE;
memcpy(dptr, rsp_data->peer, BLE_LL_EXT_ADV_TARGETA_SIZE);
dptr += BLE_LL_EXT_ADV_ADVA_SIZE;
return pdulen;
}
#endif
/**
* Called to indicate the advertising event is over.
*
* Context: Interrupt
*
* @param advsm
*
*/
static void
ble_ll_adv_tx_done(void *arg)
{
struct ble_ll_adv_sm *advsm;
/* XXX: for now, reset power to max after advertising */
ble_phy_txpwr_set(MYNEWT_VAL(BLE_LL_TX_PWR_DBM));
advsm = (struct ble_ll_adv_sm *)arg;
ble_ll_trace_u32x2(BLE_LL_TRACE_ID_ADV_TXDONE, advsm->adv_instance,
advsm->flags & BLE_LL_ADV_SM_FLAG_ACTIVE_CHANSET_MASK);
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
if (ble_ll_adv_active_chanset_is_pri(advsm)) {
ble_npl_eventq_put(&g_ble_ll_data.ll_evq, &advsm->adv_txdone_ev);
} else if (ble_ll_adv_active_chanset_is_sec(advsm)) {
ble_npl_eventq_put(&g_ble_ll_data.ll_evq, &advsm->adv_sec_txdone_ev);
} else {
assert(0);
}
#else
assert(ble_ll_adv_active_chanset_is_pri(advsm));
ble_npl_eventq_put(&g_ble_ll_data.ll_evq, &advsm->adv_txdone_ev);
#endif
ble_ll_state_set(BLE_LL_STATE_STANDBY);
ble_ll_adv_active_chanset_clear(advsm);
/* We no longer have a current state machine */
g_ble_ll_cur_adv_sm = NULL;
}
/*
* Called when an advertising event has been removed from the scheduler
* without being run.
*/
void
ble_ll_adv_event_rmvd_from_sched(struct ble_ll_adv_sm *advsm)
{
/*
* Need to set advertising channel to final chan so new event gets
* scheduled.
*/
advsm->adv_chan = ble_ll_adv_final_chan(advsm);
ble_npl_eventq_put(&g_ble_ll_data.ll_evq, &advsm->adv_txdone_ev);
}
/**
* This is the scheduler callback (called from interrupt context) which
* transmits an advertisement.
*
* Context: Interrupt (scheduler)
*
* @param sch
*
* @return int
*/
static int
ble_ll_adv_tx_start_cb(struct ble_ll_sched_item *sch)
{
int rc;
uint8_t end_trans;
uint32_t txstart;
struct ble_ll_adv_sm *advsm;
/* Get the state machine for the event */
advsm = (struct ble_ll_adv_sm *)sch->cb_arg;
/* Set the current advertiser */
g_ble_ll_cur_adv_sm = advsm;
ble_ll_adv_active_chanset_set_pri(advsm);
/* Set the power */
ble_phy_txpwr_set(advsm->adv_txpwr);
/* Set channel */
rc = ble_phy_setchan(advsm->adv_chan, BLE_ACCESS_ADDR_ADV, BLE_LL_CRCINIT_ADV);
assert(rc == 0);
#if (BLE_LL_BT5_PHY_SUPPORTED == 1)
/* Set phy mode */
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY) {
ble_phy_mode_set(BLE_PHY_MODE_1M, BLE_PHY_MODE_1M);
} else {
ble_phy_mode_set(advsm->pri_phy, advsm->pri_phy);
}
#else
ble_phy_mode_set(BLE_PHY_MODE_1M, BLE_PHY_MODE_1M);
#endif
#endif
/* Set transmit start time. */
txstart = sch->start_time + g_ble_ll_sched_offset_ticks;
rc = ble_phy_tx_set_start_time(txstart, sch->remainder);
if (rc) {
STATS_INC(ble_ll_stats, adv_late_starts);
goto adv_tx_done;
}
#if (MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) == 1)
/* XXX: automatically do this in the phy based on channel? */
ble_phy_encrypt_disable();
#endif
#if (MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) == 1)
advsm->adv_rpa_index = -1;
if (ble_ll_resolv_enabled()) {
ble_phy_resolv_list_enable();
} else {
ble_phy_resolv_list_disable();
}
#endif
/* We switch to RX after connectable or scannable legacy packets. */
if ((advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY) &&
((advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_CONNECTABLE) ||
(advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_SCANNABLE))) {
end_trans = BLE_PHY_TRANSITION_TX_RX;
ble_phy_set_txend_cb(NULL, NULL);
} else {
end_trans = BLE_PHY_TRANSITION_NONE;
ble_phy_set_txend_cb(ble_ll_adv_tx_done, advsm);
}
/* Transmit advertisement */
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
rc = ble_phy_tx(ble_ll_adv_pdu_make, advsm, end_trans);
#else
rc = ble_phy_tx(ble_ll_adv_legacy_pdu_make, advsm, end_trans);
#endif
if (rc) {
goto adv_tx_done;
}
/* Enable/disable whitelisting based on filter policy */
if (advsm->adv_filter_policy != BLE_HCI_ADV_FILT_NONE) {
ble_ll_whitelist_enable();
} else {
ble_ll_whitelist_disable();
}
/* Set link layer state to advertising */
ble_ll_state_set(BLE_LL_STATE_ADV);
/* Count # of adv. sent */
STATS_INC(ble_ll_stats, adv_txg);
return BLE_LL_SCHED_STATE_RUNNING;
adv_tx_done:
ble_ll_adv_tx_done(advsm);
return BLE_LL_SCHED_STATE_DONE;
}
static void
ble_ll_adv_set_sched(struct ble_ll_adv_sm *advsm)
{
uint32_t max_usecs;
struct ble_ll_sched_item *sch;
sch = &advsm->adv_sch;
sch->cb_arg = advsm;
sch->sched_cb = ble_ll_adv_tx_start_cb;
sch->sched_type = BLE_LL_SCHED_TYPE_ADV;
/* Set end time to maximum time this schedule item may take */
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY) {
max_usecs = ble_ll_pdu_tx_time_get(advsm->adv_pdu_len, BLE_PHY_MODE_1M);
if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_DIRECTED) {
max_usecs += BLE_LL_SCHED_DIRECT_ADV_MAX_USECS;
} else if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_CONNECTABLE) {
max_usecs += BLE_LL_SCHED_ADV_MAX_USECS;
}
} else {
/*
* In ADV_EXT_IND we always set only ADI and AUX so the payload length
* is always 7 bytes.
*/
max_usecs = ble_ll_pdu_tx_time_get(7, advsm->pri_phy);
}
#else
max_usecs = ble_ll_pdu_tx_time_get(advsm->adv_pdu_len, BLE_PHY_MODE_1M);
if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_DIRECTED) {
max_usecs += BLE_LL_SCHED_DIRECT_ADV_MAX_USECS;
} else if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_CONNECTABLE) {
max_usecs += BLE_LL_SCHED_ADV_MAX_USECS;
}
#endif
sch->start_time = advsm->adv_pdu_start_time - g_ble_ll_sched_offset_ticks;
sch->remainder = 0;
sch->end_time = advsm->adv_pdu_start_time +
ble_ll_usecs_to_ticks_round_up(max_usecs);
}
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
static int
ble_ll_adv_secondary_tx_start_cb(struct ble_ll_sched_item *sch)
{
int rc;
uint8_t end_trans;
uint32_t txstart;
struct ble_ll_adv_sm *advsm;
ble_phy_tx_pducb_t pducb;
/* Get the state machine for the event */
advsm = (struct ble_ll_adv_sm *)sch->cb_arg;
/* Set the current advertiser */
g_ble_ll_cur_adv_sm = advsm;
ble_ll_adv_active_chanset_set_sec(advsm);
/* Set the power */
ble_phy_txpwr_set(advsm->adv_txpwr);
/* Set channel */
rc = ble_phy_setchan(advsm->adv_secondary_chan, BLE_ACCESS_ADDR_ADV,
BLE_LL_CRCINIT_ADV);
assert(rc == 0);
#if (BLE_LL_BT5_PHY_SUPPORTED == 1)
/* Set phy mode */
ble_phy_mode_set(advsm->sec_phy, advsm->sec_phy);
#endif
/* Set transmit start time. */
txstart = sch->start_time + g_ble_ll_sched_offset_ticks;
rc = ble_phy_tx_set_start_time(txstart, sch->remainder);
if (rc) {
STATS_INC(ble_ll_stats, adv_late_starts);
goto adv_tx_done;
}
#if (MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) == 1)
ble_phy_encrypt_disable();
#endif
#if (MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) == 1)
advsm->adv_rpa_index = -1;
if (ble_ll_resolv_enabled()) {
ble_phy_resolv_list_enable();
} else {
ble_phy_resolv_list_disable();
}
#endif
/* Set phy mode based on type of advertisement */
if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_CONNECTABLE) {
end_trans = BLE_PHY_TRANSITION_TX_RX;
ble_phy_set_txend_cb(NULL, NULL);
pducb = ble_ll_adv_aux_pdu_make;
} else if ((advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_SCANNABLE) &&
advsm->aux_first_pdu) {
end_trans = BLE_PHY_TRANSITION_TX_RX;
ble_phy_set_txend_cb(NULL, NULL);
pducb = ble_ll_adv_aux_scannable_pdu_make;
} else {
end_trans = BLE_PHY_TRANSITION_NONE;
ble_phy_set_txend_cb(ble_ll_adv_tx_done, advsm);
pducb = ble_ll_adv_aux_pdu_make;
}
/* Transmit advertisement */
rc = ble_phy_tx(pducb, advsm, end_trans);
if (rc) {
goto adv_tx_done;
}
/* Enable/disable whitelisting based on filter policy */
if (advsm->adv_filter_policy != BLE_HCI_ADV_FILT_NONE) {
ble_ll_whitelist_enable();
} else {
ble_ll_whitelist_disable();
}
/* Set link layer state to advertising */
ble_ll_state_set(BLE_LL_STATE_ADV);
/* Count # of adv. sent */
STATS_INC(ble_ll_stats, adv_txg);
return BLE_LL_SCHED_STATE_RUNNING;
adv_tx_done:
ble_ll_adv_tx_done(advsm);
return BLE_LL_SCHED_STATE_DONE;
}
static uint8_t
ble_ll_adv_aux_scannable_pdu_payload_len(struct ble_ll_adv_sm *advsm)
{
uint8_t len;
/* Flags, AdvA and ADI always */
len = BLE_LL_EXT_ADV_HDR_LEN + BLE_LL_EXT_ADV_FLAGS_SIZE +
BLE_LL_EXT_ADV_ADVA_SIZE + BLE_LL_EXT_ADV_DATA_INFO_SIZE;
/* TargetA only for directed */
if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_DIRECTED) {
len += BLE_LL_EXT_ADV_TARGETA_SIZE;
}
/* TxPower if configured */
if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_INC_TX_PWR) {
len += BLE_LL_EXT_ADV_TX_POWER_SIZE;
}
return len;
}
static void
ble_ll_adv_aux_calculate(struct ble_ll_adv_sm *advsm,
struct ble_ll_adv_aux *aux, uint16_t aux_data_offset)
{
uint16_t rem_aux_data_len;
uint8_t hdr_len;
bool chainable;
assert(!aux->sch.enqueued);
assert((AUX_DATA_LEN(advsm) > aux_data_offset) ||
(AUX_DATA_LEN(advsm) == 0 && aux_data_offset == 0));
aux->aux_data_offset = aux_data_offset;
aux->aux_data_len = 0;
aux->payload_len = 0;
aux->ext_hdr = 0;
rem_aux_data_len = AUX_DATA_LEN(advsm) - aux_data_offset;
chainable = !(advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_CONNECTABLE);
hdr_len = BLE_LL_EXT_ADV_HDR_LEN + BLE_LL_EXT_ADV_FLAGS_SIZE;
if (!(advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_SCANNABLE)) {
/* Flags and ADI */
aux->ext_hdr |= (1 << BLE_LL_EXT_ADV_DATA_INFO_BIT);
hdr_len += BLE_LL_EXT_ADV_DATA_INFO_SIZE;
}
/* AdvA for 1st PDU in chain (i.e. AUX_ADV_IND or AUX_SCAN_RSP) */
if (aux_data_offset == 0) {
aux->ext_hdr |= (1 << BLE_LL_EXT_ADV_ADVA_BIT);
hdr_len += BLE_LL_EXT_ADV_ADVA_SIZE;
}
/* TargetA for directed connectable */
if ((advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_DIRECTED) &&
(advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_CONNECTABLE)) {
aux->ext_hdr |= (1 << BLE_LL_EXT_ADV_TARGETA_BIT);
hdr_len += BLE_LL_EXT_ADV_TARGETA_SIZE;
}
/* TxPower if configured */
if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_INC_TX_PWR) {
aux->ext_hdr |= (1 << BLE_LL_EXT_ADV_TX_POWER_BIT);
hdr_len += BLE_LL_EXT_ADV_TX_POWER_SIZE;
}
/* AdvData always */
aux->aux_data_len = min(BLE_LL_MAX_PAYLOAD_LEN - hdr_len, rem_aux_data_len);
/* AuxPtr if there are more AdvData remaining that we can fit here */
if (chainable && (rem_aux_data_len > aux->aux_data_len)) {
aux->ext_hdr |= (1 << BLE_LL_EXT_ADV_AUX_PTR_BIT);
hdr_len += BLE_LL_EXT_ADV_AUX_PTR_SIZE;
aux->aux_data_len -= BLE_LL_EXT_ADV_AUX_PTR_SIZE;
/* PDU payload should be full if chained */
assert(hdr_len + aux->aux_data_len == BLE_LL_MAX_PAYLOAD_LEN);
}
aux->payload_len = hdr_len + aux->aux_data_len;
}
static void
ble_ll_adv_aux_scheduled(struct ble_ll_adv_sm *advsm, uint32_t sch_start,
void *arg)
{
struct ble_ll_adv_aux *aux = arg;
aux->start_time = sch_start + g_ble_ll_sched_offset_ticks;
}
static void
ble_ll_adv_aux_schedule_next(struct ble_ll_adv_sm *advsm)
{
struct ble_ll_adv_aux *aux;
struct ble_ll_adv_aux *aux_next;
struct ble_ll_sched_item *sch;
uint16_t rem_aux_data_len;
uint16_t next_aux_data_offset;
uint32_t max_usecs;
assert(advsm->aux_active);
aux = AUX_CURRENT(advsm);
aux_next = AUX_NEXT(advsm);
assert(!aux_next->sch.enqueued);
/*
* Do not schedule next aux if current aux is no longer scheduled since we
* do not have reference time for scheduling.
*/
if (!aux->sch.enqueued) {
return;
}
/*
* Do not schedule next aux if current aux does not have AuxPtr in extended
* header as this means we do not need subsequent ADV_CHAIN_IND to be sent.
*/
if (!(aux->ext_hdr & (1 << BLE_LL_EXT_ADV_AUX_PTR_BIT))) {
return;
}
next_aux_data_offset = aux->aux_data_offset + aux->aux_data_len;
assert(AUX_DATA_LEN(advsm) >= next_aux_data_offset);
rem_aux_data_len = AUX_DATA_LEN(advsm) - next_aux_data_offset;
assert(rem_aux_data_len > 0);
ble_ll_adv_aux_calculate(advsm, aux_next, next_aux_data_offset);
max_usecs = ble_ll_pdu_tx_time_get(aux_next->payload_len, advsm->sec_phy);
aux_next->start_time = aux->sch.end_time +
ble_ll_usecs_to_ticks_round_up(BLE_LL_MAFS);
sch = &aux_next->sch;
sch->start_time = aux_next->start_time - g_ble_ll_sched_offset_ticks;
sch->remainder = 0;
sch->end_time = aux_next->start_time +
ble_ll_usecs_to_ticks_round_up(max_usecs);
ble_ll_sched_adv_new(&aux_next->sch, ble_ll_adv_aux_scheduled, aux_next);
/*
* In case duration is set for advertising set we need to check if newly
* scheduled aux will fit inside duration. If not, remove it from scheduler
* so advertising will stop after current aux.
*/
if (advsm->duration && (aux_next->sch.end_time > advsm->adv_end_time)) {
ble_ll_sched_rmv_elem(&aux_next->sch);
}
}
static void
ble_ll_adv_aux_schedule_first(struct ble_ll_adv_sm *advsm)
{
struct ble_ll_adv_aux *aux;
struct ble_ll_sched_item *sch;
uint32_t max_usecs;
assert(!advsm->aux_active);
assert(!advsm->aux[0].sch.enqueued);
assert(!advsm->aux[1].sch.enqueued);
advsm->aux_active = 1;
advsm->aux_index = 0;
advsm->aux_first_pdu = 1;
advsm->aux_not_scanned = 0;
aux = AUX_CURRENT(advsm);
ble_ll_adv_aux_calculate(advsm, aux, 0);
/* TODO we could use CSA2 for this
* (will be needed for periodic advertising anyway)
*/
advsm->adv_secondary_chan = rand() % BLE_PHY_NUM_DATA_CHANS;
/* Set end time to maximum time this schedule item may take */
if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_CONNECTABLE) {
max_usecs = ble_ll_pdu_tx_time_get(aux->payload_len, advsm->sec_phy) +
BLE_LL_IFS +
/* AUX_CONN_REQ */
ble_ll_pdu_tx_time_get(34 + 14, advsm->sec_phy) +
BLE_LL_IFS +
/* AUX_CONN_RSP */
ble_ll_pdu_tx_time_get(14, advsm->sec_phy);
} else if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_SCANNABLE) {
/* Scheduled aux is calculated for AUX_SCAN_RSP, 1st aux is created separately */
max_usecs = ble_ll_pdu_tx_time_get(ble_ll_adv_aux_scannable_pdu_payload_len(advsm),
advsm->sec_phy) +
BLE_LL_IFS +
/* AUX_SCAN_REQ */
ble_ll_pdu_tx_time_get(12, advsm->sec_phy) +
BLE_LL_IFS +
/* AUX_SCAN_RSP */
ble_ll_pdu_tx_time_get(aux->payload_len, advsm->sec_phy);
} else {
max_usecs = ble_ll_pdu_tx_time_get(aux->payload_len, advsm->sec_phy);
}
sch = &aux->sch;
sch->start_time = aux->start_time - g_ble_ll_sched_offset_ticks;
sch->remainder = 0;
sch->end_time = aux->start_time + ble_ll_usecs_to_ticks_round_up(max_usecs);
ble_ll_sched_adv_new(sch, ble_ll_adv_aux_scheduled, aux);
}
static void
ble_ll_adv_aux_set_start_time(struct ble_ll_adv_sm *advsm)
{
static const uint8_t bits[8] = {0, 1, 1, 2, 1, 2, 2, 3};
struct ble_ll_sched_item *sched = &advsm->adv_sch;
uint32_t adv_pdu_dur;
uint32_t adv_event_dur;
uint8_t chans;
assert(!advsm->aux_active);
assert(!advsm->aux[0].sch.enqueued);
assert(!advsm->aux[1].sch.enqueued);
assert(advsm->adv_chanmask > 0 &&
advsm->adv_chanmask <= BLE_HCI_ADV_CHANMASK_DEF);
chans = bits[advsm->adv_chanmask];
/*
* We want to schedule auxiliary packet as soon as possible after the end
* of advertising event, but no sooner than T_MAFS. The interval between
* advertising packets is 250 usecs (8.19 ticks) on LE Coded and a bit less
* on 1M, but it can vary a bit due to scheduling which we can't really
* control. Since we round ticks up for both interval and T_MAFS, we still
* have some margin here. The worst thing that can happen is that we skip
* last advertising packet which is not a bit problem so leave it as-is, no
* need to make code more complicated.
*/
/*
* XXX: this could be improved if phy has TX-TX transition with controlled
* or predefined interval, but since it makes advertising code even
* more complicated let's skip it for now...
*/
adv_pdu_dur = (int32_t)(sched->end_time - sched->start_time) -
g_ble_ll_sched_offset_ticks;
/* 9 is 8.19 ticks rounded up - see comment above */
adv_event_dur = (adv_pdu_dur * chans) + (9 * (chans - 1));
advsm->aux[0].start_time = advsm->adv_event_start_time + adv_event_dur +
ble_ll_usecs_to_ticks_round_up(BLE_LL_MAFS);
}
static void
ble_ll_adv_aux_schedule(struct ble_ll_adv_sm *advsm)
{
/*
* For secondary channel we always start by scheduling two consecutive
* auxiliary packets at once. Then, after sending one packet we try to
* schedule another one as long as there are some data left to send. This
* is to make sure we can always calculate AuxPtr to subsequent packet
* without need to scheduled it in an interrupt.
*/
ble_ll_adv_aux_set_start_time(advsm);
ble_ll_adv_aux_schedule_first(advsm);
ble_ll_adv_aux_schedule_next(advsm);
/*
* In case duration is set for advertising set we need to check if at least
* 1st aux will fit inside duration. If not, stop advertising now so we do
* not start extended advertising event which we cannot finish in time.
*/
if (advsm->duration &&
(AUX_CURRENT(advsm)->sch.end_time > advsm->adv_end_time)) {
ble_ll_adv_sm_stop_timeout(advsm);
}
}
#endif
/**
* Called when advertising need to be halted. This normally should not be called
* and is only called when a scheduled item executes but advertising is still
* running.
*
* Context: Interrupt
*/
void
ble_ll_adv_halt(void)
{
struct ble_ll_adv_sm *advsm;
if (g_ble_ll_cur_adv_sm != NULL) {
advsm = g_ble_ll_cur_adv_sm;
ble_ll_trace_u32(BLE_LL_TRACE_ID_ADV_HALT, advsm->adv_instance);
ble_phy_txpwr_set(MYNEWT_VAL(BLE_LL_TX_PWR_DBM));
ble_npl_eventq_put(&g_ble_ll_data.ll_evq, &advsm->adv_txdone_ev);
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
if (!(advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY)) {
ble_npl_eventq_put(&g_ble_ll_data.ll_evq, &advsm->adv_sec_txdone_ev);
}
#endif
ble_ll_state_set(BLE_LL_STATE_STANDBY);
ble_ll_adv_active_chanset_clear(g_ble_ll_cur_adv_sm);
g_ble_ll_cur_adv_sm = NULL;
} else {
ble_ll_trace_u32(BLE_LL_TRACE_ID_ADV_HALT, UINT32_MAX);
}
}
/**
* Called by the HCI command parser when a set advertising parameters command
* has been received.
*
* Context: Link Layer task (HCI command parser)
*
* @param cmd
*
* @return int
*/
int
ble_ll_adv_set_adv_params(uint8_t *cmd)
{
uint8_t adv_type;
uint8_t adv_filter_policy;
uint8_t adv_chanmask;
uint8_t own_addr_type;
uint8_t peer_addr_type;
uint16_t adv_itvl_min;
uint16_t adv_itvl_max;
struct ble_ll_adv_sm *advsm;
uint16_t props;
advsm = &g_ble_ll_adv_sm[0];
if (advsm->adv_enabled) {
return BLE_ERR_CMD_DISALLOWED;
}
/* Make sure intervals are OK (along with advertising type */
adv_itvl_min = get_le16(cmd);
adv_itvl_max = get_le16(cmd + 2);
adv_type = cmd[4];
/*
* Get the filter policy now since we will ignore it if we are doing
* directed advertising
*/
adv_filter_policy = cmd[14];
switch (adv_type) {
case BLE_HCI_ADV_TYPE_ADV_DIRECT_IND_HD:
adv_filter_policy = BLE_HCI_ADV_FILT_NONE;
memcpy(advsm->peer_addr, cmd + 7, BLE_DEV_ADDR_LEN);
/* Ignore min/max interval */
adv_itvl_min = 0;
adv_itvl_max = 0;
props = BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY_HD_DIR ;
break;
case BLE_HCI_ADV_TYPE_ADV_DIRECT_IND_LD:
adv_filter_policy = BLE_HCI_ADV_FILT_NONE;
memcpy(advsm->peer_addr, cmd + 7, BLE_DEV_ADDR_LEN);
props = BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY_LD_DIR ;
break;
case BLE_HCI_ADV_TYPE_ADV_IND:
props = BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY_IND;
break;
case BLE_HCI_ADV_TYPE_ADV_NONCONN_IND:
props = BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY_NONCONN;
break;
case BLE_HCI_ADV_TYPE_ADV_SCAN_IND:
props = BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY_SCAN;
break;
default:
return BLE_ERR_INV_HCI_CMD_PARMS;
}
/* Make sure intervals values are valid
* (HD directed advertising ignores those parameters)
*/
if (!(props & BLE_HCI_LE_SET_EXT_ADV_PROP_HD_DIRECTED)) {
if ((adv_itvl_min > adv_itvl_max) ||
(adv_itvl_min < BLE_HCI_ADV_ITVL_MIN) ||
(adv_itvl_min > BLE_HCI_ADV_ITVL_MAX) ||
(adv_itvl_max < BLE_HCI_ADV_ITVL_MIN) ||
(adv_itvl_max > BLE_HCI_ADV_ITVL_MAX)) {
return BLE_ERR_INV_HCI_CMD_PARMS;
}
}
/* Check own and peer address type */
own_addr_type = cmd[5];
peer_addr_type = cmd[6];
if ((own_addr_type > BLE_HCI_ADV_OWN_ADDR_MAX) ||
(peer_addr_type > BLE_HCI_ADV_PEER_ADDR_MAX)) {
return BLE_ERR_INV_HCI_CMD_PARMS;
}
advsm->adv_txpwr = MYNEWT_VAL(BLE_LL_TX_PWR_DBM);
#if (MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) == 1)
if (own_addr_type > BLE_HCI_ADV_OWN_ADDR_RANDOM) {
/* Copy peer address */
memcpy(advsm->peer_addr, cmd + 7, BLE_DEV_ADDR_LEN);
}
#else
/* If we dont support privacy some address types wont work */
if (own_addr_type > BLE_HCI_ADV_OWN_ADDR_RANDOM) {
return BLE_ERR_UNSUPPORTED;
}
#endif
/* There are only three adv channels, so check for any outside the range */
adv_chanmask = cmd[13];
if (((adv_chanmask & 0xF8) != 0) || (adv_chanmask == 0)) {
return BLE_ERR_INV_HCI_CMD_PARMS;
}
/* Check for valid filter policy */
if (adv_filter_policy > BLE_HCI_ADV_FILT_MAX) {
return BLE_ERR_INV_HCI_CMD_PARMS;
}
/* Fill out rest of advertising state machine */
advsm->own_addr_type = own_addr_type;
advsm->peer_addr_type = peer_addr_type;
advsm->adv_filter_policy = adv_filter_policy;
advsm->adv_chanmask = adv_chanmask;
advsm->adv_itvl_min = adv_itvl_min;
advsm->adv_itvl_max = adv_itvl_max;
advsm->props = props;
return 0;
}
/**
* Stop advertising state machine
*
* Context: Link Layer task.
*
* @param advsm
*/
static void
ble_ll_adv_sm_stop(struct ble_ll_adv_sm *advsm)
{
os_sr_t sr;
if (advsm->adv_enabled) {
/* Remove any scheduled advertising items */
ble_ll_sched_rmv_elem(&advsm->adv_sch);
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
advsm->aux_active = 0;
ble_ll_sched_rmv_elem(&advsm->aux[0].sch);
ble_ll_sched_rmv_elem(&advsm->aux[1].sch);
#endif
/* Set to standby if we are no longer advertising */
OS_ENTER_CRITICAL(sr);
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
if (g_ble_ll_cur_adv_sm == advsm) {
ble_phy_disable();
ble_ll_wfr_disable();
ble_ll_state_set(BLE_LL_STATE_STANDBY);
g_ble_ll_cur_adv_sm = NULL;
ble_ll_scan_chk_resume();
}
#else
if (ble_ll_state_get() == BLE_LL_STATE_ADV) {
ble_phy_disable();
ble_ll_wfr_disable();
ble_ll_state_set(BLE_LL_STATE_STANDBY);
g_ble_ll_cur_adv_sm = NULL;
ble_ll_scan_chk_resume();
}
#endif
#ifdef BLE_XCVR_RFCLK
ble_ll_sched_rfclk_chk_restart();
#endif
OS_EXIT_CRITICAL(sr);
ble_npl_eventq_remove(&g_ble_ll_data.ll_evq, &advsm->adv_txdone_ev);
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
ble_npl_eventq_remove(&g_ble_ll_data.ll_evq, &advsm->adv_sec_txdone_ev);
#endif
/* If there is an event buf we need to free it */
if (advsm->conn_comp_ev) {
ble_hci_trans_buf_free(advsm->conn_comp_ev);
advsm->conn_comp_ev = NULL;
}
ble_ll_adv_active_chanset_clear(advsm);
/* Disable advertising */
advsm->adv_enabled = 0;
}
}
static void
ble_ll_adv_sm_stop_timeout(struct ble_ll_adv_sm *advsm)
{
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
if (ble_ll_hci_adv_mode_ext()) {
ble_ll_hci_ev_send_adv_set_terminated(BLE_ERR_DIR_ADV_TMO,
advsm->adv_instance, 0,
advsm->events);
}
#endif
/*
* For high duty directed advertising we need to send connection
* complete event with proper status
*/
if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_HD_DIRECTED) {
ble_ll_conn_comp_event_send(NULL, BLE_ERR_DIR_ADV_TMO,
advsm->conn_comp_ev, advsm);
advsm->conn_comp_ev = NULL;
}
/* Disable advertising */
ble_ll_adv_sm_stop(advsm);
}
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
static void
ble_ll_adv_sm_stop_limit_reached(struct ble_ll_adv_sm *advsm)
{
ble_ll_hci_ev_send_adv_set_terminated(BLE_RR_LIMIT_REACHED,
advsm->adv_instance, 0,
advsm->events);
/*
* For high duty directed advertising we need to send connection
* complete event with proper status
*
* Spec is a bit unambiguous here since it doesn't define what code should
* be used if HD directed advertising was terminated before timeout due to
* events count limit. For now just use same code as with duration timeout.
*/
if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_HD_DIRECTED) {
ble_ll_conn_comp_event_send(NULL, BLE_ERR_DIR_ADV_TMO,
advsm->conn_comp_ev, advsm);
advsm->conn_comp_ev = NULL;
}
/* Disable advertising */
ble_ll_adv_sm_stop(advsm);
}
#endif
static void
ble_ll_adv_scheduled(struct ble_ll_adv_sm *advsm, uint32_t sch_start, void *arg)
{
/* The event start time is when we start transmission of the adv PDU */
advsm->adv_event_start_time = sch_start + g_ble_ll_sched_offset_ticks;
advsm->adv_pdu_start_time = advsm->adv_event_start_time;
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
/* this is validated for HD adv so no need to do additional checks here
* duration is in 10ms units
*/
if (advsm->duration) {
advsm->adv_end_time = advsm->adv_event_start_time +
os_cputime_usecs_to_ticks(advsm->duration * 10000);
}
#else
/* Set the time at which we must end directed, high-duty cycle advertising.
*/
if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_HD_DIRECTED) {
advsm->adv_end_time = advsm->adv_event_start_time +
os_cputime_usecs_to_ticks(BLE_LL_ADV_STATE_HD_MAX * 1000);
}
#endif
}
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
#endif
/**
* Start the advertising state machine. This is called when the host sends
* the "enable advertising" command and is not called again while in the
* advertising state.
*
* Context: Link-layer task.
*
* @param advsm Pointer to advertising state machine
*
* @return int
*/
static int
ble_ll_adv_sm_start(struct ble_ll_adv_sm *advsm)
{
uint8_t adv_chan;
uint8_t *addr;
uint8_t *evbuf;
/* only clear flags that are not set from HCI */
advsm->flags &= ~BLE_LL_ADV_SM_FLAG_TX_ADD;
advsm->flags &= ~BLE_LL_ADV_SM_FLAG_RX_ADD;
advsm->flags &= ~BLE_LL_ADV_SM_FLAG_CONN_RSP_TXD;
if (advsm->own_addr_type == BLE_HCI_ADV_OWN_ADDR_RANDOM) {
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
if (!ble_ll_is_valid_random_addr(advsm->adv_random_addr)) {
return BLE_ERR_INV_HCI_CMD_PARMS;
}
#else
if (!ble_ll_is_valid_random_addr(g_random_addr)) {
return BLE_ERR_CMD_DISALLOWED;
}
#endif
}
/*
* Get an event with which to send the connection complete event if
* this is connectable
*/
if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_CONNECTABLE) {
/* We expect this to be NULL but if not we wont allocate one... */
if (advsm->conn_comp_ev == NULL) {
evbuf = ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_HI);
if (!evbuf) {
return BLE_ERR_MEM_CAPACITY;
}
advsm->conn_comp_ev = evbuf;
}
}
/* Set advertising address */
if ((advsm->own_addr_type & 1) == 0) {
addr = g_dev_addr;
} else {
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
addr = advsm->adv_random_addr;
#else
addr = g_random_addr;
#endif
advsm->flags |= BLE_LL_ADV_SM_FLAG_TX_ADD;
}
memcpy(advsm->adva, addr, BLE_DEV_ADDR_LEN);
if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_DIRECTED) {
memcpy(advsm->initiator_addr, advsm->peer_addr, BLE_DEV_ADDR_LEN);
if (advsm->peer_addr_type & 1) {
advsm->flags |= BLE_LL_ADV_SM_FLAG_RX_ADD;
}
}
#if (MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) == 1)
/* This will generate an RPA for both initiator addr and adva */
if (advsm->own_addr_type > BLE_HCI_ADV_OWN_ADDR_RANDOM) {
ble_ll_adv_rpa_update(advsm);
}
#endif
/* Set flag telling us that advertising is enabled */
advsm->adv_enabled = 1;
/* Determine the advertising interval we will use */
if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_HD_DIRECTED) {
/* Set it to max. allowed for high duty cycle advertising */
advsm->adv_itvl_usecs = BLE_LL_ADV_PDU_ITVL_HD_MS_MAX;
} else {
advsm->adv_itvl_usecs = (uint32_t)advsm->adv_itvl_max;
advsm->adv_itvl_usecs *= BLE_LL_ADV_ITVL;
}
/* Set first advertising channel */
adv_chan = ble_ll_adv_first_chan(advsm);
advsm->adv_chan = adv_chan;
/*
* XXX: while this may not be the most efficient, schedule the first
* advertising event some time in the future (5 msecs). This will give
* time to start up any clocks or anything and also avoid a bunch of code
* to check if we are currently doing anything. Just makes this simple.
*
* Might also want to align this on a slot in the future.
*
* NOTE: adv_event_start_time gets set by the sched_adv_new
*/
advsm->adv_pdu_start_time = os_cputime_get32() +
os_cputime_usecs_to_ticks(5000);
/*
* Schedule advertising. We set the initial schedule start and end
* times to the earliest possible start/end.
*/
ble_ll_adv_set_sched(advsm);
ble_ll_sched_adv_new(&advsm->adv_sch, ble_ll_adv_scheduled, NULL);
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
if (!(advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY)) {
ble_ll_adv_aux_schedule(advsm);
}
#endif
return BLE_ERR_SUCCESS;
}
/**
* Called when the LE HCI command read advertising channel tx power command
* has been received. Returns the current advertising transmit power.
*
* Context: Link Layer task (HCI command parser)
*
* @return int
*/
int
ble_ll_adv_read_txpwr(uint8_t *rspbuf, uint8_t *rsplen)
{
rspbuf[0] = MYNEWT_VAL(BLE_LL_TX_PWR_DBM);
*rsplen = 1;
return BLE_ERR_SUCCESS;
}
/**
* Turn advertising on/off.
*
* Context: Link Layer task
*
* @param cmd
*
* @return int
*/
int
ble_ll_adv_set_enable(uint8_t instance, uint8_t enable, int duration,
uint8_t events)
{
int rc;
struct ble_ll_adv_sm *advsm;
if (instance >= BLE_ADV_INSTANCES) {
return BLE_ERR_INV_HCI_CMD_PARMS;
}
advsm = &g_ble_ll_adv_sm[instance];
rc = BLE_ERR_SUCCESS;
if (enable == 1) {
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
if (advsm->flags & BLE_LL_ADV_SM_FLAG_ADV_DATA_INCOMPLETE) {
return BLE_ERR_CMD_DISALLOWED;
}
if (ble_ll_hci_adv_mode_ext() &&
(advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_SCANNABLE) &&
!(advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY) &&
SCAN_RSP_DATA_LEN(advsm) == 0) {
return BLE_ERR_CMD_DISALLOWED;
}
/* handle specifics of HD dir adv enabled in legacy way */
if (duration < 0) {
if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_HD_DIRECTED) {
duration = BLE_LL_ADV_STATE_HD_MAX / 10;
} else {
duration = 0;
}
}
advsm->duration = duration;
advsm->events_max = events;
advsm->events = 0;
#endif
/* If already enabled, do nothing */
if (!advsm->adv_enabled) {
/* Start the advertising state machine */
rc = ble_ll_adv_sm_start(advsm);
}
} else if (enable == 0) {
ble_ll_adv_sm_stop(advsm);
} else {
rc = BLE_ERR_INV_HCI_CMD_PARMS;
}
return rc;
}
static void
ble_ll_adv_update_data_mbuf(struct os_mbuf **omp, bool new_data, uint16_t maxlen,
const void *data, uint16_t datalen)
{
struct os_mbuf *om;
int ret;
om = *omp;
if (new_data) {
if (om) {
os_mbuf_free_chain(om);
}
om = os_msys_get_pkthdr(datalen, 0);
if (!om) {
goto done;
}
}
assert(om);
if (OS_MBUF_PKTLEN(om) + datalen > maxlen) {
os_mbuf_free_chain(om);
om = NULL;
goto done;
}
ret = os_mbuf_append(om, data, datalen);
if (ret) {
os_mbuf_free_chain(om);
om = NULL;
}
done:
*omp = om;
}
static bool
instance_configured(struct ble_ll_adv_sm *advsm)
{
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
if (ble_ll_hci_adv_mode_ext()) {
return advsm->flags & BLE_LL_ADV_SM_FLAG_CONFIGURED;
}
#endif
/* legacy HCI instance is always configured */
return true;
}
/**
* Set the scan response data that the controller will send.
*
* @param cmd
* @param len
*
* @return int
*/
int
ble_ll_adv_set_scan_rsp_data(uint8_t *cmd, uint8_t cmd_len, uint8_t instance,
uint8_t operation)
{
uint8_t datalen;
struct ble_ll_adv_sm *advsm;
bool new_data;
if (instance >= BLE_ADV_INSTANCES) {
return BLE_ERR_INV_HCI_CMD_PARMS;
}
advsm = &g_ble_ll_adv_sm[instance];
datalen = cmd[0];
if (datalen > 251 || datalen > cmd_len - 1) {
return BLE_ERR_INV_HCI_CMD_PARMS;
}
if (!instance_configured(advsm)) {
return BLE_ERR_UNK_ADV_INDENT;
}
/* check if type of advertising support scan rsp */
if (!(advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_SCANNABLE)) {
if (!(advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY)) {
return BLE_ERR_INV_HCI_CMD_PARMS;
}
}
switch (operation) {
case BLE_HCI_LE_SET_EXT_SCAN_RSP_DATA_OPER_COMPLETE:
if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY) {
if (datalen > BLE_SCAN_RSP_LEGACY_DATA_MAX_LEN) {
return BLE_ERR_INV_HCI_CMD_PARMS;
}
}
break;
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
case BLE_HCI_LE_SET_EXT_SCAN_RSP_DATA_OPER_LAST:
/* TODO mark scan rsp as complete? */
/* fall through */
case BLE_HCI_LE_SET_EXT_SCAN_RSP_DATA_OPER_INT:
if (!advsm->scan_rsp_data) {
return BLE_ERR_INV_HCI_CMD_PARMS;
}
if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY) {
return BLE_ERR_INV_HCI_CMD_PARMS;
}
if (advsm->adv_enabled) {
return BLE_ERR_CMD_DISALLOWED;
}
if (!datalen) {
return BLE_ERR_INV_HCI_CMD_PARMS;
}
break;
case BLE_HCI_LE_SET_EXT_SCAN_RSP_DATA_OPER_FIRST:
if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY) {
return BLE_ERR_INV_HCI_CMD_PARMS;
}
if (advsm->adv_enabled) {
return BLE_ERR_CMD_DISALLOWED;
}
if (!datalen) {
return BLE_ERR_INV_HCI_CMD_PARMS;
}
break;
#endif
default:
return BLE_ERR_INV_HCI_CMD_PARMS;
}
new_data = (operation == BLE_HCI_LE_SET_EXT_SCAN_RSP_DATA_OPER_COMPLETE) ||
(operation == BLE_HCI_LE_SET_EXT_SCAN_RSP_DATA_OPER_FIRST);
ble_ll_adv_update_data_mbuf(&advsm->scan_rsp_data, new_data,
BLE_SCAN_RSP_DATA_MAX_LEN, cmd + 1, datalen);
if (!advsm->scan_rsp_data) {
return BLE_ERR_MEM_CAPACITY;
}
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
/* DID shall be updated when host provides new scan response data */
advsm->adi = (advsm->adi & 0xf000) | (rand() & 0x0fff);
#endif
return BLE_ERR_SUCCESS;
}
/**
* Called by the LL HCI command parser when a set advertising
* data command has been sent from the host to the controller.
*
* @param cmd Pointer to command data
* @param len Length of command data
*
* @return int 0: success; BLE_ERR_INV_HCI_CMD_PARMS otherwise.
*/
int
ble_ll_adv_set_adv_data(uint8_t *cmd, uint8_t cmd_len, uint8_t instance,
uint8_t operation)
{
uint8_t datalen;
struct ble_ll_adv_sm *advsm;
bool new_data;
if (instance >= BLE_ADV_INSTANCES) {
return BLE_ERR_INV_HCI_CMD_PARMS;
}
advsm = &g_ble_ll_adv_sm[instance];
datalen = cmd[0];
if (datalen > 251 || datalen > cmd_len - 1) {
return BLE_ERR_INV_HCI_CMD_PARMS;
}
if (!instance_configured(advsm)) {
return BLE_ERR_UNK_ADV_INDENT;
}
/* check if type of advertising support adv data */
if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY) {
if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_DIRECTED) {
if (ble_ll_hci_adv_mode_ext()) {
return BLE_ERR_INV_HCI_CMD_PARMS;
}
}
} else {
if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_SCANNABLE) {
return BLE_ERR_INV_HCI_CMD_PARMS;
}
}
switch (operation) {
case BLE_HCI_LE_SET_EXT_ADV_DATA_OPER_COMPLETE:
if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY) {
if (datalen > BLE_ADV_LEGACY_DATA_MAX_LEN) {
return BLE_ERR_INV_HCI_CMD_PARMS;
}
}
advsm->flags &= ~BLE_LL_ADV_SM_FLAG_ADV_DATA_INCOMPLETE;
break;
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
case BLE_HCI_LE_SET_EXT_ADV_DATA_OPER_UNCHANGED:
if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY) {
return BLE_ERR_INV_HCI_CMD_PARMS;
}
if (!advsm->adv_enabled || !ADV_DATA_LEN(advsm) || datalen) {
return BLE_ERR_INV_HCI_CMD_PARMS;
}
/* update DID only */
advsm->adi = (advsm->adi & 0xf000) | (rand() & 0x0fff);
return BLE_ERR_SUCCESS;
case BLE_HCI_LE_SET_EXT_ADV_DATA_OPER_LAST:
advsm->flags &= ~BLE_LL_ADV_SM_FLAG_ADV_DATA_INCOMPLETE;
/* fall through */
case BLE_HCI_LE_SET_EXT_ADV_DATA_OPER_INT:
if (!advsm->adv_data) {
return BLE_ERR_INV_HCI_CMD_PARMS;
}
if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY) {
return BLE_ERR_INV_HCI_CMD_PARMS;
}
if (!datalen) {
return BLE_ERR_INV_HCI_CMD_PARMS;
}
if (advsm->adv_enabled) {
return BLE_ERR_CMD_DISALLOWED;
}
break;
case BLE_HCI_LE_SET_EXT_ADV_DATA_OPER_FIRST:
if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY) {
return BLE_ERR_INV_HCI_CMD_PARMS;
}
if (advsm->adv_enabled) {
return BLE_ERR_CMD_DISALLOWED;
}
if (!datalen) {
return BLE_ERR_INV_HCI_CMD_PARMS;
}
advsm->flags |= BLE_LL_ADV_SM_FLAG_ADV_DATA_INCOMPLETE;
break;
#endif
default:
return BLE_ERR_INV_HCI_CMD_PARMS;
}
new_data = (operation == BLE_HCI_LE_SET_EXT_ADV_DATA_OPER_COMPLETE) ||
(operation == BLE_HCI_LE_SET_EXT_ADV_DATA_OPER_FIRST);
ble_ll_adv_update_data_mbuf(&advsm->adv_data, new_data, BLE_ADV_DATA_MAX_LEN,
cmd + 1, datalen);
if (!advsm->adv_data) {
return BLE_ERR_MEM_CAPACITY;
}
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
/* DID shall be updated when host provides new advertising data */
advsm->adi = (advsm->adi & 0xf000) | (rand() & 0x0fff);
#endif
return BLE_ERR_SUCCESS;
}
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
int
ble_ll_adv_ext_set_param(uint8_t *cmdbuf, uint8_t *rspbuf, uint8_t *rsplen)
{
int rc;
uint8_t adv_filter_policy;
uint8_t adv_chanmask;
uint8_t own_addr_type;
uint8_t peer_addr_type;
uint32_t adv_itvl_min;
uint32_t adv_itvl_max;
uint16_t props;
struct ble_ll_adv_sm *advsm;
uint8_t pri_phy;
uint8_t sec_phy;
uint8_t sid;
uint8_t scan_req_notif;
int8_t tx_power = 0;
if (cmdbuf[0] >= BLE_ADV_INSTANCES) {
rc = BLE_ERR_INV_HCI_CMD_PARMS;
goto done;
}
advsm = &g_ble_ll_adv_sm[cmdbuf[0]];
if (advsm->adv_enabled) {
rc = BLE_ERR_CMD_DISALLOWED;
goto done;
}
props = get_le16(&cmdbuf[1]);
adv_itvl_min = cmdbuf[5] << 16 | cmdbuf[4] << 8 | cmdbuf[3];
adv_itvl_max = cmdbuf[8] << 16 | cmdbuf[7] << 8 | cmdbuf[6];
if (props & ~BLE_HCI_LE_SET_EXT_ADV_PROP_MASK) {
rc = BLE_ERR_INV_HCI_CMD_PARMS;
goto done;
}
if (props & BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY) {
if (ADV_DATA_LEN(advsm) > BLE_ADV_LEGACY_DATA_MAX_LEN ||
SCAN_RSP_DATA_LEN(advsm) > BLE_SCAN_RSP_LEGACY_DATA_MAX_LEN) {
rc = BLE_ERR_INV_HCI_CMD_PARMS;
goto done;
}
/* if legacy bit is set possible values are limited */
switch (props) {
case BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY_IND:
case BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY_LD_DIR:
case BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY_HD_DIR:
case BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY_SCAN:
case BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY_NONCONN:
break;
default:
rc = BLE_ERR_INV_HCI_CMD_PARMS;
goto done;
}
} else {
/* HD directed advertising allowed only on legacy PDUs */
if (props & BLE_HCI_LE_SET_EXT_ADV_PROP_HD_DIRECTED) {
rc = BLE_ERR_INV_HCI_CMD_PARMS;
goto done;
}
/* if ext advertising PDUs are used then it shall not be both
* connectable and scanable
*/
if ((props & BLE_HCI_LE_SET_EXT_ADV_PROP_CONNECTABLE) &&
(props & BLE_HCI_LE_SET_EXT_ADV_PROP_SCANNABLE)) {
rc = BLE_ERR_INV_HCI_CMD_PARMS;
goto done;
}
}
/* High Duty Directed advertising is special */
if (props & BLE_HCI_LE_SET_EXT_ADV_PROP_HD_DIRECTED) {
if (ADV_DATA_LEN(advsm) || SCAN_RSP_DATA_LEN(advsm)) {
rc = BLE_ERR_INV_HCI_CMD_PARMS;
goto done;
}
/* Ignore min/max interval */
adv_itvl_min = 0;
adv_itvl_max = 0;
} else {
/* validate intervals for non HD-directed advertising */
if ((adv_itvl_min > adv_itvl_max) ||
(adv_itvl_min < BLE_HCI_ADV_ITVL_MIN) ||
(adv_itvl_max < BLE_HCI_ADV_ITVL_MIN)) {
rc = BLE_ERR_INV_HCI_CMD_PARMS;
goto done;
}
/* TODO for now limit those to values from legacy advertising
*
* If the primary advertising interval range is outside the advertising
* interval range supported by the Controller, then the Controller shall
* return the error code Unsupported Feature or Parameter Value (0x11).
*/
if ((adv_itvl_min > BLE_HCI_ADV_ITVL_MAX) ||
(adv_itvl_max > BLE_HCI_ADV_ITVL_MAX)) {
rc = BLE_ERR_UNSUPPORTED;
goto done;
}
}
/* There are only three adv channels, so check for any outside the range */
adv_chanmask = cmdbuf[9];
if (((adv_chanmask & 0xF8) != 0) || (adv_chanmask == 0)) {
rc = BLE_ERR_INV_HCI_CMD_PARMS;
goto done;
}
/* Check own and peer address type */
own_addr_type = cmdbuf[10];
peer_addr_type = cmdbuf[11];
if ((own_addr_type > BLE_HCI_ADV_OWN_ADDR_MAX) ||
(peer_addr_type > BLE_HCI_ADV_PEER_ADDR_MAX)) {
rc = BLE_ERR_INV_HCI_CMD_PARMS;
goto done;
}
#if (MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) == 0)
/* If we dont support privacy some address types wont work */
if (own_addr_type > BLE_HCI_ADV_OWN_ADDR_RANDOM) {
rc = BLE_ERR_UNSUPPORTED;
goto done;
}
#endif
adv_filter_policy = cmdbuf[18];
/* Check filter policy (valid only for undirected */
if (!(props & BLE_HCI_LE_SET_EXT_ADV_PROP_DIRECTED) &&
adv_filter_policy > BLE_HCI_ADV_FILT_MAX) {
rc = BLE_ERR_INV_HCI_CMD_PARMS;
goto done;
}
pri_phy = cmdbuf[20];
if (pri_phy != BLE_HCI_LE_PHY_1M && pri_phy != BLE_HCI_LE_PHY_CODED) {
rc = BLE_ERR_INV_HCI_CMD_PARMS;
goto done;
}
sec_phy = cmdbuf[22];
if (sec_phy != BLE_HCI_LE_PHY_1M && sec_phy != BLE_HCI_LE_PHY_2M &&
sec_phy != BLE_HCI_LE_PHY_CODED) {
rc = BLE_ERR_INV_HCI_CMD_PARMS;
goto done;
}
sid = cmdbuf[23];
if (sid > 0x0f) {
rc = BLE_ERR_INV_HCI_CMD_PARMS;
goto done;
}
scan_req_notif = cmdbuf[24];
if (scan_req_notif > 0x01) {
rc = BLE_ERR_INV_HCI_CMD_PARMS;
goto done;
}
rc = BLE_ERR_SUCCESS;
if (props & BLE_HCI_LE_SET_EXT_ADV_PROP_DIRECTED) {
memcpy(advsm->peer_addr, &cmdbuf[12], BLE_DEV_ADDR_LEN);
}
tx_power = (int8_t) cmdbuf[19];
if (tx_power == 127) {
/* no preference */
advsm->adv_txpwr = MYNEWT_VAL(BLE_LL_TX_PWR_DBM);
} else {
advsm->adv_txpwr = ble_phy_txpower_round(tx_power);
}
advsm->own_addr_type = own_addr_type;
advsm->peer_addr_type = peer_addr_type;
advsm->adv_filter_policy = adv_filter_policy;
advsm->adv_chanmask = adv_chanmask;
advsm->adv_itvl_min = adv_itvl_min;
advsm->adv_itvl_max = adv_itvl_max;
advsm->pri_phy = pri_phy;
advsm->sec_phy = sec_phy;
/* Update SID only */
advsm->adi = (advsm->adi & 0x0fff) | ((sid << 12));
advsm->props = props;
/* Set proper mbuf chain for aux data */
if (props & BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY) {
advsm->aux_data = NULL;
} else if (props & BLE_HCI_LE_SET_EXT_ADV_PROP_SCANNABLE) {
advsm->aux_data = &advsm->scan_rsp_data;
} else {
advsm->aux_data = &advsm->adv_data;
}
if (scan_req_notif) {
advsm->flags |= BLE_LL_ADV_SM_FLAG_SCAN_REQ_NOTIF;
} else {
advsm->flags &= ~BLE_LL_ADV_SM_FLAG_SCAN_REQ_NOTIF;
}
advsm->flags |= BLE_LL_ADV_SM_FLAG_CONFIGURED;
done:
/* Update TX power */
rspbuf[0] = ble_phy_txpower_round(tx_power);
*rsplen = 1;
return rc;
}
int
ble_ll_adv_ext_set_adv_data(uint8_t *cmdbuf, uint8_t cmdlen)
{
/* check if length is correct */
if (cmdlen < 4) {
return BLE_ERR_INV_HCI_CMD_PARMS;
}
/* TODO fragment preference ignored for now */
return ble_ll_adv_set_adv_data(cmdbuf + 3, cmdlen - 3, cmdbuf[0],
cmdbuf[1]);
}
int
ble_ll_adv_ext_set_scan_rsp(uint8_t *cmdbuf, uint8_t cmdlen)
{
/* check if length is correct */
if (cmdlen < 4) {
return BLE_ERR_INV_HCI_CMD_PARMS;
}
/* TODO fragment preference ignored for now */
return ble_ll_adv_set_scan_rsp_data(cmdbuf + 3, cmdlen - 3, cmdbuf[0],
cmdbuf[1]);
}
struct ext_adv_set {
uint8_t handle;
uint16_t duration;
uint8_t events;
} __attribute__((packed));
/**
* HCI LE extended advertising enable command
*
* @param cmd Pointer to command data
* @param len Command data length
*
* @return int BLE error code
*/
int
ble_ll_adv_ext_set_enable(uint8_t *cmd, uint8_t len)
{
struct ble_ll_adv_sm *advsm;
struct ext_adv_set* set;
uint8_t enable;
uint8_t sets;
int i, j, rc;
if (len < 2) {
return BLE_ERR_INV_HCI_CMD_PARMS;
}
enable = cmd[0];
sets = cmd[1];
cmd += 2;
/* check if length is correct */
if (len != 2 + (sets * sizeof (*set))) {
return BLE_ERR_INV_HCI_CMD_PARMS;
}
if (sets > BLE_ADV_INSTANCES) {
return BLE_ERR_INV_HCI_CMD_PARMS;
}
if (sets == 0) {
if (enable) {
return BLE_ERR_INV_HCI_CMD_PARMS;
}
/* disable all instances */
for (i = 0; i < BLE_ADV_INSTANCES; i++) {
ble_ll_adv_set_enable(i, 0, 0, 0);
}
return BLE_ERR_SUCCESS;
}
set = (void *) cmd;
/* validate instances */
for (i = 0; i < sets; i++) {
if (set->handle >= BLE_ADV_INSTANCES) {
return BLE_ERR_INV_HCI_CMD_PARMS;
}
/* validate duplicated sets */
for (j = 1; j < sets - i; j++) {
if (set->handle == set[j].handle) {
return BLE_ERR_INV_HCI_CMD_PARMS;
}
}
advsm = &g_ble_ll_adv_sm[set->handle];
if (!instance_configured(advsm)) {
return BLE_ERR_UNK_ADV_INDENT;
}
if (enable) {
if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_HD_DIRECTED) {
if (set->duration == 0 || le16toh(set->duration) > 128) {
return BLE_ERR_INV_HCI_CMD_PARMS;
}
}
}
set++;
}
set = (void *) cmd;
for (i = 0; i < sets; i++) {
rc = ble_ll_adv_set_enable(set->handle, enable, le16toh(set->duration),
set->events);
if (rc) {
return rc;
}
set++;
}
return BLE_ERR_SUCCESS;
}
int
ble_ll_adv_set_random_addr(uint8_t *addr, uint8_t instance)
{
struct ble_ll_adv_sm *advsm;
if (instance >= BLE_ADV_INSTANCES) {
return BLE_ERR_INV_HCI_CMD_PARMS;
}
advsm = &g_ble_ll_adv_sm[instance];
/*
* Reject if connectable advertising is on
* Core Spec Vol. 2 Part E 7.8.52
*/
if (advsm->adv_enabled &&
(advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_CONNECTABLE)) {
return BLE_ERR_CMD_DISALLOWED;
}
memcpy(advsm->adv_random_addr, addr, BLE_DEV_ADDR_LEN);
return BLE_ERR_SUCCESS;
}
/**
* HCI LE extended advertising remove command
*
* @param instance Advertising instance to be removed
*
* @return int BLE error code
*/
int
ble_ll_adv_remove(uint8_t instance)
{
struct ble_ll_adv_sm *advsm;
/* TODO
* Should we allow any value for instance ID?
*/
if (instance >= BLE_ADV_INSTANCES) {
return BLE_ERR_INV_HCI_CMD_PARMS;
}
advsm = &g_ble_ll_adv_sm[instance];
if (!instance_configured(advsm)) {
return BLE_ERR_UNK_ADV_INDENT;
}
if (advsm->adv_enabled) {
return BLE_ERR_CMD_DISALLOWED;
}
if (advsm->adv_data) {
os_mbuf_free_chain(advsm->adv_data);
}
if (advsm->scan_rsp_data) {
os_mbuf_free_chain(advsm->scan_rsp_data);
}
ble_ll_adv_sm_init(advsm);
return BLE_ERR_SUCCESS;
}
/**
* HCI LE extended advertising clear command
*
* @return int BLE error code
*/
int
ble_ll_adv_clear_all(void)
{
int i;
for (i = 0; i < BLE_ADV_INSTANCES; i++) {
if (g_ble_ll_adv_sm[i].adv_enabled) {
return BLE_ERR_CMD_DISALLOWED;
}
}
ble_ll_adv_reset();
return BLE_ERR_SUCCESS;
}
#endif
/**
* Called when the LL receives a scan request or connection request
*
* Context: Called from interrupt context.
*
* @param rxbuf
*
* @return -1: request not for us or is a connect request.
* 0: request (scan) is for us and we successfully went from rx to tx.
* > 0: PHY error attempting to go from rx to tx.
*/
static int
ble_ll_adv_rx_req(uint8_t pdu_type, struct os_mbuf *rxpdu)
{
int rc;
int resolved;
uint8_t chk_wl;
uint8_t txadd;
uint8_t peer_addr_type;
uint8_t *rxbuf;
uint8_t *adva;
uint8_t *peer;
struct ble_mbuf_hdr *ble_hdr;
struct ble_ll_adv_sm *advsm;
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
struct aux_conn_rsp_data rsp_data;
#endif
/* See if adva in the request (scan or connect) matches what we sent */
advsm = g_ble_ll_cur_adv_sm;
rxbuf = rxpdu->om_data;
adva = rxbuf + BLE_LL_PDU_HDR_LEN + BLE_DEV_ADDR_LEN;
if (memcmp(advsm->adva, adva, BLE_DEV_ADDR_LEN)) {
return -1;
}
/* Set device match bit if we are whitelisting */
if (pdu_type == BLE_ADV_PDU_TYPE_SCAN_REQ) {
chk_wl = advsm->adv_filter_policy & 1;
} else {
chk_wl = advsm->adv_filter_policy & 2;
}
/* Get the peer address type */
if (rxbuf[0] & BLE_ADV_PDU_HDR_TXADD_MASK) {
txadd = BLE_ADDR_RANDOM;
} else {
txadd = BLE_ADDR_PUBLIC;
}
ble_hdr = BLE_MBUF_HDR_PTR(rxpdu);
peer = rxbuf + BLE_LL_PDU_HDR_LEN;
peer_addr_type = txadd;
resolved = 0;
#if (MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) == 1)
if (ble_ll_is_rpa(peer, txadd) && ble_ll_resolv_enabled()) {
advsm->adv_rpa_index = ble_hw_resolv_list_match();
if (advsm->adv_rpa_index >= 0) {
ble_hdr->rxinfo.flags |= BLE_MBUF_HDR_F_RESOLVED;
if (chk_wl) {
peer = g_ble_ll_resolv_list[advsm->adv_rpa_index].rl_identity_addr;
peer_addr_type = g_ble_ll_resolv_list[advsm->adv_rpa_index].rl_addr_type;
resolved = 1;
}
} else {
if (chk_wl) {
return -1;
}
}
}
#endif
/* Set device match bit if we are whitelisting */
if (chk_wl && !ble_ll_whitelist_match(peer, peer_addr_type, resolved)) {
return -1;
}
/*
* We set the device match bit to tell the upper layer that we will
* accept the request
*/
ble_hdr->rxinfo.flags |= BLE_MBUF_HDR_F_DEVMATCH;
/* Setup to transmit the scan response if appropriate */
rc = -1;
if (pdu_type == BLE_ADV_PDU_TYPE_SCAN_REQ) {
/* XXX TODO: assume we do not need to change phy mode */
ble_phy_set_txend_cb(ble_ll_adv_tx_done, advsm);
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
if (advsm->flags & BLE_LL_ADV_SM_FLAG_SCAN_REQ_NOTIF) {
ble_ll_hci_ev_send_scan_req_recv(advsm->adv_instance, peer,
peer_addr_type);
}
/*
* We need to store current rxed packet header temporarily so AuxPtr
* can be calculated (if necessary) relative to AUX_SCAN_RSP instead of
* AUX_ADV_IND.
*/
advsm->rx_ble_hdr = ble_hdr;
rc = ble_phy_tx(ble_ll_adv_scan_rsp_pdu_make, advsm,
BLE_PHY_TRANSITION_NONE);
advsm->rx_ble_hdr = NULL;
#else
rc = ble_phy_tx(ble_ll_adv_scan_rsp_legacy_pdu_make, advsm,
BLE_PHY_TRANSITION_NONE);
#endif
if (!rc) {
ble_hdr->rxinfo.flags |= BLE_MBUF_HDR_F_SCAN_RSP_TXD;
STATS_INC(ble_ll_stats, scan_rsp_txg);
}
} else if (pdu_type == BLE_ADV_PDU_TYPE_AUX_CONNECT_REQ) {
/*
* Only accept connect requests from the desired address if we
* are doing directed advertising
*/
if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_DIRECTED) {
if (memcmp(advsm->peer_addr, peer, BLE_DEV_ADDR_LEN)) {
return -1;
}
}
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY) {
return -1;
}
/* use remote address used over the air */
rsp_data.advsm = advsm;
rsp_data.peer = rxbuf + BLE_LL_PDU_HDR_LEN;
rsp_data.rxadd = rxbuf[0] & BLE_ADV_PDU_HDR_TXADD_MASK;
ble_phy_set_txend_cb(ble_ll_adv_tx_done, advsm);
rc = ble_phy_tx(ble_ll_adv_aux_conn_rsp_pdu_make, &rsp_data,
BLE_PHY_TRANSITION_NONE);
if (!rc) {
advsm->flags |= BLE_LL_ADV_SM_FLAG_CONN_RSP_TXD;
STATS_INC(ble_ll_stats, aux_conn_rsp_tx);
}
#endif
}
return rc;
}
/**
* Called when a connect request has been received.
*
* Context: Link Layer
*
* @param rxbuf
* @param flags
*
* @return 0: no connection started. 1: connection started
*/
static int
ble_ll_adv_conn_req_rxd(uint8_t *rxbuf, struct ble_mbuf_hdr *hdr,
struct ble_ll_adv_sm *advsm)
{
int valid;
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY)
uint8_t resolved;
#endif
uint8_t addr_type;
uint8_t *inita;
uint8_t *ident_addr;
/* Don't create connection if AUX_CONNECT_RSP was not send */
if (!(advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY)) {
if (!(advsm->flags & BLE_LL_ADV_SM_FLAG_CONN_RSP_TXD)) {
return 0;
}
}
/* Check filter policy. */
valid = 0;
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY)
resolved = BLE_MBUF_HDR_RESOLVED(hdr);
#endif
inita = rxbuf + BLE_LL_PDU_HDR_LEN;
if (hdr->rxinfo.flags & BLE_MBUF_HDR_F_DEVMATCH) {
valid = 1;
if (rxbuf[0] & BLE_ADV_PDU_HDR_TXADD_MASK) {
addr_type = BLE_ADDR_RANDOM;
} else {
addr_type = BLE_ADDR_PUBLIC;
}
/*
* Only accept connect requests from the desired address if we
* are doing directed advertising
*/
if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_DIRECTED) {
ident_addr = inita;
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY)
if (resolved) {
ident_addr = g_ble_ll_resolv_list[advsm->adv_rpa_index].rl_identity_addr;
addr_type = g_ble_ll_resolv_list[advsm->adv_rpa_index].rl_addr_type;
}
#endif
if ((addr_type != advsm->peer_addr_type) ||
memcmp(advsm->peer_addr, ident_addr, BLE_DEV_ADDR_LEN)) {
valid = 0;
}
}
}
if (valid) {
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY)
if (resolved) {
/* Retain the resolvable private address that we received. */
memcpy(advsm->adv_rpa, inita, BLE_DEV_ADDR_LEN);
/* Update resolving list with current peer RPA */
ble_ll_resolv_set_peer_rpa(advsm->adv_rpa_index, inita);
/*
* Overwrite received inita with identity address since that
* is used from now on.
*/
memcpy(inita,
g_ble_ll_resolv_list[advsm->adv_rpa_index].rl_identity_addr,
BLE_DEV_ADDR_LEN);
/* Peer address type is an identity address */
addr_type = g_ble_ll_resolv_list[advsm->adv_rpa_index].rl_addr_type;
addr_type += 2;
}
#endif
/* Try to start slave connection. If successful, stop advertising */
valid = ble_ll_conn_slave_start(rxbuf, addr_type, hdr,
!(advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY));
if (valid) {
/* stop advertising only if not transmitting connection response */
if (!(advsm->flags & BLE_LL_ADV_SM_FLAG_CONN_RSP_TXD)) {
ble_ll_adv_sm_stop(advsm);
}
}
}
return valid;
}
/**
* Called on phy rx pdu end when in advertising state.
*
* There are only two pdu types we care about in this state: scan requests
* and connection requests. When we receive a scan request we must determine if
* we need to send a scan response and that needs to be acted on within T_IFS.
*
* When we receive a connection request, we need to determine if we will allow
* this device to start a connection with us. However, no immediate response is
* sent so we handle this at the link layer task.
*
* Context: Interrupt
*
* @param pdu_type Type of pdu received.
* @param rxpdu Pointer to received PDU
*
* @return int
* < 0: Disable the phy after reception.
* == 0: Do not disable the PHY
* > 0: Do not disable PHY as that has already been done.
*/
int
ble_ll_adv_rx_isr_end(uint8_t pdu_type, struct os_mbuf *rxpdu, int crcok)
{
int rc;
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
struct ble_mbuf_hdr *rxhdr;
#endif
rc = -1;
if (rxpdu == NULL) {
ble_ll_adv_tx_done(g_ble_ll_cur_adv_sm);
} else {
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
rxhdr = BLE_MBUF_HDR_PTR(rxpdu);
rxhdr->rxinfo.user_data = g_ble_ll_cur_adv_sm;
if (ble_ll_adv_active_chanset_is_sec(g_ble_ll_cur_adv_sm)) {
rxhdr->rxinfo.flags |= BLE_MBUF_HDR_F_EXT_ADV_SEC;
} else {
assert(ble_ll_adv_active_chanset_is_pri(g_ble_ll_cur_adv_sm));
}
#endif
if (crcok) {
if ((pdu_type == BLE_ADV_PDU_TYPE_SCAN_REQ) ||
(pdu_type == BLE_ADV_PDU_TYPE_CONNECT_REQ)) {
/* Process request */
rc = ble_ll_adv_rx_req(pdu_type, rxpdu);
}
}
if (rc) {
/* We no longer have a current state machine */
g_ble_ll_cur_adv_sm = NULL;
}
}
if (rc) {
ble_ll_state_set(BLE_LL_STATE_STANDBY);
}
return rc;
}
/**
* Process a received packet at the link layer task when in the advertising
* state
*
* Context: Link Layer
*
*
* @param ptype
* @param rxbuf
* @param hdr
*
* @return int
*/
void
ble_ll_adv_rx_pkt_in(uint8_t ptype, uint8_t *rxbuf, struct ble_mbuf_hdr *hdr)
{
int adv_event_over;
struct ble_ll_adv_sm *advsm;
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
advsm = (struct ble_ll_adv_sm *)hdr->rxinfo.user_data;
#else
advsm = &g_ble_ll_adv_sm[0];
#endif
/*
* It is possible that advertising was stopped and a packet plcaed on the
* LL receive packet queue. In this case, just ignore the received packet
* as the advertising state machine is no longer "valid"
*/
if (!advsm->adv_enabled) {
return;
}
/*
* If we have received a scan request and we are transmitting a response
* or we have received a valid connect request, dont "end" the advertising
* event. In the case of a connect request we will stop advertising. In
* the case of the scan response transmission we will get a transmit
* end callback.
*/
adv_event_over = 1;
if (BLE_MBUF_HDR_CRC_OK(hdr)) {
if (ptype == BLE_ADV_PDU_TYPE_CONNECT_REQ) {
if (ble_ll_adv_conn_req_rxd(rxbuf, hdr, advsm)) {
adv_event_over = 0;
}
} else {
if ((ptype == BLE_ADV_PDU_TYPE_SCAN_REQ) &&
(hdr->rxinfo.flags & BLE_MBUF_HDR_F_SCAN_RSP_TXD)) {
adv_event_over = 0;
}
}
}
if (adv_event_over) {
ble_ll_adv_make_done(advsm, hdr);
}
}
/**
* Called when a receive PDU has started and we are advertising.
*
* Context: interrupt
*
* @param pdu_type
* @param rxpdu
*
* @return int
* < 0: A frame we dont want to receive.
* = 0: Continue to receive frame. Dont go from rx to tx
* > 0: Continue to receive frame and go from rx to tx when done
*/
int
ble_ll_adv_rx_isr_start(uint8_t pdu_type)
{
int rc;
struct ble_ll_adv_sm *advsm;
/* Assume we will abort the frame */
rc = -1;
/* If we get a scan request we must tell the phy to go from rx to tx */
advsm = g_ble_ll_cur_adv_sm;
if (pdu_type == BLE_ADV_PDU_TYPE_SCAN_REQ) {
/* Only accept scan requests if we are indirect adv or scan adv */
if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_SCANNABLE) {
rc = 1;
}
} else {
/* Only accept connect requests if connectable advertising event */
if (pdu_type == BLE_ADV_PDU_TYPE_CONNECT_REQ) {
if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_CONNECTABLE) {
rc = 0;
}
}
}
/*
* If we abort the frame, we need to post the LL task to check if the
* advertising event is over.
*/
if (rc < 0) {
ble_ll_adv_tx_done(advsm);
}
return rc;
}
static void
ble_ll_adv_drop_event(struct ble_ll_adv_sm *advsm)
{
STATS_INC(ble_ll_stats, adv_drop_event);
ble_ll_sched_rmv_elem(&advsm->adv_sch);
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
ble_ll_sched_rmv_elem(&advsm->aux[0].sch);
ble_ll_sched_rmv_elem(&advsm->aux[1].sch);
ble_npl_eventq_remove(&g_ble_ll_data.ll_evq, &advsm->adv_sec_txdone_ev);
advsm->aux_active = 0;
#endif
advsm->adv_chan = ble_ll_adv_final_chan(advsm);
ble_npl_eventq_put(&g_ble_ll_data.ll_evq, &advsm->adv_txdone_ev);
}
static void
ble_ll_adv_reschedule_event(struct ble_ll_adv_sm *advsm)
{
int rc;
uint32_t start_time;
uint32_t max_delay_ticks;
assert(advsm->adv_enabled);
if (!advsm->adv_sch.enqueued) {
if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_HD_DIRECTED) {
max_delay_ticks = 0;
} else {
max_delay_ticks =
os_cputime_usecs_to_ticks(BLE_LL_ADV_DELAY_MS_MAX * 1000);
}
rc = ble_ll_sched_adv_reschedule(&advsm->adv_sch, &start_time,
max_delay_ticks);
if (rc) {
ble_ll_adv_drop_event(advsm);
return;
}
start_time += g_ble_ll_sched_offset_ticks;
advsm->adv_event_start_time = start_time;
advsm->adv_pdu_start_time = start_time;
}
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
if (!(advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY) &&
!advsm->aux_active) {
ble_ll_adv_aux_schedule(advsm);
}
#endif
}
/**
* Called when an advertising event is over.
*
* Context: Link Layer task.
*
* @param arg Pointer to advertising state machine.
*/
static void
ble_ll_adv_done(struct ble_ll_adv_sm *advsm)
{
int rc;
int resched_pdu;
uint8_t mask;
uint8_t final_adv_chan;
int32_t delta_t;
uint32_t itvl;
uint32_t tick_itvl;
uint32_t start_time;
assert(advsm->adv_enabled);
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY) {
/* stop advertising this was due to transmitting connection response */
if (advsm->flags & BLE_LL_ADV_SM_FLAG_CONN_RSP_TXD) {
ble_ll_adv_sm_stop(advsm);
return;
}
}
#endif
/* Remove the element from the schedule if it is still there. */
ble_ll_sched_rmv_elem(&advsm->adv_sch);
ble_npl_eventq_remove(&g_ble_ll_data.ll_evq, &advsm->adv_txdone_ev);
/*
* Check if we have ended our advertising event. If our last advertising
* packet was sent on the last channel, it means we are done with this
* event.
*/
final_adv_chan = ble_ll_adv_final_chan(advsm);
if (advsm->adv_chan == final_adv_chan) {
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
if (advsm->events_max) {
advsm->events++;
}
#endif
/* Check if we need to resume scanning */
ble_ll_scan_chk_resume();
/* Turn off the clock if not doing anything else */
#ifdef BLE_XCVR_RFCLK
ble_ll_sched_rfclk_chk_restart();
#endif
/* This event is over. Set adv channel to first one */
advsm->adv_chan = ble_ll_adv_first_chan(advsm);
/*
* Calculate start time of next advertising event. NOTE: we do not
* add the random advDelay as the scheduling code will do that.
*/
itvl = advsm->adv_itvl_usecs;
tick_itvl = os_cputime_usecs_to_ticks(itvl);
advsm->adv_event_start_time += tick_itvl;
advsm->adv_pdu_start_time = advsm->adv_event_start_time;
/*
* The scheduled time better be in the future! If it is not, we will
* just keep advancing until we the time is in the future
*/
start_time = advsm->adv_pdu_start_time - g_ble_ll_sched_offset_ticks;
delta_t = (int32_t)(start_time - os_cputime_get32());
if (delta_t < 0) {
/*
* NOTE: we just the same interval that we calculated earlier.
* No real need to keep recalculating a new interval.
*/
while (delta_t < 0) {
advsm->adv_event_start_time += tick_itvl;
advsm->adv_pdu_start_time = advsm->adv_event_start_time;
delta_t += (int32_t)tick_itvl;
}
}
resched_pdu = 0;
} else {
/*
* Move to next advertising channel. If not in the mask, just
* increment by 1. We can do this because we already checked if we
* just transmitted on the last advertising channel
*/
++advsm->adv_chan;
mask = 1 << (advsm->adv_chan - BLE_PHY_ADV_CHAN_START);
if ((mask & advsm->adv_chanmask) == 0) {
++advsm->adv_chan;
}
/*
* We will transmit right away. Set next pdu start time to now
* plus a xcvr start delay just so we dont count late adv starts
*/
advsm->adv_pdu_start_time = os_cputime_get32() +
g_ble_ll_sched_offset_ticks;
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
/* If we're past aux (unlikely, but can happen), just drop an event */
if (!(advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY) &&
advsm->aux_active &&
advsm->adv_pdu_start_time > AUX_CURRENT(advsm)->start_time) {
ble_ll_adv_drop_event(advsm);
return;
}
#endif
resched_pdu = 1;
}
/* check if advertising timed out */
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
if (advsm->duration &&
advsm->adv_pdu_start_time >= advsm->adv_end_time) {
/* Legacy PDUs need to be stop here.
* For ext adv it will be stopped when AUX is done (unless it was
* dropped so check if AUX is active here as well).
*/
if ((advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY) ||
!advsm->aux_active) {
ble_ll_adv_sm_stop_timeout(advsm);
}
return;
}
#else
if ((advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_HD_DIRECTED) &&
(advsm->adv_pdu_start_time >= advsm->adv_end_time)) {
ble_ll_adv_sm_stop_timeout(advsm);
return;
}
#endif
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
if (advsm->events_max && (advsm->events >= advsm->events_max)) {
/* Legacy PDUs need to be stop here.
* For ext adv it will be stopped when AUX is done (unless it was
* dropped so check if AUX is active here as well).
*/
if ((advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY) ||
!advsm->aux_active) {
ble_ll_adv_sm_stop_limit_reached(advsm);
}
return;
}
#endif
/* We need to regenerate our RPA's if we have passed timeout */
#if (MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) == 1)
ble_ll_adv_chk_rpa_timeout(advsm);
#endif
/* Schedule advertising transmit */
ble_ll_adv_set_sched(advsm);
if (!resched_pdu) {
ble_ll_adv_reschedule_event(advsm);
return;
}
/*
* In the unlikely event we cant reschedule this, just post a done
* event and we will reschedule the next advertising event
*/
rc = ble_ll_sched_adv_resched_pdu(&advsm->adv_sch);
if (rc) {
STATS_INC(ble_ll_stats, adv_resched_pdu_fail);
ble_ll_adv_drop_event(advsm);
}
}
static void
ble_ll_adv_event_done(struct ble_npl_event *ev)
{
ble_ll_adv_done(ble_npl_event_get_arg(ev));
}
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
/**
* Called when auxiliary packet is txd on secondary channel
*
* Context: Link Layer task.
*
* @param ev
*/
static void
ble_ll_adv_sec_done(struct ble_ll_adv_sm *advsm)
{
struct ble_ll_adv_aux *aux;
struct ble_ll_adv_aux *aux_next;
assert(advsm->adv_enabled);
assert(advsm->aux_active);
aux = AUX_CURRENT(advsm);
aux_next = AUX_NEXT(advsm);
if (advsm->aux_not_scanned) {
ble_ll_sched_rmv_elem(&aux_next->sch);
}
/* Remove anything else scheduled for secondary channel */
ble_ll_sched_rmv_elem(&aux->sch);
ble_npl_eventq_remove(&g_ble_ll_data.ll_evq, &advsm->adv_sec_txdone_ev);
/* Stop advertising due to transmitting connection response */
if (advsm->flags & BLE_LL_ADV_SM_FLAG_CONN_RSP_TXD) {
ble_ll_adv_sm_stop(advsm);
return;
}
/* If we have next AUX scheduled, try to schedule another one */
if (aux_next->sch.enqueued) {
advsm->aux_index ^= 1;
advsm->aux_first_pdu = 0;
ble_ll_adv_aux_schedule_next(advsm);
return;
}
/* Check if we need to resume scanning */
ble_ll_scan_chk_resume();
/* Check if advertising timed out */
if (advsm->duration && (advsm->adv_pdu_start_time >= advsm->adv_end_time)) {
ble_ll_adv_sm_stop_timeout(advsm);
return;
}
if (advsm->events_max && (advsm->events >= advsm->events_max)) {
ble_ll_adv_sm_stop_limit_reached(advsm);
return;
}
advsm->aux_active = 0;
ble_ll_adv_reschedule_event(advsm);
}
static void
ble_ll_adv_sec_event_done(struct ble_npl_event *ev)
{
ble_ll_adv_sec_done(ble_npl_event_get_arg(ev));
}
#endif
static void
ble_ll_adv_make_done(struct ble_ll_adv_sm *advsm, struct ble_mbuf_hdr *hdr)
{
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
if (BLE_MBUF_HDR_EXT_ADV_SEC(hdr)) {
assert(!(advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY));
assert(ble_ll_adv_active_chanset_is_sec(advsm));
ble_ll_adv_active_chanset_clear(advsm);
ble_ll_adv_sec_done(advsm);
} else {
assert(ble_ll_adv_active_chanset_is_pri(advsm));
ble_ll_adv_active_chanset_clear(advsm);
ble_ll_adv_done(advsm);
}
#else
assert(ble_ll_adv_active_chanset_is_pri(advsm));
ble_ll_adv_active_chanset_clear(advsm);
ble_ll_adv_done(advsm);
#endif
}
/**
* Checks if the controller can change the whitelist. If advertising is enabled
* and is 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_adv_can_chg_whitelist(void)
{
struct ble_ll_adv_sm *advsm;
int rc;
int i;
rc = 1;
for (i = 0; i < BLE_ADV_INSTANCES; ++i) {
advsm = &g_ble_ll_adv_sm[i];
if (advsm->adv_enabled &&
(advsm->adv_filter_policy != BLE_HCI_ADV_FILT_NONE)) {
rc = 0;
break;
}
}
return rc;
}
/**
* Sends the connection complete event when advertising a connection starts.
*
* @return uint8_t* Pointer to event buffer
*/
void
ble_ll_adv_send_conn_comp_ev(struct ble_ll_conn_sm *connsm,
struct ble_mbuf_hdr *rxhdr)
{
uint8_t *evbuf;
struct ble_ll_adv_sm *advsm;
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
advsm = (struct ble_ll_adv_sm *)rxhdr->rxinfo.user_data;
#else
advsm = &g_ble_ll_adv_sm[0];
#endif
evbuf = advsm->conn_comp_ev;
assert(evbuf != NULL);
advsm->conn_comp_ev = NULL;
ble_ll_conn_comp_event_send(connsm, BLE_ERR_SUCCESS, evbuf, advsm);
#if (MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CSA2) == 1)
ble_ll_hci_ev_le_csa(connsm);
#endif
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
if (ble_ll_hci_adv_mode_ext()) {
ble_ll_hci_ev_send_adv_set_terminated(0, advsm->adv_instance,
connsm->conn_handle, advsm->events);
}
#endif
}
/**
* Returns the local resolvable private address currently being using by
* the advertiser
*
* @return uint8_t*
*/
uint8_t *
ble_ll_adv_get_local_rpa(struct ble_ll_adv_sm *advsm)
{
uint8_t *rpa = NULL;
if (advsm->own_addr_type > BLE_HCI_ADV_OWN_ADDR_RANDOM) {
if ((advsm->flags & BLE_LL_ADV_SM_FLAG_TX_ADD) &&
ble_ll_is_rpa(advsm->adva, 1)) {
rpa = advsm->adva;
}
}
return rpa;
}
/**
* Returns the peer resolvable private address of last device connecting to us
*
* @return uint8_t*
*/
uint8_t *
ble_ll_adv_get_peer_rpa(struct ble_ll_adv_sm *advsm)
{
/* XXX: should this go into IRK list or connection? */
return advsm->adv_rpa;
}
/**
* Called when the LL wait for response timer expires while in the advertising
* state. Disables the phy and
*
*/
void
ble_ll_adv_wfr_timer_exp(void)
{
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
g_ble_ll_cur_adv_sm->aux_not_scanned = 1;
#endif
ble_phy_disable();
ble_ll_adv_tx_done(g_ble_ll_cur_adv_sm);
}
/**
* Reset the advertising state machine.
*
* Context: Link Layer task
*
*/
void
ble_ll_adv_reset(void)
{
int i;
struct ble_ll_adv_sm *advsm;
for (i = 0; i < BLE_ADV_INSTANCES; ++i) {
advsm = &g_ble_ll_adv_sm[i];
/* Stop advertising state machine */
ble_ll_adv_sm_stop(advsm);
/* clear any data present */
os_mbuf_free_chain(advsm->adv_data);
os_mbuf_free_chain(advsm->scan_rsp_data);
/* re-initialize the advertiser state machine */
ble_ll_adv_sm_init(advsm);
}
}
/* Called to determine if advertising is enabled.
*/
uint8_t
ble_ll_adv_enabled(void)
{
int i;
for (i = 0; i < BLE_ADV_INSTANCES; i++) {
if (g_ble_ll_adv_sm[i].adv_enabled) {
return 1;
}
}
return 0;
}
static void
ble_ll_adv_sm_init(struct ble_ll_adv_sm *advsm)
{
uint8_t i = advsm->adv_instance;
memset(advsm, 0, sizeof(struct ble_ll_adv_sm));
advsm->adv_instance = i;
advsm->adv_itvl_min = BLE_HCI_ADV_ITVL_DEF;
advsm->adv_itvl_max = BLE_HCI_ADV_ITVL_DEF;
advsm->adv_chanmask = BLE_HCI_ADV_CHANMASK_DEF;
/* Initialize advertising tx done event */
ble_npl_event_init(&advsm->adv_txdone_ev, ble_ll_adv_event_done, advsm);
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
ble_npl_event_init(&advsm->adv_sec_txdone_ev, ble_ll_adv_sec_event_done, advsm);
#endif
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
/* Initialize aux schedulers */
advsm->aux_active = 0;
advsm->aux[0].sch.cb_arg = advsm;
advsm->aux[0].sch.sched_cb = ble_ll_adv_secondary_tx_start_cb;
advsm->aux[0].sch.sched_type = BLE_LL_SCHED_TYPE_ADV;
advsm->aux[1].sch.cb_arg = advsm;
advsm->aux[1].sch.sched_cb = ble_ll_adv_secondary_tx_start_cb;
advsm->aux[1].sch.sched_type = BLE_LL_SCHED_TYPE_ADV;
#endif
/*XXX Configure instances to be legacy on start */
advsm->props |= BLE_HCI_LE_SET_EXT_ADV_PROP_SCANNABLE;
advsm->props |= BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY;
}
/**
* Initialize the advertising functionality of a BLE device. This should
* be called once on initialization
*/
void
ble_ll_adv_init(void)
{
int i;
/* Set default advertising parameters */
for (i = 0; i < BLE_ADV_INSTANCES; ++i) {
g_ble_ll_adv_sm[i].adv_instance = i;
ble_ll_adv_sm_init(&g_ble_ll_adv_sm[i]);
}
}