| /* |
| * 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; |
| } |