blob: 7e87891ab5cd775d9be3d53f67c5a037ab0b5d59 [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 <assert.h>
#include <string.h>
#include "sysinit/sysinit.h"
#include "syscfg/syscfg.h"
#include "os/os.h"
#include "stats/stats.h"
#include "nimble/ble.h"
#include "nimble/nimble_opt.h"
#include "nimble/hci_common.h"
#include "nimble/transport.h"
#include "controller/ble_ll_utils.h"
#include "controller/ble_hw.h"
#include "controller/ble_phy.h"
#include "controller/ble_phy_trace.h"
#include "controller/ble_ll.h"
#include "controller/ble_ll_pdu.h"
#include "controller/ble_ll_adv.h"
#include "controller/ble_ll_sched.h"
#include "controller/ble_ll_scan.h"
#include "controller/ble_ll_scan_aux.h"
#include "controller/ble_ll_hci.h"
#include "controller/ble_ll_whitelist.h"
#include "controller/ble_ll_resolv.h"
#include "controller/ble_ll_rfmgmt.h"
#include "controller/ble_ll_trace.h"
#include "controller/ble_ll_sync.h"
#include "controller/ble_fem.h"
#include "controller/ble_ll_isoal.h"
#include "controller/ble_ll_iso_big.h"
#if MYNEWT_VAL(BLE_LL_EXT)
#include "controller/ble_ll_ext.h"
#endif
#include "ble_ll_conn_priv.h"
#include "ble_ll_hci_priv.h"
#include "ble_ll_priv.h"
#include "hal/hal_system.h"
#if MYNEWT_VAL(BLE_LL_DTM)
#include "ble_ll_dtm_priv.h"
#endif
#if MYNEWT_VAL(BLE_LL_EXT)
#include <controller/ble_ll_ext.h>
#endif
/* XXX:
*
* 1) use the sanity task!
* 2) Need to figure out what to do with packets that we hand up that did
* not pass the filter policy for the given state. Currently I count all
* packets I think. Need to figure out what to do with this.
* 3) For the features defined, we need to conditionally compile code.
* 4) Should look into always disabled the wfr interrupt if we receive the
* start of a frame. Need to look at the various states to see if this is the
* right thing to do.
*/
/* This is TX power on PHY (or FEM PA if enabled) */
int8_t g_ble_ll_tx_power;
static int8_t g_ble_ll_tx_power_phy_current;
int8_t g_ble_ll_tx_power_compensation;
int8_t g_ble_ll_rx_power_compensation;
/* Supported states */
#if MYNEWT_VAL(BLE_LL_ROLE_BROADCASTER)
#define BLE_LL_S_NCA ((uint64_t)1 << 0)
#define BLE_LL_S_SA ((uint64_t)1 << 1)
#else
#define BLE_LL_S_NCA ((uint64_t)0 << 0)
#define BLE_LL_S_SA ((uint64_t)0 << 1)
#endif
#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
#define BLE_LL_S_CA ((uint64_t)1 << 2)
#define BLE_LL_S_HDCA ((uint64_t)1 << 3)
#else
#define BLE_LL_S_CA ((uint64_t)0 << 2)
#define BLE_LL_S_HDCA ((uint64_t)0 << 3)
#endif
#if MYNEWT_VAL(BLE_LL_ROLE_OBSERVER)
#define BLE_LL_S_PS ((uint64_t)1 << 4)
#define BLE_LL_S_AS ((uint64_t)1 << 5)
#else
#define BLE_LL_S_PS ((uint64_t)0 << 4)
#define BLE_LL_S_AS ((uint64_t)0 << 5)
#endif
#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
#define BLE_LL_S_INIT ((uint64_t)1 << 6)
#else
#define BLE_LL_S_INIT ((uint64_t)0 << 6)
#endif
#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
#define BLE_LL_S_PERIPH ((uint64_t)1 << 7)
#else
#define BLE_LL_S_PERIPH ((uint64_t)0 << 7)
#endif
#if MYNEWT_VAL(BLE_LL_ROLE_BROADCASTER) && MYNEWT_VAL(BLE_LL_ROLE_OBSERVER)
#define BLE_LL_S_NCA_PS ((uint64_t)1 << 8)
#define BLE_LL_S_SA_PS ((uint64_t)1 << 9)
#else
#define BLE_LL_S_NCA_PS ((uint64_t)0 << 8)
#define BLE_LL_S_SA_PS ((uint64_t)0 << 9)
#endif
#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL) && MYNEWT_VAL(BLE_LL_ROLE_OBSERVER)
#define BLE_LL_S_CA_PS ((uint64_t)1 << 10)
#define BLE_LL_S_HDCA_PS ((uint64_t)1 << 11)
#else
#define BLE_LL_S_CA_PS ((uint64_t)0 << 10)
#define BLE_LL_S_HDCA_PS ((uint64_t)0 << 11)
#endif
#if MYNEWT_VAL(BLE_LL_ROLE_BROADCASTER) && MYNEWT_VAL(BLE_LL_ROLE_OBSERVER)
#define BLE_LL_S_NCA_AS ((uint64_t)1 << 12)
#define BLE_LL_S_SA_AS ((uint64_t)1 << 13)
#else
#define BLE_LL_S_NCA_AS ((uint64_t)0 << 12)
#define BLE_LL_S_SA_AS ((uint64_t)0 << 13)
#endif
#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL) && MYNEWT_VAL(BLE_LL_ROLE_OBSERVER)
#define BLE_LL_S_CA_AS ((uint64_t)1 << 14)
#define BLE_LL_S_HDCA_AS ((uint64_t)1 << 15)
#else
#define BLE_LL_S_CA_AS ((uint64_t)0 << 14)
#define BLE_LL_S_HDCA_AS ((uint64_t)0 << 15)
#endif
#if MYNEWT_VAL(BLE_LL_ROLE_BROADCASTER) && MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
#define BLE_LL_S_NCA_INIT ((uint64_t)1 << 16)
#define BLE_LL_S_SA_INIT ((uint64_t)1 << 17)
#define BLE_LL_S_NCA_CENTRAL ((uint64_t)1 << 18)
#define BLE_LL_S_SA_CENTRAL ((uint64_t)1 << 19)
#else
#define BLE_LL_S_NCA_INIT ((uint64_t)0 << 16)
#define BLE_LL_S_SA_INIT ((uint64_t)0 << 17)
#define BLE_LL_S_NCA_CENTRAL ((uint64_t)0 << 18)
#define BLE_LL_S_SA_CENTRAL ((uint64_t)0 << 19)
#endif
#if MYNEWT_VAL(BLE_LL_ROLE_BROADCASTER) && MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
#define BLE_LL_S_NCA_PERIPH ((uint64_t)1 << 20)
#define BLE_LL_S_SA_PERIPH ((uint64_t)1 << 21)
#else
#define BLE_LL_S_NCA_PERIPH ((uint64_t)0 << 20)
#define BLE_LL_S_SA_PERIPH ((uint64_t)0 << 21)
#endif
#if MYNEWT_VAL(BLE_LL_ROLE_OBSERVER) && MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
/* We do not support passive scanning while initiating yet */
#define BLE_LL_S_PS_INIT ((uint64_t)0 << 22)
/* We do not support active scanning while initiating yet */
#define BLE_LL_S_AS_INIT ((uint64_t)0 << 23)
#define BLE_LL_S_PS_CENTRAL ((uint64_t)1 << 24)
#define BLE_LL_S_AS_CENTRAL ((uint64_t)1 << 25)
#else
#define BLE_LL_S_PS_INIT ((uint64_t)0 << 22)
#define BLE_LL_S_AS_INIT ((uint64_t)0 << 23)
#define BLE_LL_S_PS_CENTRAL ((uint64_t)0 << 24)
#define BLE_LL_S_AS_CENTRAL ((uint64_t)0 << 25)
#endif
#if MYNEWT_VAL(BLE_LL_ROLE_OBSERVER) && MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
#define BLE_LL_S_PS_PERIPH ((uint64_t)1 << 26)
#define BLE_LL_S_AS_PERIPH ((uint64_t)1 << 27)
#else
#define BLE_LL_S_PS_PERIPH ((uint64_t)0 << 26)
#define BLE_LL_S_AS_PERIPH ((uint64_t)0 << 27)
#endif
#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
#define BLE_LL_S_INIT_CENTRAL ((uint64_t)1 << 28)
#else
#define BLE_LL_S_INIT_CENTRAL ((uint64_t)0 << 28)
#endif
#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
#define BLE_LL_S_LDCA ((uint64_t)1 << 29)
#else
#define BLE_LL_S_LDCA ((uint64_t)0 << 29)
#endif
#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL) && MYNEWT_VAL(BLE_LL_ROLE_OBSERVER)
#define BLE_LL_S_LDCA_PS ((uint64_t)1 << 30)
#define BLE_LL_S_LDCA_AS ((uint64_t)1 << 31)
#else
#define BLE_LL_S_LDCA_PS ((uint64_t)0 << 30)
#define BLE_LL_S_LDCA_AS ((uint64_t)0 << 31)
#endif
#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL) && MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
#define BLE_LL_S_CA_INIT ((uint64_t)1 << 32)
#define BLE_LL_S_HDCA_INIT ((uint64_t)1 << 33)
#define BLE_LL_S_LDCA_INIT ((uint64_t)1 << 34)
#define BLE_LL_S_CA_CENTRAL ((uint64_t)1 << 35)
#define BLE_LL_S_HDCA_CENTRAL ((uint64_t)1 << 36)
#define BLE_LL_S_LDCA_CENTRAL ((uint64_t)1 << 37)
#else
#define BLE_LL_S_CA_INIT ((uint64_t)0 << 32)
#define BLE_LL_S_HDCA_INIT ((uint64_t)0 << 33)
#define BLE_LL_S_LDCA_INIT ((uint64_t)0 << 34)
#define BLE_LL_S_CA_CENTRAL ((uint64_t)0 << 35)
#define BLE_LL_S_HDCA_CENTRAL ((uint64_t)0 << 36)
#define BLE_LL_S_LDCA_CENTRAL ((uint64_t)0 << 37)
#endif
#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
#define BLE_LL_S_CA_PERIPH ((uint64_t)1 << 38)
#define BLE_LL_S_HDCA_PERIPH ((uint64_t)1 << 39)
#define BLE_LL_S_LDCA_PERIPH ((uint64_t)1 << 40)
#else
#define BLE_LL_S_CA_PERIPH ((uint64_t)0 << 38)
#define BLE_LL_S_HDCA_PERIPH ((uint64_t)0 << 39)
#define BLE_LL_S_LDCA_PERIPH ((uint64_t)0 << 40)
#endif
#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL) && MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
#define BLE_LL_S_INIT_PERIPH ((uint64_t)1 << 41)
#else
#define BLE_LL_S_INIT_PERIPH ((uint64_t)0 << 41)
#endif
#define BLE_LL_SUPPORTED_STATES \
( \
BLE_LL_S_NCA | \
BLE_LL_S_SA | \
BLE_LL_S_CA | \
BLE_LL_S_HDCA | \
BLE_LL_S_PS | \
BLE_LL_S_AS | \
BLE_LL_S_INIT | \
BLE_LL_S_PERIPH | \
BLE_LL_S_NCA_PS | \
BLE_LL_S_SA_PS | \
BLE_LL_S_CA_PS | \
BLE_LL_S_HDCA_PS | \
BLE_LL_S_NCA_AS | \
BLE_LL_S_SA_AS | \
BLE_LL_S_CA_AS | \
BLE_LL_S_HDCA_AS | \
BLE_LL_S_NCA_INIT | \
BLE_LL_S_SA_INIT | \
BLE_LL_S_NCA_CENTRAL | \
BLE_LL_S_SA_CENTRAL | \
BLE_LL_S_NCA_PERIPH | \
BLE_LL_S_SA_PERIPH | \
BLE_LL_S_PS_INIT | \
BLE_LL_S_AS_INIT | \
BLE_LL_S_PS_CENTRAL | \
BLE_LL_S_AS_CENTRAL | \
BLE_LL_S_PS_PERIPH | \
BLE_LL_S_AS_PERIPH | \
BLE_LL_S_INIT_CENTRAL | \
BLE_LL_S_LDCA | \
BLE_LL_S_LDCA_PS | \
BLE_LL_S_LDCA_AS | \
BLE_LL_S_CA_INIT | \
BLE_LL_S_HDCA_INIT | \
BLE_LL_S_LDCA_INIT | \
BLE_LL_S_CA_CENTRAL | \
BLE_LL_S_HDCA_CENTRAL | \
BLE_LL_S_LDCA_CENTRAL | \
BLE_LL_S_CA_PERIPH | \
BLE_LL_S_HDCA_PERIPH | \
BLE_LL_S_LDCA_PERIPH | \
BLE_LL_S_INIT_PERIPH)
/* The global BLE LL data object */
struct ble_ll_obj g_ble_ll_data;
/* Global link layer statistics */
STATS_SECT_DECL(ble_ll_stats) ble_ll_stats;
STATS_NAME_START(ble_ll_stats)
STATS_NAME(ble_ll_stats, hci_cmds)
STATS_NAME(ble_ll_stats, hci_cmd_errs)
STATS_NAME(ble_ll_stats, hci_events_sent)
STATS_NAME(ble_ll_stats, bad_ll_state)
STATS_NAME(ble_ll_stats, bad_acl_hdr)
STATS_NAME(ble_ll_stats, no_bufs)
STATS_NAME(ble_ll_stats, rx_adv_pdu_crc_ok)
STATS_NAME(ble_ll_stats, rx_adv_pdu_crc_err)
STATS_NAME(ble_ll_stats, rx_adv_bytes_crc_ok)
STATS_NAME(ble_ll_stats, rx_adv_bytes_crc_err)
STATS_NAME(ble_ll_stats, rx_data_pdu_crc_ok)
STATS_NAME(ble_ll_stats, rx_data_pdu_crc_err)
STATS_NAME(ble_ll_stats, rx_data_bytes_crc_ok)
STATS_NAME(ble_ll_stats, rx_data_bytes_crc_err)
STATS_NAME(ble_ll_stats, rx_adv_malformed_pkts)
STATS_NAME(ble_ll_stats, rx_adv_ind)
STATS_NAME(ble_ll_stats, rx_adv_direct_ind)
STATS_NAME(ble_ll_stats, rx_adv_nonconn_ind)
STATS_NAME(ble_ll_stats, rx_adv_ext_ind)
STATS_NAME(ble_ll_stats, rx_scan_reqs)
STATS_NAME(ble_ll_stats, rx_scan_rsps)
STATS_NAME(ble_ll_stats, rx_connect_reqs)
STATS_NAME(ble_ll_stats, rx_scan_ind)
STATS_NAME(ble_ll_stats, rx_aux_connect_rsp)
STATS_NAME(ble_ll_stats, rx_pdu_on_scan_disabled)
STATS_NAME(ble_ll_stats, adv_txg)
STATS_NAME(ble_ll_stats, adv_late_starts)
STATS_NAME(ble_ll_stats, adv_resched_pdu_fail)
STATS_NAME(ble_ll_stats, adv_drop_event)
STATS_NAME(ble_ll_stats, sched_state_conn_errs)
STATS_NAME(ble_ll_stats, sched_state_adv_errs)
STATS_NAME(ble_ll_stats, scan_starts)
STATS_NAME(ble_ll_stats, scan_stops)
STATS_NAME(ble_ll_stats, scan_req_txf)
STATS_NAME(ble_ll_stats, scan_req_txg)
STATS_NAME(ble_ll_stats, scan_rsp_txg)
STATS_NAME(ble_ll_stats, aux_missed_adv)
STATS_NAME(ble_ll_stats, aux_scheduled)
STATS_NAME(ble_ll_stats, aux_received)
STATS_NAME(ble_ll_stats, aux_fired_for_read)
STATS_NAME(ble_ll_stats, aux_allocated)
STATS_NAME(ble_ll_stats, aux_freed)
STATS_NAME(ble_ll_stats, aux_sched_cb)
STATS_NAME(ble_ll_stats, aux_conn_req_tx)
STATS_NAME(ble_ll_stats, aux_conn_rsp_tx)
STATS_NAME(ble_ll_stats, aux_conn_rsp_err)
STATS_NAME(ble_ll_stats, aux_scan_req_tx)
STATS_NAME(ble_ll_stats, aux_scan_rsp_err)
STATS_NAME(ble_ll_stats, aux_chain_cnt)
STATS_NAME(ble_ll_stats, aux_chain_err)
STATS_NAME(ble_ll_stats, aux_scan_drop)
STATS_NAME(ble_ll_stats, adv_evt_dropped)
STATS_NAME(ble_ll_stats, scan_timer_stopped)
STATS_NAME(ble_ll_stats, scan_timer_restarted)
STATS_NAME(ble_ll_stats, periodic_adv_drop_event)
STATS_NAME(ble_ll_stats, periodic_chain_drop_event)
STATS_NAME(ble_ll_stats, sync_event_failed)
STATS_NAME(ble_ll_stats, sync_received)
STATS_NAME(ble_ll_stats, sync_chain_failed)
STATS_NAME(ble_ll_stats, sync_missed_err)
STATS_NAME(ble_ll_stats, sync_crc_err)
STATS_NAME(ble_ll_stats, sync_rx_buf_err)
STATS_NAME(ble_ll_stats, sync_scheduled)
STATS_NAME(ble_ll_stats, sched_state_sync_errs)
STATS_NAME(ble_ll_stats, sched_invalid_pdu)
STATS_NAME_END(ble_ll_stats)
static void ble_ll_event_rx_pkt(struct ble_npl_event *ev);
#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL) || MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
static void ble_ll_event_tx_pkt(struct ble_npl_event *ev);
static void ble_ll_event_dbuf_overflow(struct ble_npl_event *ev);
#endif
#ifdef MYNEWT
/* The BLE LL task data structure */
struct os_task g_ble_ll_task;
OS_TASK_STACK_DEFINE(g_ble_ll_stack, MYNEWT_VAL(BLE_LL_STACK_SIZE));
#endif /* MYNEWT */
/** Our global device address (public) */
uint8_t g_dev_addr[BLE_DEV_ADDR_LEN];
/** Our random address */
uint8_t g_random_addr[BLE_DEV_ADDR_LEN];
/**
* Counts the number of advertising PDU's received, by type. For advertising
* PDU's that contain a destination address, we still count these packets even
* if they are not for us.
*
* @param pdu_type
*/
static void
ble_ll_count_rx_adv_pdus(uint8_t pdu_type)
{
/* Count received packet types */
switch (pdu_type) {
case BLE_ADV_PDU_TYPE_ADV_EXT_IND:
STATS_INC(ble_ll_stats, rx_adv_ext_ind);
break;
case BLE_ADV_PDU_TYPE_ADV_IND:
STATS_INC(ble_ll_stats, rx_adv_ind);
break;
case BLE_ADV_PDU_TYPE_ADV_DIRECT_IND:
STATS_INC(ble_ll_stats, rx_adv_direct_ind);
break;
case BLE_ADV_PDU_TYPE_ADV_NONCONN_IND:
STATS_INC(ble_ll_stats, rx_adv_nonconn_ind);
break;
case BLE_ADV_PDU_TYPE_SCAN_REQ:
STATS_INC(ble_ll_stats, rx_scan_reqs);
break;
case BLE_ADV_PDU_TYPE_SCAN_RSP:
STATS_INC(ble_ll_stats, rx_scan_rsps);
break;
case BLE_ADV_PDU_TYPE_CONNECT_IND:
STATS_INC(ble_ll_stats, rx_connect_reqs);
break;
case BLE_ADV_PDU_TYPE_AUX_CONNECT_RSP:
STATS_INC(ble_ll_stats, rx_aux_connect_rsp);
break;
case BLE_ADV_PDU_TYPE_ADV_SCAN_IND:
STATS_INC(ble_ll_stats, rx_scan_ind);
break;
default:
break;
}
}
struct os_mbuf *
ble_ll_rxpdu_alloc(uint16_t len)
{
struct os_mbuf *om_ret;
struct os_mbuf *om_next;
struct os_mbuf *om;
struct os_mbuf_pkthdr *pkthdr;
uint16_t databuf_len;
int rem_len;
/*
* Make sure that data in mbuf are word-aligned with and without packet
* header. This is essential for proper and quick copying of received PDUs
* into mbufs.
*/
_Static_assert((offsetof(struct os_mbuf, om_data) & 3) == 0,
"Unaligned om_data");
_Static_assert(((offsetof(struct os_mbuf, om_data) +
sizeof(struct os_mbuf_pkthdr) +
sizeof(struct ble_mbuf_hdr)) & 3) == 0,
"Unaligned data trailing packet header");
om_ret = os_msys_get_pkthdr(len, sizeof(struct ble_mbuf_hdr));
if (!om_ret) {
goto rxpdu_alloc_fail;
}
/* Set complete PDU length in packet header */
pkthdr = OS_MBUF_PKTHDR(om_ret);
pkthdr->omp_len = len;
rem_len = len;
/*
* Calculate length of data in memory block. We assume length is rounded
* down to word size so PHY can do word-size aligned data copy to mbufs
* (except for last one) and leave remainder unused.
*
* Note that there likely won't be any remainder here since all pools have
* block size aligned to word size anyway.
*/
databuf_len = om_ret->om_omp->omp_databuf_len & ~3;
/*
* First mbuf can store less data due to packet header. Also we reserve one
* word for leading space to prepend header when necessary (like for data
* PDU before handing over to HCI)
*/
om_ret->om_data += 4;
rem_len -= databuf_len - om_ret->om_pkthdr_len - 4;
/* Allocate and chain mbufs until there's enough space to store complete PDU */
om = om_ret;
while (rem_len > 0) {
om_next = os_msys_get(rem_len, 0);
if (!om_next) {
os_mbuf_free_chain(om_ret);
goto rxpdu_alloc_fail;
}
SLIST_NEXT(om, om_next) = om_next;
om = om_next;
rem_len -= databuf_len;
}
return om_ret;
rxpdu_alloc_fail:
STATS_INC(ble_ll_stats, no_bufs);
return NULL;
}
/**
* Checks to see if the address is a resolvable private address.
*
* NOTE: the addr_type parameter will be 0 if the address is public;
* any other value is random (all non-zero values).
*
* @param addr
* @param addr_type Public (zero) or Random (non-zero) address
*
* @return int
*/
int
ble_ll_is_rpa(const uint8_t *addr, uint8_t addr_type)
{
int rc;
if (addr_type && ((addr[5] & 0xc0) == 0x40)) {
rc = 1;
} else {
rc = 0;
}
return rc;
}
int
ble_ll_addr_is_id(uint8_t *addr, uint8_t addr_type)
{
return !addr_type || ((addr[5] & 0xc0) == 0xc0);
}
int
ble_ll_addr_subtype(const uint8_t *addr, uint8_t addr_type)
{
if (!addr_type) {
return BLE_LL_ADDR_SUBTYPE_IDENTITY;
}
switch (addr[5] >> 6) {
case 0:
return BLE_LL_ADDR_SUBTYPE_NRPA; /* NRPA */
case 1:
return BLE_LL_ADDR_SUBTYPE_RPA; /* RPA */
default:
return BLE_LL_ADDR_SUBTYPE_IDENTITY; /* static random */
}
}
static int
ble_ll_is_valid_addr(const uint8_t *addr)
{
int i;
for (i = 0; i < BLE_DEV_ADDR_LEN; ++i) {
if (addr[i]) {
return 1;
}
}
return 0;
}
/* Checks to see that the device is a valid random address */
int
ble_ll_is_valid_random_addr(const uint8_t *addr)
{
int i;
int rc;
uint16_t sum;
uint8_t addr_type;
/* Make sure all bits are neither one nor zero */
sum = 0;
for (i = 0; i < (BLE_DEV_ADDR_LEN -1); ++i) {
sum += addr[i];
}
sum += addr[5] & 0x3f;
if ((sum == 0) || (sum == ((5*255) + 0x3f))) {
return 0;
}
/* Get the upper two bits of the address */
rc = 1;
addr_type = addr[5] & 0xc0;
if (addr_type == 0xc0) {
/* Static random address. No other checks needed */
} else if (addr_type == 0x40) {
/* Resolvable */
sum = addr[3] + addr[4] + (addr[5] & 0x3f);
if ((sum == 0) || (sum == (255 + 255 + 0x3f))) {
rc = 0;
}
} else if (addr_type == 0) {
/* non-resolvable. Cant be equal to public */
if (!memcmp(g_dev_addr, addr, BLE_DEV_ADDR_LEN)) {
rc = 0;
}
} else {
/* Invalid upper two bits */
rc = 0;
}
return rc;
}
int
ble_ll_is_valid_own_addr_type(uint8_t own_addr_type, const uint8_t *random_addr)
{
int rc;
switch (own_addr_type) {
case BLE_HCI_ADV_OWN_ADDR_PUBLIC:
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY)
case BLE_HCI_ADV_OWN_ADDR_PRIV_PUB:
#endif
rc = ble_ll_is_valid_addr(g_dev_addr);
break;
case BLE_HCI_ADV_OWN_ADDR_RANDOM:
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY)
case BLE_HCI_ADV_OWN_ADDR_PRIV_RAND:
#endif
rc = ble_ll_is_valid_addr(random_addr);
break;
default:
rc = 0;
break;
}
return rc;
}
int
ble_ll_set_public_addr(const uint8_t *addr)
{
memcpy(g_dev_addr, addr, BLE_DEV_ADDR_LEN);
return BLE_ERR_SUCCESS;
}
/**
* Called from the HCI command parser when the set random address command
* is received.
*
* Context: Link Layer task (HCI command parser)
*
* @param addr Pointer to address
*
* @return int 0: success
*/
int
ble_ll_set_random_addr(const uint8_t *cmdbuf, uint8_t len, bool hci_adv_ext)
{
const struct ble_hci_le_set_rand_addr_cp *cmd = (const void *) cmdbuf;
if (len < sizeof(*cmd)) {
return BLE_ERR_INV_HCI_CMD_PARMS;
}
/* If the Host issues this command when scanning or legacy advertising is
* enabled, the Controller shall return the error code Command Disallowed.
*
* Test specification extends this also to initiating.
*/
#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
if (g_ble_ll_conn_create_sm.connsm) {
return BLE_ERR_CMD_DISALLOWED;
}
#endif
#if MYNEWT_VAL(BLE_LL_ROLE_OBSERVER)
if (ble_ll_scan_enabled()){
return BLE_ERR_CMD_DISALLOWED;
}
#endif
#if MYNEWT_VAL(BLE_LL_ROLE_BROADCASTER)
if (!hci_adv_ext && ble_ll_adv_enabled()) {
return BLE_ERR_CMD_DISALLOWED;
}
#endif
memcpy(g_random_addr, cmd->addr, BLE_DEV_ADDR_LEN);
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
#if MYNEWT_VAL(BLE_LL_ROLE_BROADCASTER)
/* For instance 0 we need same address if legacy advertising might be
* used. If extended advertising is in use than this command doesn't
* affect instance 0.
*/
if (!hci_adv_ext)
ble_ll_adv_set_random_addr(cmd->addr, 0);
#endif
#endif
return BLE_ERR_SUCCESS;
}
/**
* Checks to see if an address is our device address (either public or
* random)
*
* @param addr
* @param addr_type
*
* @return int 0: not our device address. 1: is our device address
*/
int
ble_ll_is_our_devaddr(uint8_t *addr, int addr_type)
{
int rc;
uint8_t *our_addr;
if (addr_type) {
our_addr = g_random_addr;
} else {
our_addr = g_dev_addr;
}
rc = 0;
if (!memcmp(our_addr, addr, BLE_DEV_ADDR_LEN)) {
rc = 1;
}
return rc;
}
/**
* Get identity address
*
* @param addr_type Random (1). Public(0)
*
* @return pointer to identity address of given type.
*/
uint8_t*
ble_ll_get_our_devaddr(uint8_t addr_type)
{
if (addr_type) {
return g_random_addr;
}
return g_dev_addr;
}
/**
* Wait for response timeout function
*
* Context: interrupt (ble scheduler)
*
* @param arg
*/
void
ble_ll_wfr_timer_exp(void *arg)
{
int rx_start;
uint8_t lls;
rx_start = ble_phy_rx_started();
lls = g_ble_ll_data.ll_state;
ble_ll_trace_u32x3(BLE_LL_TRACE_ID_WFR_EXP, lls, ble_phy_xcvr_state_get(),
(uint32_t)rx_start);
/* If we have started a reception, there is nothing to do here */
if (!rx_start) {
switch (lls) {
#if MYNEWT_VAL(BLE_LL_ROLE_BROADCASTER)
case BLE_LL_STATE_ADV:
ble_ll_adv_wfr_timer_exp();
break;
#endif
#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL) || MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
case BLE_LL_STATE_CONNECTION:
ble_ll_conn_wfr_timer_exp();
break;
#endif
#if MYNEWT_VAL(BLE_LL_ROLE_OBSERVER)
case BLE_LL_STATE_SCANNING:
ble_ll_scan_wfr_timer_exp();
break;
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
case BLE_LL_STATE_SCAN_AUX:
ble_ll_scan_aux_wfr_timer_exp();
break;
#endif
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV)
case BLE_LL_STATE_SYNC:
ble_ll_sync_wfr_timer_exp();
break;
#endif
#endif
#if MYNEWT_VAL(BLE_LL_DTM)
case BLE_LL_STATE_DTM:
ble_ll_dtm_wfr_timer_exp();
break;
#endif
#if MYNEWT_VAL(BLE_LL_EXT)
case BLE_LL_STATE_EXTERNAL:
ble_ll_ext_wfr_timer_exp();
break;
#endif
default:
break;
}
}
}
/**
* ll tx pkt in proc
*
* Process ACL data packet input from host
*
* Context: Link layer task
*
*/
#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL) || MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
static void
ble_ll_tx_pkt_in(void)
{
uint16_t handle;
uint16_t length;
uint16_t pb;
struct os_mbuf_pkthdr *pkthdr;
struct os_mbuf *om;
os_sr_t sr;
/* Drain all packets off the queue */
while (STAILQ_FIRST(&g_ble_ll_data.ll_tx_pkt_q)) {
/* Get mbuf pointer from packet header pointer */
pkthdr = STAILQ_FIRST(&g_ble_ll_data.ll_tx_pkt_q);
om = (struct os_mbuf *)((uint8_t *)pkthdr - sizeof(struct os_mbuf));
/* Remove from queue */
OS_ENTER_CRITICAL(sr);
STAILQ_REMOVE_HEAD(&g_ble_ll_data.ll_tx_pkt_q, omp_next);
OS_EXIT_CRITICAL(sr);
/* Strip HCI ACL header to get handle and length */
handle = get_le16(om->om_data);
length = get_le16(om->om_data + 2);
os_mbuf_adj(om, sizeof(struct hci_data_hdr));
/* Do some basic error checking */
pb = handle & 0x3000;
if ((pkthdr->omp_len != length) || (pb > 0x1000) || (length == 0)) {
/* This is a bad ACL packet. Count a stat and free it */
STATS_INC(ble_ll_stats, bad_acl_hdr);
os_mbuf_free_chain(om);
continue;
}
/* Hand to connection state machine */
ble_ll_conn_tx_pkt_in(om, handle, length);
}
}
#endif
/**
* Count Link Layer statistics for received PDUs
*
* Context: Link layer task
*
* @param hdr
* @param len
*/
static void
ble_ll_count_rx_stats(struct ble_mbuf_hdr *hdr, uint16_t len, uint8_t pdu_type)
{
uint8_t crcok;
bool connection_data;
crcok = BLE_MBUF_HDR_CRC_OK(hdr);
#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL) || MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
connection_data = (BLE_MBUF_HDR_RX_STATE(hdr) == BLE_LL_STATE_CONNECTION);
#else
connection_data = false;
#endif
#if MYNEWT_VAL(BLE_LL_DTM)
/* Reuse connection stats for DTM */
if (!connection_data) {
connection_data = (BLE_MBUF_HDR_RX_STATE(hdr) == BLE_LL_STATE_DTM);
}
#endif
if (crcok) {
if (connection_data) {
STATS_INC(ble_ll_stats, rx_data_pdu_crc_ok);
STATS_INCN(ble_ll_stats, rx_data_bytes_crc_ok, len);
} else {
STATS_INC(ble_ll_stats, rx_adv_pdu_crc_ok);
STATS_INCN(ble_ll_stats, rx_adv_bytes_crc_ok, len);
ble_ll_count_rx_adv_pdus(pdu_type);
}
} else {
if (connection_data) {
STATS_INC(ble_ll_stats, rx_data_pdu_crc_err);
STATS_INCN(ble_ll_stats, rx_data_bytes_crc_err, len);
} else {
STATS_INC(ble_ll_stats, rx_adv_pdu_crc_err);
STATS_INCN(ble_ll_stats, rx_adv_bytes_crc_err, len);
}
}
}
/**
* ll rx pkt in
*
* Process received packet from PHY.
*
* Context: Link layer task
*
*/
static void
ble_ll_rx_pkt_in(void)
{
os_sr_t sr;
uint8_t pdu_type;
uint8_t *rxbuf;
struct os_mbuf_pkthdr *pkthdr;
struct ble_mbuf_hdr *ble_hdr;
struct os_mbuf *m;
/* Drain all packets off the queue */
while (STAILQ_FIRST(&g_ble_ll_data.ll_rx_pkt_q)) {
/* Get mbuf pointer from packet header pointer */
pkthdr = STAILQ_FIRST(&g_ble_ll_data.ll_rx_pkt_q);
m = (struct os_mbuf *)((uint8_t *)pkthdr - sizeof(struct os_mbuf));
/* Remove from queue */
OS_ENTER_CRITICAL(sr);
STAILQ_REMOVE_HEAD(&g_ble_ll_data.ll_rx_pkt_q, omp_next);
OS_EXIT_CRITICAL(sr);
/* Note: pdu type wont get used unless this is an advertising pdu */
ble_hdr = BLE_MBUF_HDR_PTR(m);
rxbuf = m->om_data;
pdu_type = rxbuf[0] & BLE_ADV_PDU_HDR_TYPE_MASK;
ble_ll_count_rx_stats(ble_hdr, pkthdr->omp_len, pdu_type);
/* Process the data or advertising pdu */
/* Process the PDU */
switch (BLE_MBUF_HDR_RX_STATE(ble_hdr)) {
#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL) || MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
case BLE_LL_STATE_CONNECTION:
ble_ll_conn_rx_data_pdu(m, ble_hdr);
/* m is going to be free by function above */
m = NULL;
break;
#endif
#if MYNEWT_VAL(BLE_LL_ROLE_BROADCASTER)
case BLE_LL_STATE_ADV:
ble_ll_adv_rx_pkt_in(pdu_type, rxbuf, ble_hdr);
break;
#endif
#if MYNEWT_VAL(BLE_LL_ROLE_OBSERVER)
case BLE_LL_STATE_SCANNING:
ble_ll_scan_rx_pkt_in(pdu_type, m, ble_hdr);
break;
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV)
case BLE_LL_STATE_SYNC:
ble_ll_sync_rx_pkt_in(m, ble_hdr);
break;
#endif
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
case BLE_LL_STATE_SCAN_AUX:
ble_ll_scan_aux_rx_pkt_in(m, ble_hdr);
break;
#endif
#endif
#if MYNEWT_VAL(BLE_LL_DTM)
case BLE_LL_STATE_DTM:
ble_ll_dtm_rx_pkt_in(m, ble_hdr);
break;
#endif
#if MYNEWT_VAL(BLE_LL_EXT)
case BLE_LL_STATE_EXTERNAL:
ble_ll_ext_rx_pkt_in(m, ble_hdr);
break;
#endif
default:
/* Any other state should never occur */
STATS_INC(ble_ll_stats, bad_ll_state);
break;
}
if (m) {
/* Free the packet buffer */
os_mbuf_free_chain(m);
}
}
}
/**
* Called to put a packet on the Link Layer receive packet queue.
*
* @param rxpdu Pointer to received PDU
*/
void
ble_ll_rx_pdu_in(struct os_mbuf *rxpdu)
{
struct os_mbuf_pkthdr *pkthdr;
pkthdr = OS_MBUF_PKTHDR(rxpdu);
STAILQ_INSERT_TAIL(&g_ble_ll_data.ll_rx_pkt_q, pkthdr, omp_next);
ble_ll_event_add(&g_ble_ll_data.ll_rx_pkt_ev);
}
#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL) || MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
/**
* Called to put a packet on the Link Layer transmit packet queue.
*
* @param txpdu Pointer to transmit packet
*/
void
ble_ll_acl_data_in(struct os_mbuf *txpkt)
{
os_sr_t sr;
struct os_mbuf_pkthdr *pkthdr;
pkthdr = OS_MBUF_PKTHDR(txpkt);
OS_ENTER_CRITICAL(sr);
STAILQ_INSERT_TAIL(&g_ble_ll_data.ll_tx_pkt_q, pkthdr, omp_next);
OS_EXIT_CRITICAL(sr);
ble_ll_event_add(&g_ble_ll_data.ll_tx_pkt_ev);
}
/**
* Called to post event to Link Layer when a data buffer overflow has
* occurred.
*
* Context: Interrupt
*
*/
void
ble_ll_data_buffer_overflow(void)
{
ble_ll_event_add(&g_ble_ll_data.ll_dbuf_overflow_ev);
}
#endif
/**
* Called when a HW error occurs.
*
* Context: Interrupt
*/
void
ble_ll_hw_error(void)
{
ble_npl_callout_reset(&g_ble_ll_data.ll_hw_err_timer, 0);
}
/**
* Called when the HW error timer expires.
*
* @param arg
*/
static void
ble_ll_hw_err_timer_cb(struct ble_npl_event *ev)
{
if (ble_ll_hci_ev_hw_err(BLE_HW_ERR_HCI_SYNC_LOSS)) {
/*
* Restart callout if failed to allocate event. Try to allocate an
* event every 50 milliseconds (or each OS tick if a tick is longer
* than 100 msecs).
*/
ble_npl_callout_reset(&g_ble_ll_data.ll_hw_err_timer,
ble_npl_time_ms_to_ticks32(50));
}
}
/**
* Called upon start of received PDU
*
* Context: Interrupt
*
* @param rxpdu
* chan
*
* @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_rx_start(uint8_t *rxbuf, uint8_t chan, struct ble_mbuf_hdr *rxhdr)
{
int rc;
uint8_t pdu_type;
/* Advertising channel PDU */
pdu_type = rxbuf[0] & BLE_ADV_PDU_HDR_TYPE_MASK;
ble_ll_trace_u32x2(BLE_LL_TRACE_ID_RX_START, g_ble_ll_data.ll_state,
pdu_type);
switch (g_ble_ll_data.ll_state) {
#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL) || MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
case BLE_LL_STATE_CONNECTION:
rc = ble_ll_conn_rx_isr_start(rxhdr, ble_phy_access_addr_get());
break;
#endif
#if MYNEWT_VAL(BLE_LL_ROLE_BROADCASTER)
case BLE_LL_STATE_ADV:
rc = ble_ll_adv_rx_isr_start(pdu_type);
break;
#endif
#if MYNEWT_VAL(BLE_LL_ROLE_OBSERVER)
case BLE_LL_STATE_SCANNING:
rc = ble_ll_scan_rx_isr_start(pdu_type, &rxhdr->rxinfo.flags);
break;
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV)
case BLE_LL_STATE_SYNC:
rc = ble_ll_sync_rx_isr_start(pdu_type, rxhdr);
break;
#endif
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
case BLE_LL_STATE_SCAN_AUX:
rc = ble_ll_scan_aux_rx_isr_start(pdu_type, rxhdr);
break;
#endif
#endif
#if MYNEWT_VAL(BLE_LL_DTM)
case BLE_LL_STATE_DTM:
rc = ble_ll_dtm_rx_isr_start(rxhdr, ble_phy_access_addr_get());
break;
#endif
#if MYNEWT_VAL(BLE_LL_EXT)
case BLE_LL_STATE_EXTERNAL:
rc = ble_ll_ext_rx_isr_start(pdu_type, rxhdr);
break;
#endif
default:
/* Should not be in this state! */
rc = -1;
STATS_INC(ble_ll_stats, bad_ll_state);
break;
}
return rc;
}
/**
* Called by the PHY when a receive packet has ended.
*
* NOTE: Called from interrupt context!
*
* @param rxbuf Pointer to received PDU data
* rxhdr Pointer to BLE header of received mbuf
*
* @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_rx_end(uint8_t *rxbuf, struct ble_mbuf_hdr *rxhdr)
{
int rc;
int badpkt;
uint8_t pdu_type;
uint8_t len;
uint8_t crcok;
struct os_mbuf *rxpdu;
/* Get CRC status from BLE header */
crcok = BLE_MBUF_HDR_CRC_OK(rxhdr);
/* Get advertising PDU type and length */
pdu_type = rxbuf[0] & BLE_ADV_PDU_HDR_TYPE_MASK;
len = rxbuf[1];
ble_ll_trace_u32x3(BLE_LL_TRACE_ID_RX_END, pdu_type, len,
rxhdr->rxinfo.flags);
#if MYNEWT_VAL(BLE_LL_EXT)
if (BLE_MBUF_HDR_RX_STATE(rxhdr) == BLE_LL_STATE_EXTERNAL) {
rc = ble_ll_ext_rx_isr_end(rxbuf, rxhdr);
return rc;
}
#endif
#if MYNEWT_VAL(BLE_LL_DTM)
if (BLE_MBUF_HDR_RX_STATE(rxhdr) == BLE_LL_STATE_DTM) {
rc = ble_ll_dtm_rx_isr_end(rxbuf, rxhdr);
return rc;
}
#endif
#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL) || MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
if (BLE_MBUF_HDR_RX_STATE(rxhdr) == BLE_LL_STATE_CONNECTION) {
rc = ble_ll_conn_rx_isr_end(rxbuf, rxhdr);
return rc;
}
#endif
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV) && MYNEWT_VAL(BLE_LL_ROLE_OBSERVER)
if (BLE_MBUF_HDR_RX_STATE(rxhdr) == BLE_LL_STATE_SYNC) {
rc = ble_ll_sync_rx_isr_end(rxbuf, rxhdr);
return rc;
}
#endif
/* If the CRC checks, make sure lengths check! */
badpkt = 0;
if (crcok) {
switch (pdu_type) {
case BLE_ADV_PDU_TYPE_SCAN_REQ:
case BLE_ADV_PDU_TYPE_ADV_DIRECT_IND:
if (len != BLE_SCAN_REQ_LEN) {
badpkt = 1;
}
break;
case BLE_ADV_PDU_TYPE_SCAN_RSP:
case BLE_ADV_PDU_TYPE_ADV_IND:
case BLE_ADV_PDU_TYPE_ADV_SCAN_IND:
case BLE_ADV_PDU_TYPE_ADV_NONCONN_IND:
if ((len < BLE_DEV_ADDR_LEN) || (len > BLE_ADV_SCAN_IND_MAX_LEN)) {
badpkt = 1;
}
break;
case BLE_ADV_PDU_TYPE_AUX_CONNECT_RSP:
break;
case BLE_ADV_PDU_TYPE_ADV_EXT_IND:
break;
case BLE_ADV_PDU_TYPE_CONNECT_IND:
if (len != BLE_CONNECT_REQ_LEN) {
badpkt = 1;
}
break;
default:
badpkt = 1;
break;
}
/* If this is a malformed packet, just kill it here */
if (badpkt) {
STATS_INC(ble_ll_stats, rx_adv_malformed_pkts);
}
}
/* Hand packet to the appropriate state machine (if crc ok) */
rxpdu = NULL;
switch (BLE_MBUF_HDR_RX_STATE(rxhdr)) {
#if MYNEWT_VAL(BLE_LL_ROLE_BROADCASTER)
case BLE_LL_STATE_ADV:
if (!badpkt) {
rxpdu = ble_ll_rxpdu_alloc(len + BLE_LL_PDU_HDR_LEN);
if (rxpdu) {
ble_phy_rxpdu_copy(rxbuf, rxpdu);
}
}
rc = ble_ll_adv_rx_isr_end(pdu_type, rxpdu, crcok);
break;
#endif
#if MYNEWT_VAL(BLE_LL_ROLE_OBSERVER)
case BLE_LL_STATE_SCANNING:
if (!badpkt) {
rxpdu = ble_ll_rxpdu_alloc(len + BLE_LL_PDU_HDR_LEN);
if (rxpdu) {
ble_phy_rxpdu_copy(rxbuf, rxpdu);
}
}
rc = ble_ll_scan_rx_isr_end(rxpdu, crcok);
break;
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
case BLE_LL_STATE_SCAN_AUX:
if (!badpkt) {
rxpdu = ble_ll_rxpdu_alloc(len + BLE_LL_PDU_HDR_LEN);
if (rxpdu) {
ble_phy_rxpdu_copy(rxbuf, rxpdu);
}
}
rc = ble_ll_scan_aux_rx_isr_end(rxpdu, crcok);
break;
#endif
#endif
default:
rc = -1;
STATS_INC(ble_ll_stats, bad_ll_state);
break;
}
/* Hand packet up to higher layer (regardless of CRC failure) */
if (rxpdu) {
ble_ll_rx_pdu_in(rxpdu);
}
return rc;
}
uint8_t
ble_ll_tx_mbuf_pducb(uint8_t *dptr, void *pducb_arg, uint8_t *hdr_byte)
{
struct os_mbuf *txpdu;
struct ble_mbuf_hdr *ble_hdr;
txpdu = pducb_arg;
BLE_LL_ASSERT(txpdu);
ble_hdr = BLE_MBUF_HDR_PTR(txpdu);
os_mbuf_copydata(txpdu, ble_hdr->txinfo.offset, ble_hdr->txinfo.pyld_len,
dptr);
*hdr_byte = ble_hdr->txinfo.hdr_byte;
return ble_hdr->txinfo.pyld_len;
}
uint8_t
ble_ll_tx_flat_mbuf_pducb(uint8_t *dptr, void *pducb_arg, uint8_t *hdr_byte)
{
struct os_mbuf *txpdu;
struct ble_mbuf_hdr *ble_hdr;
txpdu = pducb_arg;
BLE_LL_ASSERT(txpdu);
ble_hdr = BLE_MBUF_HDR_PTR(txpdu);
memcpy(dptr, txpdu->om_data, ble_hdr->txinfo.pyld_len);
*hdr_byte = ble_hdr->txinfo.hdr_byte;
return ble_hdr->txinfo.pyld_len;
}
static void
ble_ll_event_rx_pkt(struct ble_npl_event *ev)
{
ble_ll_rx_pkt_in();
}
#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL) || MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
static void
ble_ll_event_tx_pkt(struct ble_npl_event *ev)
{
ble_ll_tx_pkt_in();
}
static void
ble_ll_event_dbuf_overflow(struct ble_npl_event *ev)
{
ble_ll_hci_ev_databuf_overflow();
}
static void
ble_ll_event_comp_pkts(struct ble_npl_event *ev)
{
ble_ll_conn_num_comp_pkts_event_send(NULL);
}
#endif
/**
* Link Layer task.
*
* This is the task that runs the Link Layer.
*
* @param arg
*/
void
ble_ll_task(void *arg)
{
struct ble_npl_event *ev;
/* Init ble phy */
ble_phy_init();
/* Set output power to default */
g_ble_ll_tx_power = ble_ll_tx_power_round(MIN(MYNEWT_VAL(BLE_LL_TX_PWR_DBM),
MYNEWT_VAL(BLE_LL_TX_PWR_MAX_DBM)));
g_ble_ll_tx_power_phy_current = INT8_MAX;
while (1) {
ev = ble_npl_eventq_get(&g_ble_ll_data.ll_evq, BLE_NPL_TIME_FOREVER);
BLE_LL_ASSERT(ev);
ble_npl_event_run(ev);
}
}
/**
* ble ll state set
*
* Called to set the current link layer state.
*
* Context: Interrupt and Link Layer task
*
* @param ll_state
*/
void
ble_ll_state_set(uint8_t ll_state)
{
g_ble_ll_data.ll_state = ll_state;
if (ll_state == BLE_LL_STATE_STANDBY) {
BLE_LL_DEBUG_GPIO(SCHED_ITEM, 0);
}
}
/**
* ble ll state get
*
* Called to get the current link layer state.
*
* Context: Link Layer task (can be called from interrupt context though).
*
* @return ll_state
*/
uint8_t
ble_ll_state_get(void)
{
return g_ble_ll_data.ll_state;
}
/**
* ble ll event send
*
* Add an event to the Link Layer task
*
* @param ev Event to add to the Link Layer event queue.
*/
void
ble_ll_event_add(struct ble_npl_event *ev)
{
ble_npl_eventq_put(&g_ble_ll_data.ll_evq, ev);
}
/**
* ble ll event remove
*
* Remove an event from the Link Layer task
*
* @param ev Event to remove from the Link Layer event queue.
*/
void
ble_ll_event_remove(struct ble_npl_event *ev)
{
ble_npl_eventq_remove(&g_ble_ll_data.ll_evq, ev);
}
/**
* Returns the features supported by the link layer
*
* @return uint8_t bitmask of supported features.
*/
uint64_t
ble_ll_read_supp_states(void)
{
return BLE_LL_SUPPORTED_STATES;
}
/**
* Returns the features supported by the link layer
*
* @return uint64_t bitmask of supported features.
*/
uint64_t
ble_ll_read_supp_features(void)
{
return g_ble_ll_data.ll_supp_features;
}
/**
* Sets the features controlled by the host.
*
* @return HCI command status
*/
int
ble_ll_set_host_feat(const uint8_t *cmdbuf, uint8_t len)
{
const struct ble_hci_le_set_host_feature_cp *cmd = (const void *) cmdbuf;
uint64_t mask;
if (len != sizeof(*cmd)) {
return BLE_ERR_INV_HCI_CMD_PARMS;
}
#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL) || MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
if (!SLIST_EMPTY(&g_ble_ll_conn_active_list)) {
return BLE_ERR_CMD_DISALLOWED;
}
#endif
if ((cmd->bit_num > 0x3F) || (cmd->bit_val > 1)) {
return BLE_ERR_INV_HCI_CMD_PARMS;
}
mask = (uint64_t)1 << (cmd->bit_num);
if (!(mask & BLE_LL_HOST_CONTROLLED_FEATURES)) {
return BLE_ERR_UNSUPPORTED;
}
if (cmd->bit_val == 0) {
g_ble_ll_data.ll_supp_features &= ~(mask);
} else {
g_ble_ll_data.ll_supp_features |= mask;
}
return BLE_ERR_SUCCESS;
}
/**
* Flush a link layer packet queue.
*
* @param pktq
*/
static void
ble_ll_flush_pkt_queue(struct ble_ll_pkt_q *pktq)
{
struct os_mbuf_pkthdr *pkthdr;
struct os_mbuf *om;
/* FLush all packets from Link layer queues */
while (STAILQ_FIRST(pktq)) {
/* Get mbuf pointer from packet header pointer */
pkthdr = STAILQ_FIRST(pktq);
om = OS_MBUF_PKTHDR_TO_MBUF(pkthdr);
/* Remove from queue and free the mbuf */
STAILQ_REMOVE_HEAD(pktq, omp_next);
os_mbuf_free_chain(om);
}
}
/**
* Called to initialize a mbuf used by the controller
*
* NOTE: this is only used when the mbuf is created by the controller;
* it should not be used for data packets (ACL data packets) that come from
* the host. This routine assumes that the entire pdu length can fit in
* one mbuf contiguously.
*
* @param m
* @param pdulen
* @param hdr
*/
void
ble_ll_mbuf_init(struct os_mbuf *m, uint8_t pdulen, uint8_t hdr)
{
struct ble_mbuf_hdr *ble_hdr;
/* Set mbuf length and packet length */
m->om_len = pdulen;
OS_MBUF_PKTHDR(m)->omp_len = pdulen;
/* Set BLE transmit header */
ble_hdr = BLE_MBUF_HDR_PTR(m);
ble_hdr->txinfo.flags = 0;
ble_hdr->txinfo.offset = 0;
ble_hdr->txinfo.pyld_len = pdulen;
ble_hdr->txinfo.hdr_byte = hdr;
}
static void
ble_ll_validate_task(void)
{
#ifdef MYNEWT
#ifndef NDEBUG
struct os_task_info oti;
os_task_info_get(&g_ble_ll_task, &oti);
BLE_LL_ASSERT(oti.oti_stkusage < oti.oti_stksize);
#endif
#endif
}
/**
* Called to reset the controller. This performs a "software reset" of the link
* layer; it does not perform a HW reset of the controller nor does it reset
* the HCI interface.
*
* Context: Link Layer task (HCI command)
*
* @return int The ble error code to place in the command complete event that
* is returned when this command is issued.
*/
int
ble_ll_reset(void)
{
uint8_t phy_mask;
int rc;
os_sr_t sr;
/* do sanity check on LL task stack */
ble_ll_validate_task();
OS_ENTER_CRITICAL(sr);
ble_phy_disable();
ble_ll_sched_stop();
#if MYNEWT_VAL(BLE_LL_ROLE_OBSERVER)
ble_ll_scan_reset();
#endif
ble_ll_rfmgmt_reset();
OS_EXIT_CRITICAL(sr);
#if MYNEWT_VAL(BLE_LL_EXT)
ble_ll_ext_reset();
#endif
#if MYNEWT_VAL(BLE_LL_ROLE_BROADCASTER)
/* Stop any advertising */
ble_ll_adv_reset();
#endif
#if MYNEWT_VAL(BLE_LL_DTM)
ble_ll_dtm_reset();
#endif
/* Stop sync */
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV) && MYNEWT_VAL(BLE_LL_ROLE_OBSERVER)
ble_ll_sync_reset();
#endif
/* reset power compensation */
g_ble_ll_tx_power_compensation = 0;
g_ble_ll_rx_power_compensation = 0;
/* Set output power to default */
g_ble_ll_tx_power = ble_ll_tx_power_round(MIN(MYNEWT_VAL(BLE_LL_TX_PWR_DBM),
MYNEWT_VAL(BLE_LL_TX_PWR_MAX_DBM)));
g_ble_ll_tx_power_phy_current = INT8_MAX;
/* FLush all packets from Link layer queues */
ble_ll_flush_pkt_queue(&g_ble_ll_data.ll_tx_pkt_q);
ble_ll_flush_pkt_queue(&g_ble_ll_data.ll_rx_pkt_q);
/* Reset LL stats */
STATS_RESET(ble_ll_stats);
/* Reset any preferred PHYs */
phy_mask = BLE_PHY_MASK_1M;
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_2M_PHY)
phy_mask |= BLE_PHY_MASK_2M;
#endif
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY)
phy_mask |= BLE_PHY_MASK_CODED;
#endif
phy_mask &= MYNEWT_VAL(BLE_LL_CONN_PHY_DEFAULT_PREF_MASK);
BLE_LL_ASSERT(phy_mask);
g_ble_ll_data.ll_pref_tx_phys = phy_mask;
g_ble_ll_data.ll_pref_rx_phys = phy_mask;
/* Enable all channels in channel map */
g_ble_ll_data.chan_map_used = BLE_PHY_NUM_DATA_CHANS;
memset(g_ble_ll_data.chan_map, 0xff, BLE_LL_CHAN_MAP_LEN - 1);
g_ble_ll_data.chan_map[4] = 0x1f;
#if MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL) || MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
/* Reset connection module */
ble_ll_conn_module_reset();
#endif
/* All this does is re-initialize the event masks so call the hci init */
ble_ll_hci_init();
/* Reset scheduler */
ble_ll_sched_init();
/* Set state to standby */
ble_ll_state_set(BLE_LL_STATE_STANDBY);
/* Reset our random address */
memset(g_random_addr, 0, BLE_DEV_ADDR_LEN);
/* Clear the whitelist */
ble_ll_whitelist_clear();
/* Reset resolving list */
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY)
ble_ll_resolv_list_reset();
#endif
#if MYNEWT_VAL(BLE_FEM_PA)
ble_fem_pa_init();
#endif
#if MYNEWT_VAL(BLE_FEM_LNA)
ble_fem_lna_init();
#endif
#if MYNEWT_VAL(BLE_LL_ISO)
ble_ll_isoal_reset();
#endif
#if MYNEWT_VAL(BLE_LL_ISO_BROADCASTER)
ble_ll_iso_big_reset();
#endif
/* Re-initialize the PHY */
rc = ble_phy_init();
return rc;
}
uint16_t
ble_ll_pdu_max_tx_octets_get(uint32_t usecs, int phy_mode)
{
uint32_t header_tx_time;
uint16_t octets = 0;
BLE_LL_ASSERT(phy_mode < BLE_PHY_NUM_MODE);
header_tx_time = ble_ll_pdu_us(0, phy_mode);
/*
* Current conn max tx time can be too short to even send a packet header
* and this can happen if we changed connection form uncoded to coded phy.
* However, the lower bound for conn max tx time (all of them) depends on
* current phy (uncoded/coded) but it always allows to send at least 27
* bytes of payload thus we always return at least 27 from here.
*
* Reference:
* Core v5.0, Vol 6, Part B, section 4.5.10
* see connEffectiveMaxTxTime and connEffectiveMaxRxTime definitions
*/
if (usecs < header_tx_time) {
return 27;
}
usecs -= header_tx_time;
if (phy_mode == BLE_PHY_MODE_1M) {
/* 8 usecs per byte */
octets = usecs >> 3;
} else if (phy_mode == BLE_PHY_MODE_2M) {
/* 4 usecs per byte */
octets = usecs >> 2;
} else if (phy_mode == BLE_PHY_MODE_CODED_125KBPS) {
/* S=8 => 8 * 8 = 64 usecs per byte */
octets = usecs >> 6;
} else if (phy_mode == BLE_PHY_MODE_CODED_500KBPS) {
/* S=2 => 2 * 8 = 16 usecs per byte */
octets = usecs >> 4;
} else {
BLE_LL_ASSERT(0);
}
/* see comment at the beginning */
return MAX(27, octets);
}
static inline bool
ble_ll_is_addr_empty(const uint8_t *addr)
{
return memcmp(addr, BLE_ADDR_ANY, BLE_DEV_ADDR_LEN) == 0;
}
#if MYNEWT_VAL(BLE_LL_HCI_VS_EVENT_ON_ASSERT)
void
ble_ll_assert(const char *file, unsigned line)
{
ble_ll_hci_ev_send_vs_assert(file, line);
if (hal_debugger_connected()) {
__BKPT(0);
}
while (1);
}
#endif
/**
* Initialize the Link Layer. Should be called only once
*
* @return int
*/
void
ble_ll_init(void)
{
int rc;
uint64_t features;
#if MYNEWT_VAL(BLE_LL_PUBLIC_DEV_ADDR)
uint64_t pub_dev_addr;
int i;
#endif
ble_addr_t addr;
struct ble_ll_obj *lldata;
/* Ensure this function only gets called by sysinit. */
SYSINIT_ASSERT_ACTIVE();
ble_ll_trace_init();
ble_phy_trace_init();
/* Set public device address if not already set */
if (ble_ll_is_addr_empty(g_dev_addr)) {
#if MYNEWT_VAL(BLE_LL_PUBLIC_DEV_ADDR)
pub_dev_addr = MYNEWT_VAL(BLE_LL_PUBLIC_DEV_ADDR);
for (i = 0; i < BLE_DEV_ADDR_LEN; i++) {
g_dev_addr[i] = pub_dev_addr & 0xff;
pub_dev_addr >>= 8;
}
#else
memcpy(g_dev_addr, MYNEWT_VAL(BLE_PUBLIC_DEV_ADDR), BLE_DEV_ADDR_LEN);
#endif
if (ble_ll_is_addr_empty(g_dev_addr)) {
rc = ble_hw_get_public_addr(&addr);
if (!rc) {
memcpy(g_dev_addr, &addr.val[0], BLE_DEV_ADDR_LEN);
}
}
}
ble_ll_rfmgmt_init();
/* Get pointer to global data object */
lldata = &g_ble_ll_data;
#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL) || MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
/* Set acl pkt size and number */
lldata->ll_num_acl_pkts = MYNEWT_VAL(BLE_TRANSPORT_ACL_FROM_HS_COUNT);
lldata->ll_acl_pkt_size = MYNEWT_VAL(BLE_TRANSPORT_ACL_SIZE);
#endif
#if MYNEWT_VAL(BLE_LL_ISO)
lldata->ll_num_iso_pkts = MYNEWT_VAL(BLE_TRANSPORT_ISO_FROM_HS_COUNT);
lldata->ll_iso_pkt_size = MYNEWT_VAL(BLE_TRANSPORT_ISO_SIZE);
#endif
/* Initialize eventq */
ble_npl_eventq_init(&lldata->ll_evq);
/* Initialize the transmit (from host) and receive (from phy) queues */
STAILQ_INIT(&lldata->ll_tx_pkt_q);
STAILQ_INIT(&lldata->ll_rx_pkt_q);
/* Initialize transmit (from host) and receive packet (from phy) event */
ble_npl_event_init(&lldata->ll_rx_pkt_ev, ble_ll_event_rx_pkt, NULL);
#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL) || MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
ble_npl_event_init(&lldata->ll_tx_pkt_ev, ble_ll_event_tx_pkt, NULL);
#endif
#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL) || MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
/* Initialize data buffer overflow event and completed packets */
ble_npl_event_init(&lldata->ll_dbuf_overflow_ev, ble_ll_event_dbuf_overflow, NULL);
ble_npl_event_init(&lldata->ll_comp_pkt_ev, ble_ll_event_comp_pkts, NULL);
#endif
/* Initialize the HW error timer */
ble_npl_callout_init(&g_ble_ll_data.ll_hw_err_timer,
&g_ble_ll_data.ll_evq,
ble_ll_hw_err_timer_cb,
NULL);
/* Initialize LL HCI */
ble_ll_hci_init();
/* Init the scheduler */
ble_ll_sched_init();
#if MYNEWT_VAL(BLE_LL_ROLE_BROADCASTER)
/* Initialize advertiser */
ble_ll_adv_init();
#endif
#if MYNEWT_VAL(BLE_LL_ROLE_OBSERVER)
/* Initialize a scanner */
ble_ll_scan_init();
#endif
#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL) || MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
/* Initialize the connection module */
ble_ll_conn_module_init();
#endif
/* Set the supported features. NOTE: we always support extended reject. */
features = BLE_LL_FEAT_EXTENDED_REJ;
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_DATA_LEN_EXT)
features |= BLE_LL_FEAT_DATA_LEN_EXT;
#endif
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_CONN_PARAM_REQ)
features |= BLE_LL_FEAT_CONN_PARM_REQ;
#endif
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_PERIPH_INIT_FEAT_XCHG)
features |= BLE_LL_FEAT_PERIPH_INIT;
#endif
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION)
features |= BLE_LL_FEAT_LE_ENCRYPTION;
#endif
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY)
features |= (BLE_LL_FEAT_LL_PRIVACY | BLE_LL_FEAT_EXT_SCAN_FILT);
ble_ll_resolv_init();
#endif
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_PING)
features |= BLE_LL_FEAT_LE_PING;
#endif
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV)
features |= BLE_LL_FEAT_EXT_ADV;
#endif
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CSA2)
/* CSA2 */
features |= BLE_LL_FEAT_CSA2;
#endif
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_2M_PHY)
features |= BLE_LL_FEAT_LE_2M_PHY;
#endif
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY)
features |= BLE_LL_FEAT_LE_CODED_PHY;
#endif
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV)
features |= BLE_LL_FEAT_PERIODIC_ADV;
#if MYNEWT_VAL(BLE_LL_ROLE_OBSERVER)
ble_ll_sync_init();
#endif
#endif
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV_SYNC_TRANSFER)
features |= BLE_LL_FEAT_SYNC_TRANS_RECV;
features |= BLE_LL_FEAT_SYNC_TRANS_SEND;
#endif
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_SCA_UPDATE)
features |= BLE_LL_FEAT_SCA_UPDATE;
#endif
#if MYNEWT_VAL(BLE_LL_ISO_BROADCASTER)
features |= BLE_LL_FEAT_ISO_BROADCASTER;
#endif
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_ENHANCED_CONN_UPDATE)
features |= BLE_LL_FEAT_CONN_SUBRATING;
#endif
#if MYNEWT_VAL(BLE_LL_CHANNEL_SOUNDING)
features |= BLE_LL_FEAT_CS;
features |= BLE_LL_FEAT_CS_PCT_QUALITY_IND;
#endif
lldata->ll_supp_features = features;
/* Initialize random number generation */
ble_ll_rand_init();
/* Start the random number generator */
ble_ll_rand_start();
rc = stats_init_and_reg(STATS_HDR(ble_ll_stats),
STATS_SIZE_INIT_PARMS(ble_ll_stats, STATS_SIZE_32),
STATS_NAME_INIT_PARMS(ble_ll_stats),
"ble_ll");
SYSINIT_PANIC_ASSERT(rc == 0);
#if MYNEWT_VAL(BLE_LL_DTM)
ble_ll_dtm_init();
#endif
#if MYNEWT_VAL(BLE_LL_HCI_VS)
ble_ll_hci_vs_init();
#endif
#if MYNEWT_VAL(BLE_LL_ISO)
ble_ll_isoal_init();
#endif
#if MYNEWT_VAL(BLE_LL_ISO_BROADCASTER)
ble_ll_iso_big_init();
#endif
#if MYNEWT_VAL(BLE_LL_EXT)
ble_ll_ext_init();
#endif
#ifdef MYNEWT
/* Initialize the LL task */
os_task_init(&g_ble_ll_task, "ble_ll", ble_ll_task, NULL,
MYNEWT_VAL(BLE_LL_PRIO), OS_WAIT_FOREVER, g_ble_ll_stack,
MYNEWT_VAL(BLE_LL_STACK_SIZE));
#else
/*
* For non-Mynewt OS it is required that OS creates task for LL and run LL
* routine which is wrapped by nimble_port_ll_task_func().
*/
#endif
}
/* Transport APIs for LL side */
int
ble_transport_to_ll_cmd_impl(void *buf)
{
return ble_ll_hci_cmd_rx(buf);
}
int
ble_transport_to_ll_acl_impl(struct os_mbuf *om)
{
return ble_ll_hci_acl_rx(om);
}
int
ble_transport_to_ll_iso_impl(struct os_mbuf *om)
{
return ble_ll_hci_iso_rx(om);
}
void
ble_transport_ll_init(void)
{
/* Tell the host that we are ready to receive packets */
ble_ll_hci_send_noop();
}
int
ble_ll_tx_power_round(int tx_power)
{
#if MYNEWT_VAL(BLE_FEM_PA)
#if MYNEWT_VAL(BLE_FEM_PA_GAIN_TUNABLE)
tx_power = ble_fem_pa_tx_power_round(tx_power);
#else
tx_power = ble_phy_tx_power_round(tx_power);
tx_power += MYNEWT_VAL(BLE_FEM_PA_GAIN);
#endif
#else
tx_power = ble_phy_tx_power_round(tx_power);
#endif
return tx_power;
}
void
ble_ll_tx_power_set(int tx_power)
{
#if MYNEWT_VAL(BLE_FEM_PA)
#if MYNEWT_VAL(BLE_FEM_PA_GAIN_TUNABLE)
/* TODO should rounding be in assert only? or just skip it and assume
* power is already rounded?
*/
tx_power = ble_fem_pa_tx_power_round(tx_power);
tx_power = ble_fem_pa_tx_power_set(tx_power);
#else
tx_power -= MYNEWT_VAL(BLE_FEM_PA_GAIN);
#endif
#endif
/* If current TX power configuration matches requested one we don't need
* to update PHY tx power.
*/
if (g_ble_ll_tx_power_phy_current == tx_power) {
return;
}
g_ble_ll_tx_power_phy_current = tx_power;
ble_phy_tx_power_set(tx_power);
}
int
ble_ll_is_busy(unsigned int flags)
{
#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL) || MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
struct ble_ll_conn_sm *cur;
int i = 0;
#endif
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV) && MYNEWT_VAL(BLE_LL_ROLE_OBSERVER)
if (ble_ll_sync_enabled()) {
return 1;
}
#endif
#if MYNEWT_VAL(BLE_LL_ROLE_BROADCASTER)
if (ble_ll_adv_enabled()) {
return 1;
}
#endif
#if MYNEWT_VAL(BLE_LL_ROLE_OBSERVER)
if (ble_ll_scan_enabled()) {
return 1;
}
#endif
#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL)
if (g_ble_ll_conn_create_sm.connsm) {
return 1;
}
#endif
#if MYNEWT_VAL(BLE_LL_ROLE_CENTRAL) || MYNEWT_VAL(BLE_LL_ROLE_PERIPHERAL)
if (!(flags & BLE_LL_BUSY_EXCLUDE_CONNECTIONS)) {
STAILQ_FOREACH(cur, &g_ble_ll_conn_free_list, free_stqe) {
i++;
}
/* check if all connection objects are free */
if (i < MYNEWT_VAL(BLE_MAX_CONNECTIONS)) {
return 1;
}
}
#endif
return 0;
}