/* * Copyright (c) 2016-2017 Nordic Semiconductor ASA * Copyright (c) 2016 Vinayak Kariappa Chettimada * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #include #include #include #include #include "ll.h" #include "hal/cpu.h" #include "hal/rand.h" #include "hal/ecb.h" #include "hal/ccm.h" #include "hal/radio.h" #include "hal/debug.h" #include "util/util.h" #include "util/mem.h" #include "util/memq.h" #include "util/mayfly.h" #include "ticker/ticker.h" #include "pdu.h" #include "ctrl.h" #include "ctrl_internal.h" #include "ll_filter.h" #define BT_DBG_ENABLED IS_ENABLED(CONFIG_BLUETOOTH_DEBUG_HCI_DRIVER) #include "common/log.h" #define RADIO_TIFS 150 #define RADIO_CONN_EVENTS(x, y) ((u16_t)(((x) + (y) - 1) / (y))) #define RADIO_TICKER_JITTER_US 16 #define RADIO_TICKER_START_PART_US 300 #define RADIO_TICKER_XTAL_OFFSET_US 1200 #define RADIO_TICKER_PREEMPT_PART_US 0 #define RADIO_TICKER_PREEMPT_PART_MIN_US 0 #define RADIO_TICKER_PREEMPT_PART_MAX_US RADIO_TICKER_XTAL_OFFSET_US #if defined(CONFIG_BLUETOOTH_CONTROLLER_CONN_RSSI) #define RADIO_RSSI_SAMPLE_COUNT 10 #define RADIO_RSSI_THRESHOLD 4 #endif /* CONFIG_BLUETOOTH_CONTROLLER_CONN_RSSI */ #define RADIO_IRK_COUNT_MAX 8 #define SILENT_CONNECTION 0 enum role { ROLE_NONE, ROLE_ADV, ROLE_SCAN, ROLE_SLAVE, ROLE_MASTER, }; enum state { STATE_NONE, STATE_RX, STATE_TX, STATE_CLOSE, STATE_STOP, STATE_ABORT, }; struct advertiser { struct shdr hdr; u8_t is_enabled:1; u8_t chan_map_current:3; u8_t rfu:4; #if defined(CONFIG_BLUETOOTH_CONTROLLER_ADV_EXT) u8_t phy_p:3; #endif /* CONFIG_BLUETOOTH_CONTROLLER_ADV_EXT */ u8_t chan_map:3; u8_t filter_policy:2; #if defined(CONFIG_BLUETOOTH_CONTROLLER_PRIVACY) u8_t rl_idx; #endif /* CONFIG_BLUETOOTH_CONTROLLER_PRIVACY */ struct radio_adv_data adv_data; struct radio_adv_data scan_data; struct connection *conn; }; struct scanner { struct shdr hdr; u8_t is_enabled:1; u8_t state:1; u8_t chan:2; u8_t rfu:4; #if defined(CONFIG_BLUETOOTH_CONTROLLER_ADV_EXT) u8_t phy:3; #endif /* CONFIG_BLUETOOTH_CONTROLLER_ADV_EXT */ u8_t type:1; u8_t filter_policy:2; u8_t adv_addr_type:1; u8_t init_addr_type:1; #if defined(CONFIG_BLUETOOTH_CONTROLLER_PRIVACY) u8_t rpa_gen:1; /* initiator only */ u8_t rl_idx; #endif /* CONFIG_BLUETOOTH_CONTROLLER_PRIVACY */ u8_t init_addr[BDADDR_SIZE]; u8_t adv_addr[BDADDR_SIZE]; u32_t ticks_window; u16_t conn_interval; u16_t conn_latency; u16_t conn_timeout; u32_t ticks_conn_slot; struct connection *conn; u32_t win_offset_us; }; static struct { struct device *hf_clock; u32_t ticks_anchor; u32_t remainder_anchor; u8_t volatile ticker_id_prepare; u8_t volatile ticker_id_event; u8_t volatile ticker_id_stop; enum role volatile role; enum state state; struct advertiser advertiser; struct scanner scanner; void *conn_pool; void *conn_free; u8_t connection_count; struct connection *conn_curr; u8_t packet_counter; u8_t crc_expire; u8_t data_chan_map[5]; u8_t data_chan_count; u8_t sca; #if defined(CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH) /* DLE global settings */ u16_t default_tx_octets; u16_t default_tx_time; #endif /* CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH */ #if defined(CONFIG_BLUETOOTH_CONTROLLER_PHY) u16_t default_phy_tx; u16_t default_phy_rx; #endif /* CONFIG_BLUETOOTH_CONTROLLER_PHY */ /** @todo below members to be made role specific and quota managed for * Rx-es. */ /* Advertiser, Scanner, and Connections Rx data pool */ void *pkt_rx_data_pool; void *pkt_rx_data_free; u16_t packet_data_octets_max; u16_t packet_rx_data_pool_size; u16_t packet_rx_data_size; u8_t packet_rx_data_count; /* Free queue Rx data buffers */ struct radio_pdu_node_rx **packet_rx; u8_t packet_rx_count; u8_t volatile packet_rx_last; u8_t packet_rx_acquire; /* Controller to Host event-cum-data queue */ void *link_rx_pool; void *link_rx_free; void *link_rx_head; void *volatile link_rx_tail; u8_t link_rx_data_quota; /* Connections common Tx ctrl and data pool */ void *pkt_tx_ctrl_pool; void *pkt_tx_ctrl_free; void *pkt_tx_data_pool; void *pkt_tx_data_free; u16_t packet_tx_data_size; /* Host to Controller Tx, and Controller to Host Num complete queue */ struct pdu_data_q_tx *pkt_tx; struct pdu_data_q_tx *pkt_release; u8_t packet_tx_count; u8_t volatile packet_tx_first; u8_t packet_tx_last; u8_t packet_release_first; u8_t volatile packet_release_last; u16_t fc_handle[TRIPLE_BUFFER_SIZE]; u8_t volatile fc_req; u8_t fc_ack; u8_t fc_ena; u32_t ticks_active_to_start; struct connection *conn_upd; } _radio; static u16_t const gc_lookup_ppm[] = { 500, 250, 150, 100, 75, 50, 30, 20 }; static void common_init(void); static void ticker_success_assert(u32_t status, void *params); static void ticker_stop_adv_assert(u32_t status, void *params); static void ticker_stop_scan_assert(u32_t status, void *params); static void ticker_update_adv_assert(u32_t status, void *params); static void ticker_update_slave_assert(u32_t status, void *params); static void event_inactive(u32_t ticks_at_expire, u32_t remainder, u16_t lazy, void *context); #if defined(RADIO_UNIT_TEST) && \ defined(CONFIG_BLUETOOTH_CONTROLLER_CHAN_SEL_2) static void chan_sel_2_ut(void); #endif /* CONFIG_BLUETOOTH_CONTROLLER_CHAN_SEL_2 */ static void adv_setup(void); static void event_adv(u32_t ticks_at_expire, u32_t remainder, u16_t lazy, void *context); static void event_scan(u32_t ticks_at_expire, u32_t remainder, u16_t lazy, void *context); static void event_slave_prepare(u32_t ticks_at_expire, u32_t remainder, u16_t lazy, void *context); static void event_slave(u32_t ticks_at_expire, u32_t remainder, u16_t lazy, void *context); static void event_master_prepare(u32_t ticks_at_expire, u32_t remainder, u16_t lazy, void *context); static void event_master(u32_t ticks_at_expire, u32_t remainder, u16_t lazy, void *context); static void rx_packet_set(struct connection *conn, struct pdu_data *pdu_data_rx); static void tx_packet_set(struct connection *conn, struct pdu_data *pdu_data_tx); static void prepare_pdu_data_tx(struct connection *conn, struct pdu_data **pdu_data_tx); static void packet_rx_allocate(u8_t max); static u8_t packet_rx_acquired_count_get(void); static struct radio_pdu_node_rx *packet_rx_reserve_get(u8_t count); static void packet_rx_enqueue(void); static void packet_tx_enqueue(u8_t max); static struct pdu_data *empty_tx_enqueue(struct connection *conn); static void ctrl_tx_enqueue(struct connection *conn, struct radio_pdu_node_tx *node_tx); static void pdu_node_tx_release(u16_t handle, struct radio_pdu_node_tx *node_tx); static void connection_release(struct connection *conn); static void terminate_ind_rx_enqueue(struct connection *conn, u8_t reason); static u32_t conn_update(struct connection *conn, struct pdu_data *pdu_data_rx); static u32_t is_peer_compatible(struct connection *conn); static u32_t conn_update_req(struct connection *conn); static u32_t chan_map_update(struct connection *conn, struct pdu_data *pdu_data_rx); #if defined(CONFIG_BLUETOOTH_CONTROLLER_PHY) static inline u32_t phy_upd_ind(struct radio_pdu_node_rx *radio_pdu_node_rx, u8_t *rx_enqueue); #endif /* CONFIG_BLUETOOTH_CONTROLLER_PHY */ #if defined(CONFIG_BLUETOOTH_CONTROLLER_LE_ENC) static void enc_req_reused_send(struct connection *conn, struct radio_pdu_node_tx *node_tx); static void enc_rsp_send(struct connection *conn); static void start_enc_rsp_send(struct connection *conn, struct pdu_data *pdu_ctrl_tx); static void pause_enc_rsp_send(struct connection *conn); #endif /* CONFIG_BLUETOOTH_CONTROLLER_LE_ENC */ static void unknown_rsp_send(struct connection *conn, u8_t type); static void feature_rsp_send(struct connection *conn); static void version_ind_send(struct connection *conn); #if defined(CONFIG_BLUETOOTH_CONTROLLER_LE_PING) static void ping_resp_send(struct connection *conn); #endif /* CONFIG_BLUETOOTH_CONTROLLER_LE_PING */ static void reject_ind_ext_send(struct connection *conn, u8_t reject_opcode, u8_t error_code); #if defined(CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH) static void length_resp_send(struct connection *conn, u16_t eff_rx_octets, u16_t eff_tx_octets); #endif /* CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH */ #if defined(CONFIG_BLUETOOTH_CONTROLLER_PHY) static void phy_rsp_send(struct connection *conn); #endif /* CONFIG_BLUETOOTH_CONTROLLER_PHY */ static u32_t role_disable(u8_t ticker_id_primary, u8_t ticker_id_stop); static void rx_fc_lock(u16_t handle); /***************************************************************************** *RADIO ****************************************************************************/ u32_t radio_init(void *hf_clock, u8_t sca, u8_t connection_count_max, u8_t rx_count_max, u8_t tx_count_max, u16_t packet_data_octets_max, u16_t packet_tx_data_size, u8_t *mem_radio, u16_t mem_size) { u32_t retcode; u8_t *mem_radio_end; /* intialise hf_clock device to use in prepare */ _radio.hf_clock = hf_clock; /* initialise SCA */ _radio.sca = sca; /* initialised radio mem end variable */ mem_radio_end = mem_radio + mem_size; /* initialise connection context memory */ _radio.connection_count = connection_count_max; _radio.conn_pool = mem_radio; mem_radio += (sizeof(struct connection) * _radio.connection_count); /* initialise rx and tx queue counts */ /* additional for pdu to NACK or receive empty PDU, * 1 scan resp and 1* ctrl event. */ rx_count_max += 3; /* additional pdu to send enc_req ctrl pdu */ tx_count_max += 1; _radio.packet_rx_count = (rx_count_max + 1); _radio.packet_tx_count = (tx_count_max + 1); _radio.link_rx_data_quota = rx_count_max; /* initialise rx queue memory */ _radio.packet_rx = (void *)mem_radio; mem_radio += (sizeof(struct radio_pdu_node_rx *)*_radio.packet_rx_count); /* initialise tx queue memory */ _radio.pkt_tx = (void *)mem_radio; mem_radio += (sizeof(struct pdu_data_q_tx) * _radio.packet_tx_count); /* initialise tx release queue memory */ _radio.pkt_release = (void *)mem_radio; mem_radio += (sizeof(struct pdu_data_q_tx) * _radio.packet_tx_count); /* initialise rx memory size and count */ _radio.packet_data_octets_max = packet_data_octets_max; if ((PDU_AC_SIZE_MAX + PDU_AC_SIZE_EXTRA) < (offsetof(struct pdu_data, payload) + _radio.packet_data_octets_max)) { _radio.packet_rx_data_pool_size = (MROUND(offsetof(struct radio_pdu_node_rx, pdu_data) + offsetof(struct pdu_data, payload) + _radio.packet_data_octets_max) * rx_count_max); } else { _radio.packet_rx_data_pool_size = (MROUND(offsetof(struct radio_pdu_node_rx, pdu_data) + (PDU_AC_SIZE_MAX + PDU_AC_SIZE_EXTRA)) * rx_count_max); } _radio.packet_rx_data_size = PACKET_RX_DATA_SIZE_MIN; _radio.packet_rx_data_count = (_radio.packet_rx_data_pool_size / _radio.packet_rx_data_size); /* initialise rx data pool memory */ _radio.pkt_rx_data_pool = mem_radio; mem_radio += _radio.packet_rx_data_pool_size; /* initialise rx link pool memory */ _radio.link_rx_pool = mem_radio; mem_radio += (sizeof(void *) * 2 * (_radio.packet_rx_count + _radio.connection_count)); /* initialise tx ctrl pool memory */ _radio.pkt_tx_ctrl_pool = mem_radio; mem_radio += PACKET_TX_CTRL_SIZE_MIN * PACKET_MEM_COUNT_TX_CTRL; /* initialise tx data memory size and count */ _radio.packet_tx_data_size = MROUND(offsetof(struct radio_pdu_node_tx, pdu_data) + offsetof(struct pdu_data, payload) + packet_tx_data_size); /* initialise tx data pool memory */ _radio.pkt_tx_data_pool = mem_radio; mem_radio += (_radio.packet_tx_data_size * tx_count_max); /* check for sufficient memory allocation for stack * configuration. */ retcode = (mem_radio - mem_radio_end); if (retcode) { return (retcode + mem_size); } /* enable connection handle based on-off flow control feature. * This is a simple flow control to rx data only on one selected * connection handle. * TODO: replace this feature with host-to-controller flowcontrol * implementation/design. */ _radio.fc_ena = 1; /* memory allocations */ common_init(); #if defined(RADIO_UNIT_TEST) && \ defined(CONFIG_BLUETOOTH_CONTROLLER_CHAN_SEL_2) chan_sel_2_ut(); #endif /* RADIO_UNIT_TEST && CONFIG_BLUETOOTH_CONTROLLER_CHAN_SEL_2 */ return retcode; } void ll_reset(void) { u16_t conn_handle; /* disable advertiser events */ role_disable(RADIO_TICKER_ID_ADV, RADIO_TICKER_ID_ADV_STOP); /* disable oberver events */ role_disable(RADIO_TICKER_ID_SCAN, RADIO_TICKER_ID_SCAN_STOP); /* disable connection events */ for (conn_handle = 0; conn_handle < _radio.connection_count; conn_handle++) { role_disable(RADIO_TICKER_ID_FIRST_CONNECTION + conn_handle, TICKER_NULL); } /* reset controller context members */ _radio.advertiser.is_enabled = 0; _radio.advertiser.conn = NULL; _radio.scanner.is_enabled = 0; _radio.scanner.conn = NULL; _radio.packet_rx_data_size = PACKET_RX_DATA_SIZE_MIN; _radio.packet_rx_data_count = (_radio.packet_rx_data_pool_size / _radio.packet_rx_data_size); _radio.packet_rx_last = 0; _radio.packet_rx_acquire = 0; _radio.link_rx_data_quota = _radio.packet_rx_count - 1; _radio.packet_tx_first = 0; _radio.packet_tx_last = 0; _radio.packet_release_first = 0; _radio.packet_release_last = 0; /* reset whitelist and resolving list */ ll_filter_reset(false); /* memory allocations */ common_init(); } static void common_init(void) { void *link; /* initialise connection pool. */ if (_radio.connection_count) { mem_init(_radio.conn_pool, CONNECTION_T_SIZE, _radio.connection_count, &_radio.conn_free); } else { _radio.conn_free = NULL; } /* initialise rx pool. */ mem_init(_radio.pkt_rx_data_pool, _radio.packet_rx_data_size, _radio.packet_rx_data_count, &_radio.pkt_rx_data_free); /* initialise rx link pool. */ mem_init(_radio.link_rx_pool, (sizeof(void *) * 2), (_radio.packet_rx_count + _radio.connection_count), &_radio.link_rx_free); /* initialise ctrl tx pool. */ mem_init(_radio.pkt_tx_ctrl_pool, PACKET_TX_CTRL_SIZE_MIN, PACKET_MEM_COUNT_TX_CTRL, &_radio.pkt_tx_ctrl_free); /* initialise data tx pool. */ mem_init(_radio.pkt_tx_data_pool, _radio.packet_tx_data_size, (_radio.packet_tx_count - 1), &_radio.pkt_tx_data_free); /* initialise the event-cum-data memq */ link = mem_acquire(&_radio.link_rx_free); LL_ASSERT(link); memq_init(link, &_radio.link_rx_head, (void *)&_radio.link_rx_tail); /* initialise advertiser channel map */ _radio.advertiser.chan_map = 0x07; /* initialise connection channel map */ _radio.data_chan_map[0] = 0xFF; _radio.data_chan_map[1] = 0xFF; _radio.data_chan_map[2] = 0xFF; _radio.data_chan_map[3] = 0xFF; _radio.data_chan_map[4] = 0x1F; _radio.data_chan_count = 37; #if defined(CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH) /* Initialize the DLE defaults */ _radio.default_tx_octets = RADIO_LL_LENGTH_OCTETS_RX_MIN; _radio.default_tx_time = RADIO_LL_LENGTH_TIME_RX_MIN; #endif /* CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH */ #if defined(CONFIG_BLUETOOTH_CONTROLLER_PHY) /* Initialize the DLE defaults */ _radio.default_phy_tx = BIT(0); _radio.default_phy_rx = BIT(0); #if defined(CONFIG_BLUETOOTH_CONTROLLER_PHY_2M) /* Initialize the DLE defaults */ _radio.default_phy_tx |= BIT(1); _radio.default_phy_rx |= BIT(1); #endif /* CONFIG_BLUETOOTH_CONTROLLER_PHY_2M */ #if defined(CONFIG_BLUETOOTH_CONTROLLER_PHY_CODED) /* Initialize the DLE defaults */ _radio.default_phy_tx |= BIT(2); _radio.default_phy_rx |= BIT(2); #endif /* CONFIG_BLUETOOTH_CONTROLLER_PHY_CODED */ #endif /* CONFIG_BLUETOOTH_CONTROLLER_PHY */ /* allocate the rx queue */ packet_rx_allocate(0xFF); } static inline u32_t addr_us_get(u8_t phy) { switch (phy) { default: case BIT(0): return 40; case BIT(1): return 24; case BIT(2): return 376; } } #if defined(SILENT_CONNECTION) static inline u32_t empty_pkt_us_get(u8_t phy) { switch (phy) { default: case BIT(0): return 80; case BIT(1): return 40; case BIT(2): return 720; } } #endif static inline void isr_radio_state_tx(void) { u32_t hcto; _radio.state = STATE_RX; hcto = radio_tmr_end_get() + RADIO_TIFS + 4 + 1; /* 1us, end jitter */ radio_tmr_tifs_set(RADIO_TIFS); switch (_radio.role) { case ROLE_ADV: radio_switch_complete_and_tx(0, 0, 0, 0); radio_pkt_rx_set(radio_pkt_scratch_get()); /* assert if radio packet ptr is not set and radio started rx */ LL_ASSERT(!radio_is_ready()); #if defined(CONFIG_BLUETOOTH_CONTROLLER_PRIVACY) if (ctrl_rl_enabled()) { u8_t count, *irks = ctrl_irks_get(&count); radio_ar_configure(count, irks); } #endif /* CONFIG_BLUETOOTH_CONTROLLER_PRIVACY */ hcto += radio_rx_chain_delay_get(0, 0); hcto += addr_us_get(0); hcto -= radio_tx_chain_delay_get(0, 0); radio_tmr_hcto_configure(hcto); radio_tmr_end_capture(); #if defined(CONFIG_BLUETOOTH_CONTROLLER_SCAN_REQ_RSSI) radio_rssi_measure(); #endif /* CONFIG_BLUETOOTH_CONTROLLER_SCAN_REQ_RSSI */ break; case ROLE_SCAN: radio_switch_complete_and_tx(0, 0, 0, 0); radio_pkt_rx_set(_radio.packet_rx[_radio.packet_rx_last]-> pdu_data); /* assert if radio packet ptr is not set and radio started rx */ LL_ASSERT(!radio_is_ready()); hcto += radio_rx_chain_delay_get(0, 0); hcto += addr_us_get(0); hcto -= radio_tx_chain_delay_get(0, 0); radio_tmr_hcto_configure(hcto); radio_rssi_measure(); break; case ROLE_MASTER: #if defined(CONFIG_BLUETOOTH_CONTROLLER_CONN_RSSI) if (_radio.packet_counter == 0) { radio_rssi_measure(); } #endif /* CONFIG_BLUETOOTH_CONTROLLER_CONN_RSSI */ /* fall thru */ case ROLE_SLAVE: #if defined(CONFIG_BLUETOOTH_CONTROLLER_PHY) radio_switch_complete_and_tx(_radio.conn_curr->phy_rx, 0, _radio.conn_curr->phy_tx, _radio.conn_curr->phy_flags); #else /* !CONFIG_BLUETOOTH_CONTROLLER_PHY */ radio_switch_complete_and_tx(0, 0, 0, 0); #endif /* !CONFIG_BLUETOOTH_CONTROLLER_PHY */ rx_packet_set(_radio.conn_curr, (struct pdu_data *)_radio. packet_rx[_radio.packet_rx_last]->pdu_data); /* assert if radio packet ptr is not set and radio started rx */ LL_ASSERT(!radio_is_ready()); #if defined(CONFIG_BLUETOOTH_CONTROLLER_PHY) hcto += radio_rx_chain_delay_get(_radio.conn_curr->phy_rx, _radio.conn_curr->phy_flags); hcto += addr_us_get(_radio.conn_curr->phy_rx); hcto -= radio_tx_chain_delay_get(_radio.conn_curr->phy_rx, _radio.conn_curr->phy_flags); #else /* !CONFIG_BLUETOOTH_CONTROLLER_PHY */ hcto += radio_rx_chain_delay_get(0, 0); hcto += addr_us_get(0); hcto -= radio_tx_chain_delay_get(0, 0); #endif /* !CONFIG_BLUETOOTH_CONTROLLER_PHY */ radio_tmr_hcto_configure(hcto); radio_tmr_end_capture(); /* Route the tx packet to respective connections */ /* TODO: use timebox for tx enqueue (instead of 1 packet * that is routed, which may not be for the current connection) * try to route as much tx packet in queue into corresponding * connection's tx list. */ packet_tx_enqueue(1); break; case ROLE_NONE: default: LL_ASSERT(0); break; } } #if defined(CONFIG_BLUETOOTH_CONTROLLER_SCAN_REQ_NOTIFY) static u32_t isr_rx_adv_sr_report(struct pdu_adv *pdu_adv_rx, u8_t rssi_ready) { struct radio_pdu_node_rx *radio_pdu_node_rx; struct pdu_adv *pdu_adv; u8_t pdu_len; radio_pdu_node_rx = packet_rx_reserve_get(3); if (radio_pdu_node_rx == 0) { return 1; } /* Prepare the report (scan req) */ radio_pdu_node_rx->hdr.handle = 0xffff; radio_pdu_node_rx->hdr.type = NODE_RX_TYPE_SCAN_REQ; /* Make a copy of PDU into Rx node (as the received PDU is in the * scratch buffer), and save the RSSI value. */ pdu_adv = (struct pdu_adv *)radio_pdu_node_rx->pdu_data; pdu_len = offsetof(struct pdu_adv, payload) + pdu_adv_rx->len; memcpy(pdu_adv, pdu_adv_rx, pdu_len); ((u8_t *)pdu_adv)[pdu_len] = (rssi_ready) ? (radio_rssi_get() & 0x7f) : 0x7f; packet_rx_enqueue(); return 0; } #endif /* CONFIG_BLUETOOTH_CONTROLLER_SCAN_REQ_NOTIFY */ static inline bool isr_adv_sr_adva_check(struct pdu_adv *adv, struct pdu_adv *sr) { return (adv->tx_addr == sr->rx_addr) && !memcmp(adv->payload.adv_ind.addr, sr->payload.scan_req.adv_addr, BDADDR_SIZE); } static inline bool isr_adv_sr_check(struct pdu_adv *adv, struct pdu_adv *sr, u8_t devmatch_ok, u8_t *rl_idx) { #if defined(CONFIG_BLUETOOTH_CONTROLLER_PRIVACY) return ((((_radio.advertiser.filter_policy & 0x01) == 0) && ctrl_rl_addr_allowed(sr->tx_addr, sr->payload.scan_req.scan_addr, rl_idx)) || (((_radio.advertiser.filter_policy & 0x01) != 0) && (devmatch_ok || ctrl_irk_whitelisted(*rl_idx)))) && isr_adv_sr_adva_check(adv, sr); #else return (((_radio.advertiser.filter_policy & 0x01) == 0) || (devmatch_ok)) && isr_adv_sr_adva_check(adv, sr); #endif /* CONFIG_BLUETOOTH_CONTROLLER_PRIVACY */ } static inline bool isr_adv_ci_tgta_check(struct pdu_adv *adv, struct pdu_adv *ci, u8_t rl_idx) { #if defined(CONFIG_BLUETOOTH_CONTROLLER_PRIVACY) if (rl_idx != FILTER_IDX_NONE) { return rl_idx == _radio.advertiser.rl_idx; } #endif /* CONFIG_BLUETOOTH_CONTROLLER_PRIVACY */ return (adv->rx_addr == ci->tx_addr) && !memcmp(adv->payload.direct_ind.tgt_addr, ci->payload.connect_ind.init_addr, BDADDR_SIZE); } static inline bool isr_adv_ci_adva_check(struct pdu_adv *adv, struct pdu_adv *ci) { return (adv->tx_addr == ci->rx_addr) && (((adv->type == PDU_ADV_TYPE_DIRECT_IND) && !memcmp(adv->payload.direct_ind.adv_addr, ci->payload.connect_ind.adv_addr, BDADDR_SIZE)) || (!memcmp(adv->payload.adv_ind.addr, ci->payload.connect_ind.adv_addr, BDADDR_SIZE))); } static inline bool isr_adv_ci_check(struct pdu_adv *adv, struct pdu_adv *ci, u8_t devmatch_ok, u8_t *rl_idx) { /* LL 4.3.2: filter policy shall be ignored for directed adv */ if (adv->type == PDU_ADV_TYPE_DIRECT_IND) { #if defined(CONFIG_BLUETOOTH_CONTROLLER_PRIVACY) return ctrl_rl_addr_allowed(ci->tx_addr, ci->payload.connect_ind.init_addr, rl_idx) && #else return (1) && #endif isr_adv_ci_adva_check(adv, ci) && isr_adv_ci_tgta_check(adv, ci, *rl_idx); } #if defined(CONFIG_BLUETOOTH_CONTROLLER_PRIVACY) return ((((_radio.advertiser.filter_policy & 0x02) == 0) && ctrl_rl_addr_allowed(ci->tx_addr, ci->payload.connect_ind.init_addr, rl_idx)) || (((_radio.advertiser.filter_policy & 0x02) != 0) && (devmatch_ok || ctrl_irk_whitelisted(*rl_idx)))) && isr_adv_ci_adva_check(adv, ci); #else return (((_radio.advertiser.filter_policy & 0x02) == 0) || (devmatch_ok)) && isr_adv_ci_adva_check(adv, ci); #endif /* CONFIG_BLUETOOTH_CONTROLLER_PRIVACY */ } static inline u32_t isr_rx_adv(u8_t devmatch_ok, u8_t devmatch_id, u8_t irkmatch_ok, u8_t irkmatch_id, u8_t rssi_ready) { struct pdu_adv *pdu_adv, *_pdu_adv; struct radio_pdu_node_rx *radio_pdu_node_rx; #if defined(CONFIG_BLUETOOTH_CONTROLLER_PRIVACY) /* An IRK match implies address resolution enabled */ u8_t rl_idx = irkmatch_ok ? ctrl_rl_irk_idx(irkmatch_id) : FILTER_IDX_NONE; #else u8_t rl_idx = FILTER_IDX_NONE; #endif /* CONFIG_BLUETOOTH_CONTROLLER_PRIVACY */ pdu_adv = (struct pdu_adv *)radio_pkt_scratch_get(); _pdu_adv = (struct pdu_adv *)&_radio.advertiser.adv_data.data [_radio.advertiser.adv_data.first][0]; if ((pdu_adv->type == PDU_ADV_TYPE_SCAN_REQ) && (pdu_adv->len == sizeof(struct pdu_adv_payload_scan_req)) && isr_adv_sr_check(_pdu_adv, pdu_adv, devmatch_ok, &rl_idx)) { #if defined(CONFIG_BLUETOOTH_CONTROLLER_SCAN_REQ_NOTIFY) if (!IS_ENABLED(CONFIG_BLUETOOTH_CONTROLLER_ADV_EXT) || 0 /* TODO: extended adv. scan req notification enabled */) { u32_t err; /* Generate the scan request event */ err = isr_rx_adv_sr_report(pdu_adv, rssi_ready); if (err) { /* Scan Response will not be transmitted */ return err; } } #endif /* CONFIG_BLUETOOTH_CONTROLLER_SCAN_REQ_NOTIFY */ _radio.state = STATE_CLOSE; radio_switch_complete_and_disable(); radio_pkt_tx_set(&_radio.advertiser.scan_data. data[_radio.advertiser.scan_data.first][0]); return 0; } else if ((pdu_adv->type == PDU_ADV_TYPE_CONNECT_IND) && (pdu_adv->len == sizeof(struct pdu_adv_payload_connect_ind)) && isr_adv_ci_check(_pdu_adv, pdu_adv, devmatch_ok, &rl_idx) && ((_radio.fc_ena == 0) || (_radio.fc_req == _radio.fc_ack)) && (_radio.advertiser.conn)) { struct radio_le_conn_cmplt *radio_le_conn_cmplt; struct pdu_data *pdu_data; struct connection *conn; u32_t ticks_slot_offset; u32_t conn_interval_us; u32_t conn_offset_us; u32_t rx_ready_delay; u32_t ticker_status; if (IS_ENABLED(CONFIG_BLUETOOTH_CONTROLLER_CHAN_SEL_2)) { radio_pdu_node_rx = packet_rx_reserve_get(4); } else { radio_pdu_node_rx = packet_rx_reserve_get(3); } if (radio_pdu_node_rx == 0) { return 1; } _radio.state = STATE_STOP; radio_disable(); /* acquire the slave context from advertiser */ conn = _radio.advertiser.conn; _radio.advertiser.conn = NULL; /* Populate the slave context */ conn->handle = mem_index_get(conn, _radio.conn_pool, CONNECTION_T_SIZE); memcpy(&conn->crc_init[0], &pdu_adv->payload.connect_ind.lldata.crc_init[0], 3); memcpy(&conn->access_addr[0], &pdu_adv->payload.connect_ind.lldata.access_addr[0], 4); memcpy(&conn->data_chan_map[0], &pdu_adv->payload.connect_ind.lldata.chan_map[0], sizeof(conn->data_chan_map)); conn->data_chan_count = util_ones_count_get(&conn->data_chan_map[0], sizeof(conn->data_chan_map)); conn->data_chan_hop = pdu_adv->payload.connect_ind.lldata.hop; conn->conn_interval = pdu_adv->payload.connect_ind.lldata.interval; conn_interval_us = pdu_adv->payload.connect_ind.lldata.interval * 1250; conn->latency = pdu_adv->payload.connect_ind.lldata.latency; memcpy((void *)&conn->slave.force, &conn->access_addr[0], sizeof(conn->slave.force)); conn->supervision_reload = RADIO_CONN_EVENTS((pdu_adv->payload.connect_ind.lldata.timeout * 10 * 1000), conn_interval_us); conn->procedure_reload = RADIO_CONN_EVENTS((40 * 1000 * 1000), conn_interval_us); #if defined(CONFIG_BLUETOOTH_CONTROLLER_LE_PING) /* APTO in no. of connection events */ conn->apto_reload = RADIO_CONN_EVENTS((30 * 1000 * 1000), conn_interval_us); /* Dispatch LE Ping PDU 6 connection events (that peer would * listen to) before 30s timeout * TODO: "peer listens to" is greater than 30s due to latency */ conn->appto_reload = (conn->apto_reload > (conn->latency + 6)) ? (conn->apto_reload - (conn->latency + 6)) : conn->apto_reload; #endif /* CONFIG_BLUETOOTH_CONTROLLER_LE_PING */ /* Prepare the rx packet structure */ radio_pdu_node_rx->hdr.handle = conn->handle; radio_pdu_node_rx->hdr.type = NODE_RX_TYPE_CONNECTION; /* prepare connection complete structure */ pdu_data = (struct pdu_data *)radio_pdu_node_rx->pdu_data; radio_le_conn_cmplt = (struct radio_le_conn_cmplt *)&pdu_data->payload; radio_le_conn_cmplt->status = 0x00; radio_le_conn_cmplt->role = 0x01; #if defined(CONFIG_BLUETOOTH_CONTROLLER_PRIVACY) radio_le_conn_cmplt->own_addr_type = pdu_adv->rx_addr; memcpy(&radio_le_conn_cmplt->own_addr[0], &pdu_adv->payload.connect_ind.adv_addr[0], BDADDR_SIZE); if (rl_idx != FILTER_IDX_NONE) { /* TODO: store rl_idx instead if safe */ /* Store identity address */ ll_rl_id_addr_get(rl_idx, &radio_le_conn_cmplt->peer_addr_type, &radio_le_conn_cmplt->peer_addr[0]); /* Mark it as identity address from RPA (0x02, 0x03) */ radio_le_conn_cmplt->peer_addr_type += 2; /* Store peer RPA */ memcpy(&radio_le_conn_cmplt->peer_rpa[0], &pdu_adv->payload.connect_ind.init_addr[0], BDADDR_SIZE); } else { memset(&radio_le_conn_cmplt->peer_rpa[0], 0x0, BDADDR_SIZE); #else if (1) { #endif /* CONFIG_BLUETOOTH_CONTROLLER_PRIVACY */ radio_le_conn_cmplt->peer_addr_type = pdu_adv->tx_addr; memcpy(&radio_le_conn_cmplt->peer_addr[0], &pdu_adv->payload.connect_ind.init_addr[0], BDADDR_SIZE); } radio_le_conn_cmplt->interval = pdu_adv->payload.connect_ind.lldata.interval; radio_le_conn_cmplt->latency = pdu_adv->payload.connect_ind.lldata.latency; radio_le_conn_cmplt->timeout = pdu_adv->payload.connect_ind.lldata.timeout; radio_le_conn_cmplt->mca = pdu_adv->payload.connect_ind.lldata.sca; /* enqueue connection complete structure into queue */ rx_fc_lock(conn->handle); packet_rx_enqueue(); /* Use Channel Selection Algorithm #2 if peer too supports it */ if (IS_ENABLED(CONFIG_BLUETOOTH_CONTROLLER_CHAN_SEL_2)) { struct radio_le_chan_sel_algo *le_chan_sel_algo; /* Generate LE Channel Selection Algorithm event */ radio_pdu_node_rx = packet_rx_reserve_get(3); LL_ASSERT(radio_pdu_node_rx); radio_pdu_node_rx->hdr.handle = conn->handle; radio_pdu_node_rx->hdr.type = NODE_RX_TYPE_CHAN_SEL_ALGO; pdu_data = (struct pdu_data *) radio_pdu_node_rx->pdu_data; le_chan_sel_algo = (struct radio_le_chan_sel_algo *) &pdu_data->payload; if (pdu_adv->chan_sel) { u16_t aa_ls = ((u16_t)conn->access_addr[1] << 8) | conn->access_addr[0]; u16_t aa_ms = ((u16_t)conn->access_addr[3] << 8) | conn->access_addr[2]; conn->data_chan_sel = 1; conn->data_chan_id = aa_ms ^ aa_ls; le_chan_sel_algo->chan_sel_algo = 0x01; } else { le_chan_sel_algo->chan_sel_algo = 0x00; } packet_rx_enqueue(); } /* calculate the window widening */ conn->slave.sca = pdu_adv->payload.connect_ind.lldata.sca; conn->slave.window_widening_periodic_us = (((gc_lookup_ppm[_radio.sca] + gc_lookup_ppm[conn->slave.sca]) * conn_interval_us) + (1000000 - 1)) / 1000000; conn->slave.window_widening_max_us = (conn_interval_us >> 1) - RADIO_TIFS; conn->slave.window_size_event_us = pdu_adv->payload.connect_ind.lldata.win_size * 1250; conn->slave.window_size_prepare_us = 0; rx_ready_delay = radio_rx_ready_delay_get(0); /* calculate slave slot */ conn->hdr.ticks_slot = TICKER_US_TO_TICKS(RADIO_TICKER_START_PART_US + rx_ready_delay + 328 + RADIO_TIFS + 328); conn->hdr.ticks_active_to_start = _radio.ticks_active_to_start; conn->hdr.ticks_xtal_to_start = TICKER_US_TO_TICKS(RADIO_TICKER_XTAL_OFFSET_US); conn->hdr.ticks_preempt_to_start = TICKER_US_TO_TICKS(RADIO_TICKER_PREEMPT_PART_MIN_US); ticks_slot_offset = (conn->hdr.ticks_active_to_start < conn->hdr.ticks_xtal_to_start) ? conn->hdr.ticks_xtal_to_start : conn->hdr.ticks_active_to_start; conn_interval_us -= conn->slave.window_widening_periodic_us; conn_offset_us = radio_tmr_end_get(); conn_offset_us += ((u64_t)pdu_adv->payload.connect_ind.lldata.win_offset + 1) * 1250; conn_offset_us -= radio_tx_chain_delay_get(0, 0); conn_offset_us -= rx_ready_delay; conn_offset_us -= RADIO_TICKER_JITTER_US << 1; conn_offset_us -= RADIO_TICKER_JITTER_US; /* Stop Advertiser */ ticker_status = ticker_stop(RADIO_TICKER_INSTANCE_ID_RADIO, RADIO_TICKER_USER_ID_WORKER, RADIO_TICKER_ID_ADV, ticker_stop_adv_assert, (void *)__LINE__); ticker_stop_adv_assert(ticker_status, (void *)__LINE__); /* Stop Direct Adv Stopper */ if (_pdu_adv->type == PDU_ADV_TYPE_DIRECT_IND) { /* Advertiser stop can expire while here in this ISR. * Deferred attempt to stop can fail as it would have * expired, hence ignore failure. */ ticker_stop(RADIO_TICKER_INSTANCE_ID_RADIO, RADIO_TICKER_USER_ID_WORKER, RADIO_TICKER_ID_ADV_STOP, NULL, NULL); } /* Start Slave */ ticker_status = ticker_start(RADIO_TICKER_INSTANCE_ID_RADIO, RADIO_TICKER_USER_ID_WORKER, RADIO_TICKER_ID_FIRST_CONNECTION + conn->handle, (_radio.ticks_anchor - ticks_slot_offset), TICKER_US_TO_TICKS(conn_offset_us), TICKER_US_TO_TICKS(conn_interval_us), TICKER_REMAINDER(conn_interval_us), TICKER_NULL_LAZY, (ticks_slot_offset + conn->hdr.ticks_slot), event_slave_prepare, conn, ticker_success_assert, (void *)__LINE__); LL_ASSERT((ticker_status == TICKER_STATUS_SUCCESS) || (ticker_status == TICKER_STATUS_BUSY)); return 0; } return 1; } static u32_t isr_rx_scan_report(u8_t rssi_ready, u8_t rl_idx) { struct radio_pdu_node_rx *radio_pdu_node_rx; struct pdu_adv *pdu_adv_rx; radio_pdu_node_rx = packet_rx_reserve_get(3); if (radio_pdu_node_rx == 0) { return 1; } /* Prepare the report (adv or scan resp) */ radio_pdu_node_rx->hdr.handle = 0xffff; if (0) { #if defined(CONFIG_BLUETOOTH_CONTROLLER_ADV_EXT) } else if (_radio.scanner.phy) { switch (_radio.scanner.phy) { case BIT(0): radio_pdu_node_rx->hdr.type = NODE_RX_TYPE_EXT_1M_REPORT; break; case BIT(2): radio_pdu_node_rx->hdr.type = NODE_RX_TYPE_EXT_CODED_REPORT; break; default: LL_ASSERT(0); break; } #endif /* CONFIG_BLUETOOTH_CONTROLLER_ADV_EXT */ } else { radio_pdu_node_rx->hdr.type = NODE_RX_TYPE_REPORT; } /* save the RSSI value */ pdu_adv_rx = (struct pdu_adv *)radio_pdu_node_rx->pdu_data; ((u8_t *)pdu_adv_rx)[offsetof(struct pdu_adv, payload) + pdu_adv_rx->len] = (rssi_ready) ? (radio_rssi_get() & 0x7f) : 0x7f; #if defined(CONFIG_BLUETOOTH_CONTROLLER_PRIVACY) /* save the resolving list index */ ((u8_t *)pdu_adv_rx)[offsetof(struct pdu_adv, payload) + pdu_adv_rx->len + 1] = rl_idx; #endif /* CONFIG_BLUETOOTH_CONTROLLER_PRIVACY */ packet_rx_enqueue(); return 0; } static inline bool isr_rx_scan_check(u8_t irkmatch_ok, u8_t devmatch_ok, u8_t rl_idx) { #if defined(CONFIG_BLUETOOTH_CONTROLLER_PRIVACY) return (((_radio.scanner.filter_policy & 0x01) == 0) && (!devmatch_ok || ctrl_rl_idx_allowed(irkmatch_ok, rl_idx))) || (((_radio.scanner.filter_policy & 0x01) != 0) && (devmatch_ok || ctrl_irk_whitelisted(rl_idx))); #else return ((_radio.scanner.filter_policy & 0x01) == 0) || devmatch_ok; #endif /* CONFIG_BLUETOOTH_CONTROLLER_PRIVACY */ } static inline bool isr_scan_init_adva_check(struct pdu_adv *pdu, u8_t rl_idx) { #if defined(CONFIG_BLUETOOTH_CONTROLLER_PRIVACY) /* Only applies to initiator with no whitelist */ if (rl_idx != FILTER_IDX_NONE) { return (rl_idx == _radio.scanner.rl_idx); } #endif /* CONFIG_BLUETOOTH_CONTROLLER_PRIVACY */ return ((_radio.scanner.adv_addr_type == pdu->tx_addr) && (memcmp(&_radio.scanner.adv_addr[0], &pdu->payload.adv_ind.addr[0], BDADDR_SIZE) == 0)); } static inline bool isr_scan_tgta_check(bool init, struct pdu_adv *pdu, u8_t rl_idx) { #if defined(CONFIG_BLUETOOTH_CONTROLLER_PRIVACY) if (ctrl_rl_addr_resolve(pdu->rx_addr, pdu->payload.direct_ind.tgt_addr, rl_idx)) { return true; } return (((!init || _radio.scanner.rl_idx == FILTER_IDX_NONE) && #else return (((1) && #endif /* CONFIG_BLUETOOTH_CONTROLLER_PRIVACY */ ((_radio.scanner.init_addr_type == pdu->rx_addr) && (memcmp(&_radio.scanner.init_addr[0], &pdu->payload.direct_ind.tgt_addr[0], BDADDR_SIZE) == 0))) || /* allow directed adv packets where TargetA address * is resolvable private address (scanner only) */ (((_radio.scanner.filter_policy & 0x02) != 0) && (pdu->rx_addr != 0) && ((pdu->payload.direct_ind.tgt_addr[5] & 0xc0) == 0x40))); } static inline bool isr_scan_init_check(struct pdu_adv *pdu, u8_t rl_idx) { return ((((_radio.scanner.filter_policy & 0x01) != 0) || isr_scan_init_adva_check(pdu, rl_idx)) && ((pdu->type == PDU_ADV_TYPE_ADV_IND) || ((pdu->type == PDU_ADV_TYPE_DIRECT_IND) && (/* allow directed adv packets addressed to this device */ isr_scan_tgta_check(true, pdu, rl_idx))))); } static inline u32_t isr_rx_scan(u8_t devmatch_ok, u8_t devmatch_id, u8_t irkmatch_ok, u8_t irkmatch_id, u8_t rl_idx, u8_t rssi_ready) { struct pdu_adv *pdu_adv_rx; pdu_adv_rx = (struct pdu_adv *) _radio.packet_rx[_radio.packet_rx_last]->pdu_data; /* Initiator */ if ((_radio.scanner.conn) && ((_radio.fc_ena == 0) || (_radio.fc_req == _radio.fc_ack)) && isr_scan_init_check(pdu_adv_rx, rl_idx) && ((radio_tmr_end_get() + 502) < (TICKER_TICKS_TO_US(_radio.scanner.hdr.ticks_slot) - RADIO_TICKER_START_PART_US))) { struct radio_le_conn_cmplt *radio_le_conn_cmplt; struct radio_pdu_node_rx *radio_pdu_node_rx; struct pdu_adv *pdu_adv_tx; struct pdu_data *pdu_data; struct connection *conn; u32_t ticks_slot_offset; u32_t conn_interval_us; u32_t conn_offset_us; u32_t ticker_status; u32_t conn_space_us; #if defined(CONFIG_BLUETOOTH_CONTROLLER_PRIVACY) bt_addr_t *lrpa; #endif /* CONFIG_BLUETOOTH_CONTROLLER_PRIVACY */ if (IS_ENABLED(CONFIG_BLUETOOTH_CONTROLLER_CHAN_SEL_2)) { radio_pdu_node_rx = packet_rx_reserve_get(4); } else { radio_pdu_node_rx = packet_rx_reserve_get(3); } if (radio_pdu_node_rx == 0) { return 1; } _radio.state = STATE_STOP; /* acquire the master context from scanner */ conn = _radio.scanner.conn; _radio.scanner.conn = NULL; /* Tx the connect request packet */ pdu_adv_tx = (struct pdu_adv *)radio_pkt_scratch_get(); pdu_adv_tx->type = PDU_ADV_TYPE_CONNECT_IND; if (IS_ENABLED(CONFIG_BLUETOOTH_CONTROLLER_CHAN_SEL_2)) { pdu_adv_tx->chan_sel = 1; } else { pdu_adv_tx->chan_sel = 0; } pdu_adv_tx->rx_addr = pdu_adv_rx->tx_addr; pdu_adv_tx->len = sizeof(struct pdu_adv_payload_connect_ind); #if defined(CONFIG_BLUETOOTH_CONTROLLER_PRIVACY) lrpa = ctrl_lrpa_get(rl_idx); if (_radio.scanner.rpa_gen && lrpa) { pdu_adv_tx->tx_addr = 1; memcpy(&pdu_adv_tx->payload.connect_ind.init_addr[0], lrpa->val, BDADDR_SIZE); } else { #else if (1) { #endif /* CONFIG_BLUETOOTH_CONTROLLER_PRIVACY */ pdu_adv_tx->tx_addr = _radio.scanner.init_addr_type; memcpy(&pdu_adv_tx->payload.connect_ind.init_addr[0], &_radio.scanner.init_addr[0], BDADDR_SIZE); } memcpy(&pdu_adv_tx->payload.connect_ind.adv_addr[0], &pdu_adv_rx->payload.adv_ind.addr[0], BDADDR_SIZE); memcpy(&pdu_adv_tx->payload.connect_ind.lldata. access_addr[0], &conn->access_addr[0], 4); memcpy(&pdu_adv_tx->payload.connect_ind.lldata.crc_init[0], &conn->crc_init[0], 3); pdu_adv_tx->payload.connect_ind.lldata. win_size = 1; conn_interval_us = (u32_t)_radio.scanner.conn_interval * 1250; conn_offset_us = radio_tmr_end_get() + 502 + 1250; conn_offset_us -= radio_tx_chain_delay_get(0, 0); conn_offset_us -= radio_tx_ready_delay_get(0, 0); if (_radio.scanner.win_offset_us == 0) { conn_space_us = conn_offset_us; pdu_adv_tx->payload.connect_ind.lldata.win_offset = 0; } else { conn_space_us = _radio.scanner.win_offset_us; while ((conn_space_us & ((u32_t)1 << 31)) || (conn_space_us < conn_offset_us)) { conn_space_us += conn_interval_us; } pdu_adv_tx->payload.connect_ind.lldata.win_offset = (conn_space_us - conn_offset_us) / 1250; } /* Workaround: Due to the missing remainder param in * ticker_start function for first interval; add half a * tick in microseconds so as to restrict the offset * within the +/- 16us jitter for the offset. */ conn_space_us += (30517578125UL/1000000000UL) >> 1; pdu_adv_tx->payload.connect_ind.lldata.interval = _radio.scanner.conn_interval; pdu_adv_tx->payload.connect_ind.lldata.latency = _radio.scanner.conn_latency; pdu_adv_tx->payload.connect_ind.lldata.timeout = _radio.scanner.conn_timeout; memcpy(&pdu_adv_tx->payload.connect_ind.lldata.chan_map[0], &conn->data_chan_map[0], sizeof(pdu_adv_tx->payload.connect_ind.lldata.chan_map)); pdu_adv_tx->payload.connect_ind.lldata.hop = conn->data_chan_hop; pdu_adv_tx->payload.connect_ind.lldata.sca = _radio.sca; radio_switch_complete_and_disable(); radio_pkt_tx_set(pdu_adv_tx); /* assert if radio packet ptr is not set and radio started tx */ LL_ASSERT(!radio_is_ready()); radio_tmr_end_capture(); /* block CPU so that there is no CRC error on pdu tx, * this is only needed if we want the CPU to sleep. * while(!radio_has_disabled()) * {cpu_sleep();} * radio_status_reset(); */ /* Populate the master context */ conn->handle = mem_index_get(conn, _radio.conn_pool, CONNECTION_T_SIZE); /* Prepare the rx packet structure */ radio_pdu_node_rx->hdr.handle = conn->handle; radio_pdu_node_rx->hdr.type = NODE_RX_TYPE_CONNECTION; /* prepare connection complete structure */ pdu_data = (struct pdu_data *)radio_pdu_node_rx->pdu_data; radio_le_conn_cmplt = (struct radio_le_conn_cmplt *)&pdu_data->payload; radio_le_conn_cmplt->status = 0x00; radio_le_conn_cmplt->role = 0x00; #if defined(CONFIG_BLUETOOTH_CONTROLLER_PRIVACY) radio_le_conn_cmplt->own_addr_type = pdu_adv_tx->tx_addr; memcpy(&radio_le_conn_cmplt->own_addr[0], &pdu_adv_tx->payload.connect_ind.init_addr[0], BDADDR_SIZE); if (irkmatch_ok && rl_idx != FILTER_IDX_NONE) { /* TODO: store rl_idx instead if safe */ /* Store identity address */ ll_rl_id_addr_get(rl_idx, &radio_le_conn_cmplt->peer_addr_type, &radio_le_conn_cmplt->peer_addr[0]); /* Mark it as identity address from RPA (0x02, 0x03) */ radio_le_conn_cmplt->peer_addr_type += 2; /* Store peer RPA */ memcpy(&radio_le_conn_cmplt->peer_rpa[0], &pdu_adv_tx->payload.connect_ind.adv_addr[0], BDADDR_SIZE); } else { memset(&radio_le_conn_cmplt->peer_rpa[0], 0x0, BDADDR_SIZE); #else if (1) { #endif /* CONFIG_BLUETOOTH_CONTROLLER_PRIVACY */ radio_le_conn_cmplt->peer_addr_type = pdu_adv_tx->rx_addr; memcpy(&radio_le_conn_cmplt->peer_addr[0], &pdu_adv_tx->payload.connect_ind.adv_addr[0], BDADDR_SIZE); } radio_le_conn_cmplt->interval = _radio.scanner.conn_interval; radio_le_conn_cmplt->latency = _radio.scanner. conn_latency; radio_le_conn_cmplt->timeout = _radio.scanner.conn_timeout; radio_le_conn_cmplt->mca = pdu_adv_tx->payload.connect_ind.lldata.sca; /* enqueue connection complete structure into queue */ rx_fc_lock(conn->handle); packet_rx_enqueue(); /* Use Channel Selection Algorithm #2 if peer too supports it */ if (IS_ENABLED(CONFIG_BLUETOOTH_CONTROLLER_CHAN_SEL_2)) { struct radio_le_chan_sel_algo *le_chan_sel_algo; /* Generate LE Channel Selection Algorithm event */ radio_pdu_node_rx = packet_rx_reserve_get(3); LL_ASSERT(radio_pdu_node_rx); radio_pdu_node_rx->hdr.handle = conn->handle; radio_pdu_node_rx->hdr.type = NODE_RX_TYPE_CHAN_SEL_ALGO; pdu_data = (struct pdu_data *) radio_pdu_node_rx->pdu_data; le_chan_sel_algo = (struct radio_le_chan_sel_algo *) &pdu_data->payload; if (pdu_adv_rx->chan_sel) { u16_t aa_ls = ((u16_t)conn->access_addr[1] << 8) | conn->access_addr[0]; u16_t aa_ms = ((u16_t)conn->access_addr[3] << 8) | conn->access_addr[2]; conn->data_chan_sel = 1; conn->data_chan_id = aa_ms ^ aa_ls; le_chan_sel_algo->chan_sel_algo = 0x01; } else { le_chan_sel_algo->chan_sel_algo = 0x00; } packet_rx_enqueue(); } /* Calculate master slot */ conn->hdr.ticks_slot = _radio.scanner.ticks_conn_slot; conn->hdr.ticks_active_to_start = _radio.ticks_active_to_start; conn->hdr.ticks_xtal_to_start = TICKER_US_TO_TICKS(RADIO_TICKER_XTAL_OFFSET_US); conn->hdr.ticks_preempt_to_start = TICKER_US_TO_TICKS(RADIO_TICKER_PREEMPT_PART_MIN_US); ticks_slot_offset = (conn->hdr. ticks_active_to_start < conn->hdr.ticks_xtal_to_start) ? conn->hdr.ticks_xtal_to_start : conn->hdr.ticks_active_to_start; /* Stop Scanner */ ticker_status = ticker_stop(RADIO_TICKER_INSTANCE_ID_RADIO, RADIO_TICKER_USER_ID_WORKER, RADIO_TICKER_ID_SCAN, ticker_stop_scan_assert, (void *)__LINE__); ticker_stop_scan_assert(ticker_status, (void *)__LINE__); /* Scanner stop can expire while here in this ISR. * Deferred attempt to stop can fail as it would have * expired, hence ignore failure. */ ticker_stop(RADIO_TICKER_INSTANCE_ID_RADIO, RADIO_TICKER_USER_ID_WORKER, RADIO_TICKER_ID_SCAN_STOP, NULL, NULL); /* Start master */ ticker_status = ticker_start(RADIO_TICKER_INSTANCE_ID_RADIO, RADIO_TICKER_USER_ID_WORKER, RADIO_TICKER_ID_FIRST_CONNECTION + conn->handle, (_radio.ticks_anchor - ticks_slot_offset), TICKER_US_TO_TICKS(conn_space_us), TICKER_US_TO_TICKS(conn_interval_us), TICKER_REMAINDER(conn_interval_us), TICKER_NULL_LAZY, (ticks_slot_offset + conn->hdr.ticks_slot), event_master_prepare, conn, ticker_success_assert, (void *)__LINE__); LL_ASSERT((ticker_status == TICKER_STATUS_SUCCESS) || (ticker_status == TICKER_STATUS_BUSY)); return 0; } /* Active scanner */ else if (((pdu_adv_rx->type == PDU_ADV_TYPE_ADV_IND) || (pdu_adv_rx->type == PDU_ADV_TYPE_SCAN_IND)) && (_radio.scanner.type != 0) && (_radio.scanner.conn == 0)) { struct pdu_adv *pdu_adv_tx; #if defined(CONFIG_BLUETOOTH_CONTROLLER_PRIVACY) bt_addr_t *lrpa; #endif /* CONFIG_BLUETOOTH_CONTROLLER_PRIVACY */ u32_t err; /* save the adv packet */ err = isr_rx_scan_report(rssi_ready, irkmatch_ok ? rl_idx : FILTER_IDX_NONE); if (err) { return err; } /* prepare the scan request packet */ pdu_adv_tx = (struct pdu_adv *)radio_pkt_scratch_get(); pdu_adv_tx->type = PDU_ADV_TYPE_SCAN_REQ; pdu_adv_tx->rx_addr = pdu_adv_rx->tx_addr; pdu_adv_tx->len = sizeof(struct pdu_adv_payload_scan_req); #if defined(CONFIG_BLUETOOTH_CONTROLLER_PRIVACY) lrpa = ctrl_lrpa_get(rl_idx); if (_radio.scanner.rpa_gen && lrpa) { pdu_adv_tx->tx_addr = 1; memcpy(&pdu_adv_tx->payload.scan_req.scan_addr[0], lrpa->val, BDADDR_SIZE); } else { #else if (1) { #endif /* CONFIG_BLUETOOTH_CONTROLLER_PRIVACY */ pdu_adv_tx->tx_addr = _radio.scanner.init_addr_type; memcpy(&pdu_adv_tx->payload.scan_req.scan_addr[0], &_radio.scanner.init_addr[0], BDADDR_SIZE); } memcpy(&pdu_adv_tx->payload.scan_req.adv_addr[0], &pdu_adv_rx->payload.adv_ind.addr[0], BDADDR_SIZE); /* switch scanner state to active */ _radio.scanner.state = 1; _radio.state = STATE_TX; radio_tmr_tifs_set(RADIO_TIFS); radio_switch_complete_and_rx(0); radio_pkt_tx_set(pdu_adv_tx); radio_tmr_end_capture(); return 0; } /* Passive scanner or scan responses */ else if (((pdu_adv_rx->type == PDU_ADV_TYPE_ADV_IND) || ((pdu_adv_rx->type == PDU_ADV_TYPE_DIRECT_IND) && (/* allow directed adv packets addressed to this device */ isr_scan_tgta_check(false, pdu_adv_rx, rl_idx))) || (pdu_adv_rx->type == PDU_ADV_TYPE_NONCONN_IND) || (pdu_adv_rx->type == PDU_ADV_TYPE_SCAN_IND) || #if defined(CONFIG_BLUETOOTH_CONTROLLER_ADV_EXT) ((pdu_adv_rx->type == PDU_ADV_TYPE_EXT_IND) && (_radio.scanner.phy)) || #endif /* CONFIG_BLUETOOTH_CONTROLLER_ADV_EXT */ ((pdu_adv_rx->type == PDU_ADV_TYPE_SCAN_RSP) && (_radio.scanner.state != 0))) && (pdu_adv_rx->len != 0) && (!_radio.scanner.conn)) { u32_t err; /* save the scan response packet */ err = isr_rx_scan_report(rssi_ready, irkmatch_ok ? rl_idx : FILTER_IDX_NONE); if (err) { return err; } } /* invalid PDU */ else { /* ignore and close this rx/tx chain ( code below ) */ return 1; } return 1; } static inline u8_t isr_rx_conn_pkt_ack(struct pdu_data *pdu_data_tx, struct radio_pdu_node_tx **node_tx) { u8_t terminate = 0; switch (pdu_data_tx->payload.llctrl.opcode) { case PDU_DATA_LLCTRL_TYPE_TERMINATE_IND: _radio.state = STATE_CLOSE; radio_disable(); /* assert if radio packet ptr is not set and radio started tx */ LL_ASSERT(!radio_is_ready()); terminate_ind_rx_enqueue(_radio.conn_curr, (pdu_data_tx->payload.llctrl.ctrldata.terminate_ind. error_code == 0x13) ? 0x16 : pdu_data_tx->payload.llctrl.ctrldata.terminate_ind. error_code); /* Ack received, hence terminate */ terminate = 1; break; #if defined(CONFIG_BLUETOOTH_CONTROLLER_LE_ENC) case PDU_DATA_LLCTRL_TYPE_ENC_REQ: /* things from master stored for session key calculation */ memcpy(&_radio.conn_curr->llcp.encryption.skd[0], &pdu_data_tx->payload.llctrl.ctrldata.enc_req.skdm[0], 8); memcpy(&_radio.conn_curr->ccm_rx.iv[0], &pdu_data_tx->payload.llctrl.ctrldata.enc_req.ivm[0], 4); /* pause data packet tx */ _radio.conn_curr->pause_tx = 1; /* Start Procedure Timeout (this will not replace terminate * procedure which always gets place before any packets * going out, hence safe by design). */ _radio.conn_curr->procedure_expire = _radio.conn_curr->procedure_reload; break; case PDU_DATA_LLCTRL_TYPE_ENC_RSP: /* pause data packet tx */ _radio.conn_curr->pause_tx = 1; break; case PDU_DATA_LLCTRL_TYPE_START_ENC_REQ: /* Nothing to do. * Remember that we may have received encrypted START_ENC_RSP * alongwith this tx ack at this point in time. */ break; case PDU_DATA_LLCTRL_TYPE_PAUSE_ENC_REQ: /* pause data packet tx */ _radio.conn_curr->pause_tx = 1; /* key refresh */ _radio.conn_curr->refresh = 1; /* Start Procedure Timeout (this will not replace terminate * procedure which always gets place before any packets * going out, hence safe by design). */ _radio.conn_curr->procedure_expire = _radio.conn_curr->procedure_reload; break; case PDU_DATA_LLCTRL_TYPE_PAUSE_ENC_RSP: if (_radio.role == ROLE_MASTER) { /* reused tx-ed PDU and send enc req */ enc_req_reused_send(_radio.conn_curr, *node_tx); /* dont release ctrl PDU memory */ *node_tx = NULL; } else { /* pause data packet tx */ _radio.conn_curr->pause_tx = 1; } break; case PDU_DATA_LLCTRL_TYPE_REJECT_IND: /* resume data packet rx and tx */ _radio.conn_curr->pause_rx = 0; _radio.conn_curr->pause_tx = 0; /* Procedure complete */ _radio.conn_curr->procedure_expire = 0; break; #endif /* CONFIG_BLUETOOTH_CONTROLLER_LE_ENC */ #if defined(CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH) case PDU_DATA_LLCTRL_TYPE_LENGTH_REQ: if ((_radio.conn_curr->llcp_length.req != _radio.conn_curr->llcp_length.ack) && (_radio.conn_curr->llcp_length.state == LLCP_LENGTH_STATE_ACK_WAIT)){ /* pause data packet tx */ _radio.conn_curr->pause_tx = 1; /* wait for response */ _radio.conn_curr->llcp_length.state = LLCP_LENGTH_STATE_RSP_WAIT; } break; #endif /* CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH */ #if defined(CONFIG_BLUETOOTH_CONTROLLER_PHY) case PDU_DATA_LLCTRL_TYPE_PHY_REQ: _radio.conn_curr->llcp_phy.state = LLCP_PHY_STATE_RSP_WAIT; break; #endif /* CONFIG_BLUETOOTH_CONTROLLER_PHY */ default: /* Do nothing for other ctrl packet ack */ break; } return terminate; } static inline struct radio_pdu_node_tx * isr_rx_conn_pkt_release(struct radio_pdu_node_tx *node_tx) { struct connection *conn = _radio.conn_curr; conn->packet_tx_head_len = 0; conn->packet_tx_head_offset = 0; /* release */ if (conn->pkt_tx_head == conn->pkt_tx_ctrl) { if (node_tx) { if (conn->pkt_tx_ctrl == conn->pkt_tx_ctrl_last) { conn->pkt_tx_ctrl_last = conn->pkt_tx_ctrl_last->next; } conn->pkt_tx_ctrl = conn->pkt_tx_ctrl->next; conn->pkt_tx_head = conn->pkt_tx_ctrl; if (conn->pkt_tx_ctrl == conn->pkt_tx_data) { conn->pkt_tx_ctrl = NULL; conn->pkt_tx_ctrl_last = NULL; } mem_release(node_tx, &_radio. pkt_tx_ctrl_free); } } else { if (conn->pkt_tx_head == conn->pkt_tx_data) { conn->pkt_tx_data = conn->pkt_tx_data->next; } conn->pkt_tx_head = conn->pkt_tx_head->next; return node_tx; } return NULL; } static inline u32_t feat_get(u8_t *features) { u32_t feat; feat = ~RADIO_BLE_FEAT_BIT_MASK_VALID | features[0] | (features[1] << 8) | (features[2] << 16); feat &= RADIO_BLE_FEAT_BIT_MASK; return feat; } static inline void isr_rx_conn_pkt_ctrl_rej_conn_upd(struct radio_pdu_node_rx *radio_pdu_node_rx, u8_t *rx_enqueue) { LL_ASSERT(_radio.conn_upd == _radio.conn_curr); /* reset mutex */ _radio.conn_upd = NULL; /* update to next ticks offsets */ if (_radio.conn_curr->role) { _radio.conn_curr->slave.ticks_to_offset = _radio.conn_curr->llcp.connection_update.ticks_to_offset_next; } /* conn param req procedure, if any, is complete */ _radio.conn_curr->procedure_expire = 0; /* enqueue the reject ind ext */ if (!_radio.conn_curr->llcp.connection_update.is_internal) { struct radio_le_conn_update_cmplt *radio_le_conn_update_cmplt; struct pdu_data *pdu_data_rx; radio_pdu_node_rx->hdr.type = NODE_RX_TYPE_CONN_UPDATE; /* prepare connection update complete structure */ pdu_data_rx = (struct pdu_data *)radio_pdu_node_rx->pdu_data; radio_le_conn_update_cmplt = (struct radio_le_conn_update_cmplt *) &pdu_data_rx->payload; radio_le_conn_update_cmplt->status = 0x00; radio_le_conn_update_cmplt->interval = _radio.conn_curr->conn_interval; radio_le_conn_update_cmplt->latency = _radio.conn_curr->latency; radio_le_conn_update_cmplt->timeout = _radio.conn_curr->supervision_reload * _radio.conn_curr->conn_interval * 125 / 1000; *rx_enqueue = 1; } } #if defined(CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH) static inline void isr_rx_conn_pkt_ctrl_rej_dle(struct radio_pdu_node_rx *radio_pdu_node_rx, u8_t *rx_enqueue) { struct pdu_data_llctrl_reject_ext_ind *rej_ext_ind; struct pdu_data *pdu_data_rx; pdu_data_rx = (struct pdu_data *)radio_pdu_node_rx->pdu_data; rej_ext_ind = (struct pdu_data_llctrl_reject_ext_ind *) &pdu_data_rx->payload.llctrl.ctrldata.reject_ext_ind; if (rej_ext_ind->reject_opcode == PDU_DATA_LLCTRL_TYPE_LENGTH_REQ) { struct pdu_data_llctrl_length_req_rsp *lr; /* Procedure complete */ _radio.conn_curr->llcp_length.ack = _radio.conn_curr->llcp_length.req; _radio.conn_curr->procedure_expire = 0; /* Resume data packet tx */ _radio.conn_curr->pause_tx = 0; /* prepare length rsp structure */ pdu_data_rx->len = offsetof(struct pdu_data_llctrl, ctrldata) + sizeof(struct pdu_data_llctrl_length_req_rsp); pdu_data_rx->payload.llctrl.opcode = PDU_DATA_LLCTRL_TYPE_LENGTH_RSP; lr = (struct pdu_data_llctrl_length_req_rsp *) &pdu_data_rx->payload.llctrl.ctrldata.length_req; lr->max_rx_octets = _radio.conn_curr->max_rx_octets; lr->max_rx_time = ((_radio.conn_curr->max_rx_octets + 14) << 3); lr->max_tx_octets = _radio.conn_curr->max_tx_octets; lr->max_tx_time = ((_radio.conn_curr->max_tx_octets + 14) << 3); /* enqueue a length rsp */ *rx_enqueue = 1; } } #endif /* CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH */ #if defined(CONFIG_BLUETOOTH_CONTROLLER_PHY) static inline void isr_rx_conn_pkt_ctrl_rej_phy_upd(struct radio_pdu_node_rx *radio_pdu_node_rx, u8_t *rx_enqueue) { struct pdu_data_llctrl_reject_ext_ind *rej_ext_ind; struct pdu_data *pdu_data_rx; pdu_data_rx = (struct pdu_data *)radio_pdu_node_rx->pdu_data; rej_ext_ind = (struct pdu_data_llctrl_reject_ext_ind *) &pdu_data_rx->payload.llctrl.ctrldata.reject_ext_ind; if (rej_ext_ind->reject_opcode == PDU_DATA_LLCTRL_TYPE_PHY_REQ) { struct radio_le_phy_upd_cmplt *p; /* Same Procedure or Different Procedure Collision */ /* If not same procedure, stop procedure timeout, else * continue timer until phy upd ind is received. */ if (rej_ext_ind->error_code != 0x23) { /* Procedure complete */ _radio.conn_curr->llcp_phy.ack = _radio.conn_curr->llcp_phy.req; /* Stop procedure timeout */ _radio.conn_curr->procedure_expire = 0; } /* skip event generation if not cmd initiated */ if (!_radio.conn_curr->llcp_phy.cmd) { return; } /* generate phy update complete event with error code */ radio_pdu_node_rx->hdr.type = NODE_RX_TYPE_PHY_UPDATE; p = (void *) &pdu_data_rx->payload; p->status = rej_ext_ind->error_code; p->tx = _radio.conn_curr->phy_tx; p->rx = _radio.conn_curr->phy_rx; /* enqueue the phy update complete */ *rx_enqueue = 1; } } #endif /* CONFIG_BLUETOOTH_CONTROLLER_PHY */ static inline void isr_rx_conn_pkt_ctrl_rej(struct radio_pdu_node_rx *radio_pdu_node_rx, u8_t *rx_enqueue) { if (0) { #if defined(CONFIG_BLUETOOTH_CONTROLLER_PHY) } else if (_radio.conn_curr->llcp_phy.ack != _radio.conn_curr->llcp_phy.req) { isr_rx_conn_pkt_ctrl_rej_phy_upd(radio_pdu_node_rx, rx_enqueue); #endif /* CONFIG_BLUETOOTH_CONTROLLER_PHY */ } else if (_radio.conn_curr->llcp_ack != _radio.conn_curr->llcp_req) { /* reset ctrl procedure */ _radio.conn_curr->llcp_ack = _radio.conn_curr->llcp_req; switch (_radio.conn_curr->llcp_type) { case LLCP_CONNECTION_UPDATE: isr_rx_conn_pkt_ctrl_rej_conn_upd(radio_pdu_node_rx, rx_enqueue); break; default: LL_ASSERT(0); break; } #if defined(CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH) } else if (_radio.conn_curr->llcp_length.ack != _radio.conn_curr->llcp_length.req) { isr_rx_conn_pkt_ctrl_rej_dle(radio_pdu_node_rx, rx_enqueue); #endif /* CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH */ #if defined(CONFIG_BLUETOOTH_CONTROLLER_LE_ENC) } else { struct pdu_data_llctrl_reject_ext_ind *rej_ext_ind; struct pdu_data *pdu_rx; pdu_rx = (void *)radio_pdu_node_rx->pdu_data; rej_ext_ind = (void *) &pdu_rx->payload.llctrl.ctrldata.reject_ext_ind; switch (rej_ext_ind->reject_opcode) { case PDU_DATA_LLCTRL_TYPE_ENC_REQ: /* resume data packet rx and tx */ _radio.conn_curr->pause_rx = 0; _radio.conn_curr->pause_tx = 0; /* Procedure complete */ _radio.conn_curr->procedure_expire = 0; /* enqueue as if it were a reject ind */ pdu_rx->payload.llctrl.opcode = PDU_DATA_LLCTRL_TYPE_REJECT_IND; pdu_rx->payload.llctrl.ctrldata.reject_ind.error_code = rej_ext_ind->error_code; *rx_enqueue = 1; break; default: /* Ignore */ break; } #endif /* CONFIG_BLUETOOTH_CONTROLLER_LE_ENC */ } } #if defined(CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH) static inline u8_t isr_rx_conn_pkt_ctrl_dle(struct pdu_data *pdu_data_rx, u8_t *rx_enqueue) { u16_t eff_rx_octets; u16_t eff_tx_octets; u8_t nack = 0; eff_rx_octets = _radio.conn_curr->max_rx_octets; eff_tx_octets = _radio.conn_curr->max_tx_octets; if (/* Local idle, and Peer request then complete the Peer procedure * with response. */ ((_radio.conn_curr->llcp_length.req == _radio.conn_curr->llcp_length.ack) && (pdu_data_rx->payload.llctrl.opcode == PDU_DATA_LLCTRL_TYPE_LENGTH_REQ)) || /* or Local has active... */ ((_radio.conn_curr->llcp_length.req != _radio.conn_curr->llcp_length.ack) && /* with Local requested and Peer request then complete the * Peer procedure with response. */ ((((_radio.conn_curr->llcp_length.state == LLCP_LENGTH_STATE_REQ) || (_radio.conn_curr->llcp_length.state == LLCP_LENGTH_STATE_ACK_WAIT)) && (pdu_data_rx->payload.llctrl.opcode == PDU_DATA_LLCTRL_TYPE_LENGTH_REQ)) || /* with Local waiting for response, and Peer response then * complete the Local procedure or Peer request then complete the * Peer procedure with response. */ ((_radio.conn_curr->llcp_length.state == LLCP_LENGTH_STATE_RSP_WAIT) && ((pdu_data_rx->payload.llctrl.opcode == PDU_DATA_LLCTRL_TYPE_LENGTH_RSP) || (pdu_data_rx->payload.llctrl.opcode == PDU_DATA_LLCTRL_TYPE_LENGTH_REQ)))))) { struct pdu_data_llctrl_length_req_rsp *lr; lr = (struct pdu_data_llctrl_length_req_rsp *) &pdu_data_rx->payload.llctrl.ctrldata.length_req; /* use the minimal of our default_tx_octets and * peer max_rx_octets */ if (lr->max_rx_octets >= RADIO_LL_LENGTH_OCTETS_RX_MIN) { eff_tx_octets = min(lr->max_rx_octets, _radio.conn_curr->default_tx_octets); } /* use the minimal of our max supported and * peer max_tx_octets */ if (lr->max_tx_octets >= RADIO_LL_LENGTH_OCTETS_RX_MIN) { eff_rx_octets = min(lr->max_tx_octets, RADIO_LL_LENGTH_OCTETS_RX_MAX); } /* check if change in rx octets */ if (eff_rx_octets != _radio.conn_curr->max_rx_octets) { u16_t free_count_rx; free_count_rx = packet_rx_acquired_count_get() + mem_free_count_get(_radio.pkt_rx_data_free); LL_ASSERT(free_count_rx <= 0xFF); if (_radio.packet_rx_data_count == free_count_rx) { /* accept the effective tx */ _radio.conn_curr->max_tx_octets = eff_tx_octets; /* trigger or retain the ctrl procedure so as * to resize the rx buffers. */ _radio.conn_curr->llcp_length.rx_octets = eff_rx_octets; _radio.conn_curr->llcp_length.tx_octets = eff_tx_octets; _radio.conn_curr->llcp_length.ack = (_radio.conn_curr->llcp_length.req - 1); _radio.conn_curr->llcp_length.state = LLCP_LENGTH_STATE_RESIZE; /* close the current connection event, so as * to perform rx octet change. */ _radio.state = STATE_CLOSE; } else { nack = 1; } } else { /* resume data packet tx */ _radio.conn_curr->pause_tx = 0; /* accept the effective tx */ _radio.conn_curr->max_tx_octets = eff_tx_octets; /* Procedure complete */ _radio.conn_curr->llcp_length.ack = _radio.conn_curr->llcp_length.req; _radio.conn_curr->procedure_expire = 0; /* prepare event params */ lr->max_rx_octets = eff_rx_octets; lr->max_rx_time = ((eff_rx_octets + 14) << 3); lr->max_tx_octets = eff_tx_octets; lr->max_tx_time = ((eff_tx_octets + 14) << 3); /* Enqueue data length change event (with no change in * rx length happened), safe to enqueue rx. */ *rx_enqueue = 1; } } else { /* Drop response with no Local initiated request. */ LL_ASSERT(pdu_data_rx->payload.llctrl.opcode == PDU_DATA_LLCTRL_TYPE_LENGTH_RSP); } if ((PDU_DATA_LLCTRL_TYPE_LENGTH_REQ == pdu_data_rx->payload.llctrl.opcode) && !nack) { length_resp_send(_radio.conn_curr, eff_rx_octets, eff_tx_octets); } return nack; } #endif /* CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH */ static inline u8_t isr_rx_conn_pkt_ctrl(struct radio_pdu_node_rx *radio_pdu_node_rx, u8_t *rx_enqueue) { struct pdu_data *pdu_data_rx; u8_t nack = 0; pdu_data_rx = (struct pdu_data *)radio_pdu_node_rx->pdu_data; /* Invalid packet */ if (_radio.conn_curr->role) { /* Slave */ switch (pdu_data_rx->payload.llctrl.opcode) { case PDU_DATA_LLCTRL_TYPE_ENC_RSP: case PDU_DATA_LLCTRL_TYPE_START_ENC_REQ: case PDU_DATA_LLCTRL_TYPE_SLAVE_FEATURE_REQ: case PDU_DATA_LLCTRL_TYPE_CONN_PARAM_RSP: case PDU_DATA_LLCTRL_TYPE_PHY_RSP: unknown_rsp_send(_radio.conn_curr, pdu_data_rx->payload.llctrl.opcode); return 0; default: break; } } else { /* Master */ switch (pdu_data_rx->payload.llctrl.opcode) { case PDU_DATA_LLCTRL_TYPE_CONN_UPDATE_IND: case PDU_DATA_LLCTRL_TYPE_CHAN_MAP_IND: case PDU_DATA_LLCTRL_TYPE_ENC_REQ: case PDU_DATA_LLCTRL_TYPE_FEATURE_REQ: case PDU_DATA_LLCTRL_TYPE_PAUSE_ENC_REQ: case PDU_DATA_LLCTRL_TYPE_PHY_UPD_IND: unknown_rsp_send(_radio.conn_curr, pdu_data_rx->payload.llctrl.opcode); return 0; default: break; } } switch (pdu_data_rx->payload.llctrl.opcode) { case PDU_DATA_LLCTRL_TYPE_CONN_UPDATE_IND: if (conn_update(_radio.conn_curr, pdu_data_rx) == 0) { /* conn param req procedure, if any, is complete */ _radio.conn_curr->procedure_expire = 0; } else { _radio.conn_curr->llcp_terminate.reason_peer = 0x28; } break; case PDU_DATA_LLCTRL_TYPE_CHAN_MAP_IND: if (chan_map_update(_radio.conn_curr, pdu_data_rx)) { _radio.conn_curr->llcp_terminate.reason_peer = 0x28; } break; case PDU_DATA_LLCTRL_TYPE_TERMINATE_IND: /* Ack and then terminate */ _radio.conn_curr->llcp_terminate.reason_peer = pdu_data_rx->payload.llctrl.ctrldata.terminate_ind.error_code; break; #if defined(CONFIG_BLUETOOTH_CONTROLLER_LE_ENC) case PDU_DATA_LLCTRL_TYPE_ENC_REQ: /* things from master stored for session key calculation */ memcpy(&_radio.conn_curr->llcp.encryption.skd[0], &pdu_data_rx->payload.llctrl.ctrldata.enc_req.skdm[0], 8); memcpy(&_radio.conn_curr->ccm_rx.iv[0], &pdu_data_rx->payload.llctrl.ctrldata.enc_req.ivm[0], 4); /* pause rx data packets */ _radio.conn_curr->pause_rx = 1; /* Start Procedure Timeout (TODO: this shall not replace * terminate procedure). */ _radio.conn_curr->procedure_expire = _radio.conn_curr->procedure_reload; /* TODO: remove this code block. * test peer master for overlapping contrl procedure. */ #if 0 if (_radio.conn_curr->llcp_version.tx == 0) { _radio.conn_curr->llcp_version.tx = 1; version_ind_send(_radio.conn_curr); } #endif #if defined(CONFIG_BLUETOOTH_CONTROLLER_FAST_ENC) /* TODO BT Spec. text: may finalize the sending of additional * data channel PDUs queued in the controller. */ enc_rsp_send(_radio.conn_curr); #endif /* CONFIG_BLUETOOTH_CONTROLLER_FAST_ENC */ /* enqueue the enc req */ *rx_enqueue = 1; break; case PDU_DATA_LLCTRL_TYPE_ENC_RSP: /* things sent by slave stored for session key calculation */ memcpy(&_radio.conn_curr->llcp.encryption.skd[8], &pdu_data_rx->payload.llctrl.ctrldata.enc_rsp.skds[0], 8); memcpy(&_radio.conn_curr->ccm_rx.iv[4], &pdu_data_rx->payload.llctrl.ctrldata.enc_rsp.ivs[0], 4); /* pause rx data packets */ _radio.conn_curr->pause_rx = 1; break; case PDU_DATA_LLCTRL_TYPE_START_ENC_REQ: LL_ASSERT(_radio.conn_curr->llcp_req == _radio.conn_curr->llcp_ack); /* start enc rsp to be scheduled in master prepare */ _radio.conn_curr->llcp_type = LLCP_ENCRYPTION; _radio.conn_curr->llcp_ack--; break; case PDU_DATA_LLCTRL_TYPE_START_ENC_RSP: if (_radio.role == ROLE_SLAVE) { #if !defined(CONFIG_BLUETOOTH_CONTROLLER_FAST_ENC) LL_ASSERT(_radio.conn_curr->llcp_req == _radio.conn_curr->llcp_ack); /* start enc rsp to be scheduled in slave prepare */ _radio.conn_curr->llcp_type = LLCP_ENCRYPTION; _radio.conn_curr->llcp_ack--; #else /* CONFIG_BLUETOOTH_CONTROLLER_FAST_ENC */ /* enable transmit encryption */ _radio.conn_curr->enc_tx = 1; start_enc_rsp_send(_radio.conn_curr, NULL); /* resume data packet rx and tx */ _radio.conn_curr->pause_rx = 0; _radio.conn_curr->pause_tx = 0; #endif /* CONFIG_BLUETOOTH_CONTROLLER_FAST_ENC */ } else { /* resume data packet rx and tx */ _radio.conn_curr->pause_rx = 0; _radio.conn_curr->pause_tx = 0; } /* enqueue the start enc resp (encryption change/refresh) */ if (_radio.conn_curr->refresh) { _radio.conn_curr->refresh = 0; /* key refresh event */ radio_pdu_node_rx->hdr.type = NODE_RX_TYPE_ENC_REFRESH; } *rx_enqueue = 1; /* Procedure complete */ _radio.conn_curr->procedure_expire = 0; break; #endif /* CONFIG_BLUETOOTH_CONTROLLER_LE_ENC */ case PDU_DATA_LLCTRL_TYPE_FEATURE_REQ: case PDU_DATA_LLCTRL_TYPE_SLAVE_FEATURE_REQ: { struct pdu_data_llctrl_feature_req *req; req = &pdu_data_rx->payload.llctrl.ctrldata.feature_req; /* AND the feature set to get Feature USED */ _radio.conn_curr->llcp_features &= feat_get(&req->features[0]); /* features exchanged */ _radio.conn_curr->common.fex_valid = 1; feature_rsp_send(_radio.conn_curr); } break; case PDU_DATA_LLCTRL_TYPE_FEATURE_RSP: { struct pdu_data_llctrl_feature_rsp *rsp; rsp = &pdu_data_rx->payload.llctrl.ctrldata.feature_rsp; /* AND the feature set to get Feature USED */ _radio.conn_curr->llcp_features &= feat_get(&rsp->features[0]); /* features exchanged */ _radio.conn_curr->common.fex_valid = 1; /* enqueue the feature resp */ *rx_enqueue = 1; /* Procedure complete */ _radio.conn_curr->procedure_expire = 0; } break; #if defined(CONFIG_BLUETOOTH_CONTROLLER_LE_ENC) case PDU_DATA_LLCTRL_TYPE_PAUSE_ENC_REQ: pause_enc_rsp_send(_radio.conn_curr); /* pause data packet rx */ _radio.conn_curr->pause_rx = 1; /* key refresh */ _radio.conn_curr->refresh = 1; /* disable receive encryption */ _radio.conn_curr->enc_rx = 0; break; case PDU_DATA_LLCTRL_TYPE_PAUSE_ENC_RSP: if (_radio.role == ROLE_MASTER) { /* reply with pause enc rsp */ pause_enc_rsp_send(_radio.conn_curr); /* pause data packet rx */ _radio.conn_curr->pause_rx = 1; /* disable receive encryption */ _radio.conn_curr->enc_rx = 0; } /* disable transmit encryption */ _radio.conn_curr->enc_tx = 0; break; #endif /* CONFIG_BLUETOOTH_CONTROLLER_LE_ENC */ case PDU_DATA_LLCTRL_TYPE_VERSION_IND: _radio.conn_curr->llcp_version.version_number = pdu_data_rx->payload.llctrl.ctrldata. version_ind.version_number; _radio.conn_curr->llcp_version. company_id = pdu_data_rx->payload.llctrl.ctrldata.version_ind.company_id; _radio.conn_curr->llcp_version.sub_version_number = pdu_data_rx->payload.llctrl.ctrldata.version_ind.sub_version_number; if ((_radio.conn_curr->llcp_version.tx != 0) && (_radio.conn_curr->llcp_version.rx == 0)) { /* enqueue the version ind */ *rx_enqueue = 1; /* Procedure complete */ _radio.conn_curr->procedure_expire = 0; } _radio.conn_curr->llcp_version.rx = 1; if (_radio.conn_curr->llcp_version.tx == 0) { _radio.conn_curr->llcp_version.tx = 1; version_ind_send(_radio.conn_curr); } break; #if defined(CONFIG_BLUETOOTH_CONTROLLER_LE_ENC) case PDU_DATA_LLCTRL_TYPE_REJECT_IND: /* resume data packet rx and tx */ _radio.conn_curr->pause_rx = 0; _radio.conn_curr->pause_tx = 0; /* Procedure complete */ _radio.conn_curr->procedure_expire = 0; /* enqueue the reject ind */ *rx_enqueue = 1; break; #endif /* CONFIG_BLUETOOTH_CONTROLLER_LE_ENC */ case PDU_DATA_LLCTRL_TYPE_CONN_PARAM_REQ: /* connection update or params req in progress * 1. if connection update in progress, then both master and * slave ignore this param req (in out impl. we assert, * see below assert). * 2. if connection param req to be initiated, slave drop * initiation and respond to this req, master ignore this * param req and continue to initiate. * 3. if connection param rsp waited for, slave drop waiting * and respond to this req, master ignore this param req and * master continue waiting. */ /* no ctrl procedures in progress or master req while slave * waiting resp. */ if (((_radio.conn_curr->llcp_req == _radio.conn_curr->llcp_ack) && (_radio.conn_upd == 0)) || ((_radio.conn_curr->llcp_req != _radio.conn_curr->llcp_ack) && (_radio.conn_curr->role) && (_radio.conn_curr == _radio.conn_upd) && (_radio.conn_curr->llcp_type == LLCP_CONNECTION_UPDATE) && ((_radio.conn_curr->llcp.connection_update.state == LLCP_CONN_STATE_INITIATE) || (_radio.conn_curr->llcp.connection_update.state == LLCP_CONN_STATE_REQ) || (_radio.conn_curr->llcp.connection_update.state == LLCP_CONN_STATE_RSP_WAIT)))) { /* set mutex */ if (_radio.conn_upd == 0) { _radio.conn_upd = _radio.conn_curr; } /* resp to be generated by app, for now save * parameters */ _radio.conn_curr->llcp.connection_update.interval = pdu_data_rx->payload.llctrl.ctrldata.conn_param_req.interval_min; _radio.conn_curr->llcp.connection_update.latency = pdu_data_rx->payload.llctrl.ctrldata.conn_param_req.latency; _radio.conn_curr->llcp.connection_update.timeout = pdu_data_rx->payload.llctrl.ctrldata.conn_param_req.timeout; _radio.conn_curr->llcp.connection_update.preferred_periodicity = pdu_data_rx->payload.llctrl.ctrldata.conn_param_req.preferred_periodicity; _radio.conn_curr->llcp.connection_update.instant = pdu_data_rx->payload.llctrl.ctrldata.conn_param_req. reference_conn_event_count; _radio.conn_curr->llcp.connection_update.offset0 = pdu_data_rx->payload.llctrl.ctrldata.conn_param_req.offset0; _radio.conn_curr->llcp.connection_update.offset1 = pdu_data_rx->payload.llctrl.ctrldata.conn_param_req.offset1; _radio.conn_curr->llcp.connection_update.offset2 = pdu_data_rx->payload.llctrl.ctrldata.conn_param_req.offset2; _radio.conn_curr->llcp.connection_update.offset3 = pdu_data_rx->payload.llctrl.ctrldata.conn_param_req.offset3; _radio.conn_curr->llcp.connection_update.offset4 = pdu_data_rx->payload.llctrl.ctrldata.conn_param_req.offset4; _radio.conn_curr->llcp.connection_update.offset5 = pdu_data_rx->payload.llctrl.ctrldata.conn_param_req.offset5; /* enqueue the conn param req, if parameters changed, * else respond */ if ((_radio.conn_curr->llcp.connection_update.interval != _radio.conn_curr->conn_interval) || (_radio.conn_curr->llcp.connection_update.latency != _radio.conn_curr->latency) || (_radio.conn_curr->llcp.connection_update.timeout != (_radio.conn_curr->conn_interval * _radio.conn_curr->supervision_reload * 125 / 1000))) { *rx_enqueue = 1; _radio.conn_curr->llcp.connection_update.state = LLCP_CONN_STATE_APP_WAIT; _radio.conn_curr->llcp.connection_update.is_internal = 0; _radio.conn_curr->llcp_type = LLCP_CONNECTION_UPDATE; _radio.conn_curr->llcp_ack--; } else { _radio.conn_curr->llcp.connection_update.win_size = 1; _radio.conn_curr->llcp.connection_update.win_offset_us = 0; _radio.conn_curr->llcp.connection_update.state = LLCP_CONN_STATE_RSP; _radio.conn_curr->llcp.connection_update.is_internal = 0; _radio.conn_curr->llcp_type = LLCP_CONNECTION_UPDATE; _radio.conn_curr->llcp_ack--; } } /* master in conn update procedure, master in any state we * ignore this req */ else if ((_radio.conn_curr->llcp_req != _radio.conn_curr->llcp_ack) && (!_radio.conn_curr->role) && (_radio.conn_curr == _radio.conn_upd) && (_radio.conn_curr->llcp_type == LLCP_CONNECTION_UPDATE)) { /* ignore this req, as master continue initiating or * waiting for resp */ } /* no ctrl procedure in this connection, but conn update mutex * set (another connection update in progress), hence reject * this req. */ else if (_radio.conn_curr->llcp_req == _radio.conn_curr->llcp_ack) { reject_ind_ext_send(_radio.conn_curr, PDU_DATA_LLCTRL_TYPE_CONN_PARAM_REQ, /* @todo use correct error_code */ 0x20); } else { /* different transaction collision */ /* 1. conn update in progress, instant not reached */ /* 2. some other ctrl procedure in progress */ LL_ASSERT(0); } break; case PDU_DATA_LLCTRL_TYPE_CONN_PARAM_RSP: /* TODO: send conn_update ind */ break; case PDU_DATA_LLCTRL_TYPE_REJECT_EXT_IND: isr_rx_conn_pkt_ctrl_rej(radio_pdu_node_rx, rx_enqueue); break; #if defined(CONFIG_BLUETOOTH_CONTROLLER_LE_PING) case PDU_DATA_LLCTRL_TYPE_PING_REQ: ping_resp_send(_radio.conn_curr); break; case PDU_DATA_LLCTRL_TYPE_PING_RSP: /* Procedure complete */ _radio.conn_curr->procedure_expire = 0; break; #endif /* CONFIG_BLUETOOTH_CONTROLLER_LE_PING */ case PDU_DATA_LLCTRL_TYPE_UNKNOWN_RSP: if (_radio.conn_curr->llcp_req != _radio.conn_curr->llcp_ack) { /* reset ctrl procedure */ _radio.conn_curr->llcp_ack = _radio.conn_curr->llcp_req; switch (_radio.conn_curr->llcp_type) { default: LL_ASSERT(0); break; } #if defined(CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH) } else if (_radio.conn_curr->llcp_length.req != _radio.conn_curr->llcp_length.ack) { /* Procedure complete */ _radio.conn_curr->llcp_length.ack = _radio.conn_curr->llcp_length.req; /* resume data packet tx */ _radio.conn_curr->pause_tx = 0; /* propagate the data length procedure to * host */ *rx_enqueue = 1; #endif /* CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH */ #if defined(CONFIG_BLUETOOTH_CONTROLLER_PHY) } else if (_radio.conn_curr->llcp_phy.req != _radio.conn_curr->llcp_phy.ack) { struct radio_le_phy_upd_cmplt *p; /* Procedure complete */ _radio.conn_curr->llcp_phy.ack = _radio.conn_curr->llcp_phy.req; /* skip event generation is not cmd initiated */ if (_radio.conn_curr->llcp_phy.cmd) { /* generate phy update complete event */ radio_pdu_node_rx->hdr.type = NODE_RX_TYPE_PHY_UPDATE; p = (struct radio_le_phy_upd_cmplt *) &pdu_data_rx->payload; p->status = 0; p->tx = _radio.conn_curr->phy_tx; p->rx = _radio.conn_curr->phy_rx; /* enqueue the phy update complete */ *rx_enqueue = 1; } #endif /* CONFIG_BLUETOOTH_CONTROLLER_PHY */ } else { struct pdu_data_llctrl *llctrl; llctrl = (struct pdu_data_llctrl *) &pdu_data_rx->payload.llctrl; switch (llctrl->ctrldata.unknown_rsp.type) { #if defined(CONFIG_BLUETOOTH_CONTROLLER_LE_PING) case PDU_DATA_LLCTRL_TYPE_PING_REQ: /* unknown rsp to LE Ping Req completes the * procedure; nothing to do here. */ break; #endif /* CONFIG_BLUETOOTH_CONTROLLER_LE_PING */ default: /* enqueue the error and let HCI handle it */ *rx_enqueue = 1; break; } } /* Procedure complete */ _radio.conn_curr->procedure_expire = 0; break; #if defined(CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH) case PDU_DATA_LLCTRL_TYPE_LENGTH_RSP: case PDU_DATA_LLCTRL_TYPE_LENGTH_REQ: nack = isr_rx_conn_pkt_ctrl_dle(pdu_data_rx, rx_enqueue); break; #endif /* CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH */ #if defined(CONFIG_BLUETOOTH_CONTROLLER_PHY) case PDU_DATA_LLCTRL_TYPE_PHY_REQ: if (_radio.role == ROLE_MASTER) { if ((_radio.conn_curr->llcp_phy.ack != _radio.conn_curr->llcp_phy.req) && ((_radio.conn_curr->llcp_phy.state == LLCP_PHY_STATE_ACK_WAIT) || (_radio.conn_curr->llcp_phy.state == LLCP_PHY_STATE_RSP_WAIT) || (_radio.conn_curr->llcp_phy.state == LLCP_PHY_STATE_UPD))) { /* Same procedure collision */ reject_ind_ext_send(_radio.conn_curr, PDU_DATA_LLCTRL_TYPE_PHY_REQ, 0x23); } else if (_radio.conn_curr->llcp_req != _radio.conn_curr->llcp_ack) { /* Different procedure collision */ reject_ind_ext_send(_radio.conn_curr, PDU_DATA_LLCTRL_TYPE_PHY_REQ, 0x2a); } else { struct pdu_data_llctrl *c = &pdu_data_rx->payload.llctrl; struct pdu_data_llctrl_phy_req_rsp *p = &c->ctrldata.phy_req; _radio.conn_curr->llcp_phy.state = LLCP_PHY_STATE_UPD; if (_radio.conn_curr->llcp_phy.ack == _radio.conn_curr->llcp_phy.req) { _radio.conn_curr->llcp_phy.ack--; _radio.conn_curr->llcp_phy.cmd = 0; _radio.conn_curr->llcp_phy.tx = _radio.conn_curr->phy_pref_tx; _radio.conn_curr->llcp_phy.rx = _radio.conn_curr->phy_pref_rx; } _radio.conn_curr->llcp_phy.tx &= p->rx_phys; _radio.conn_curr->llcp_phy.rx &= p->tx_phys; } } else { phy_rsp_send(_radio.conn_curr); /* Start Procedure Timeout (TODO: this shall not replace * terminate procedure). */ _radio.conn_curr->procedure_expire = _radio.conn_curr->procedure_reload; } break; case PDU_DATA_LLCTRL_TYPE_PHY_RSP: if ((_radio.role == ROLE_MASTER) && (_radio.conn_curr->llcp_phy.ack != _radio.conn_curr->llcp_phy.req) && (_radio.conn_curr->llcp_phy.state == LLCP_PHY_STATE_RSP_WAIT)) { struct pdu_data_llctrl_phy_req_rsp *p = &pdu_data_rx->payload.llctrl.ctrldata.phy_rsp; _radio.conn_curr->llcp_phy.state = LLCP_PHY_STATE_UPD; _radio.conn_curr->llcp_phy.tx &= p->rx_phys; _radio.conn_curr->llcp_phy.rx &= p->tx_phys; /* Procedure timeout is stopped */ _radio.conn_curr->procedure_expire = 0; } break; case PDU_DATA_LLCTRL_TYPE_PHY_UPD_IND: if (phy_upd_ind(radio_pdu_node_rx, rx_enqueue)) { _radio.conn_curr->llcp_terminate.reason_peer = 0x28; } break; #endif /* CONFIG_BLUETOOTH_CONTROLLER_PHY */ default: unknown_rsp_send(_radio.conn_curr, pdu_data_rx->payload.llctrl.opcode); break; } return nack; } static inline bool isr_rx_conn_enc_unexpected(struct connection *conn, struct pdu_data *pdu_data) { u8_t opcode = pdu_data->payload.llctrl.opcode; return (pdu_data->ll_id != PDU_DATA_LLID_CTRL) || (!conn->role && ((!conn->refresh && (opcode != PDU_DATA_LLCTRL_TYPE_TERMINATE_IND) && (opcode != PDU_DATA_LLCTRL_TYPE_START_ENC_REQ) && (opcode != PDU_DATA_LLCTRL_TYPE_START_ENC_RSP) && (opcode != PDU_DATA_LLCTRL_TYPE_REJECT_IND) && (opcode != PDU_DATA_LLCTRL_TYPE_REJECT_EXT_IND)) || (conn->refresh && (opcode != PDU_DATA_LLCTRL_TYPE_TERMINATE_IND) && (opcode != PDU_DATA_LLCTRL_TYPE_PAUSE_ENC_RSP) && (opcode != PDU_DATA_LLCTRL_TYPE_ENC_RSP) && (opcode != PDU_DATA_LLCTRL_TYPE_START_ENC_REQ) && (opcode != PDU_DATA_LLCTRL_TYPE_START_ENC_RSP) && (opcode != PDU_DATA_LLCTRL_TYPE_REJECT_IND) && (opcode != PDU_DATA_LLCTRL_TYPE_REJECT_EXT_IND)))) || (conn->role && ((!conn->refresh && (opcode != PDU_DATA_LLCTRL_TYPE_TERMINATE_IND) && (opcode != PDU_DATA_LLCTRL_TYPE_START_ENC_RSP) && (opcode != PDU_DATA_LLCTRL_TYPE_REJECT_IND) && (opcode != PDU_DATA_LLCTRL_TYPE_REJECT_EXT_IND)) || (conn->refresh && (opcode != PDU_DATA_LLCTRL_TYPE_TERMINATE_IND) && (opcode != PDU_DATA_LLCTRL_TYPE_PAUSE_ENC_RSP) && (opcode != PDU_DATA_LLCTRL_TYPE_ENC_REQ) && (opcode != PDU_DATA_LLCTRL_TYPE_START_ENC_RSP) && (opcode != PDU_DATA_LLCTRL_TYPE_REJECT_IND) && (opcode != PDU_DATA_LLCTRL_TYPE_REJECT_EXT_IND)))); } static inline u32_t isr_rx_conn_pkt(struct radio_pdu_node_rx *radio_pdu_node_rx, struct radio_pdu_node_tx **tx_release, u8_t *rx_enqueue) { struct pdu_data *pdu_data_rx; struct pdu_data *pdu_data_tx; u8_t terminate = 0; u8_t nack = 0; /* Ack for transmitted data */ pdu_data_rx = (struct pdu_data *)radio_pdu_node_rx->pdu_data; if (pdu_data_rx->nesn != _radio.conn_curr->sn) { /* Increment serial number */ _radio.conn_curr->sn++; /* First ack (and redundantly any other ack) enable use of * slave latency. */ if (_radio.role == ROLE_SLAVE) { _radio.conn_curr->slave.latency_enabled = 1; } if (_radio.conn_curr->empty == 0) { struct radio_pdu_node_tx *node_tx; u8_t pdu_data_tx_len, pdu_data_tx_ll_id; node_tx = _radio.conn_curr->pkt_tx_head; pdu_data_tx = (struct pdu_data *) (node_tx->pdu_data + _radio.conn_curr->packet_tx_head_offset); pdu_data_tx_len = pdu_data_tx->len; pdu_data_tx_ll_id = pdu_data_tx->ll_id; if (pdu_data_tx_len != 0) { /* if encrypted increment tx counter */ if (_radio.conn_curr->enc_tx) { _radio.conn_curr->ccm_tx.counter++; } /* process ctrl packet on tx cmplt */ if (pdu_data_tx_ll_id == PDU_DATA_LLID_CTRL) { terminate = isr_rx_conn_pkt_ack(pdu_data_tx, &node_tx); } } _radio.conn_curr->packet_tx_head_offset += pdu_data_tx_len; if (_radio.conn_curr->packet_tx_head_offset == _radio.conn_curr->packet_tx_head_len) { *tx_release = isr_rx_conn_pkt_release(node_tx); } } else { _radio.conn_curr->empty = 0; } } /* local initiated disconnect procedure completed */ if (terminate) { connection_release(_radio.conn_curr); _radio.conn_curr = NULL; return terminate; } /* process received data */ if ((pdu_data_rx->sn == _radio.conn_curr->nesn) && /* check so that we will NEVER use the rx buffer reserved for empty * packet and internal control enqueue */ (packet_rx_reserve_get(3) != 0) && ((_radio.fc_ena == 0) || ((_radio.link_rx_head == _radio.link_rx_tail) && (_radio.fc_req == _radio.fc_ack)) || ((_radio.link_rx_head != _radio.link_rx_tail) && (_radio.fc_req != _radio.fc_ack) && (((_radio.fc_req == 0) && (_radio.fc_handle[TRIPLE_BUFFER_SIZE - 1] == _radio.conn_curr->handle)) || ((_radio.fc_req != 0) && (_radio.fc_handle[_radio.fc_req - 1] == _radio.conn_curr->handle)))))) { u8_t ccm_rx_increment = 0; if (pdu_data_rx->len != 0) { /* If required, wait for CCM to finish */ if (_radio.conn_curr->enc_rx) { u32_t done; done = radio_ccm_is_done(); LL_ASSERT(done); ccm_rx_increment = 1; } /* MIC Failure Check or data rx during pause */ if ((_radio.conn_curr->enc_rx && !radio_ccm_mic_is_valid()) || (_radio.conn_curr->pause_rx && isr_rx_conn_enc_unexpected(_radio.conn_curr, pdu_data_rx))) { _radio.state = STATE_CLOSE; radio_disable(); /* assert if radio packet ptr is not set and * radio started tx */ LL_ASSERT(!radio_is_ready()); terminate_ind_rx_enqueue(_radio.conn_curr, 0x3d); connection_release(_radio.conn_curr); _radio.conn_curr = NULL; return 1; /* terminated */ } #if defined(CONFIG_BLUETOOTH_CONTROLLER_LE_PING) /* stop authenticated payload (pre) timeout */ _radio.conn_curr->appto_expire = 0; _radio.conn_curr->apto_expire = 0; #endif /* CONFIG_BLUETOOTH_CONTROLLER_LE_PING */ switch (pdu_data_rx->ll_id) { case PDU_DATA_LLID_DATA_CONTINUE: case PDU_DATA_LLID_DATA_START: /* enqueue data packet */ *rx_enqueue = 1; break; case PDU_DATA_LLID_CTRL: nack = isr_rx_conn_pkt_ctrl(radio_pdu_node_rx, rx_enqueue); break; case PDU_DATA_LLID_RESV: default: LL_ASSERT(0); break; } #if defined(CONFIG_BLUETOOTH_CONTROLLER_LE_PING) } else if ((_radio.conn_curr->enc_rx) || (_radio.conn_curr->pause_rx)) { struct connection *conn = _radio.conn_curr; u16_t appto_reload_new; /* check for change in apto */ appto_reload_new = (conn->apto_reload > (conn->latency + 6)) ? (conn->apto_reload - (conn->latency + 6)) : conn->apto_reload; if (conn->appto_reload != appto_reload_new) { conn->appto_reload = appto_reload_new; conn->apto_expire = 0; } /* start authenticated payload (pre) timeout */ if (conn->apto_expire == 0) { conn->appto_expire = conn->appto_reload; conn->apto_expire = conn->apto_reload; } #endif /* CONFIG_BLUETOOTH_CONTROLLER_LE_PING */ } if (!nack) { _radio.conn_curr->nesn++; if (ccm_rx_increment) { _radio.conn_curr->ccm_rx.counter++; } } } return 0; } static inline void isr_rx_conn(u8_t crc_ok, u8_t trx_done, u8_t rssi_ready) { struct radio_pdu_node_rx *radio_pdu_node_rx; struct radio_pdu_node_tx *tx_release = NULL; u8_t is_empty_pdu_tx_retry; struct pdu_data *pdu_data_rx; struct pdu_data *pdu_data_tx; u8_t rx_enqueue = 0; u8_t crc_close = 0; #if defined(CONFIG_BLUETOOTH_CONTROLLER_PROFILE_ISR) static u8_t s_lmax; static u8_t s_lmin = (u8_t) -1; static u8_t s_lprv; static u8_t s_max; static u8_t s_min = (u8_t) -1; static u8_t s_prv; u32_t sample; u8_t latency, elapsed, prv; u8_t chg = 0; #endif /* CONFIG_BLUETOOTH_CONTROLLER_PROFILE_ISR */ #if defined(CONFIG_BLUETOOTH_CONTROLLER_CONN_RSSI) /* Collect RSSI for connection */ if (_radio.packet_counter == 0) { if (rssi_ready) { u8_t rssi = radio_rssi_get(); _radio.conn_curr->rssi_latest = rssi; if (((_radio.conn_curr->rssi_reported - rssi) & 0xFF) > RADIO_RSSI_THRESHOLD) { if (_radio.conn_curr->rssi_sample_count) { _radio.conn_curr->rssi_sample_count--; } } else { _radio.conn_curr->rssi_sample_count = RADIO_RSSI_SAMPLE_COUNT; } } } #else /* !CONFIG_BLUETOOTH_CONTROLLER_CONN_RSSI */ ARG_UNUSED(rssi_ready); #endif /* !CONFIG_BLUETOOTH_CONTROLLER_CONN_RSSI */ /* Increment packet counter for this connection event */ _radio.packet_counter++; /* received data packet */ radio_pdu_node_rx = _radio.packet_rx[_radio.packet_rx_last]; radio_pdu_node_rx->hdr.type = NODE_RX_TYPE_DC_PDU; if (crc_ok) { u32_t terminate; terminate = isr_rx_conn_pkt(radio_pdu_node_rx, &tx_release, &rx_enqueue); if (terminate) { goto isr_rx_conn_exit; } /* Reset CRC expiry counter */ _radio.crc_expire = 0; /* Reset supervision counter */ _radio.conn_curr->supervision_expire = 0; } else { /* Start CRC error countdown, if not already started */ if (_radio.crc_expire == 0) { _radio.crc_expire = 2; } /* Check crc error countdown expiry */ _radio.crc_expire--; crc_close = (_radio.crc_expire == 0); /* Start supervision timeout, if not started already */ if (!_radio.conn_curr->supervision_expire) { _radio.conn_curr->supervision_expire = _radio.conn_curr->supervision_reload; } } /* prepare transmit packet */ is_empty_pdu_tx_retry = _radio.conn_curr->empty; prepare_pdu_data_tx(_radio.conn_curr, &pdu_data_tx); /* silent connection */ if (SILENT_CONNECTION) { /* slave silent, enter/be in supervision timeout */ if (_radio.packet_counter == 0) { _radio.packet_counter = 0xFF; } /* master silent, hence avoid slave drift compensation, and * close slave if no tx packets */ if (!trx_done) { /* avoid slave drift compensation if first packet * missed */ if (_radio.packet_counter == 1) { _radio.packet_counter = 0xFF; } /* no Rx-ed packet and none to Tx, close event */ if ((_radio.conn_curr->empty) && (pdu_data_tx->md == 0)) { _radio.state = STATE_CLOSE; radio_disable(); goto isr_rx_conn_exit; } } } /* Decide on event continuation and hence Radio Shorts to use */ pdu_data_rx = (struct pdu_data *)radio_pdu_node_rx->pdu_data; _radio.state = ((_radio.state == STATE_CLOSE) || (crc_close) || ((crc_ok) && (pdu_data_rx->md == 0) && (pdu_data_tx->len == 0)) || _radio.conn_curr->llcp_terminate.reason_peer) ? STATE_CLOSE : STATE_TX; if (_radio.state == STATE_CLOSE) { /* Event close for master */ if (_radio.role == ROLE_MASTER) { _radio.conn_curr->empty = is_empty_pdu_tx_retry; radio_disable(); goto isr_rx_conn_exit; } /* Event close for slave */ else { radio_switch_complete_and_disable(); } } else { /* if (_radio.state == STATE_TX) */ radio_tmr_tifs_set(RADIO_TIFS); #if defined(CONFIG_BLUETOOTH_CONTROLLER_PHY) radio_switch_complete_and_rx(_radio.conn_curr->phy_rx); #else /* !CONFIG_BLUETOOTH_CONTROLLER_PHY */ radio_switch_complete_and_rx(0); #endif /* !CONFIG_BLUETOOTH_CONTROLLER_PHY */ radio_tmr_end_capture(); } /* fill sn and nesn */ pdu_data_tx->sn = _radio.conn_curr->sn; pdu_data_tx->nesn = _radio.conn_curr->nesn; /* setup the radio tx packet buffer */ tx_packet_set(_radio.conn_curr, pdu_data_tx); isr_rx_conn_exit: #if defined(CONFIG_BLUETOOTH_CONTROLLER_PROFILE_ISR) /* get the ISR latency sample */ sample = radio_tmr_sample_get(); /* sample the packet timer again, use it to calculate ISR execution time * and use it in profiling event */ radio_tmr_sample(); #endif /* CONFIG_BLUETOOTH_CONTROLLER_PROFILE_ISR */ /* release tx node and generate event for num complete */ if (tx_release) { pdu_node_tx_release(_radio.conn_curr->handle, tx_release); } /* enqueue any rx packet/event towards application */ if (rx_enqueue) { /* set data flow control lock on currently rx-ed connection */ rx_fc_lock(_radio.conn_curr->handle); /* set the connection handle and enqueue */ radio_pdu_node_rx->hdr.handle = _radio.conn_curr->handle; packet_rx_enqueue(); } #if defined(CONFIG_BLUETOOTH_CONTROLLER_PROFILE_ISR) /* calculate the elapsed time in us since on-air radio packet end * to ISR entry */ latency = sample - radio_tmr_end_get(); /* check changes in min, avg and max of latency */ if (latency > s_lmax) { s_lmax = latency; chg = 1; } if (latency < s_lmin) { s_lmin = latency; chg = 1; } /* check for +/- 1us change */ prv = ((u16_t)s_lprv + latency) >> 1; if (prv != s_lprv) { s_lprv = latency; chg = 1; } /* calculate the elapsed time in us since ISR entry */ elapsed = radio_tmr_sample_get() - sample; /* check changes in min, avg and max */ if (elapsed > s_max) { s_max = elapsed; chg = 1; } if (elapsed < s_min) { s_min = elapsed; chg = 1; } /* check for +/- 1us change */ prv = ((u16_t)s_prv + elapsed) >> 1; if (prv != s_prv) { s_prv = elapsed; chg = 1; } /* generate event if any change */ if (chg) { /* NOTE: enqueue only if rx buffer available, else ignore */ radio_pdu_node_rx = packet_rx_reserve_get(2); if (radio_pdu_node_rx) { radio_pdu_node_rx->hdr.handle = 0xFFFF; radio_pdu_node_rx->hdr.type = NODE_RX_TYPE_PROFILE; pdu_data_rx = (struct pdu_data *) radio_pdu_node_rx->pdu_data; pdu_data_rx->payload.profile.lcur = latency; pdu_data_rx->payload.profile.lmin = s_lmin; pdu_data_rx->payload.profile.lmax = s_lmax; pdu_data_rx->payload.profile.cur = elapsed; pdu_data_rx->payload.profile.min = s_min; pdu_data_rx->payload.profile.max = s_max; packet_rx_enqueue(); } } #endif /* CONFIG_BLUETOOTH_CONTROLLER_PROFILE_ISR */ return; } static inline void isr_radio_state_rx(u8_t trx_done, u8_t crc_ok, u8_t devmatch_ok, u8_t devmatch_id, u8_t irkmatch_ok, u8_t irkmatch_id, u8_t rssi_ready) { u32_t err; u8_t rl_idx; if (!((trx_done) || ((SILENT_CONNECTION) && (_radio.role == ROLE_SLAVE)))) { _radio.state = STATE_CLOSE; radio_disable(); return; } switch (_radio.role) { case ROLE_ADV: if (crc_ok) { err = isr_rx_adv(devmatch_ok, devmatch_id, irkmatch_ok, irkmatch_id, rssi_ready); } else { err = 1; } if (err) { _radio.state = STATE_CLOSE; radio_disable(); } break; case ROLE_SCAN: #if defined(CONFIG_BLUETOOTH_CONTROLLER_PRIVACY) rl_idx = devmatch_ok ? ctrl_rl_idx(!!(_radio.scanner.filter_policy & 0x01), devmatch_id) : irkmatch_ok ? ctrl_rl_irk_idx(irkmatch_id) : FILTER_IDX_NONE; #else rl_idx = FILTER_IDX_NONE; #endif if (crc_ok && isr_rx_scan_check(irkmatch_ok, devmatch_ok, rl_idx)) { err = isr_rx_scan(devmatch_ok, devmatch_id, irkmatch_ok, irkmatch_id, rl_idx, rssi_ready); } else { err = 1; } if (err) { _radio.state = STATE_CLOSE; radio_disable(); /* switch scanner state to idle */ _radio.scanner.state = 0; } break; case ROLE_SLAVE: case ROLE_MASTER: isr_rx_conn(crc_ok, trx_done, rssi_ready); break; case ROLE_NONE: default: LL_ASSERT(0); break; } } static inline u32_t isr_close_adv(void) { u32_t dont_close = 0; if ((_radio.state == STATE_CLOSE) && (_radio.advertiser.chan_map_current != 0)) { dont_close = 1; adv_setup(); radio_tx_enable(); radio_tmr_end_capture(); } else { struct pdu_adv *pdu_adv; radio_filter_disable(); pdu_adv = (struct pdu_adv *) &_radio.advertiser.adv_data.data[_radio.advertiser.adv_data.first][0]; if ((_radio.state == STATE_CLOSE) && (pdu_adv->type != PDU_ADV_TYPE_DIRECT_IND)) { u32_t ticker_status; u8_t random_delay; /** @todo use random 0-10 */ random_delay = 10; /* Call to ticker_update can fail under the race * condition where in the Adv role is being stopped but * at the same time it is preempted by Adv event that * gets into close state. Accept failure when Adv role * is being stopped. */ ticker_status = ticker_update(RADIO_TICKER_INSTANCE_ID_RADIO, RADIO_TICKER_USER_ID_WORKER, RADIO_TICKER_ID_ADV, TICKER_US_TO_TICKS(random_delay * 1000), 0, 0, 0, 0, 0, ticker_update_adv_assert, (void *)__LINE__); LL_ASSERT((ticker_status == TICKER_STATUS_SUCCESS) || (ticker_status == TICKER_STATUS_BUSY) || (_radio.ticker_id_stop == RADIO_TICKER_ID_ADV)); } } return dont_close; } static inline u32_t isr_close_scan(void) { u32_t dont_close = 0; if (_radio.state == STATE_CLOSE) { dont_close = 1; radio_tmr_tifs_set(RADIO_TIFS); radio_switch_complete_and_tx(0, 0, 0, 0); radio_pkt_rx_set(_radio.packet_rx[_radio.packet_rx_last]-> pdu_data); radio_rssi_measure(); #if defined(CONFIG_BLUETOOTH_CONTROLLER_PRIVACY) if (ctrl_rl_enabled()) { u8_t count, *irks = ctrl_irks_get(&count); radio_ar_configure(count, irks); } #endif /* CONFIG_BLUETOOTH_CONTROLLER_PRIVACY */ _radio.state = STATE_RX; radio_rx_enable(); radio_tmr_end_capture(); } else { radio_filter_disable(); if (_radio.state == STATE_ABORT) { /* Scanner stop can expire while here in this ISR. * Deferred attempt to stop can fail as it would have * expired, hence ignore failure. */ ticker_stop(RADIO_TICKER_INSTANCE_ID_RADIO, RADIO_TICKER_USER_ID_WORKER, RADIO_TICKER_ID_SCAN_STOP, NULL, NULL); } } return dont_close; } static inline void isr_close_conn(void) { u16_t ticks_drift_plus; u16_t ticks_drift_minus; u16_t latency_event; u16_t elapsed_event; u8_t reason_peer; u16_t lazy; u8_t force; /* Local initiated terminate happened */ if (_radio.conn_curr == 0) { return; } /* Master transmitted ack for the received terminate ind or * Slave received terminate ind. */ reason_peer = _radio.conn_curr->llcp_terminate.reason_peer; if (reason_peer && ((_radio.role == ROLE_SLAVE) || _radio.conn_curr->master.terminate_ack)) { terminate_ind_rx_enqueue(_radio.conn_curr, reason_peer); connection_release(_radio.conn_curr); _radio.conn_curr = NULL; return; } ticks_drift_plus = 0; ticks_drift_minus = 0; latency_event = _radio.conn_curr->latency_event; elapsed_event = latency_event + 1; /* calculate drift if anchor point sync-ed */ if (_radio.packet_counter && (!SILENT_CONNECTION || (_radio.packet_counter != 0xFF))) { if (_radio.role == ROLE_SLAVE) { u32_t start_to_address_expected_us; u32_t start_to_address_actual_us; u32_t window_widening_event_us; u32_t preamble_to_addr_us; /* calculate the drift in ticks */ start_to_address_actual_us = radio_tmr_aa_get(); window_widening_event_us = _radio.conn_curr->slave.window_widening_event_us; #if defined(CONFIG_BLUETOOTH_CONTROLLER_PHY) preamble_to_addr_us = addr_us_get(_radio.conn_curr->phy_rx); #else /* !CONFIG_BLUETOOTH_CONTROLLER_PHY */ preamble_to_addr_us = addr_us_get(0); #endif /* !CONFIG_BLUETOOTH_CONTROLLER_PHY */ start_to_address_expected_us = RADIO_TICKER_JITTER_US + (RADIO_TICKER_JITTER_US << 1) + preamble_to_addr_us + window_widening_event_us; if (start_to_address_actual_us <= start_to_address_expected_us) { ticks_drift_plus = TICKER_US_TO_TICKS(window_widening_event_us); ticks_drift_minus = TICKER_US_TO_TICKS((u64_t)(start_to_address_expected_us - start_to_address_actual_us)); } else { ticks_drift_plus = TICKER_US_TO_TICKS(start_to_address_actual_us); ticks_drift_minus = TICKER_US_TO_TICKS(RADIO_TICKER_JITTER_US + (RADIO_TICKER_JITTER_US << 1) + preamble_to_addr_us); } /* Reset window widening, as anchor point sync-ed */ _radio.conn_curr->slave.window_widening_event_us = 0; _radio.conn_curr->slave.window_size_event_us = 0; /* apply latency if no more data */ if (_radio.conn_curr->pkt_tx_head) { struct pdu_data *pdu_data_tx; pdu_data_tx = (struct pdu_data *) _radio.conn_curr->pkt_tx_head->pdu_data; if (pdu_data_tx->len || _radio.conn_curr->packet_tx_head_offset) { _radio.conn_curr->latency_event = 0; } } else if (_radio.conn_curr->slave.latency_enabled) { _radio.conn_curr->latency_event = _radio.conn_curr->latency; } } else if (reason_peer) { _radio.conn_curr->master.terminate_ack = 1; } /* Reset connection failed to establish procedure */ _radio.conn_curr->connect_expire = 0; } /* check connection failed to establish */ else if (_radio.conn_curr->connect_expire) { if (_radio.conn_curr->connect_expire > elapsed_event) { _radio.conn_curr->connect_expire -= elapsed_event; } else { terminate_ind_rx_enqueue(_radio.conn_curr, 0x3e); connection_release(_radio.conn_curr); _radio.conn_curr = NULL; return; } } /* if anchor point not sync-ed, start supervision timeout, and break * latency if any. */ else { /* Start supervision timeout, if not started already */ if (!_radio.conn_curr->supervision_expire) { _radio.conn_curr->supervision_expire = _radio.conn_curr->supervision_reload; } } /* check supervision timeout */ force = 0; if (_radio.conn_curr->supervision_expire) { if (_radio.conn_curr->supervision_expire > elapsed_event) { _radio.conn_curr->supervision_expire -= elapsed_event; /* break latency */ _radio.conn_curr->latency_event = 0; /* Force both master and slave when close to * supervision timeout. */ if (_radio.conn_curr->supervision_expire <= 6) { force = 1; } /* use randomness to force slave role when anchor * points are being missed. */ else if (_radio.role == ROLE_SLAVE) { if (latency_event != 0) { force = 1; } else { force = _radio.conn_curr->slave.force & 0x01; /* rotate force bits */ _radio.conn_curr->slave.force >>= 1; if (force) { _radio.conn_curr->slave.force |= ((u32_t)1 << 31); } } } } else { terminate_ind_rx_enqueue(_radio.conn_curr, 0x08); connection_release(_radio.conn_curr); _radio.conn_curr = NULL; return; } } /* check procedure timeout */ if (_radio.conn_curr->procedure_expire != 0) { if (_radio.conn_curr->procedure_expire > elapsed_event) { _radio.conn_curr->procedure_expire -= elapsed_event; } else { terminate_ind_rx_enqueue(_radio.conn_curr, 0x22); connection_release(_radio.conn_curr); _radio.conn_curr = NULL; return; } } #if defined(CONFIG_BLUETOOTH_CONTROLLER_LE_PING) /* check apto */ if (_radio.conn_curr->apto_expire != 0) { if (_radio.conn_curr->apto_expire > elapsed_event) { _radio.conn_curr->apto_expire -= elapsed_event; } else { struct radio_pdu_node_rx *radio_pdu_node_rx; _radio.conn_curr->apto_expire = 0; /* Prepare the rx packet structure */ radio_pdu_node_rx = packet_rx_reserve_get(2); LL_ASSERT(radio_pdu_node_rx); radio_pdu_node_rx->hdr.handle = _radio.conn_curr->handle; radio_pdu_node_rx->hdr.type = NODE_RX_TYPE_APTO; /* enqueue apto event into rx queue */ packet_rx_enqueue(); } } /* check appto */ if (_radio.conn_curr->appto_expire != 0) { if (_radio.conn_curr->appto_expire > elapsed_event) { _radio.conn_curr->appto_expire -= elapsed_event; } else { _radio.conn_curr->appto_expire = 0; if ((_radio.conn_curr->procedure_expire == 0) && (_radio.conn_curr->llcp_req == _radio.conn_curr->llcp_ack)) { _radio.conn_curr->llcp_type = LLCP_PING; _radio.conn_curr->llcp_ack--; } } } #endif /* CONFIG_BLUETOOTH_CONTROLLER_LE_PING */ #if defined(CONFIG_BLUETOOTH_CONTROLLER_CONN_RSSI) /* generate RSSI event */ if (_radio.conn_curr->rssi_sample_count == 0) { struct radio_pdu_node_rx *radio_pdu_node_rx; struct pdu_data *pdu_data_rx; radio_pdu_node_rx = packet_rx_reserve_get(2); if (radio_pdu_node_rx) { _radio.conn_curr->rssi_reported = _radio.conn_curr->rssi_latest; _radio.conn_curr->rssi_sample_count = RADIO_RSSI_SAMPLE_COUNT; /* Prepare the rx packet structure */ radio_pdu_node_rx->hdr.handle = _radio.conn_curr->handle; radio_pdu_node_rx->hdr.type = NODE_RX_TYPE_RSSI; /* prepare connection RSSI structure */ pdu_data_rx = (struct pdu_data *) radio_pdu_node_rx->pdu_data; pdu_data_rx->payload.rssi = _radio.conn_curr->rssi_reported; /* enqueue connection RSSI structure into queue */ packet_rx_enqueue(); } } #endif /* CONFIG_BLUETOOTH_CONTROLLER_CONN_RSSI */ /* break latency based on ctrl procedure pending */ if ((_radio.conn_curr->llcp_ack != _radio.conn_curr->llcp_req) && ((_radio.conn_curr->llcp_type == LLCP_CONNECTION_UPDATE) || (_radio.conn_curr->llcp_type == LLCP_CHAN_MAP))) { _radio.conn_curr->latency_event = 0; } /* check if latency needs update */ lazy = 0; if ((force) || (latency_event != _radio.conn_curr->latency_event)) { lazy = _radio.conn_curr->latency_event + 1; } if ((ticks_drift_plus != 0) || (ticks_drift_minus != 0) || (lazy != 0) || (force != 0)) { u32_t ticker_status; u8_t ticker_id = RADIO_TICKER_ID_FIRST_CONNECTION + _radio.conn_curr->handle; /* Call to ticker_update can fail under the race * condition where in the Slave role is being stopped but * at the same time it is preempted by Slave event that * gets into close state. Accept failure when Slave role * is being stopped. */ ticker_status = ticker_update(RADIO_TICKER_INSTANCE_ID_RADIO, RADIO_TICKER_USER_ID_WORKER, ticker_id, ticks_drift_plus, ticks_drift_minus, 0, 0, lazy, force, ticker_update_slave_assert, (void *)(u32_t)ticker_id); LL_ASSERT((ticker_status == TICKER_STATUS_SUCCESS) || (ticker_status == TICKER_STATUS_BUSY) || (_radio.ticker_id_stop == ticker_id)); } } static inline void isr_radio_state_close(void) { u32_t dont_close = 0; switch (_radio.role) { case ROLE_ADV: dont_close = isr_close_adv(); #if defined(CONFIG_BLUETOOTH_CONTROLLER_ADV_INDICATION) if (!dont_close) { struct radio_pdu_node_rx *radio_pdu_node_rx; radio_pdu_node_rx = packet_rx_reserve_get(3); if (radio_pdu_node_rx) { radio_pdu_node_rx->hdr.type = NODE_RX_TYPE_ADV_INDICATION; radio_pdu_node_rx->hdr.handle = 0xFFFF; /* TODO: add other info by defining a payload * structure. */ packet_rx_enqueue(); } } #endif /* CONFIG_BLUETOOTH_CONTROLLER_ADV_INDICATION */ break; case ROLE_SCAN: dont_close = isr_close_scan(); break; case ROLE_SLAVE: case ROLE_MASTER: isr_close_conn(); break; case ROLE_NONE: /* If a role closes graceful while it is being stopped, then * Radio ISR will be triggered to process the stop state with * no active role at that instance in time. * Just reset the state to none. The role has gracefully closed * before this ISR run. * The above applies to aborting a role event too. */ LL_ASSERT((_radio.state == STATE_STOP) || (_radio.state == STATE_ABORT)); _radio.state = STATE_NONE; return; default: LL_ASSERT(0); break; } if (dont_close) { return; } _radio.role = ROLE_NONE; _radio.state = STATE_NONE; _radio.ticker_id_event = 0; radio_tmr_stop(); event_inactive(0, 0, 0, NULL); clock_control_off(_radio.hf_clock, NULL); mayfly_enable(RADIO_TICKER_USER_ID_WORKER, RADIO_TICKER_USER_ID_JOB, 1); DEBUG_RADIO_CLOSE(0); } static void isr(void) { u8_t trx_done; u8_t crc_ok; u8_t devmatch_ok; u8_t devmatch_id; u8_t irkmatch_ok; u8_t irkmatch_id; u8_t rssi_ready; DEBUG_RADIO_ISR(1); /* Read radio status and events */ trx_done = radio_is_done(); if (trx_done) { #if defined(CONFIG_BLUETOOTH_CONTROLLER_PROFILE_ISR) /* sample the packet timer here, use it to calculate ISR latency * and generate the profiling event at the end of the ISR. */ radio_tmr_sample(); #endif /* CONFIG_BLUETOOTH_CONTROLLER_PROFILE_ISR */ crc_ok = radio_crc_is_valid(); devmatch_ok = radio_filter_has_match(); devmatch_id = radio_filter_match_get(); irkmatch_ok = radio_ar_has_match(); irkmatch_id = radio_ar_match_get(); rssi_ready = radio_rssi_is_ready(); } else { crc_ok = devmatch_ok = irkmatch_ok = rssi_ready = 0; devmatch_id = irkmatch_id = 0xFF; } /* Clear radio status and events */ radio_status_reset(); radio_tmr_status_reset(); radio_filter_status_reset(); radio_ar_status_reset(); radio_rssi_status_reset(); switch (_radio.state) { case STATE_TX: isr_radio_state_tx(); break; case STATE_RX: isr_radio_state_rx(trx_done, crc_ok, devmatch_ok, devmatch_id, irkmatch_ok, irkmatch_id, rssi_ready); break; case STATE_ABORT: case STATE_STOP: case STATE_CLOSE: isr_radio_state_close(); break; case STATE_NONE: /* Ignore Duplicate Radio Disabled IRQ due to forced stop * using Radio Disable task. */ break; default: LL_ASSERT(0); break; } DEBUG_RADIO_ISR(0); } #if (RADIO_TICKER_USER_ID_WORKER_PRIO == RADIO_TICKER_USER_ID_JOB_PRIO) static void ticker_job_disable(u32_t status, void *op_context) { ARG_UNUSED(status); ARG_UNUSED(op_context); if (_radio.state != STATE_NONE) { mayfly_enable(RADIO_TICKER_USER_ID_JOB, RADIO_TICKER_USER_ID_JOB, 0); } } #endif static void ticker_if_done(u32_t status, void *ops_context) { *((u32_t volatile *)ops_context) = status; } static void ticker_success_assert(u32_t status, void *params) { ARG_UNUSED(params); LL_ASSERT(status == TICKER_STATUS_SUCCESS); } static void ticker_stop_adv_assert(u32_t status, void *params) { ARG_UNUSED(params); if (status == TICKER_STATUS_FAILURE) { if (_radio.ticker_id_stop == RADIO_TICKER_ID_ADV) { /* ticker_stop failed due to race condition * while in role_disable. Let the role_disable * be made aware of, so it can return failure * (to stop Adv role as it is now transitioned * to Slave role). */ _radio.ticker_id_stop = 0; } else { LL_ASSERT(0); } } else { /* This assert shall not happen if advertiser role's slot * calculation is correct, and next event shall not * overlap/pre-empt the current advertise role event. */ LL_ASSERT(_radio.ticker_id_prepare != RADIO_TICKER_ID_ADV); } } static void ticker_stop_scan_assert(u32_t status, void *params) { ARG_UNUSED(params); if (status == TICKER_STATUS_FAILURE) { if (_radio.ticker_id_stop == RADIO_TICKER_ID_SCAN) { /* ticker_stop failed due to race condition * while in role_disable. Let the role_disable * be made aware of, so it can return failure * (to stop Scan role as it is now transitioned * to Master role). */ _radio.ticker_id_stop = 0; } else { LL_ASSERT(0); } } else { /* This assert shall not happen if scanner role's slot * calculation is correct, and next event shall not * overlap/pre-empt the current scanner role event. */ LL_ASSERT(_radio.ticker_id_prepare != RADIO_TICKER_ID_SCAN); } } static void ticker_update_adv_assert(u32_t status, void *params) { ARG_UNUSED(params); LL_ASSERT((status == TICKER_STATUS_SUCCESS) || (_radio.ticker_id_stop == RADIO_TICKER_ID_ADV)); } static void ticker_update_slave_assert(u32_t status, void *params) { u8_t ticker_id = (u32_t)params & 0xFF; LL_ASSERT((status == TICKER_STATUS_SUCCESS) || (_radio.ticker_id_stop == ticker_id)); } static void mayfly_radio_active(void *params) { static u8_t s_active; if ((u32_t)params) { if (s_active++) { return; } DEBUG_RADIO_ACTIVE(1); radio_active_callback(1); } else { LL_ASSERT(s_active); if (--s_active) { return; } DEBUG_RADIO_ACTIVE(0); radio_active_callback(0); } } static void event_active(u32_t ticks_at_expire, u32_t remainder, u16_t lazy, void *context) { static void *s_link[2]; static struct mayfly s_mfy_radio_active = {0, 0, s_link, (void *)1, mayfly_radio_active}; u32_t retval; ARG_UNUSED(ticks_at_expire); ARG_UNUSED(remainder); ARG_UNUSED(lazy); ARG_UNUSED(context); retval = mayfly_enqueue(RADIO_TICKER_USER_ID_WORKER, RADIO_TICKER_USER_ID_WORKER, 0, &s_mfy_radio_active); LL_ASSERT(!retval); } static void mayfly_radio_inactive(void *params) { ARG_UNUSED(params); mayfly_radio_active(0); DEBUG_RADIO_CLOSE(0); } static void event_inactive(u32_t ticks_at_expire, u32_t remainder, u16_t lazy, void *context) { static void *s_link[2]; static struct mayfly s_mfy_radio_inactive = {0, 0, s_link, NULL, mayfly_radio_inactive}; u32_t retval; ARG_UNUSED(ticks_at_expire); ARG_UNUSED(remainder); ARG_UNUSED(lazy); ARG_UNUSED(context); retval = mayfly_enqueue(RADIO_TICKER_USER_ID_WORKER, RADIO_TICKER_USER_ID_WORKER, 0, &s_mfy_radio_inactive); LL_ASSERT(!retval); } static void mayfly_xtal_start(void *params) { ARG_UNUSED(params); /* turn on 16MHz clock, non-blocking mode. */ clock_control_on(_radio.hf_clock, NULL); } static void event_xtal(u32_t ticks_at_expire, u32_t remainder, u16_t lazy, void *context) { static void *s_link[2]; static struct mayfly s_mfy_xtal_start = {0, 0, s_link, NULL, mayfly_xtal_start}; u32_t retval; ARG_UNUSED(ticks_at_expire); ARG_UNUSED(remainder); ARG_UNUSED(lazy); ARG_UNUSED(context); retval = mayfly_enqueue(RADIO_TICKER_USER_ID_WORKER, RADIO_TICKER_USER_ID_WORKER, 0, &s_mfy_xtal_start); LL_ASSERT(!retval); } static void mayfly_xtal_stop(void *params) { ARG_UNUSED(params); clock_control_off(_radio.hf_clock, NULL); DEBUG_RADIO_CLOSE(0); } #if defined(CONFIG_BLUETOOTH_CONTROLLER_XTAL_ADVANCED) static void mayfly_xtal_retain(u8_t caller_id, u8_t retain) { static u8_t s_xtal_retained; if (retain) { if (!s_xtal_retained) { static void *s_link[2]; static struct mayfly s_mfy_xtal_start = {0, 0, s_link, NULL, mayfly_xtal_start}; u32_t retval; /* Only user id job will try to retain the XTAL. */ LL_ASSERT(caller_id == RADIO_TICKER_USER_ID_JOB); s_xtal_retained = 1; retval = mayfly_enqueue(caller_id, RADIO_TICKER_USER_ID_WORKER, 0, &s_mfy_xtal_start); LL_ASSERT(!retval); } } else { if (s_xtal_retained) { static void *s_link[2][2]; static struct mayfly s_mfy_xtal_stop[2] = { {0, 0, s_link[0], NULL, mayfly_xtal_stop}, {0, 0, s_link[1], NULL, mayfly_xtal_stop} }; struct mayfly *p_mfy_xtal_stop = NULL; u32_t retval; s_xtal_retained = 0; switch (caller_id) { case RADIO_TICKER_USER_ID_WORKER: p_mfy_xtal_stop = &s_mfy_xtal_stop[0]; break; case RADIO_TICKER_USER_ID_JOB: p_mfy_xtal_stop = &s_mfy_xtal_stop[1]; break; default: LL_ASSERT(0); break; } retval = mayfly_enqueue(caller_id, RADIO_TICKER_USER_ID_WORKER, 0, p_mfy_xtal_stop); LL_ASSERT(!retval); } } } static void prepare_reduced(u32_t status, void *op_context) { /* It is acceptable that ticker_update will fail, if ticker is stopped; * for example, scan ticker is stopped on connection estblishment but * is also preempted. */ if (status == 0) { struct shdr *hdr = (struct shdr *)op_context; hdr->ticks_xtal_to_start |= ((u32_t)1 << 31); } } static void prepare_normal(u32_t status, void *op_context) { /* It is acceptable that ticker_update will fail, if ticker is stopped; * for example, scan ticker is stopped on connection estblishment but * is also preempted. */ if (status == 0) { struct shdr *hdr = (struct shdr *)op_context; hdr->ticks_xtal_to_start &= ~((u32_t)1 << 31); } } static void prepare_normal_set(struct shdr *hdr, u8_t ticker_user_id, u8_t ticker_id) { if (hdr->ticks_xtal_to_start & ((u32_t)1 << 31)) { u32_t ticker_status; u32_t ticks_prepare_to_start = (hdr->ticks_active_to_start > hdr->ticks_preempt_to_start) ? hdr-> ticks_active_to_start : hdr->ticks_preempt_to_start; u32_t ticks_drift_minus = (hdr->ticks_xtal_to_start & (~((u32_t)1 << 31))) - ticks_prepare_to_start; ticker_status = ticker_update(RADIO_TICKER_INSTANCE_ID_RADIO, ticker_user_id, ticker_id, 0, ticks_drift_minus, ticks_drift_minus, 0, 0, 0, prepare_normal, hdr); LL_ASSERT((ticker_status == TICKER_STATUS_SUCCESS) || (ticker_status == TICKER_STATUS_BUSY)); } } #if (RADIO_TICKER_PREEMPT_PART_US <= RADIO_TICKER_PREEMPT_PART_MIN_US) static u32_t preempt_calc(struct shdr *hdr, u8_t ticker_id, u32_t ticks_at_expire) { u32_t diff = ticker_ticks_diff_get(ticker_ticks_now_get(), ticks_at_expire); diff += 3; if (diff > TICKER_US_TO_TICKS(RADIO_TICKER_START_PART_US)) { mayfly_xtal_retain(RADIO_TICKER_USER_ID_WORKER, 0); prepare_normal_set(hdr, RADIO_TICKER_USER_ID_WORKER, ticker_id); diff += hdr->ticks_preempt_to_start; if (diff < TICKER_US_TO_TICKS(RADIO_TICKER_PREEMPT_PART_MAX_US)) { hdr->ticks_preempt_to_start = diff; } return 1; } return 0; } #endif /** @brief This function decides to start (additional call) xtal ahead of next * ticker, if next ticker is close to current ticker expire. * * @note This function also detects if two tickers of same interval are drifting * close and issues a conn param req or does a conn update. * * @todo Detect drift for overlapping tickers. */ static void mayfly_xtal_stop_calc(void *params) { u32_t volatile ret_cb = TICKER_STATUS_BUSY; u32_t ticks_to_expire; u32_t ticks_current; u8_t ticker_id; u32_t ret; ticker_id = 0xff; ticks_to_expire = 0; do { ret = ticker_next_slot_get(RADIO_TICKER_INSTANCE_ID_RADIO, RADIO_TICKER_USER_ID_JOB, &ticker_id, &ticks_current, &ticks_to_expire, ticker_if_done, (void *)&ret_cb); if (ret == TICKER_STATUS_BUSY) { while (ret_cb == TICKER_STATUS_BUSY) { ticker_job_sched(RADIO_TICKER_INSTANCE_ID_RADIO, RADIO_TICKER_USER_ID_JOB); } } LL_ASSERT(ret_cb == TICKER_STATUS_SUCCESS); } while (ticker_id != 0xff && ticker_id >= (RADIO_TICKER_ID_FIRST_CONNECTION + _radio.connection_count)); if ((ticker_id != 0xff) && (ticks_to_expire < TICKER_US_TO_TICKS(CONFIG_BLUETOOTH_CONTROLLER_XTAL_THRESHOLD))) { mayfly_xtal_retain(RADIO_TICKER_USER_ID_JOB, 1); if (ticker_id >= RADIO_TICKER_ID_ADV) { #if defined(CONFIG_BLUETOOTH_CONTROLLER_SCHED_ADVANCED) u8_t ticker_id_current = ((u32_t)params & 0xff); struct connection *conn_curr = NULL; #endif /* CONFIG_BLUETOOTH_CONTROLLER_SCHED_ADVANCED */ u32_t ticks_prepare_to_start; struct connection *conn = NULL; struct shdr *hdr = NULL; /* Select the role's scheduling header */ if (ticker_id >= RADIO_TICKER_ID_FIRST_CONNECTION) { conn = mem_get(_radio.conn_pool, CONNECTION_T_SIZE, (ticker_id - RADIO_TICKER_ID_FIRST_CONNECTION)); hdr = &conn->hdr; } else if (ticker_id == RADIO_TICKER_ID_ADV) { hdr = &_radio.advertiser.hdr; } else if (ticker_id == RADIO_TICKER_ID_SCAN) { hdr = &_radio.scanner.hdr; } else { LL_ASSERT(0); } /* compensate for reduced next ticker's prepare or * reduce next ticker's prepare. */ ticks_prepare_to_start = (hdr->ticks_active_to_start > hdr->ticks_preempt_to_start) ? hdr->ticks_active_to_start : hdr->ticks_preempt_to_start; if ((hdr->ticks_xtal_to_start & ((u32_t)1 << 31)) != 0) { ticks_to_expire -= ((hdr->ticks_xtal_to_start & (~((u32_t)1 << 31))) - ticks_prepare_to_start); } else { /* Postpone the primary because we dont have * to start xtal. */ if (hdr->ticks_xtal_to_start > ticks_prepare_to_start) { u32_t ticks_drift_plus = hdr->ticks_xtal_to_start - ticks_prepare_to_start; u32_t ticker_status; ticker_status = ticker_update( RADIO_TICKER_INSTANCE_ID_RADIO, RADIO_TICKER_USER_ID_JOB, ticker_id, ticks_drift_plus, 0, 0, ticks_drift_plus, 0, 0, prepare_reduced, hdr); LL_ASSERT((TICKER_STATUS_SUCCESS == ticker_status) || (TICKER_STATUS_BUSY == ticker_status)); } } #if defined(CONFIG_BLUETOOTH_CONTROLLER_SCHED_ADVANCED) if (ticker_id_current >= RADIO_TICKER_ID_FIRST_CONNECTION) { /* compensate the current ticker for reduced * prepare. */ conn_curr = mem_get(_radio.conn_pool, CONNECTION_T_SIZE, (ticker_id_current - RADIO_TICKER_ID_FIRST_CONNECTION)); ticks_prepare_to_start = (conn_curr->hdr.ticks_active_to_start > conn_curr->hdr.ticks_preempt_to_start) ? conn_curr->hdr.ticks_active_to_start : conn_curr->hdr.ticks_preempt_to_start; if ((conn_curr->hdr.ticks_xtal_to_start & ((u32_t)1 << 31)) != 0) { ticks_to_expire += ((conn_curr->hdr.ticks_xtal_to_start & (~((u32_t)1 << 31))) - ticks_prepare_to_start); } } /* auto conn param req or conn update procedure to * avoid connection collisions. */ if ((conn) && (conn_curr) && (conn_curr->conn_interval == conn->conn_interval)) { u32_t ticks_conn_interval = TICKER_US_TO_TICKS(conn->conn_interval * 1250); /* remove laziness, if any, from * ticks_to_expire. */ while (ticks_to_expire > ticks_conn_interval) { ticks_to_expire -= ticks_conn_interval; } /* if next ticker close to this ticker, send * conn param req. */ if (conn_curr->role && !conn->role && (ticks_to_expire < (TICKER_US_TO_TICKS(RADIO_TICKER_XTAL_OFFSET_US + 625) + conn_curr->hdr.ticks_slot))) { u32_t status; status = conn_update_req(conn_curr); if ((status == 2) && (conn->llcp_version.rx)) { conn_update_req(conn); } } else if (!conn_curr->role && conn->role && (ticks_to_expire < (TICKER_US_TO_TICKS(RADIO_TICKER_XTAL_OFFSET_US + 625) + conn_curr->hdr.ticks_slot))) { u32_t status; status = conn_update_req(conn); if ((status == 2) && (conn_curr->llcp_version.rx)) { conn_update_req(conn_curr); } } } #endif /* CONFIG_BLUETOOTH_CONTROLLER_SCHED_ADVANCED */ } } else { mayfly_xtal_retain(RADIO_TICKER_USER_ID_JOB, 0); if ((ticker_id != 0xff) && (ticker_id >= RADIO_TICKER_ID_ADV)) { struct shdr *hdr = NULL; /* Select the role's scheduling header */ if (ticker_id >= RADIO_TICKER_ID_FIRST_CONNECTION) { struct connection *conn; conn = mem_get(_radio.conn_pool, CONNECTION_T_SIZE, (ticker_id - RADIO_TICKER_ID_FIRST_CONNECTION)); hdr = &conn->hdr; } else if (ticker_id == RADIO_TICKER_ID_ADV) { hdr = &_radio.advertiser.hdr; } else if (ticker_id == RADIO_TICKER_ID_SCAN) { hdr = &_radio.scanner.hdr; } else { LL_ASSERT(0); } /* Use normal prepare */ prepare_normal_set(hdr, RADIO_TICKER_USER_ID_JOB, ticker_id); } } } #endif /* CONFIG_BLUETOOTH_CONTROLLER_XTAL_ADVANCED */ #if defined(CONFIG_BLUETOOTH_CONTROLLER_SCHED_ADVANCED) static void sched_after_mstr_free_slot_get(u8_t user_id, u32_t ticks_slot_abs, u32_t *ticks_anchor, u32_t *us_offset) { u8_t ticker_id; u8_t ticker_id_prev; u32_t ticks_to_expire; u32_t ticks_to_expire_prev; u32_t ticks_slot_prev_abs; ticker_id = ticker_id_prev = 0xff; ticks_to_expire = ticks_to_expire_prev = *us_offset = 0; ticks_slot_prev_abs = 0; while (1) { u32_t volatile ret_cb = TICKER_STATUS_BUSY; struct connection *conn; u32_t ret; ret = ticker_next_slot_get(RADIO_TICKER_INSTANCE_ID_RADIO, user_id, &ticker_id, ticks_anchor, &ticks_to_expire, ticker_if_done, (void *)&ret_cb); if (ret == TICKER_STATUS_BUSY) { while (ret_cb == TICKER_STATUS_BUSY) { ticker_job_sched(RADIO_TICKER_INSTANCE_ID_RADIO, user_id); } } LL_ASSERT(ret_cb == TICKER_STATUS_SUCCESS); if (ticker_id == 0xff) { break; } if (ticker_id < RADIO_TICKER_ID_FIRST_CONNECTION || ticker_id >= (RADIO_TICKER_ID_FIRST_CONNECTION + _radio.connection_count)) { continue; } conn = mem_get(_radio.conn_pool, CONNECTION_T_SIZE, (ticker_id - RADIO_TICKER_ID_FIRST_CONNECTION)); if (conn && !conn->role) { u32_t ticks_to_expire_normal = ticks_to_expire; if (conn->hdr.ticks_xtal_to_start & ((u32_t)1 << 31)) { u32_t ticks_prepare_to_start = (conn->hdr.ticks_active_to_start > conn->hdr.ticks_preempt_to_start) ? conn->hdr.ticks_active_to_start : conn->hdr.ticks_preempt_to_start; ticks_to_expire_normal -= ((conn->hdr.ticks_xtal_to_start & (~((u32_t)1 << 31))) - ticks_prepare_to_start); } if ((ticker_id_prev != 0xFF) && (ticker_ticks_diff_get(ticks_to_expire_normal, ticks_to_expire_prev) > (ticks_slot_prev_abs + ticks_slot_abs + TICKER_US_TO_TICKS(RADIO_TICKER_JITTER_US << 2)))) { break; } ticker_id_prev = ticker_id; ticks_to_expire_prev = ticks_to_expire_normal; ticks_slot_prev_abs = TICKER_US_TO_TICKS(RADIO_TICKER_XTAL_OFFSET_US) + conn->hdr.ticks_slot; } } if (ticker_id_prev != 0xff) { *us_offset = TICKER_TICKS_TO_US(ticks_to_expire_prev + ticks_slot_prev_abs) + (RADIO_TICKER_JITTER_US << 1); } } static void sched_after_mstr_free_offset_get(u16_t conn_interval, u32_t ticks_slot, u32_t ticks_anchor, u32_t *win_offset_us) { u32_t ticks_anchor_offset = ticks_anchor; sched_after_mstr_free_slot_get(RADIO_TICKER_USER_ID_JOB, (TICKER_US_TO_TICKS( RADIO_TICKER_XTAL_OFFSET_US) + ticks_slot), &ticks_anchor_offset, win_offset_us); if (ticks_anchor_offset != ticks_anchor) { *win_offset_us += TICKER_TICKS_TO_US(ticker_ticks_diff_get(ticks_anchor_offset, ticks_anchor)); } if ((*win_offset_us & ((u32_t)1 << 31)) == 0) { u32_t conn_interval_us = conn_interval * 1250; while (*win_offset_us > conn_interval_us) { *win_offset_us -= conn_interval_us; } } } static void mayfly_sched_after_mstr_free_offset_get(void *params) { sched_after_mstr_free_offset_get(_radio.scanner.conn_interval, _radio.scanner.ticks_conn_slot, (u32_t)params, &_radio.scanner.win_offset_us); } static void mayfly_sched_win_offset_use(void *params) { struct connection *conn = (struct connection *)params; u16_t win_offset; sched_after_mstr_free_offset_get(conn->conn_interval, conn->hdr.ticks_slot, conn->llcp.connection_update.ticks_ref, &conn->llcp.connection_update.win_offset_us); win_offset = conn->llcp.connection_update.win_offset_us / 1250; memcpy(conn->llcp.connection_update.pdu_win_offset, &win_offset, sizeof(u16_t)); } static void sched_free_win_offset_calc(struct connection *conn_curr, u8_t is_select, u32_t *ticks_to_offset_next, u16_t conn_interval, u8_t *offset_max, u8_t *win_offset) { u32_t ticks_prepare_reduced = 0; u32_t ticks_anchor; u32_t ticks_anchor_prev; u32_t ticks_to_expire_prev; u32_t ticks_to_expire; u32_t ticks_slot_prev_abs; u8_t ticker_id; u8_t ticker_id_prev; u8_t ticker_id_other; u8_t offset_index; u16_t _win_offset; if (conn_curr->hdr.ticks_xtal_to_start & ((u32_t)1 << 31)) { u32_t ticks_prepare_to_start = (conn_curr->hdr.ticks_active_to_start > conn_curr->hdr.ticks_preempt_to_start) ? conn_curr->hdr.ticks_active_to_start : conn_curr->hdr.ticks_preempt_to_start; ticks_prepare_reduced = ((conn_curr->hdr.ticks_xtal_to_start & (~((u32_t)1 << 31))) - ticks_prepare_to_start); } ticker_id = ticker_id_prev = ticker_id_other = 0xFF; ticks_to_expire = ticks_to_expire_prev = ticks_anchor = ticks_anchor_prev = offset_index = _win_offset = 0; ticks_slot_prev_abs = 0; do { u32_t volatile ret_cb = TICKER_STATUS_BUSY; struct connection *conn; u32_t ret; ret = ticker_next_slot_get(RADIO_TICKER_INSTANCE_ID_RADIO, RADIO_TICKER_USER_ID_JOB, &ticker_id, &ticks_anchor, &ticks_to_expire, ticker_if_done, (void *)&ret_cb); if (ret == TICKER_STATUS_BUSY) { while (ret_cb == TICKER_STATUS_BUSY) { ticker_job_sched(RADIO_TICKER_INSTANCE_ID_RADIO, RADIO_TICKER_USER_ID_JOB); } } LL_ASSERT(ret_cb == TICKER_STATUS_SUCCESS); if (ticker_id == 0xff) { break; } /* ticks_anchor shall not change during this loop */ if ((ticker_id_prev != 0xff) && (ticks_anchor != ticks_anchor_prev)) { LL_ASSERT(0); } /* consider advertiser time as available. Any other time used by * tickers declared outside the controller is also available. */ if (ticker_id <= RADIO_TICKER_ID_ADV || ticker_id >= (RADIO_TICKER_ID_FIRST_CONNECTION + _radio.connection_count)) { continue; } if (ticker_id < RADIO_TICKER_ID_FIRST_CONNECTION) { /* non conn role found which could have preempted a * conn role, hence do not consider this free space * and any further as free slot for offset, */ ticker_id_other = ticker_id; continue; } /* TODO: handle scanner; for now we exit with as much we * where able to fill (offsets). */ if (ticker_id_other != 0xFF) { break; } conn = mem_get(_radio.conn_pool, CONNECTION_T_SIZE, (ticker_id - RADIO_TICKER_ID_FIRST_CONNECTION)); if ((conn != conn_curr) && (is_select || !conn->role)) { u32_t ticks_to_expire_normal = ticks_to_expire + ticks_prepare_reduced; if (conn->hdr.ticks_xtal_to_start & ((u32_t)1 << 31)) { u32_t ticks_prepare_to_start = (conn->hdr.ticks_active_to_start > conn->hdr.ticks_preempt_to_start) ? conn->hdr.ticks_active_to_start : conn->hdr.ticks_preempt_to_start; ticks_to_expire_normal -= ((conn->hdr.ticks_xtal_to_start & (~((u32_t)1 << 31))) - ticks_prepare_to_start); } if (*ticks_to_offset_next < ticks_to_expire_normal) { if (ticks_to_expire_prev < *ticks_to_offset_next) { ticks_to_expire_prev = *ticks_to_offset_next; } while ((offset_index < *offset_max) && (ticker_ticks_diff_get(ticks_to_expire_normal, ticks_to_expire_prev) >= (ticks_slot_prev_abs + TICKER_US_TO_TICKS(RADIO_TICKER_XTAL_OFFSET_US + 625 + 1250) + conn->hdr.ticks_slot))) { _win_offset = TICKER_TICKS_TO_US(ticks_to_expire_prev + ticks_slot_prev_abs) / 1250; if (_win_offset >= conn_interval) { ticks_to_expire_prev = 0; break; } memcpy(win_offset + (sizeof(u16_t) * offset_index), &_win_offset, sizeof(u16_t)); offset_index++; ticks_to_expire_prev += TICKER_US_TO_TICKS(1250); } *ticks_to_offset_next = ticks_to_expire_prev; if (_win_offset >= conn_interval) { break; } } ticks_anchor_prev = ticks_anchor; ticker_id_prev = ticker_id; ticks_to_expire_prev = ticks_to_expire_normal; ticks_slot_prev_abs = TICKER_US_TO_TICKS(RADIO_TICKER_XTAL_OFFSET_US + 625 + 1250) + conn->hdr.ticks_slot; } } while (offset_index < *offset_max); if (ticker_id == 0xFF) { if (ticks_to_expire_prev < *ticks_to_offset_next) { ticks_to_expire_prev = *ticks_to_offset_next; } while (offset_index < *offset_max) { _win_offset = TICKER_TICKS_TO_US(ticks_to_expire_prev + ticks_slot_prev_abs) / 1250; if (_win_offset >= conn_interval) { ticks_to_expire_prev = 0; break; } memcpy(win_offset + (sizeof(u16_t) * offset_index), &_win_offset, sizeof(u16_t)); offset_index++; ticks_to_expire_prev += TICKER_US_TO_TICKS(1250); } *ticks_to_offset_next = ticks_to_expire_prev; } *offset_max = offset_index; } static void mayfly_sched_free_win_offset_calc(void *params) { struct connection *conn = (struct connection *)params; u32_t ticks_to_offset_default = 0; u32_t *ticks_to_offset_next; u8_t offset_max = 6; ticks_to_offset_next = &ticks_to_offset_default; if (conn->role) { conn->llcp.connection_update.ticks_to_offset_next = conn->slave.ticks_to_offset; ticks_to_offset_next = &conn->llcp.connection_update.ticks_to_offset_next; } sched_free_win_offset_calc(conn, 0, ticks_to_offset_next, conn->llcp.connection_update.interval, &offset_max, (u8_t *)conn->llcp.connection_update.pdu_win_offset); } static void mayfly_sched_win_offset_select(void *params) { #define OFFSET_S_MAX 6 #define OFFSET_M_MAX 6 struct connection *conn = (struct connection *)params; u32_t ticks_to_offset; u16_t win_offset_m[OFFSET_M_MAX]; u8_t offset_m_max = OFFSET_M_MAX; u16_t win_offset_s; u8_t offset_index_s = 0; ticks_to_offset = TICKER_US_TO_TICKS(conn->llcp.connection_update.offset0 * 1250); sched_free_win_offset_calc(conn, 1, &ticks_to_offset, conn->llcp.connection_update.interval, &offset_m_max, (u8_t *)&win_offset_m[0]); while (offset_index_s < OFFSET_S_MAX) { u8_t offset_index_m = 0; memcpy((u8_t *)&win_offset_s, ((u8_t *)&conn->llcp.connection_update.offset0 + (sizeof(u16_t) * offset_index_s)), sizeof(u16_t)); while (offset_index_m < offset_m_max) { if ((win_offset_s != 0xffff) && (win_offset_s == win_offset_m[offset_index_m])) { break; } offset_index_m++; } if (offset_index_m < offset_m_max) { break; } offset_index_s++; } if (offset_index_s < OFFSET_S_MAX) { conn->llcp.connection_update.win_offset_us = win_offset_s * 1250; memcpy(conn->llcp.connection_update.pdu_win_offset, &win_offset_s, sizeof(u16_t)); } else { struct pdu_data *pdu_ctrl_tx; /* procedure request acked */ conn->llcp_ack = conn->llcp_req; /* reset mutex */ _radio.conn_upd = NULL; /* send reject_ind_ext */ pdu_ctrl_tx = (struct pdu_data *) ((u8_t *)conn->llcp.connection_update.pdu_win_offset - offsetof(struct pdu_data, payload.llctrl.ctrldata.conn_update_ind.win_offset)); pdu_ctrl_tx->ll_id = PDU_DATA_LLID_CTRL; pdu_ctrl_tx->len = offsetof(struct pdu_data_llctrl, ctrldata) + sizeof(struct pdu_data_llctrl_reject_ext_ind); pdu_ctrl_tx->payload.llctrl.opcode = PDU_DATA_LLCTRL_TYPE_REJECT_EXT_IND; pdu_ctrl_tx->payload.llctrl.ctrldata.reject_ext_ind. reject_opcode = PDU_DATA_LLCTRL_TYPE_CONN_PARAM_REQ; pdu_ctrl_tx->payload.llctrl.ctrldata.reject_ext_ind. error_code = 0x20; /* Unsupported parameter value */ } } #endif /* CONFIG_BLUETOOTH_CONTROLLER_SCHED_ADVANCED */ static void mayfly_radio_stop(void *params) { enum state state = (enum state)((u32_t)params & 0xff); u32_t radio_used; LL_ASSERT((state == STATE_STOP) || (state == STATE_ABORT)); radio_used = ((_radio.state != STATE_NONE) && (_radio.state != STATE_STOP) && (_radio.state != STATE_ABORT)); if (radio_used || !radio_is_idle()) { if (radio_used) { _radio.state = state; } /** @todo try designing so as to not to abort tx packet */ radio_disable(); } } static void event_stop(u32_t ticks_at_expire, u32_t remainder, u16_t lazy, void *context) { static void *s_link[2]; static struct mayfly s_mfy_radio_stop = {0, 0, s_link, NULL, mayfly_radio_stop}; u32_t retval; ARG_UNUSED(ticks_at_expire); ARG_UNUSED(remainder); ARG_UNUSED(lazy); /* Radio state requested (stop or abort) stored in context is supplied * in params. */ s_mfy_radio_stop.param = context; /* Stop Radio Tx/Rx */ retval = mayfly_enqueue(RADIO_TICKER_USER_ID_WORKER, RADIO_TICKER_USER_ID_WORKER, 0, &s_mfy_radio_stop); LL_ASSERT(!retval); } static void event_common_prepare(u32_t ticks_at_expire, u32_t remainder, u32_t *ticks_xtal_to_start, u32_t *ticks_active_to_start, u32_t ticks_preempt_to_start, u8_t ticker_id, ticker_timeout_func ticker_timeout_fp, void *context) { u32_t ticker_status; u32_t _ticks_xtal_to_start = *ticks_xtal_to_start; u32_t _ticks_active_to_start = *ticks_active_to_start; u32_t ticks_to_start; /* in case this event is short prepare, xtal to start duration will be * active to start duration. */ if (_ticks_xtal_to_start & ((u32_t)1 << 31)) { _ticks_xtal_to_start = (_ticks_active_to_start > ticks_preempt_to_start) ? _ticks_active_to_start : ticks_preempt_to_start; } /* decide whether its XTAL start or active event that is the current * execution context and accordingly setup the ticker for the other * event (XTAL or active event). These are oneshot ticker. */ if (_ticks_active_to_start < _ticks_xtal_to_start) { u32_t ticks_to_active; /* XTAL is before Active */ ticks_to_active = _ticks_xtal_to_start - _ticks_active_to_start; ticks_to_start = _ticks_xtal_to_start; ticker_status = ticker_start(RADIO_TICKER_INSTANCE_ID_RADIO, RADIO_TICKER_USER_ID_WORKER, RADIO_TICKER_ID_MARKER_0, ticks_at_expire, ticks_to_active, TICKER_NULL_PERIOD, TICKER_NULL_REMAINDER, TICKER_NULL_LAZY, TICKER_NULL_SLOT, event_active, NULL, ticker_success_assert, (void *)__LINE__); LL_ASSERT((ticker_status == TICKER_STATUS_SUCCESS) || (ticker_status == TICKER_STATUS_BUSY)); event_xtal(0, 0, 0, NULL); } else if (_ticks_active_to_start > _ticks_xtal_to_start) { u32_t ticks_to_xtal; /* Active is before XTAL */ ticks_to_xtal = _ticks_active_to_start - _ticks_xtal_to_start; ticks_to_start = _ticks_active_to_start; event_active(0, 0, 0, NULL); ticker_status = ticker_start(RADIO_TICKER_INSTANCE_ID_RADIO, RADIO_TICKER_USER_ID_WORKER, RADIO_TICKER_ID_MARKER_0, ticks_at_expire, ticks_to_xtal, TICKER_NULL_PERIOD, TICKER_NULL_REMAINDER, TICKER_NULL_LAZY, TICKER_NULL_SLOT, event_xtal, NULL, ticker_success_assert, (void *)__LINE__); LL_ASSERT((ticker_status == TICKER_STATUS_SUCCESS) || (ticker_status == TICKER_STATUS_BUSY)); } else { /* Active and XTAL are at the same time, * no ticker required to be setup. */ ticks_to_start = _ticks_xtal_to_start; event_active(0, 0, 0, NULL); event_xtal(0, 0, 0, NULL); } /* remember the remainder to be used in pkticker */ _radio.remainder_anchor = remainder; /* setup the start ticker */ ticker_status = ticker_start(RADIO_TICKER_INSTANCE_ID_RADIO, RADIO_TICKER_USER_ID_WORKER, RADIO_TICKER_ID_EVENT, ticks_at_expire, ticks_to_start, TICKER_NULL_PERIOD, TICKER_NULL_REMAINDER, TICKER_NULL_LAZY, TICKER_NULL_SLOT, ticker_timeout_fp, context, ticker_success_assert, (void *)__LINE__); LL_ASSERT((ticker_status == TICKER_STATUS_SUCCESS) || (ticker_status == TICKER_STATUS_BUSY)); #define RADIO_DEFERRED_PREEMPT 0 #if RADIO_DEFERRED_PREEMPT /* setup pre-empt ticker if any running state present */ if (_radio.state != STATE_NONE) { ticker_status = ticker_start(RADIO_TICKER_INSTANCE_ID_RADIO, RADIO_TICKER_USER_ID_WORKER, RADIO_TICKER_ID_PRE_EMPT, ticks_at_expire, (ticks_to_start - conn->hdr.ticks_preempt_to_start), TICKER_NULL_PERIOD, TICKER_NULL_REMAINDER, TICKER_NULL_LAZY, TICKER_NULL_SLOT, event_stop, (void *)STATE_ABORT, ticker_success_assert, (void *)__LINE__); LL_ASSERT((ticker_status == TICKER_STATUS_SUCCESS) || (ticker_status == TICKER_STATUS_BUSY)); } #else event_stop(0, 0, 0, (void *)STATE_ABORT); #endif #undef RADIO_DEFERRED_PREEMPT /** Handle change in _ticks_active_to_start */ if (_radio.ticks_active_to_start != _ticks_active_to_start) { u32_t ticks_to_start_new = ((_radio.ticks_active_to_start < (*ticks_xtal_to_start & ~(((u32_t)1 << 31)))) ? (*ticks_xtal_to_start & ~(((u32_t)1 << 31))) : _radio.ticks_active_to_start); *ticks_active_to_start = _radio.ticks_active_to_start; if ((*ticks_xtal_to_start) & ((u32_t)1 << 31)) { *ticks_xtal_to_start &= ~(((u32_t)1 << 31)); } /* drift the primary as required due to active line change */ ticker_status = ticker_update(RADIO_TICKER_INSTANCE_ID_RADIO, RADIO_TICKER_USER_ID_WORKER, ticker_id, ticks_to_start, ticks_to_start_new, ticks_to_start_new, ticks_to_start, 0, 0, ticker_success_assert, (void *)__LINE__); LL_ASSERT((ticker_status == TICKER_STATUS_SUCCESS) || (ticker_status == TICKER_STATUS_BUSY)); } /* route all packets queued for connections */ packet_tx_enqueue(0xFF); #if defined(CONFIG_BLUETOOTH_CONTROLLER_XTAL_ADVANCED) /* calc whether xtal needs to be retained after this event */ { static void *s_link[2]; static struct mayfly s_mfy_xtal_stop_calc = {0, 0, s_link, NULL, mayfly_xtal_stop_calc}; u32_t retval; s_mfy_xtal_stop_calc.param = (void *)(u32_t)ticker_id; retval = mayfly_enqueue(RADIO_TICKER_USER_ID_WORKER, RADIO_TICKER_USER_ID_JOB, 1, &s_mfy_xtal_stop_calc); LL_ASSERT(!retval); } #endif /* CONFIG_BLUETOOTH_CONTROLLER_XTAL_ADVANCED */ } static u8_t chan_sel_remap(u8_t *chan_map, u8_t chan_index) { u8_t chan_next; u8_t byte_count; chan_next = 0; byte_count = 5; while (byte_count--) { u8_t bite; u8_t bit_count; bite = *chan_map; bit_count = 8; while (bit_count--) { if (bite & 0x01) { if (chan_index == 0) { break; } chan_index--; } chan_next++; bite >>= 1; } if (bit_count < 8) { break; } chan_map++; } return chan_next; } static u8_t chan_sel_1(u8_t *chan_use, u8_t hop, u16_t latency, u8_t *chan_map, u8_t chan_count) { u8_t chan_next; chan_next = ((*chan_use) + (hop * (1 + latency))) % 37; *chan_use = chan_next; if ((chan_map[chan_next >> 3] & (1 << (chan_next % 8))) == 0) { u8_t chan_index; chan_index = chan_next % chan_count; chan_next = chan_sel_remap(chan_map, chan_index); } else { /* channel can be used, return it */ } return chan_next; } #if defined(CONFIG_BLUETOOTH_CONTROLLER_CHAN_SEL_2) static u8_t chan_rev_8(u8_t i) { u8_t iterate; u8_t o; o = 0; for (iterate = 0; iterate < 8; iterate++) { o <<= 1; o |= (i & 1); i >>= 1; } return o; } static u16_t chan_perm(u16_t i) { return (chan_rev_8((i >> 8) & 0xFF) << 8) | chan_rev_8(i & 0xFF); } static u16_t chan_mam(u16_t a, u16_t b) { return ((u32_t)a * 17 + b) & 0xFFFF; } static u16_t chan_prn(u16_t counter, u16_t chan_id) { u8_t iterate; u16_t prn_e; prn_e = counter ^ chan_id; for (iterate = 0; iterate < 3; iterate++) { prn_e = chan_perm(prn_e); prn_e = chan_mam(prn_e, chan_id); } prn_e ^= chan_id; return prn_e; } static u8_t chan_sel_2(u16_t counter, u16_t chan_id, u8_t *chan_map, u8_t chan_count) { u8_t chan_next; u16_t prn_e; prn_e = chan_prn(counter, chan_id); chan_next = prn_e % 37; if ((chan_map[chan_next >> 3] & (1 << (chan_next % 8))) == 0) { u8_t chan_index; chan_index = ((u32_t)chan_count * prn_e) >> 16; chan_next = chan_sel_remap(chan_map, chan_index); } else { /* channel can be used, return it */ } return chan_next; } #if defined(RADIO_UNIT_TEST) static void chan_sel_2_ut(void) { u8_t chan_map_1[] = {0xFF, 0xFF, 0xFF, 0xFF, 0x1F}; u8_t chan_map_2[] = {0x00, 0x06, 0xE0, 0x00, 0x1E}; u8_t m; m = chan_sel_2(1, 0x305F, chan_map_1, 37); LL_ASSERT(m == 20); m = chan_sel_2(2, 0x305F, chan_map_1, 37); LL_ASSERT(m == 6); m = chan_sel_2(3, 0x305F, chan_map_1, 37); LL_ASSERT(m == 21); m = chan_sel_2(6, 0x305F, chan_map_2, 9); LL_ASSERT(m == 23); m = chan_sel_2(7, 0x305F, chan_map_2, 9); LL_ASSERT(m == 9); m = chan_sel_2(8, 0x305F, chan_map_2, 9); LL_ASSERT(m == 34); } #endif /* RADIO_UNIT_TEST */ #endif /* CONFIG_BLUETOOTH_CONTROLLER_CHAN_SEL_2 */ static void chan_set(u32_t chan) { switch (chan) { case 37: radio_freq_chan_set(2); break; case 38: radio_freq_chan_set(26); break; case 39: radio_freq_chan_set(80); break; default: if (chan < 11) { radio_freq_chan_set(4 + (2 * chan)); } else if (chan < 40) { radio_freq_chan_set(28 + (2 * (chan - 11))); } else { LL_ASSERT(0); } break; } radio_whiten_iv_set(chan); } /** @brief Prepare access address as per BT Spec. * * - It shall have no more than six consecutive zeros or ones. * - It shall not be the advertising channel packets' Access Address. * - It shall not be a sequence that differs from the advertising channel * packets Access Address by only one bit. * - It shall not have all four octets equal. * - It shall have no more than 24 transitions. * - It shall have a minimum of two transitions in the most significant six * bits. */ static u32_t access_addr_get(void) { u8_t consecutive_cnt; u8_t consecutive_bit; u32_t adv_aa_check; u32_t access_addr; u8_t transitions; u8_t bit_idx; u8_t retry; u8_t len; retry = 3; again: LL_ASSERT(retry); retry--; len = sizeof(u32_t); while (len) { len = rand_get(len, (u8_t *)&access_addr); if (len) { cpu_sleep(); } } bit_idx = 31; transitions = 0; consecutive_cnt = 1; consecutive_bit = (access_addr >> bit_idx) & 0x01; while (bit_idx--) { u8_t bit; bit = (access_addr >> bit_idx) & 0x01; if (bit == consecutive_bit) { consecutive_cnt++; } else { consecutive_cnt = 1; consecutive_bit = bit; transitions++; } /* It shall have no more than six consecutive zeros or ones. */ /* It shall have a minimum of two transitions in the most * significant six bits. */ if ((consecutive_cnt > 6) || ((bit_idx < 28) && (transitions < 1)) || ((bit_idx < 27) && (transitions < 2))) { if (consecutive_bit) { consecutive_bit = 0; access_addr &= ~BIT(bit_idx); } else { consecutive_bit = 1; access_addr |= BIT(bit_idx); } consecutive_cnt = 1; transitions++; } /* It shall have no more than 24 transitions */ if (transitions > 24) { if (consecutive_bit) { access_addr &= ~(BIT(bit_idx + 1) - 1); } else { access_addr |= (BIT(bit_idx + 1) - 1); } break; } } /* It shall not be the advertising channel packets Access Address. * It shall not be a sequence that differs from the advertising channel * packets Access Address by only one bit. */ adv_aa_check = access_addr ^ 0x8e89bed6; if (util_ones_count_get((u8_t *)&adv_aa_check, sizeof(adv_aa_check)) <= 1) { goto again; } /* It shall not have all four octets equal. */ if (!((access_addr & 0xFFFF) ^ (access_addr >> 16)) && !((access_addr & 0xFF) ^ (access_addr >> 24))) { goto again; } return access_addr; } static void adv_scan_conn_configure(void) { radio_reset(); radio_tx_power_set(0); radio_isr_set(isr); } static void adv_scan_configure(u8_t phy, u8_t flags) { u32_t aa = 0x8e89bed6; adv_scan_conn_configure(); radio_phy_set(phy, flags); radio_aa_set((u8_t *)&aa); radio_pkt_configure(8, PDU_AC_PAYLOAD_SIZE_MAX, (phy << 1)); radio_crc_configure(((0x5bUL) | ((0x06UL) << 8) | ((0x00UL) << 16)), 0x555555); } void radio_event_adv_prepare(u32_t ticks_at_expire, u32_t remainder, u16_t lazy, void *context) { ARG_UNUSED(lazy); ARG_UNUSED(context); DEBUG_RADIO_PREPARE_A(1); LL_ASSERT(!_radio.ticker_id_prepare); _radio.ticker_id_prepare = RADIO_TICKER_ID_ADV; event_common_prepare(ticks_at_expire, remainder, &_radio.advertiser.hdr.ticks_xtal_to_start, &_radio.advertiser.hdr.ticks_active_to_start, _radio.advertiser.hdr.ticks_preempt_to_start, RADIO_TICKER_ID_ADV, event_adv, NULL); DEBUG_RADIO_PREPARE_A(0); } static void adv_setup(void) { struct pdu_adv *pdu; u8_t bitmap; u8_t chan; u8_t upd = 0; /* Use latest adv data PDU buffer */ if (_radio.advertiser.adv_data.first != _radio.advertiser.adv_data.last) { u8_t first; first = _radio.advertiser.adv_data.first + 1; if (first == DOUBLE_BUFFER_SIZE) { first = 0; } _radio.advertiser.adv_data.first = first; upd = 1; } /* Use latest scan data PDU buffer */ if (_radio.advertiser.scan_data.first != _radio. advertiser.scan_data.last) { u8_t first; first = _radio.advertiser.scan_data.first + 1; if (first == DOUBLE_BUFFER_SIZE) { first = 0; } _radio.advertiser.scan_data.first = first; upd = 1; } pdu = (struct pdu_adv *) _radio.advertiser.adv_data.data[ _radio.advertiser.adv_data.first]; #if defined(CONFIG_BLUETOOTH_CONTROLLER_PRIVACY) if (upd) { struct pdu_adv *scan_pdu = (struct pdu_adv *) _radio.advertiser.scan_data.data[ _radio.advertiser.scan_data.first]; /* Copy the address from the adv packet we will send into the * scan response. */ memcpy(&scan_pdu->payload.scan_rsp.addr[0], &pdu->payload.adv_ind.addr[0], BDADDR_SIZE); } #else ARG_UNUSED(upd); #endif /* !CONFIG_BLUETOOTH_CONTROLLER_PRIVACY */ radio_pkt_tx_set(pdu); if ((pdu->type != PDU_ADV_TYPE_NONCONN_IND) && (!IS_ENABLED(CONFIG_BLUETOOTH_CONTROLLER_ADV_EXT) || (pdu->type != PDU_ADV_TYPE_EXT_IND))) { _radio.state = STATE_TX; radio_tmr_tifs_set(RADIO_TIFS); radio_switch_complete_and_rx(0); } else { _radio.state = STATE_CLOSE; radio_switch_complete_and_disable(); } bitmap = _radio.advertiser.chan_map_current; chan = 0; while ((bitmap & 0x01) == 0) { chan++; bitmap >>= 1; } _radio.advertiser.chan_map_current &= (_radio.advertiser.chan_map_current - 1); chan_set(37 + chan); } static void event_adv(u32_t ticks_at_expire, u32_t remainder, u16_t lazy, void *context) { ARG_UNUSED(remainder); ARG_UNUSED(lazy); ARG_UNUSED(context); DEBUG_RADIO_START_A(1); LL_ASSERT(_radio.role == ROLE_NONE); LL_ASSERT(_radio.ticker_id_prepare == RADIO_TICKER_ID_ADV); /** @todo check if XTAL is started, * options 1: abort Radio Start, * 2: wait for XTAL start. */ _radio.role = ROLE_ADV; _radio.ticker_id_prepare = 0; _radio.ticker_id_event = RADIO_TICKER_ID_ADV; _radio.ticks_anchor = ticks_at_expire; #if defined(CONFIG_BLUETOOTH_CONTROLLER_ADV_EXT) /* TODO: if coded we use S8? */ adv_scan_configure(_radio.advertiser.phy_p, 1); #else /* !CONFIG_BLUETOOTH_CONTROLLER_ADV_EXT */ adv_scan_configure(0, 0); #endif /* !CONFIG_BLUETOOTH_CONTROLLER_ADV_EXT */ _radio.advertiser.chan_map_current = _radio.advertiser.chan_map; adv_setup(); #if defined(CONFIG_BLUETOOTH_CONTROLLER_PRIVACY) if (ctrl_rl_enabled()) { struct ll_filter *filter = ctrl_filter_get(!!(_radio.advertiser.filter_policy)); radio_filter_configure(filter->enable_bitmask, filter->addr_type_bitmask, (u8_t *)filter->bdaddr); } else #endif /* CONFIG_BLUETOOTH_CONTROLLER_PRIVACY */ /* Setup Radio Filter */ if (_radio.advertiser.filter_policy) { struct ll_filter *wl = ctrl_filter_get(true); radio_filter_configure(wl->enable_bitmask, wl->addr_type_bitmask, (u8_t *)wl->bdaddr); } radio_tmr_start(1, ticks_at_expire + TICKER_US_TO_TICKS(RADIO_TICKER_START_PART_US), _radio.remainder_anchor); radio_tmr_end_capture(); #if (defined(CONFIG_BLUETOOTH_CONTROLLER_XTAL_ADVANCED) && \ (RADIO_TICKER_PREEMPT_PART_US <= RADIO_TICKER_PREEMPT_PART_MIN_US)) /* check if preempt to start has changed */ if (preempt_calc(&_radio.advertiser.hdr, RADIO_TICKER_ID_ADV, ticks_at_expire) != 0) { _radio.state = STATE_STOP; radio_disable(); } else #endif /* CONFIG_BLUETOOTH_CONTROLLER_XTAL_ADVANCED */ { /* Ticker Job Silence */ #if (RADIO_TICKER_USER_ID_WORKER_PRIO == RADIO_TICKER_USER_ID_JOB_PRIO) u32_t ticker_status; ticker_status = ticker_job_idle_get(RADIO_TICKER_INSTANCE_ID_RADIO, RADIO_TICKER_USER_ID_WORKER, ticker_job_disable, NULL); LL_ASSERT((ticker_status == TICKER_STATUS_SUCCESS) || (ticker_status == TICKER_STATUS_BUSY)); #endif } DEBUG_RADIO_START_A(0); } void event_adv_stop(u32_t ticks_at_expire, u32_t remainder, u16_t lazy, void *context) { struct radio_le_conn_cmplt *radio_le_conn_cmplt; struct radio_pdu_node_rx *radio_pdu_node_rx; struct pdu_data *pdu_data_rx; u32_t ticker_status; ARG_UNUSED(ticks_at_expire); ARG_UNUSED(remainder); ARG_UNUSED(lazy); ARG_UNUSED(context); /* Abort an event, if any, to avoid Rx queue corruption used by Radio * ISR. */ event_stop(0, 0, 0, (void *)STATE_ABORT); /* Stop Direct Adv */ ticker_status = ticker_stop(RADIO_TICKER_INSTANCE_ID_RADIO, RADIO_TICKER_USER_ID_WORKER, RADIO_TICKER_ID_ADV, ticker_success_assert, (void *)__LINE__); LL_ASSERT((ticker_status == TICKER_STATUS_SUCCESS) || (ticker_status == TICKER_STATUS_BUSY)); /* Prepare the rx packet structure */ radio_pdu_node_rx = packet_rx_reserve_get(1); LL_ASSERT(radio_pdu_node_rx); /** Connection handle */ radio_pdu_node_rx->hdr.handle = 0xffff; radio_pdu_node_rx->hdr.type = NODE_RX_TYPE_CONNECTION; /* prepare connection complete structure */ pdu_data_rx = (struct pdu_data *)radio_pdu_node_rx->pdu_data; radio_le_conn_cmplt = (struct radio_le_conn_cmplt *)&pdu_data_rx->payload; memset(radio_le_conn_cmplt, 0x00, sizeof(struct radio_le_conn_cmplt)); radio_le_conn_cmplt->status = 0x3c; /* enqueue connection complete structure into queue */ packet_rx_enqueue(); } static void event_scan_prepare(u32_t ticks_at_expire, u32_t remainder, u16_t lazy, void *context) { ARG_UNUSED(lazy); ARG_UNUSED(context); DEBUG_RADIO_PREPARE_O(1); LL_ASSERT(!_radio.ticker_id_prepare); _radio.ticker_id_prepare = RADIO_TICKER_ID_SCAN; event_common_prepare(ticks_at_expire, remainder, &_radio.scanner.hdr.ticks_xtal_to_start, &_radio.scanner.hdr.ticks_active_to_start, _radio.scanner.hdr.ticks_preempt_to_start, RADIO_TICKER_ID_SCAN, event_scan, NULL); #if defined(CONFIG_BLUETOOTH_CONTROLLER_SCHED_ADVANCED) /* calc next group in us for the anchor where first connection event * to be placed */ if (_radio.scanner.conn) { static void *s_link[2]; static struct mayfly s_mfy_sched_after_mstr_free_offset_get = { 0, 0, s_link, NULL, mayfly_sched_after_mstr_free_offset_get}; u32_t ticks_at_expire_normal = ticks_at_expire; u32_t retval; if (_radio.scanner.hdr.ticks_xtal_to_start & ((u32_t)1 << 31)) { u32_t ticks_prepare_to_start = (_radio.scanner.hdr.ticks_active_to_start > _radio.scanner.hdr.ticks_preempt_to_start) ? _radio.scanner.hdr.ticks_active_to_start : _radio.scanner.hdr.ticks_preempt_to_start; ticks_at_expire_normal -= ((_radio.scanner.hdr.ticks_xtal_to_start & (~((u32_t)1 << 31))) - ticks_prepare_to_start); } s_mfy_sched_after_mstr_free_offset_get.param = (void *)ticks_at_expire_normal; retval = mayfly_enqueue(RADIO_TICKER_USER_ID_WORKER, RADIO_TICKER_USER_ID_JOB, 1, &s_mfy_sched_after_mstr_free_offset_get); LL_ASSERT(!retval); } #endif /* CONFIG_BLUETOOTH_CONTROLLER_SCHED_ADVANCED */ DEBUG_RADIO_PREPARE_O(0); } static void event_scan(u32_t ticks_at_expire, u32_t remainder, u16_t lazy, void *context) { u32_t ticker_status; ARG_UNUSED(remainder); ARG_UNUSED(lazy); ARG_UNUSED(context); DEBUG_RADIO_START_O(1); LL_ASSERT(_radio.role == ROLE_NONE); LL_ASSERT(_radio.ticker_id_prepare == RADIO_TICKER_ID_SCAN); /** @todo check if XTAL is started, options 1: abort Radio Start, * 2: wait for XTAL start */ _radio.role = ROLE_SCAN; _radio.state = STATE_RX; _radio.ticker_id_prepare = 0; _radio.ticker_id_event = RADIO_TICKER_ID_SCAN; _radio.ticks_anchor = ticks_at_expire; _radio.scanner.state = 0; #if defined(CONFIG_BLUETOOTH_CONTROLLER_ADV_EXT) adv_scan_configure(_radio.scanner.phy, 1); /* if coded then use S8. */ #else /* !CONFIG_BLUETOOTH_CONTROLLER_ADV_EXT */ adv_scan_configure(0, 0); #endif /* !CONFIG_BLUETOOTH_CONTROLLER_ADV_EXT */ chan_set(37 + _radio.scanner.chan++); if (_radio.scanner.chan == 3) { _radio.scanner.chan = 0; } radio_tmr_tifs_set(RADIO_TIFS); radio_switch_complete_and_tx(0, 0, 0, 0); radio_pkt_rx_set(_radio.packet_rx[_radio.packet_rx_last]->pdu_data); radio_rssi_measure(); #if defined(CONFIG_BLUETOOTH_CONTROLLER_PRIVACY) if (ctrl_rl_enabled()) { struct ll_filter *filter = ctrl_filter_get(!!(_radio.scanner.filter_policy & 0x1)); u8_t count, *irks = ctrl_irks_get(&count); radio_filter_configure(filter->enable_bitmask, filter->addr_type_bitmask, (u8_t *)filter->bdaddr); radio_ar_configure(count, irks); } else #endif /* CONFIG_BLUETOOTH_CONTROLLER_PRIVACY */ /* Setup Radio Filter */ if (_radio.scanner.filter_policy) { struct ll_filter *wl = ctrl_filter_get(true); radio_filter_configure(wl->enable_bitmask, wl->addr_type_bitmask, (u8_t *)wl->bdaddr); } radio_tmr_start(0, ticks_at_expire + TICKER_US_TO_TICKS(RADIO_TICKER_START_PART_US), _radio.remainder_anchor); radio_tmr_end_capture(); #if (defined(CONFIG_BLUETOOTH_CONTROLLER_XTAL_ADVANCED) && \ (RADIO_TICKER_PREEMPT_PART_US <= RADIO_TICKER_PREEMPT_PART_MIN_US)) /* check if preempt to start has changed */ if (preempt_calc(&_radio.scanner.hdr, RADIO_TICKER_ID_SCAN, ticks_at_expire) != 0) { _radio.state = STATE_STOP; radio_disable(); } else #endif /* CONFIG_BLUETOOTH_CONTROLLER_XTAL_ADVANCED */ { /* start window close timeout */ ticker_status = ticker_start(RADIO_TICKER_INSTANCE_ID_RADIO, RADIO_TICKER_USER_ID_WORKER, RADIO_TICKER_ID_SCAN_STOP, ticks_at_expire, _radio.scanner.ticks_window + TICKER_US_TO_TICKS(RADIO_TICKER_START_PART_US), TICKER_NULL_PERIOD, TICKER_NULL_REMAINDER, TICKER_NULL_LAZY, TICKER_NULL_SLOT, event_stop, (void *)STATE_STOP, ticker_success_assert, (void *)__LINE__); LL_ASSERT((ticker_status == TICKER_STATUS_SUCCESS) || (ticker_status == TICKER_STATUS_BUSY)); /* Ticker Job Silence */ #if (RADIO_TICKER_USER_ID_WORKER_PRIO == RADIO_TICKER_USER_ID_JOB_PRIO) { u32_t ticker_status; ticker_status = ticker_job_idle_get(RADIO_TICKER_INSTANCE_ID_RADIO, RADIO_TICKER_USER_ID_WORKER, ticker_job_disable, NULL); LL_ASSERT((ticker_status == TICKER_STATUS_SUCCESS) || (ticker_status == TICKER_STATUS_BUSY)); } #endif } DEBUG_RADIO_START_O(0); } static inline void event_conn_update_st_init(struct connection *conn, u16_t event_counter, struct pdu_data *pdu_ctrl_tx, u32_t ticks_at_expire, struct mayfly *mayfly_sched_offset, void (*fp_mayfly_select_or_use)(void *)) { /* move to in progress */ conn->llcp.connection_update.state = LLCP_CONN_STATE_INPROG; /* set instant */ conn->llcp.connection_update.instant = event_counter + conn->latency + 6; /* place the conn update req packet as next in tx queue */ pdu_ctrl_tx->ll_id = PDU_DATA_LLID_CTRL; pdu_ctrl_tx->len = offsetof(struct pdu_data_llctrl, ctrldata) + sizeof(struct pdu_data_llctrl_conn_update_ind); pdu_ctrl_tx->payload.llctrl.opcode = PDU_DATA_LLCTRL_TYPE_CONN_UPDATE_IND; pdu_ctrl_tx->payload.llctrl.ctrldata.conn_update_ind.win_size = conn->llcp.connection_update.win_size; pdu_ctrl_tx->payload.llctrl.ctrldata.conn_update_ind. win_offset = conn->llcp.connection_update.win_offset_us / 1250; pdu_ctrl_tx->payload.llctrl.ctrldata.conn_update_ind.interval = conn->llcp.connection_update.interval; pdu_ctrl_tx->payload.llctrl.ctrldata.conn_update_ind.latency = conn->llcp.connection_update.latency; pdu_ctrl_tx->payload.llctrl.ctrldata.conn_update_ind.timeout = conn->llcp.connection_update.timeout; pdu_ctrl_tx->payload.llctrl.ctrldata.conn_update_ind.instant = conn->llcp.connection_update.instant; #if CONFIG_BLUETOOTH_CONTROLLER_SCHED_ADVANCED { u32_t retval; /* calculate window offset that places the connection in the * next available slot after existing masters. */ conn->llcp.connection_update.ticks_ref = ticks_at_expire; if (conn->hdr.ticks_xtal_to_start & ((u32_t)1 << 31)) { u32_t ticks_prepare_to_start = (conn->hdr.ticks_active_to_start > conn->hdr.ticks_preempt_to_start) ? conn->hdr.ticks_active_to_start : conn->hdr.ticks_preempt_to_start; conn->llcp.connection_update.ticks_ref -= ((conn->hdr.ticks_xtal_to_start & (~((u32_t)1 << 31))) - ticks_prepare_to_start); } conn->llcp.connection_update.pdu_win_offset = (u16_t *) &pdu_ctrl_tx->payload.llctrl.ctrldata.conn_update_ind.win_offset; mayfly_sched_offset->fp = fp_mayfly_select_or_use; mayfly_sched_offset->param = (void *)conn; retval = mayfly_enqueue(RADIO_TICKER_USER_ID_WORKER, RADIO_TICKER_USER_ID_JOB, 1, mayfly_sched_offset); LL_ASSERT(!retval); } #else /* !CONFIG_BLUETOOTH_CONTROLLER_SCHED_ADVANCED */ ARG_UNUSED(ticks_at_expire); ARG_UNUSED(mayfly_sched_offset); ARG_UNUSED(fp_mayfly_select_or_use); #endif /* !CONFIG_BLUETOOTH_CONTROLLER_SCHED_ADVANCED */ } static inline void event_conn_update_st_req(struct connection *conn, u16_t event_counter, struct pdu_data *pdu_ctrl_tx, u32_t ticks_at_expire, struct mayfly *mayfly_sched_offset) { /* move to wait for conn_update/rsp/rej */ conn->llcp.connection_update.state = LLCP_CONN_STATE_RSP_WAIT; /* place the conn param req packet as next in tx queue */ pdu_ctrl_tx->ll_id = PDU_DATA_LLID_CTRL; pdu_ctrl_tx->len = offsetof(struct pdu_data_llctrl, ctrldata) + sizeof(struct pdu_data_llctrl_conn_param_req); pdu_ctrl_tx->payload.llctrl.opcode = PDU_DATA_LLCTRL_TYPE_CONN_PARAM_REQ; pdu_ctrl_tx->payload.llctrl.ctrldata.conn_param_req.interval_min = conn->llcp.connection_update.interval; pdu_ctrl_tx->payload.llctrl.ctrldata.conn_param_req.interval_max = conn->llcp.connection_update.interval; pdu_ctrl_tx->payload.llctrl.ctrldata.conn_param_req.latency = conn->llcp.connection_update.latency; pdu_ctrl_tx->payload.llctrl.ctrldata.conn_param_req.timeout = conn->llcp.connection_update.timeout; pdu_ctrl_tx->payload.llctrl.ctrldata.conn_param_req.preferred_periodicity = 0; pdu_ctrl_tx->payload.llctrl.ctrldata.conn_param_req.reference_conn_event_count = event_counter; pdu_ctrl_tx->payload.llctrl.ctrldata.conn_param_req.offset0 = 0x0000; pdu_ctrl_tx->payload.llctrl.ctrldata.conn_param_req.offset1 = 0xffff; pdu_ctrl_tx->payload.llctrl.ctrldata.conn_param_req.offset2 = 0xffff; pdu_ctrl_tx->payload.llctrl.ctrldata.conn_param_req.offset3 = 0xffff; pdu_ctrl_tx->payload.llctrl.ctrldata.conn_param_req.offset4 = 0xffff; pdu_ctrl_tx->payload.llctrl.ctrldata.conn_param_req.offset5 = 0xffff; /* Start Procedure Timeout (TODO: this shall not replace * terminate procedure). */ conn->procedure_expire = conn->procedure_reload; #if defined(CONFIG_BLUETOOTH_CONTROLLER_SCHED_ADVANCED) { u32_t retval; conn->llcp.connection_update.ticks_ref = ticks_at_expire; if (conn->hdr.ticks_xtal_to_start & ((u32_t)1 << 31)) { u32_t ticks_prepare_to_start = (conn->hdr.ticks_active_to_start > conn->hdr.ticks_preempt_to_start) ? conn->hdr.ticks_active_to_start : conn->hdr.ticks_preempt_to_start; conn->llcp.connection_update.ticks_ref -= ((conn->hdr.ticks_xtal_to_start & (~((u32_t)1 << 31))) - ticks_prepare_to_start); } conn->llcp.connection_update.pdu_win_offset = (u16_t *) &pdu_ctrl_tx->payload.llctrl.ctrldata.conn_param_req.offset0; mayfly_sched_offset->fp = mayfly_sched_free_win_offset_calc; mayfly_sched_offset->param = (void *)conn; retval = mayfly_enqueue(RADIO_TICKER_USER_ID_WORKER, RADIO_TICKER_USER_ID_JOB, 1, mayfly_sched_offset); LL_ASSERT(!retval); } #else /* !CONFIG_BLUETOOTH_CONTROLLER_SCHED_ADVANCED */ ARG_UNUSED(ticks_at_expire); ARG_UNUSED(mayfly_sched_offset); #endif /* !CONFIG_BLUETOOTH_CONTROLLER_SCHED_ADVANCED */ } static inline void event_conn_update_st_rsp(struct connection *conn, struct pdu_data *pdu_ctrl_tx) { /* procedure request acked */ conn->llcp_ack = conn->llcp_req; /* reset mutex */ _radio.conn_upd = NULL; /** @todo REJECT_IND_EXT */ /* place the conn param rsp packet as next in tx queue */ pdu_ctrl_tx->ll_id = PDU_DATA_LLID_CTRL; pdu_ctrl_tx->len = offsetof(struct pdu_data_llctrl, ctrldata) + sizeof(struct pdu_data_llctrl_conn_param_rsp); pdu_ctrl_tx->payload.llctrl.opcode = PDU_DATA_LLCTRL_TYPE_CONN_PARAM_RSP; pdu_ctrl_tx->payload.llctrl.ctrldata.conn_param_req.interval_min = conn->llcp.connection_update.interval; pdu_ctrl_tx->payload.llctrl.ctrldata.conn_param_req.interval_max = conn->llcp.connection_update.interval; pdu_ctrl_tx->payload.llctrl.ctrldata.conn_param_req.latency = conn->llcp.connection_update.latency; pdu_ctrl_tx->payload.llctrl.ctrldata.conn_param_req.timeout = conn->llcp.connection_update.timeout; pdu_ctrl_tx->payload.llctrl.ctrldata.conn_param_req.preferred_periodicity = conn->llcp.connection_update.preferred_periodicity; pdu_ctrl_tx->payload.llctrl.ctrldata.conn_param_req.reference_conn_event_count = conn->llcp.connection_update.instant; pdu_ctrl_tx->payload.llctrl.ctrldata.conn_param_req.offset0 = conn->llcp.connection_update.offset0; pdu_ctrl_tx->payload.llctrl.ctrldata.conn_param_req.offset1 = conn->llcp.connection_update.offset1; pdu_ctrl_tx->payload.llctrl.ctrldata.conn_param_req.offset2 = conn->llcp.connection_update.offset2; pdu_ctrl_tx->payload.llctrl.ctrldata.conn_param_req.offset3 = conn->llcp.connection_update.offset3; pdu_ctrl_tx->payload.llctrl.ctrldata.conn_param_req.offset4 = conn->llcp.connection_update.offset4; pdu_ctrl_tx->payload.llctrl.ctrldata.conn_param_req.offset5 = conn->llcp.connection_update.offset5; } static inline u32_t event_conn_update_prep(struct connection *conn, u16_t event_counter, u32_t ticks_at_expire) { struct connection *conn_upd; u16_t instant_latency; conn_upd = _radio.conn_upd; /* set mutex */ if (!conn_upd) { _radio.conn_upd = conn; } instant_latency = ((event_counter - conn->llcp.connection_update.instant) & 0xffff); if (conn->llcp.connection_update.state) { if (((conn_upd == 0) || (conn_upd == conn)) && (conn->llcp.connection_update.state != LLCP_CONN_STATE_APP_WAIT) && (conn->llcp.connection_update.state != LLCP_CONN_STATE_RSP_WAIT)) { #if defined(CONFIG_BLUETOOTH_CONTROLLER_SCHED_ADVANCED) static void *s_link[2]; static struct mayfly s_mfy_sched_offset = {0, 0, s_link, NULL, NULL }; void (*fp_mayfly_select_or_use)(void *); #endif /* CONFIG_BLUETOOTH_CONTROLLER_SCHED_ADVANCED */ struct radio_pdu_node_tx *node_tx; struct pdu_data *pdu_ctrl_tx; u8_t state; node_tx = mem_acquire(&_radio.pkt_tx_ctrl_free); if (!node_tx) { return 1; } pdu_ctrl_tx = (struct pdu_data *)node_tx->pdu_data; #if defined(CONFIG_BLUETOOTH_CONTROLLER_SCHED_ADVANCED) fp_mayfly_select_or_use = mayfly_sched_win_offset_use; #endif /* CONFIG_BLUETOOTH_CONTROLLER_SCHED_ADVANCED */ state = conn->llcp.connection_update.state; if ((state == LLCP_CONN_STATE_RSP) && !conn->role) { state = LLCP_CONN_STATE_INITIATE; #if defined(CONFIG_BLUETOOTH_CONTROLLER_SCHED_ADVANCED) fp_mayfly_select_or_use = mayfly_sched_win_offset_select; #endif /* CONFIG_BLUETOOTH_CONTROLLER_SCHED_ADVANCED */ } switch (state) { case LLCP_CONN_STATE_INITIATE: if (!conn->role) { #if defined(CONFIG_BLUETOOTH_CONTROLLER_SCHED_ADVANCED) event_conn_update_st_init(conn, event_counter, pdu_ctrl_tx, ticks_at_expire, &s_mfy_sched_offset, fp_mayfly_select_or_use); #else /* !CONFIG_BLUETOOTH_CONTROLLER_SCHED_ADVANCED */ event_conn_update_st_init(conn, event_counter, pdu_ctrl_tx, ticks_at_expire, NULL, NULL); #endif /* !CONFIG_BLUETOOTH_CONTROLLER_SCHED_ADVANCED */ break; } /* fall thru if slave */ case LLCP_CONN_STATE_REQ: #if defined(CONFIG_BLUETOOTH_CONTROLLER_SCHED_ADVANCED) event_conn_update_st_req(conn, event_counter, pdu_ctrl_tx, ticks_at_expire, &s_mfy_sched_offset); #else /* !CONFIG_BLUETOOTH_CONTROLLER_SCHED_ADVANCED */ event_conn_update_st_req(conn, event_counter, pdu_ctrl_tx, ticks_at_expire, NULL); #endif /* !CONFIG_BLUETOOTH_CONTROLLER_SCHED_ADVANCED */ break; case LLCP_CONN_STATE_RSP: event_conn_update_st_rsp(conn, pdu_ctrl_tx); break; default: LL_ASSERT(0); break; } ctrl_tx_enqueue(conn, node_tx); } } else if (instant_latency <= 0x7FFF) { struct radio_pdu_node_rx *radio_pdu_node_rx; struct pdu_data *pdu_data_rx; struct radio_le_conn_update_cmplt *radio_le_conn_update_cmplt; u32_t ticker_status; u32_t conn_interval_us; u32_t periodic_us; u32_t ticks_win_offset; u32_t ticks_slot_offset; u16_t conn_interval_old; u16_t conn_interval_new; u16_t latency; u32_t mayfly_was_enabled; /* procedure request acked */ conn->llcp_ack = conn->llcp_req; /* Reset ticker_id_prepare as role is not continued further * due to conn update at this event. */ _radio.ticker_id_prepare = 0; /* reset mutex */ if (_radio.conn_upd == conn) { _radio.conn_upd = NULL; } /* Prepare the rx packet structure */ if ((conn->llcp.connection_update.interval != conn->conn_interval) || (conn->llcp.connection_update.latency != conn->latency) || (conn->llcp.connection_update.timeout != (conn->conn_interval * conn->supervision_reload * 125 / 1000))) { radio_pdu_node_rx = packet_rx_reserve_get(2); LL_ASSERT(radio_pdu_node_rx); radio_pdu_node_rx->hdr.handle = conn->handle; radio_pdu_node_rx->hdr.type = NODE_RX_TYPE_CONN_UPDATE; /* prepare connection update complete structure */ pdu_data_rx = (struct pdu_data *)radio_pdu_node_rx->pdu_data; radio_le_conn_update_cmplt = (struct radio_le_conn_update_cmplt *) &pdu_data_rx->payload; radio_le_conn_update_cmplt->status = 0x00; radio_le_conn_update_cmplt->interval = conn->llcp.connection_update.interval; radio_le_conn_update_cmplt->latency = conn->llcp.connection_update.latency; radio_le_conn_update_cmplt->timeout = conn->llcp.connection_update.timeout; /* enqueue connection update complete structure * into queue. */ packet_rx_enqueue(); } /* restore to normal prepare */ if (conn->hdr.ticks_xtal_to_start & ((u32_t)1 << 31)) { u32_t ticks_prepare_to_start = (conn->hdr.ticks_active_to_start > conn->hdr.ticks_preempt_to_start) ? conn->hdr.ticks_active_to_start : conn->hdr.ticks_preempt_to_start; conn->hdr.ticks_xtal_to_start &= ~((u32_t)1 << 31); ticks_at_expire -= (conn->hdr.ticks_xtal_to_start - ticks_prepare_to_start); } /* compensate for instant_latency due to laziness */ conn_interval_old = instant_latency * conn->conn_interval; latency = conn_interval_old / conn->llcp.connection_update.interval; conn_interval_new = latency * conn->llcp.connection_update.interval; if (conn_interval_new > conn_interval_old) { ticks_at_expire += TICKER_US_TO_TICKS((conn_interval_new - conn_interval_old) * 1250); } else { ticks_at_expire -= TICKER_US_TO_TICKS((conn_interval_old - conn_interval_new) * 1250); } conn->latency_prepare -= (instant_latency - latency); /* calculate the offset, window widening and interval */ ticks_slot_offset = (conn->hdr.ticks_active_to_start < conn->hdr.ticks_xtal_to_start) ? conn->hdr.ticks_xtal_to_start : conn->hdr.ticks_active_to_start; conn_interval_us = conn->llcp.connection_update.interval * 1250; periodic_us = conn_interval_us; if (conn->role) { conn->slave.window_widening_prepare_us -= conn->slave.window_widening_periodic_us * instant_latency; conn->slave.window_widening_periodic_us = (((gc_lookup_ppm[_radio.sca] + gc_lookup_ppm[conn->slave.sca]) * conn_interval_us) + (1000000 - 1)) / 1000000; conn->slave.window_widening_max_us = (conn_interval_us >> 1) - RADIO_TIFS; conn->slave.window_size_prepare_us = conn->llcp.connection_update.win_size * 1250; conn->slave.ticks_to_offset = 0; conn->slave.window_widening_prepare_us += conn->slave.window_widening_periodic_us * latency; if (conn->slave.window_widening_prepare_us > conn->slave.window_widening_max_us) { conn->slave.window_widening_prepare_us = conn->slave.window_widening_max_us; } ticks_at_expire -= TICKER_US_TO_TICKS(conn->slave.window_widening_periodic_us * latency); ticks_win_offset = TICKER_US_TO_TICKS((conn->llcp.connection_update.win_offset_us / 1250) * 1250); periodic_us -= conn->slave.window_widening_periodic_us; if (conn->llcp.connection_update.is_internal == 2) { conn_update_req(conn); } } else { ticks_win_offset = TICKER_US_TO_TICKS(conn->llcp.connection_update.win_offset_us); /* Workaround: Due to the missing remainder param in * ticker_start function for first interval; add half a * tick in microseconds so as to restrict the offset * within the +/- 16us jitter for the offset. */ ticks_win_offset += (30517578125UL/1000000000UL) >> 1; } conn->conn_interval = conn->llcp.connection_update.interval; conn->latency = conn->llcp.connection_update.latency; conn->supervision_reload = RADIO_CONN_EVENTS((conn->llcp.connection_update.timeout * 10 * 1000), conn_interval_us); conn->procedure_reload = RADIO_CONN_EVENTS((40 * 1000 * 1000), conn_interval_us); #if defined(CONFIG_BLUETOOTH_CONTROLLER_LE_PING) /* APTO in no. of connection events */ conn->apto_reload = RADIO_CONN_EVENTS((30 * 1000 * 1000), conn_interval_us); /* Dispatch LE Ping PDU 6 connection events (that peer would * listen to) before 30s timeout * TODO: "peer listens to" is greater than 30s due to latency */ conn->appto_reload = (conn->apto_reload > (conn->latency + 6)) ? (conn->apto_reload - (conn->latency + 6)) : conn->apto_reload; #endif /* CONFIG_BLUETOOTH_CONTROLLER_LE_PING */ if (!conn->llcp.connection_update.is_internal) { conn->supervision_expire = 0; } /* disable ticker job, in order to chain stop and start * to avoid RTC being stopped if no tickers active. */ mayfly_was_enabled = mayfly_is_enabled(RADIO_TICKER_USER_ID_WORKER, RADIO_TICKER_USER_ID_JOB); mayfly_enable(RADIO_TICKER_USER_ID_WORKER, RADIO_TICKER_USER_ID_JOB, 0); /* start slave/master with new timings */ ticker_status = ticker_stop(RADIO_TICKER_INSTANCE_ID_RADIO, RADIO_TICKER_USER_ID_WORKER, RADIO_TICKER_ID_FIRST_CONNECTION + conn->handle, ticker_success_assert, (void *)__LINE__); LL_ASSERT((ticker_status == TICKER_STATUS_SUCCESS) || (ticker_status == TICKER_STATUS_BUSY)); ticker_status = ticker_start(RADIO_TICKER_INSTANCE_ID_RADIO, RADIO_TICKER_USER_ID_WORKER, RADIO_TICKER_ID_FIRST_CONNECTION + conn->handle, ticks_at_expire, ticks_win_offset, TICKER_US_TO_TICKS(periodic_us), TICKER_REMAINDER(periodic_us), TICKER_NULL_LAZY, (ticks_slot_offset + conn->hdr.ticks_slot), conn->role ? event_slave_prepare : event_master_prepare, conn, ticker_success_assert, (void *)__LINE__); LL_ASSERT((ticker_status == TICKER_STATUS_SUCCESS) || (ticker_status == TICKER_STATUS_BUSY)); /* enable ticker job, if disabled in this function */ if (mayfly_was_enabled) { mayfly_enable(RADIO_TICKER_USER_ID_WORKER, RADIO_TICKER_USER_ID_JOB, 1); } return 0; } return 1; } static inline void event_ch_map_prep(struct connection *conn, u16_t event_counter) { if (conn->llcp.chan_map.initiate) { struct radio_pdu_node_tx *node_tx; node_tx = mem_acquire(&_radio.pkt_tx_ctrl_free); if (node_tx) { struct pdu_data *pdu_ctrl_tx = (struct pdu_data *)node_tx->pdu_data; /* reset initiate flag */ conn->llcp.chan_map.initiate = 0; /* set instant */ conn->llcp.chan_map.instant = event_counter + conn->latency + 6; /* place the channel map req packet as next in * tx queue */ pdu_ctrl_tx->ll_id = PDU_DATA_LLID_CTRL; pdu_ctrl_tx->len = offsetof(struct pdu_data_llctrl, ctrldata) + sizeof(struct pdu_data_llctrl_chan_map_ind); pdu_ctrl_tx->payload.llctrl.opcode = PDU_DATA_LLCTRL_TYPE_CHAN_MAP_IND; memcpy(&pdu_ctrl_tx->payload.llctrl. ctrldata.chan_map_ind.chm[0], &conn->llcp.chan_map.chm[0], sizeof(pdu_ctrl_tx->payload. llctrl.ctrldata.chan_map_ind.chm)); pdu_ctrl_tx->payload.llctrl.ctrldata.chan_map_ind.instant = conn->llcp.chan_map.instant; ctrl_tx_enqueue(conn, node_tx); } } else if (((event_counter - conn->llcp.chan_map.instant) & 0xFFFF) <= 0x7FFF) { /* procedure request acked */ conn->llcp_ack = conn->llcp_req; /* copy to active channel map */ memcpy(&conn->data_chan_map[0], &conn->llcp.chan_map.chm[0], sizeof(conn->data_chan_map)); conn->data_chan_count = util_ones_count_get(&conn->data_chan_map[0], sizeof(conn->data_chan_map)); } } #if defined(CONFIG_BLUETOOTH_CONTROLLER_LE_ENC) static inline void event_enc_reject_prep(struct connection *conn, struct pdu_data *pdu) { if (conn->common.fex_valid && (conn->llcp_features & BIT(BT_LE_FEAT_BIT_EXT_REJ_IND))) { struct pdu_data_llctrl_reject_ext_ind *p; pdu->payload.llctrl.opcode = PDU_DATA_LLCTRL_TYPE_REJECT_EXT_IND; p = (void *)&pdu->payload.llctrl.ctrldata.reject_ext_ind; p->reject_opcode = PDU_DATA_LLCTRL_TYPE_ENC_REQ; p->error_code = conn->llcp.encryption.error_code; pdu->len = sizeof(struct pdu_data_llctrl_reject_ext_ind); } else { struct pdu_data_llctrl_reject_ind *p; pdu->payload.llctrl.opcode = PDU_DATA_LLCTRL_TYPE_REJECT_IND; p = (void *)&pdu->payload.llctrl.ctrldata.reject_ind; p->error_code = conn->llcp.encryption.error_code; pdu->len = sizeof(struct pdu_data_llctrl_reject_ind); } pdu->len += offsetof(struct pdu_data_llctrl, ctrldata); conn->llcp.encryption.error_code = 0; } static inline void event_enc_prep(struct connection *conn) { struct radio_pdu_node_tx *node_tx; node_tx = mem_acquire(&_radio.pkt_tx_ctrl_free); if (node_tx) { struct pdu_data *pdu_ctrl_tx = (struct pdu_data *)node_tx->pdu_data; /* procedure request acked */ conn->llcp_ack = conn->llcp_req; /* master sends encrypted enc start rsp in control priority */ if (!conn->role) { /* calc the Session Key */ ecb_encrypt(&conn->llcp.encryption.ltk[0], &conn->llcp.encryption.skd[0], NULL, &conn->ccm_rx.key[0]); /* copy the Session Key */ memcpy(&conn->ccm_tx.key[0], &conn->ccm_rx.key[0], sizeof(conn->ccm_tx.key)); /* copy the IV */ memcpy(&conn->ccm_tx.iv[0], &conn->ccm_rx.iv[0], sizeof(conn->ccm_tx.iv)); /* initialise counter */ conn->ccm_rx.counter = 0; conn->ccm_tx.counter = 0; /* set direction: slave to master = 0, * master to slave = 1 */ conn->ccm_rx.direction = 0; conn->ccm_tx.direction = 1; /* enable receive and transmit encryption */ conn->enc_rx = 1; conn->enc_tx = 1; /* send enc start resp */ start_enc_rsp_send(conn, pdu_ctrl_tx); } /* slave send reject ind or start enc req at control priority */ #if defined(CONFIG_BLUETOOTH_CONTROLLER_FAST_ENC) else { #else /* !CONFIG_BLUETOOTH_CONTROLLER_FAST_ENC */ else if (!conn->pause_tx || conn->refresh) { #endif /* !CONFIG_BLUETOOTH_CONTROLLER_FAST_ENC */ /* ll ctrl packet */ pdu_ctrl_tx->ll_id = PDU_DATA_LLID_CTRL; /* place the reject ind packet as next in tx queue */ if (conn->llcp.encryption.error_code) { event_enc_reject_prep(conn, pdu_ctrl_tx); } /* place the start enc req packet as next in tx queue */ else { #if !defined(CONFIG_BLUETOOTH_CONTROLLER_FAST_ENC) /* TODO BT Spec. text: may finalize the sending * of additional data channel PDUs queued in the * controller. */ enc_rsp_send(conn); #endif /* !CONFIG_BLUETOOTH_CONTROLLER_FAST_ENC */ /* calc the Session Key */ ecb_encrypt(&conn->llcp.encryption.ltk[0], &conn->llcp.encryption.skd[0], NULL, &conn->ccm_rx.key[0]); /* copy the Session Key */ memcpy(&conn->ccm_tx.key[0], &conn->ccm_rx.key[0], sizeof(conn->ccm_tx.key)); /* copy the IV */ memcpy(&conn->ccm_tx.iv[0], &conn->ccm_rx.iv[0], sizeof(conn->ccm_tx.iv)); /* initialise counter */ conn->ccm_rx.counter = 0; conn->ccm_tx.counter = 0; /* set direction: slave to master = 0, * master to slave = 1 */ conn->ccm_rx.direction = 1; conn->ccm_tx.direction = 0; /* enable receive encryption (transmit turned * on when start enc resp from master is * received) */ conn->enc_rx = 1; /* prepare the start enc req */ pdu_ctrl_tx->len = offsetof(struct pdu_data_llctrl, ctrldata); pdu_ctrl_tx->payload.llctrl.opcode = PDU_DATA_LLCTRL_TYPE_START_ENC_REQ; } #if !defined(CONFIG_BLUETOOTH_CONTROLLER_FAST_ENC) } else { /* enable transmit encryption */ _radio.conn_curr->enc_tx = 1; start_enc_rsp_send(_radio.conn_curr, NULL); /* resume data packet rx and tx */ _radio.conn_curr->pause_rx = 0; _radio.conn_curr->pause_tx = 0; #endif /* !CONFIG_BLUETOOTH_CONTROLLER_FAST_ENC */ } ctrl_tx_enqueue(conn, node_tx); } } #endif /* CONFIG_BLUETOOTH_CONTROLLER_LE_ENC */ static inline void event_fex_prep(struct connection *conn) { struct radio_pdu_node_tx *node_tx; node_tx = mem_acquire(&_radio.pkt_tx_ctrl_free); if (node_tx) { struct pdu_data *pdu_ctrl_tx = (struct pdu_data *)node_tx->pdu_data; /* procedure request acked */ conn->llcp_ack = conn->llcp_req; /* use initial feature bitmap */ conn->llcp_features = RADIO_BLE_FEAT; /* place the feature exchange req packet as next in tx queue */ pdu_ctrl_tx->ll_id = PDU_DATA_LLID_CTRL; pdu_ctrl_tx->len = offsetof(struct pdu_data_llctrl, ctrldata) + sizeof(struct pdu_data_llctrl_feature_req); pdu_ctrl_tx->payload.llctrl.opcode = !conn->role ? PDU_DATA_LLCTRL_TYPE_FEATURE_REQ : PDU_DATA_LLCTRL_TYPE_SLAVE_FEATURE_REQ; memset(&pdu_ctrl_tx->payload.llctrl.ctrldata.feature_req.features[0], 0x00, sizeof(pdu_ctrl_tx->payload.llctrl.ctrldata.feature_req.features)); pdu_ctrl_tx->payload.llctrl.ctrldata.feature_req.features[0] = conn->llcp_features & 0xFF; pdu_ctrl_tx->payload.llctrl.ctrldata.feature_req.features[1] = (conn->llcp_features >> 8) & 0xFF; pdu_ctrl_tx->payload.llctrl.ctrldata.feature_req.features[2] = (conn->llcp_features >> 16) & 0xFF; ctrl_tx_enqueue(conn, node_tx); /* Start Procedure Timeout (TODO: this shall not replace * terminate procedure) */ conn->procedure_expire = conn->procedure_reload; } } static inline void event_vex_prep(struct connection *conn) { if (conn->llcp_version.tx == 0) { struct radio_pdu_node_tx *node_tx; node_tx = mem_acquire(&_radio.pkt_tx_ctrl_free); if (node_tx) { struct pdu_data *pdu_ctrl_tx = (struct pdu_data *)node_tx->pdu_data; /* procedure request acked */ conn->llcp_ack = conn->llcp_req; /* set version ind tx-ed flag */ conn->llcp_version.tx = 1; /* place the version ind packet as next in tx queue */ pdu_ctrl_tx->ll_id = PDU_DATA_LLID_CTRL; pdu_ctrl_tx->len = offsetof(struct pdu_data_llctrl, ctrldata) + sizeof(struct pdu_data_llctrl_version_ind); pdu_ctrl_tx->payload.llctrl.opcode = PDU_DATA_LLCTRL_TYPE_VERSION_IND; pdu_ctrl_tx->payload.llctrl.ctrldata.version_ind.version_number = RADIO_BLE_VERSION_NUMBER; pdu_ctrl_tx->payload.llctrl.ctrldata.version_ind.company_id = RADIO_BLE_COMPANY_ID; pdu_ctrl_tx->payload.llctrl.ctrldata.version_ind.sub_version_number = RADIO_BLE_SUB_VERSION_NUMBER; ctrl_tx_enqueue(conn, node_tx); /* Start Procedure Timeout (TODO: this shall not * replace terminate procedure) */ conn->procedure_expire = conn->procedure_reload; } } else if (conn->llcp_version.rx != 0) { struct radio_pdu_node_rx *radio_pdu_node_rx; struct pdu_data *pdu_ctrl_rx; /* procedure request acked */ conn->llcp_ack = conn->llcp_req; /* Prepare the rx packet structure */ radio_pdu_node_rx = packet_rx_reserve_get(2); LL_ASSERT(radio_pdu_node_rx); radio_pdu_node_rx->hdr.handle = conn->handle; radio_pdu_node_rx->hdr.type = NODE_RX_TYPE_DC_PDU; /* prepare version ind structure */ pdu_ctrl_rx = (struct pdu_data *)radio_pdu_node_rx->pdu_data; pdu_ctrl_rx->ll_id = PDU_DATA_LLID_CTRL; pdu_ctrl_rx->len = offsetof(struct pdu_data_llctrl, ctrldata) + sizeof(struct pdu_data_llctrl_version_ind); pdu_ctrl_rx->payload.llctrl.opcode = PDU_DATA_LLCTRL_TYPE_VERSION_IND; pdu_ctrl_rx->payload.llctrl.ctrldata.version_ind.version_number = conn->llcp_version.version_number; pdu_ctrl_rx->payload.llctrl.ctrldata.version_ind.company_id = conn->llcp_version.company_id; pdu_ctrl_rx->payload.llctrl.ctrldata.version_ind.sub_version_number = conn->llcp_version.sub_version_number; /* enqueue version ind structure into rx queue */ packet_rx_enqueue(); } else { /* tx-ed but no rx, and new request placed */ LL_ASSERT(0); } } #if defined(CONFIG_BLUETOOTH_CONTROLLER_LE_PING) static inline void event_ping_prep(struct connection *conn) { struct radio_pdu_node_tx *node_tx; node_tx = mem_acquire(&_radio.pkt_tx_ctrl_free); if (node_tx) { struct pdu_data *pdu_ctrl_tx = (struct pdu_data *)node_tx->pdu_data; /* procedure request acked */ conn->llcp_ack = conn->llcp_req; /* place the ping req packet as next in tx queue */ pdu_ctrl_tx->ll_id = PDU_DATA_LLID_CTRL; pdu_ctrl_tx->len = offsetof(struct pdu_data_llctrl, ctrldata); pdu_ctrl_tx->payload.llctrl.opcode = PDU_DATA_LLCTRL_TYPE_PING_REQ; ctrl_tx_enqueue(conn, node_tx); /* Start Procedure Timeout (TODO: this shall not replace * terminate procedure) */ conn->procedure_expire = conn->procedure_reload; } } #endif /* CONFIG_BLUETOOTH_CONTROLLER_LE_PING */ #if defined(CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH) static inline void event_len_prep(struct connection *conn) { switch (conn->llcp_length.state) { case LLCP_LENGTH_STATE_REQ: { struct pdu_data_llctrl_length_req_rsp *lr; struct radio_pdu_node_tx *node_tx; struct pdu_data *pdu_ctrl_tx; u16_t free_count_rx; free_count_rx = packet_rx_acquired_count_get() + mem_free_count_get(_radio.pkt_rx_data_free); LL_ASSERT(free_count_rx <= 0xFF); if (_radio.packet_rx_data_count != free_count_rx) { break; } node_tx = mem_acquire(&_radio.pkt_tx_ctrl_free); if (!node_tx) { break; } /* wait for resp before completing the procedure */ conn->llcp_length.state = LLCP_LENGTH_STATE_ACK_WAIT; /* set the default tx octets to requested value */ conn->default_tx_octets = conn->llcp_length.tx_octets; /* place the length req packet as next in tx queue */ pdu_ctrl_tx = (struct pdu_data *) node_tx->pdu_data; pdu_ctrl_tx->ll_id = PDU_DATA_LLID_CTRL; pdu_ctrl_tx->len = offsetof(struct pdu_data_llctrl, ctrldata) + sizeof(struct pdu_data_llctrl_length_req_rsp); pdu_ctrl_tx->payload.llctrl.opcode = PDU_DATA_LLCTRL_TYPE_LENGTH_REQ; lr = (struct pdu_data_llctrl_length_req_rsp *) &pdu_ctrl_tx->payload.llctrl.ctrldata.length_req; lr->max_rx_octets = RADIO_LL_LENGTH_OCTETS_RX_MAX; lr->max_rx_time = ((RADIO_LL_LENGTH_OCTETS_RX_MAX + 14) << 3); lr->max_tx_octets = conn->default_tx_octets; lr->max_tx_time = ((conn->default_tx_octets + 14) << 3); ctrl_tx_enqueue(conn, node_tx); /* Start Procedure Timeout (TODO: this shall not replace * terminate procedure). */ conn->procedure_expire = conn->procedure_reload; } break; case LLCP_LENGTH_STATE_RESIZE: { struct pdu_data_llctrl_length_req_rsp *lr; struct radio_pdu_node_rx *node_rx; struct pdu_data *pdu_ctrl_rx; u16_t packet_rx_data_size; u16_t free_count_conn; u16_t free_count_rx; /* Ensure the rx pool is not in use. * This is important to be able to re-size the pool * ensuring there is no chance that an operation on * the pool is pre-empted causing memory corruption. */ free_count_rx = packet_rx_acquired_count_get() + mem_free_count_get(_radio.pkt_rx_data_free); LL_ASSERT(free_count_rx <= 0xFF); if (_radio.packet_rx_data_count != free_count_rx) { /** TODO another role instance has obtained * memory from rx pool. */ LL_ASSERT(0); } /* Procedure complete */ conn->llcp_length.ack = conn->llcp_length.req; conn->procedure_expire = 0; /* resume data packet tx */ _radio.conn_curr->pause_tx = 0; /* Use the new rx octets in the connection */ conn->max_rx_octets = conn->llcp_length.rx_octets; /** TODO This design is exception as memory initialization * and allocation is done in radio context here, breaking the * rule that the rx buffers are allocated in application * context. * Design mem_* such that mem_init could interrupt mem_acquire, * when the pool is full? */ free_count_conn = mem_free_count_get(_radio.conn_free); if (_radio.advertiser.conn) { free_count_conn++; } if (_radio.scanner.conn) { free_count_conn++; } packet_rx_data_size = MROUND(offsetof(struct radio_pdu_node_rx, pdu_data) + offsetof(struct pdu_data, payload) + conn->max_rx_octets); /* Resize to lower or higher size if this is the only active * connection, or resize to only higher sizes as there may be * other connections using the current size. */ if (((free_count_conn + 1) == _radio.connection_count) || (packet_rx_data_size > _radio.packet_rx_data_size)) { /* as rx mem is to be re-sized, release acquired * memq link. */ while (_radio.packet_rx_acquire != _radio.packet_rx_last) { struct radio_pdu_node_rx *node_rx; if (_radio.packet_rx_acquire == 0) { _radio.packet_rx_acquire = _radio.packet_rx_count - 1; } else { _radio.packet_rx_acquire -= 1; } node_rx = _radio.packet_rx[ _radio.packet_rx_acquire]; mem_release(node_rx->hdr.onion.link, &_radio.link_rx_free); LL_ASSERT(_radio.link_rx_data_quota < (_radio.packet_rx_count - 1)); _radio.link_rx_data_quota++; /* no need to release node_rx as we mem_init * later down in code. */ } /* calculate the new rx node size and new count */ if (conn->max_rx_octets < (PDU_AC_SIZE_MAX + PDU_AC_SIZE_EXTRA)) { _radio.packet_rx_data_size = MROUND(offsetof(struct radio_pdu_node_rx, pdu_data) + (PDU_AC_SIZE_MAX + PDU_AC_SIZE_EXTRA)); } else { _radio.packet_rx_data_size = packet_rx_data_size; } _radio.packet_rx_data_count = _radio.packet_rx_data_pool_size / _radio.packet_rx_data_size; LL_ASSERT(_radio.packet_rx_data_count); /* re-size (re-init) the free rx pool */ mem_init(_radio.pkt_rx_data_pool, _radio.packet_rx_data_size, _radio.packet_rx_data_count, &_radio.pkt_rx_data_free); /* allocate the rx queue include one extra for * generating event in following lines. */ packet_rx_allocate(4); } /* Prepare the rx packet structure */ node_rx = packet_rx_reserve_get(2); LL_ASSERT(node_rx); node_rx->hdr.handle = conn->handle; node_rx->hdr.type = NODE_RX_TYPE_DC_PDU; /* prepare length rsp structure */ pdu_ctrl_rx = (struct pdu_data *) node_rx->pdu_data; pdu_ctrl_rx->ll_id = PDU_DATA_LLID_CTRL; pdu_ctrl_rx->len = offsetof(struct pdu_data_llctrl, ctrldata) + sizeof(struct pdu_data_llctrl_length_req_rsp); pdu_ctrl_rx->payload.llctrl.opcode = PDU_DATA_LLCTRL_TYPE_LENGTH_RSP; lr = (struct pdu_data_llctrl_length_req_rsp *) &pdu_ctrl_rx->payload.llctrl.ctrldata.length_req; lr->max_rx_octets = conn->max_rx_octets; lr->max_rx_time = ((conn->max_rx_octets + 14) << 3); lr->max_tx_octets = conn->max_tx_octets; lr->max_tx_time = ((conn->max_tx_octets + 14) << 3); /* enqueue version ind structure into rx queue */ packet_rx_enqueue(); } break; case LLCP_LENGTH_STATE_ACK_WAIT: case LLCP_LENGTH_STATE_RSP_WAIT: /* no nothing */ break; default: LL_ASSERT(0); break; } } #endif /* CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH */ #if defined(CONFIG_BLUETOOTH_CONTROLLER_PHY) static inline void event_phy_req_prep(struct connection *conn) { switch (conn->llcp_phy.state) { case LLCP_PHY_STATE_REQ: { struct pdu_data_llctrl_phy_req_rsp *pr; struct radio_pdu_node_tx *node_tx; struct pdu_data *pdu_ctrl_tx; node_tx = mem_acquire(&_radio.pkt_tx_ctrl_free); if (!node_tx) { break; } conn->llcp_phy.state = LLCP_PHY_STATE_ACK_WAIT; /* update preferred phy */ conn->phy_pref_tx = conn->llcp_phy.tx; conn->phy_pref_rx = conn->llcp_phy.rx; conn->phy_pref_flags = conn->llcp_phy.flags; /* place the phy req packet as next in tx queue */ pdu_ctrl_tx = (struct pdu_data *) node_tx->pdu_data; pdu_ctrl_tx->ll_id = PDU_DATA_LLID_CTRL; pdu_ctrl_tx->len = offsetof(struct pdu_data_llctrl, ctrldata) + sizeof(struct pdu_data_llctrl_phy_req_rsp); pdu_ctrl_tx->payload.llctrl.opcode = PDU_DATA_LLCTRL_TYPE_PHY_REQ; pr = (struct pdu_data_llctrl_phy_req_rsp *) &pdu_ctrl_tx->payload.llctrl.ctrldata.phy_req; pr->tx_phys = conn->llcp_phy.tx; pr->rx_phys = conn->llcp_phy.rx; ctrl_tx_enqueue(conn, node_tx); /* Start Procedure Timeout (TODO: this shall not replace * terminate procedure). */ conn->procedure_expire = conn->procedure_reload; } break; case LLCP_PHY_STATE_UPD: { /* Procedure complete */ conn->llcp_phy.ack = conn->llcp_phy.req; /* select only one tx phy, prefer 2M */ if (conn->llcp_phy.tx & BIT(1)) { conn->llcp_phy.tx = BIT(1); } else if (conn->llcp_phy.tx & BIT(0)) { conn->llcp_phy.tx = BIT(0); } else if (conn->llcp_phy.tx & BIT(2)) { conn->llcp_phy.tx = BIT(2); } else { conn->llcp_phy.tx = 0; } /* select only one rx phy, prefer 2M */ if (conn->llcp_phy.rx & BIT(1)) { conn->llcp_phy.rx = BIT(1); } else if (conn->llcp_phy.rx & BIT(0)) { conn->llcp_phy.rx = BIT(0); } else if (conn->llcp_phy.rx & BIT(2)) { conn->llcp_phy.rx = BIT(2); } else { conn->llcp_phy.rx = 0; } /* Initiate PHY Update Ind */ if (conn->llcp_phy.tx != conn->phy_tx) { conn->llcp.phy_upd_ind.tx = conn->llcp_phy.tx; } else { conn->llcp.phy_upd_ind.tx = 0; } if (conn->llcp_phy.rx != conn->phy_rx) { conn->llcp.phy_upd_ind.rx = conn->llcp_phy.rx; } else { conn->llcp.phy_upd_ind.rx = 0; } /* conn->llcp.phy_upd_ind.instant = 0; */ conn->llcp.phy_upd_ind.initiate = 1; conn->llcp.phy_upd_ind.cmd = conn->llcp_phy.cmd; conn->llcp_type = LLCP_PHY_UPD; conn->llcp_ack--; } break; case LLCP_PHY_STATE_ACK_WAIT: case LLCP_PHY_STATE_RSP_WAIT: /* no nothing */ break; default: LL_ASSERT(0); break; } } static inline void event_phy_upd_ind_prep(struct connection *conn, u16_t event_counter) { if (conn->llcp.phy_upd_ind.initiate) { struct radio_pdu_node_tx *node_tx; node_tx = mem_acquire(&_radio.pkt_tx_ctrl_free); if (node_tx) { struct pdu_data *pdu_ctrl_tx = (struct pdu_data *) node_tx->pdu_data; struct pdu_data_llctrl_phy_upd_ind *p; /* reset initiate flag */ conn->llcp.phy_upd_ind.initiate = 0; /* Check if both tx and rx PHY unchanged */ if (!((conn->llcp.phy_upd_ind.tx | conn->llcp.phy_upd_ind.rx) & 0x07)) { /* Procedure complete */ conn->llcp_ack = conn->llcp_req; /* 0 instant */ conn->llcp.phy_upd_ind.instant = 0; /* generate phy update event */ if (conn->llcp.phy_upd_ind.cmd) { struct radio_pdu_node_rx *node_rx; struct radio_le_phy_upd_cmplt *p; struct pdu_data *pdu_data; node_rx = packet_rx_reserve_get(2); LL_ASSERT(node_rx); node_rx->hdr.handle = conn->handle; node_rx->hdr.type = NODE_RX_TYPE_PHY_UPDATE; pdu_data = (void *)&node_rx->pdu_data; p = (void *)&pdu_data->payload; p->status = 0; p->tx = conn->phy_tx; p->rx = conn->phy_rx; packet_rx_enqueue(); } } else { /* set instant */ conn->llcp.phy_upd_ind.instant = event_counter + conn->latency + 6; } /* place the phy update ind packet as next in * tx queue */ pdu_ctrl_tx->ll_id = PDU_DATA_LLID_CTRL; pdu_ctrl_tx->len = offsetof(struct pdu_data_llctrl, ctrldata) + sizeof(struct pdu_data_llctrl_phy_upd_ind); pdu_ctrl_tx->payload.llctrl.opcode = PDU_DATA_LLCTRL_TYPE_PHY_UPD_IND; p = &pdu_ctrl_tx->payload.llctrl.ctrldata.phy_upd_ind; p->m_to_s_phy = conn->llcp.phy_upd_ind.tx; p->s_to_m_phy = conn->llcp.phy_upd_ind.rx; p->instant = conn->llcp.phy_upd_ind.instant; ctrl_tx_enqueue(conn, node_tx); } } else if (((event_counter - conn->llcp.phy_upd_ind.instant) & 0xFFFF) <= 0x7FFF) { struct radio_pdu_node_rx *node_rx; struct radio_le_phy_upd_cmplt *p; struct pdu_data *pdu_data; u8_t old_tx, old_rx; /* procedure request acked */ conn->llcp_ack = conn->llcp_req; /* apply new phy */ old_tx = conn->phy_tx; old_rx = conn->phy_rx; if (conn->llcp.phy_upd_ind.tx) { conn->phy_tx = conn->llcp.phy_upd_ind.tx; } if (conn->llcp.phy_upd_ind.rx) { conn->phy_rx = conn->llcp.phy_upd_ind.rx; } conn->phy_flags = conn->phy_pref_flags; /* generate event if phy changed or initiated by cmd */ if (!conn->llcp.phy_upd_ind.cmd && (conn->phy_tx == old_tx) && (conn->phy_rx == old_rx)) { return; } node_rx = packet_rx_reserve_get(2); LL_ASSERT(node_rx); node_rx->hdr.handle = conn->handle; node_rx->hdr.type = NODE_RX_TYPE_PHY_UPDATE; pdu_data = (struct pdu_data *)&node_rx->pdu_data; p = (struct radio_le_phy_upd_cmplt *)&pdu_data->payload; p->status = 0; p->tx = conn->phy_tx; p->rx = conn->phy_rx; packet_rx_enqueue(); } } #endif /* CONFIG_BLUETOOTH_CONTROLLER_PHY */ static void event_connection_prepare(u32_t ticks_at_expire, u32_t remainder, u16_t lazy, struct connection *conn) { u16_t event_counter; LL_ASSERT(!_radio.ticker_id_prepare); _radio.ticker_id_prepare = RADIO_TICKER_ID_FIRST_CONNECTION + conn->handle; /* Calc window widening */ if (conn->role) { conn->slave.window_widening_prepare_us += conn->slave.window_widening_periodic_us * (lazy + 1); if (conn->slave.window_widening_prepare_us > conn->slave.window_widening_max_us) { conn->slave.window_widening_prepare_us = conn->slave.window_widening_max_us; } } /* save the latency for use in event */ conn->latency_prepare += lazy; /* calc current event counter value */ event_counter = conn->event_counter + conn->latency_prepare; #if defined(CONFIG_BLUETOOTH_CONTROLLER_PHY) /* check if PHY Req procedure is requested and no other procedure * using instant is active. */ if ((conn->llcp_ack == conn->llcp_req) && (conn->llcp_phy.ack != conn->llcp_phy.req)) { /* Stop previous event, to avoid Radio DMA corrupting the * rx queue */ event_stop(0, 0, 0, (void *)STATE_ABORT); /* handle PHY Upd state machine */ event_phy_req_prep(conn); } #endif /* CONFIG_BLUETOOTH_CONTROLLER_PHY */ /* check if procedure is requested */ if (conn->llcp_ack != conn->llcp_req) { /* Stop previous event, to avoid Radio DMA corrupting the * rx queue */ event_stop(0, 0, 0, (void *)STATE_ABORT); switch (conn->llcp_type) { case LLCP_CONNECTION_UPDATE: if (event_conn_update_prep(conn, event_counter, ticks_at_expire) == 0) { return; } break; case LLCP_CHAN_MAP: event_ch_map_prep(conn, event_counter); break; #if defined(CONFIG_BLUETOOTH_CONTROLLER_LE_ENC) case LLCP_ENCRYPTION: event_enc_prep(conn); break; #endif /* CONFIG_BLUETOOTH_CONTROLLER_LE_ENC */ case LLCP_FEATURE_EXCHANGE: event_fex_prep(conn); break; case LLCP_VERSION_EXCHANGE: event_vex_prep(conn); break; #if defined(CONFIG_BLUETOOTH_CONTROLLER_LE_PING) case LLCP_PING: event_ping_prep(conn); break; #endif /* CONFIG_BLUETOOTH_CONTROLLER_LE_PING */ #if defined(CONFIG_BLUETOOTH_CONTROLLER_PHY) case LLCP_PHY_UPD: event_phy_upd_ind_prep(conn, event_counter); break; #endif /* CONFIG_BLUETOOTH_CONTROLLER_PHY */ default: LL_ASSERT(0); break; } } /* Terminate Procedure Request */ if (conn->llcp_terminate.ack != conn->llcp_terminate.req) { struct radio_pdu_node_tx *node_tx; /* Stop previous event, to avoid Radio DMA corrupting the rx * queue */ event_stop(0, 0, 0, (void *)STATE_ABORT); node_tx = mem_acquire(&_radio.pkt_tx_ctrl_free); if (node_tx) { struct pdu_data *pdu_ctrl_tx = (struct pdu_data *)node_tx->pdu_data; /* Terminate Procedure acked */ conn->llcp_terminate.ack = conn->llcp_terminate.req; /* place the terminate ind packet in tx queue */ pdu_ctrl_tx->ll_id = PDU_DATA_LLID_CTRL; pdu_ctrl_tx->len = offsetof(struct pdu_data_llctrl, ctrldata) + sizeof(struct pdu_data_llctrl_terminate_ind); pdu_ctrl_tx->payload.llctrl.opcode = PDU_DATA_LLCTRL_TYPE_TERMINATE_IND; pdu_ctrl_tx->payload.llctrl.ctrldata.terminate_ind. error_code = conn->llcp_terminate.reason_own; ctrl_tx_enqueue(conn, node_tx); /* Terminate Procedure timeout is started, will * replace any other timeout running */ conn->procedure_expire = conn->supervision_reload; /* NOTE: if supervision timeout equals connection * interval, dont timeout in current event. */ if (conn->procedure_expire <= 1) { conn->procedure_expire++; } } } #if defined(CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH) /* check if procedure is requested */ if (conn->llcp_length.ack != conn->llcp_length.req) { /* Stop previous event, to avoid Radio DMA corrupting the * rx queue */ event_stop(0, 0, 0, (void *)STATE_ABORT); /* handle DLU state machine */ event_len_prep(conn); } #endif /* CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH */ /* Setup XTAL startup and radio active events */ event_common_prepare(ticks_at_expire, remainder, &conn->hdr.ticks_xtal_to_start, &conn->hdr.ticks_active_to_start, conn->hdr.ticks_preempt_to_start, (RADIO_TICKER_ID_FIRST_CONNECTION + conn->handle), conn->role ? event_slave : event_master, conn); /* store the next event counter value */ conn->event_counter = event_counter + 1; } static void connection_configure(struct connection *conn) { adv_scan_conn_configure(); radio_aa_set(conn->access_addr); radio_crc_configure(((0x5bUL) | ((0x06UL) << 8) | ((0x00UL) << 16)), (((u32_t)conn->crc_init[2] << 16) | ((u32_t)conn->crc_init[1] << 8) | ((u32_t)conn->crc_init[0]))); } static void event_slave_prepare(u32_t ticks_at_expire, u32_t remainder, u16_t lazy, void *context) { DEBUG_RADIO_PREPARE_S(1); event_connection_prepare(ticks_at_expire, remainder, lazy, context); DEBUG_RADIO_PREPARE_S(0); } static void event_slave(u32_t ticks_at_expire, u32_t remainder, u16_t lazy, void *context) { struct connection *conn; u8_t data_chan_use = 0; u32_t remainder_us; u32_t hcto; ARG_UNUSED(remainder); ARG_UNUSED(lazy); DEBUG_RADIO_START_S(1); LL_ASSERT(_radio.role == ROLE_NONE); conn = (struct connection *)context; LL_ASSERT(_radio.ticker_id_prepare == (RADIO_TICKER_ID_FIRST_CONNECTION + conn->handle)); _radio.role = ROLE_SLAVE; _radio.state = STATE_RX; _radio.ticker_id_prepare = 0; _radio.ticker_id_event = (RADIO_TICKER_ID_FIRST_CONNECTION + conn->handle); _radio.ticks_anchor = ticks_at_expire; _radio.packet_counter = 0; _radio.crc_expire = 0; _radio.conn_curr = conn; conn->latency_event = conn->latency_prepare; conn->latency_prepare = 0; connection_configure(conn); radio_tmr_tifs_set(RADIO_TIFS); #if defined(CONFIG_BLUETOOTH_CONTROLLER_PHY) radio_switch_complete_and_tx(conn->phy_rx, 0, conn->phy_tx, conn->phy_flags); #else /* !CONFIG_BLUETOOTH_CONTROLLER_PHY */ radio_switch_complete_and_tx(0, 0, 0, 0); #endif /* !CONFIG_BLUETOOTH_CONTROLLER_PHY */ rx_packet_set(conn, (struct pdu_data *) _radio.packet_rx[_radio.packet_rx_last]->pdu_data); /* Setup Radio Channel */ if (conn->data_chan_sel) { #if defined(CONFIG_BLUETOOTH_CONTROLLER_CHAN_SEL_2) data_chan_use = chan_sel_2(conn->event_counter - 1, conn->data_chan_id, &conn->data_chan_map[0], conn->data_chan_count); #else /* !CONFIG_BLUETOOTH_CONTROLLER_CHAN_SEL_2 */ LL_ASSERT(0); #endif /* !CONFIG_BLUETOOTH_CONTROLLER_CHAN_SEL_2 */ } else { data_chan_use = chan_sel_1(&conn->data_chan_use, conn->data_chan_hop, conn->latency_event, &conn->data_chan_map[0], conn->data_chan_count); } chan_set(data_chan_use); /* current window widening */ conn->slave.window_widening_event_us += conn->slave.window_widening_prepare_us; conn->slave.window_widening_prepare_us = 0; if (conn->slave.window_widening_event_us > conn->slave.window_widening_max_us) { conn->slave.window_widening_event_us = conn->slave.window_widening_max_us; } /* current window size */ conn->slave.window_size_event_us += conn->slave.window_size_prepare_us; conn->slave.window_size_prepare_us = 0; remainder_us = radio_tmr_start(0, ticks_at_expire + TICKER_US_TO_TICKS(RADIO_TICKER_START_PART_US), _radio.remainder_anchor); radio_tmr_aa_capture(); hcto = remainder_us + RADIO_TICKER_JITTER_US + (RADIO_TICKER_JITTER_US << 2) + (conn->slave.window_widening_event_us << 1) + conn->slave.window_size_event_us; #if defined(CONFIG_BLUETOOTH_CONTROLLER_PHY) hcto += radio_rx_ready_delay_get(conn->phy_rx); hcto += addr_us_get(conn->phy_rx); #else /* !CONFIG_BLUETOOTH_CONTROLLER_PHY */ hcto += radio_rx_ready_delay_get(0); hcto += addr_us_get(0); #endif /* !CONFIG_BLUETOOTH_CONTROLLER_PHY */ radio_tmr_hcto_configure(hcto); radio_tmr_end_capture(); #if defined(CONFIG_BLUETOOTH_CONTROLLER_CONN_RSSI) radio_rssi_measure(); #endif /* CONFIG_BLUETOOTH_CONTROLLER_CONN_RSSI */ #if (defined(CONFIG_BLUETOOTH_CONTROLLER_XTAL_ADVANCED) && \ (RADIO_TICKER_PREEMPT_PART_US <= RADIO_TICKER_PREEMPT_PART_MIN_US)) /* check if preempt to start has changed */ if (preempt_calc(&conn->hdr, (RADIO_TICKER_ID_FIRST_CONNECTION + conn->handle), ticks_at_expire) != 0) { _radio.state = STATE_STOP; radio_disable(); } else #endif /* CONFIG_BLUETOOTH_CONTROLLER_XTAL_ADVANCED */ { /* Ticker Job Silence */ #if (RADIO_TICKER_USER_ID_WORKER_PRIO == RADIO_TICKER_USER_ID_JOB_PRIO) u32_t ticker_status; ticker_status = ticker_job_idle_get(RADIO_TICKER_INSTANCE_ID_RADIO, RADIO_TICKER_USER_ID_WORKER, ticker_job_disable, NULL); LL_ASSERT((ticker_status == TICKER_STATUS_SUCCESS) || (ticker_status == TICKER_STATUS_BUSY)); #endif } /* Route the tx packet to respective connections */ packet_tx_enqueue(2); DEBUG_RADIO_START_S(0); } static void event_master_prepare(u32_t ticks_at_expire, u32_t remainder, u16_t lazy, void *context) { DEBUG_RADIO_PREPARE_M(1); event_connection_prepare(ticks_at_expire, remainder, lazy, context); DEBUG_RADIO_PREPARE_M(0); } static void event_master(u32_t ticks_at_expire, u32_t remainder, u16_t lazy, void *context) { u8_t data_chan_use = 0; struct pdu_data *pdu_data_tx; struct connection *conn; ARG_UNUSED(remainder); ARG_UNUSED(lazy); DEBUG_RADIO_START_M(1); LL_ASSERT(_radio.role == ROLE_NONE); conn = (struct connection *)context; LL_ASSERT(_radio.ticker_id_prepare == (RADIO_TICKER_ID_FIRST_CONNECTION + conn->handle)); _radio.role = ROLE_MASTER; _radio.state = STATE_TX; _radio.ticker_id_prepare = 0; _radio.ticker_id_event = (RADIO_TICKER_ID_FIRST_CONNECTION + conn->handle); _radio.ticks_anchor = ticks_at_expire; _radio.packet_counter = 0; _radio.crc_expire = 0; _radio.conn_curr = conn; conn->latency_event = conn->latency_prepare; conn->latency_prepare = 0; /* Route the tx packet to respective connections */ packet_tx_enqueue(2); /* prepare transmit packet */ prepare_pdu_data_tx(conn, &pdu_data_tx); pdu_data_tx->sn = conn->sn; pdu_data_tx->nesn = conn->nesn; connection_configure(conn); radio_tmr_tifs_set(RADIO_TIFS); #if defined(CONFIG_BLUETOOTH_CONTROLLER_PHY) radio_switch_complete_and_rx(conn->phy_rx); #else /* !CONFIG_BLUETOOTH_CONTROLLER_PHY */ radio_switch_complete_and_rx(0); #endif /* !CONFIG_BLUETOOTH_CONTROLLER_PHY */ tx_packet_set(conn, pdu_data_tx); /* Setup Radio Channel */ if (conn->data_chan_sel) { #if defined(CONFIG_BLUETOOTH_CONTROLLER_CHAN_SEL_2) data_chan_use = chan_sel_2(conn->event_counter - 1, conn->data_chan_id, &conn->data_chan_map[0], conn->data_chan_count); #else /* !CONFIG_BLUETOOTH_CONTROLLER_CHAN_SEL_2 */ LL_ASSERT(0); #endif /* !CONFIG_BLUETOOTH_CONTROLLER_CHAN_SEL_2 */ } else { data_chan_use = chan_sel_1(&conn->data_chan_use, conn->data_chan_hop, conn->latency_event, &conn->data_chan_map[0], conn->data_chan_count); } chan_set(data_chan_use); /* normal connection! */ #if SILENT_CONNECTION if (!conn->empty || pdu_data_tx->md || (conn->supervision_expire && (conn->supervision_expire <= 6)) || (conn->connect_expire && (conn->connect_expire <= 6))) { #endif radio_tmr_start(1, ticks_at_expire + TICKER_US_TO_TICKS(RADIO_TICKER_START_PART_US), _radio.remainder_anchor); radio_tmr_end_capture(); #if SILENT_CONNECTION /* silent connection! */ } else { u32_t remainder_us; u32_t hcto; /* start in RX state */ _radio.state = STATE_RX; _radio.packet_counter = 0xFF; radio_tmr_tifs_set(RADIO_TIFS); #if defined(CONFIG_BLUETOOTH_CONTROLLER_PHY) radio_switch_complete_and_tx(conn->phy_rx, 0, conn->phy_tx, conn->phy_flags); #else /* !CONFIG_BLUETOOTH_CONTROLLER_PHY */ radio_switch_complete_and_tx(0, 0, 0, 0); #endif /* !CONFIG_BLUETOOTH_CONTROLLER_PHY */ rx_packet_set(conn, (struct pdu_data *)_radio. packet_rx[_radio.packet_rx_last]->pdu_data); /* setup pkticker and hcto */ remainder_us = radio_tmr_start(0, ticks_at_expire + TICKER_US_TO_TICKS(RADIO_TICKER_START_PART_US), _radio.remainder_anchor); radio_tmr_aa_capture(); hcto = remainder_us + RADIO_TIFS; #if defined(CONFIG_BLUETOOTH_CONTROLLER_PHY) hcto += radio_tx_ready_delay_get(conn->phy_tx, conn->phy_flags); hcto += empty_pkt_us_get(conn->phy_rx); hcto += addr_us_get(conn->phy_rx); #else /* !CONFIG_BLUETOOTH_CONTROLLER_PHY */ hcto += radio_tx_ready_delay_get(0, 0); hcto += empty_pkt_us_get(0); hcto += addr_us_get(0); #endif /* !CONFIG_BLUETOOTH_CONTROLLER_PHY */ /* TODO: account for slave window widening */ hcto += 256; radio_tmr_hcto_configure(hcto); } #endif #if (defined(CONFIG_BLUETOOTH_CONTROLLER_XTAL_ADVANCED) && \ (RADIO_TICKER_PREEMPT_PART_US <= RADIO_TICKER_PREEMPT_PART_MIN_US)) /* check if preempt to start has changed */ if (0 != preempt_calc(&conn->hdr, (RADIO_TICKER_ID_FIRST_CONNECTION + conn->handle), ticks_at_expire)) { _radio.state = STATE_STOP; radio_disable(); } else #endif /* CONFIG_BLUETOOTH_CONTROLLER_XTAL_ADVANCED */ { /* Ticker Job Silence */ #if (RADIO_TICKER_USER_ID_WORKER_PRIO == RADIO_TICKER_USER_ID_JOB_PRIO) u32_t ticker_status; ticker_status = ticker_job_idle_get(RADIO_TICKER_INSTANCE_ID_RADIO, RADIO_TICKER_USER_ID_WORKER, ticker_job_disable, NULL); LL_ASSERT((ticker_status == TICKER_STATUS_SUCCESS) || (ticker_status == TICKER_STATUS_BUSY)); #endif } DEBUG_RADIO_START_M(0); } static void rx_packet_set(struct connection *conn, struct pdu_data *pdu_data_rx) { u16_t max_rx_octets; u8_t phy; #if defined(CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH) max_rx_octets = conn->max_rx_octets; #else /* !CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH */ max_rx_octets = RADIO_LL_LENGTH_OCTETS_RX_MIN; #endif /* !CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH */ #if defined(CONFIG_BLUETOOTH_CONTROLLER_PHY) phy = conn->phy_rx; #else /* !CONFIG_BLUETOOTH_CONTROLLER_PHY */ phy = 0; #endif /* !CONFIG_BLUETOOTH_CONTROLLER_PHY */ radio_phy_set(phy, 0); if (conn->enc_rx) { radio_pkt_configure(8, (max_rx_octets + 4), (phy << 1) | 0x01); radio_pkt_rx_set(radio_ccm_rx_pkt_set(&conn->ccm_rx, pdu_data_rx)); } else { radio_pkt_configure(8, max_rx_octets, (phy << 1) | 0x01); radio_pkt_rx_set(pdu_data_rx); } } static void tx_packet_set(struct connection *conn, struct pdu_data *pdu_data_tx) { u16_t max_tx_octets; u8_t phy, flags; #if defined(CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH) max_tx_octets = conn->max_tx_octets; #else /* !CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH */ max_tx_octets = RADIO_LL_LENGTH_OCTETS_RX_MIN; #endif /* !CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH */ #if defined(CONFIG_BLUETOOTH_CONTROLLER_PHY) phy = conn->phy_tx; flags = conn->phy_flags; #else /* !CONFIG_BLUETOOTH_CONTROLLER_PHY */ phy = 0; flags = 0; #endif /* !CONFIG_BLUETOOTH_CONTROLLER_PHY */ radio_phy_set(phy, flags); if (conn->enc_tx) { radio_pkt_configure(8, (max_tx_octets + 4), (phy << 1) | 0x01); radio_pkt_tx_set(radio_ccm_tx_pkt_set(&conn->ccm_tx, pdu_data_tx)); } else { radio_pkt_configure(8, max_tx_octets, (phy << 1) | 0x01); radio_pkt_tx_set(pdu_data_tx); } } static void prepare_pdu_data_tx(struct connection *conn, struct pdu_data **pdu_data_tx) { struct pdu_data *_pdu_data_tx; if (/* empty packet */ conn->empty || /* no ctrl or data packet */ !conn->pkt_tx_head || /* data tx paused, only control packets allowed */ (conn->pause_tx && (conn->pkt_tx_head != conn->pkt_tx_ctrl))) { _pdu_data_tx = empty_tx_enqueue(conn); } else { u16_t max_tx_octets; _pdu_data_tx = (void *)(conn->pkt_tx_head->pdu_data + conn->packet_tx_head_offset); if (!conn->packet_tx_head_len) { conn->packet_tx_head_len = _pdu_data_tx->len; } if (conn->packet_tx_head_offset) { _pdu_data_tx->ll_id = PDU_DATA_LLID_DATA_CONTINUE; } _pdu_data_tx->len = conn->packet_tx_head_len - conn->packet_tx_head_offset; _pdu_data_tx->md = 0; #if defined(CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH) max_tx_octets = conn->max_tx_octets; #else /* !CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH */ max_tx_octets = RADIO_LL_LENGTH_OCTETS_RX_MIN; #endif /* !CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH */ if (_pdu_data_tx->len > max_tx_octets) { _pdu_data_tx->len = max_tx_octets; _pdu_data_tx->md = 1; } if (conn->pkt_tx_head->next) { _pdu_data_tx->md = 1; } if (!conn->pkt_tx_ctrl && (conn->pkt_tx_head != conn->pkt_tx_data)) { conn->pkt_tx_ctrl = conn->pkt_tx_ctrl_last = conn->pkt_tx_head; } } _pdu_data_tx->rfu = 0; #if !defined(CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH_CLEAR) _pdu_data_tx->resv = 0; #endif /* !CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH_CLEAR */ *pdu_data_tx = _pdu_data_tx; } static void packet_rx_allocate(u8_t max) { u8_t acquire; if (max > _radio.link_rx_data_quota) { max = _radio.link_rx_data_quota; } acquire = _radio.packet_rx_acquire + 1; if (acquire == _radio.packet_rx_count) { acquire = 0; } while ((max--) && (acquire != _radio.packet_rx_last)) { void *link; struct radio_pdu_node_rx *radio_pdu_node_rx; link = mem_acquire(&_radio.link_rx_free); if (!link) { break; } radio_pdu_node_rx = mem_acquire(&_radio.pkt_rx_data_free); if (!radio_pdu_node_rx) { mem_release(link, &_radio.link_rx_free); break; } radio_pdu_node_rx->hdr.onion.link = link; _radio.packet_rx[_radio.packet_rx_acquire] = radio_pdu_node_rx; _radio.packet_rx_acquire = acquire; acquire = _radio.packet_rx_acquire + 1; if (acquire == _radio.packet_rx_count) { acquire = 0; } _radio.link_rx_data_quota--; } } static u8_t packet_rx_acquired_count_get(void) { if (_radio.packet_rx_acquire >= _radio.packet_rx_last) { return (_radio.packet_rx_acquire - _radio.packet_rx_last); } else { return (_radio.packet_rx_count - _radio.packet_rx_last + _radio.packet_rx_acquire); } } static struct radio_pdu_node_rx *packet_rx_reserve_get(u8_t count) { struct radio_pdu_node_rx *radio_pdu_node_rx; if (count > packet_rx_acquired_count_get()) { return 0; } radio_pdu_node_rx = _radio.packet_rx[_radio.packet_rx_last]; radio_pdu_node_rx->hdr.type = NODE_RX_TYPE_DC_PDU; return radio_pdu_node_rx; } static void packet_rx_callback(void) { /* Inline call of callback. If JOB configured as lower priority then * callback will tailchain at end of every radio ISR. If JOB configured * as same then call inline so as to have callback for every radio ISR. */ #if (RADIO_TICKER_USER_ID_WORKER_PRIO == RADIO_TICKER_USER_ID_JOB_PRIO) radio_event_callback(); #else static void *s_link[2]; static struct mayfly s_mfy_callback = {0, 0, s_link, NULL, (void *)radio_event_callback}; mayfly_enqueue(RADIO_TICKER_USER_ID_WORKER, RADIO_TICKER_USER_ID_JOB, 1, &s_mfy_callback); #endif } static void packet_rx_enqueue(void) { void *link; struct radio_pdu_node_rx *radio_pdu_node_rx; u8_t last; LL_ASSERT(_radio.packet_rx_last != _radio.packet_rx_acquire); /* Remember the rx node and acquired link mem */ radio_pdu_node_rx = _radio.packet_rx[_radio.packet_rx_last]; link = radio_pdu_node_rx->hdr.onion.link; /* serialize release queue with rx queue by storing reference to last * element in release queue */ radio_pdu_node_rx->hdr.onion.packet_release_last = _radio.packet_release_last; /* dequeue from acquired rx queue */ last = _radio.packet_rx_last + 1; if (last == _radio.packet_rx_count) { last = 0; } _radio.packet_rx_last = last; /* Enqueue into event-cum-data queue */ link = memq_enqueue(radio_pdu_node_rx, link, (void *)&_radio.link_rx_tail); LL_ASSERT(link); /* callback to trigger application action */ packet_rx_callback(); } static void packet_tx_enqueue(u8_t max) { while ((max--) && (_radio.packet_tx_first != _radio.packet_tx_last)) { struct pdu_data_q_tx *pdu_data_q_tx; struct radio_pdu_node_tx *node_tx_new; struct connection *conn; u8_t first; pdu_data_q_tx = &_radio.pkt_tx[_radio.packet_tx_first]; node_tx_new = pdu_data_q_tx->node_tx; node_tx_new->next = NULL; conn = mem_get(_radio.conn_pool, CONNECTION_T_SIZE, pdu_data_q_tx->handle); if (conn->handle == pdu_data_q_tx->handle) { if (conn->pkt_tx_data == 0) { conn->pkt_tx_data = node_tx_new; if (conn->pkt_tx_head == 0) { conn->pkt_tx_head = node_tx_new; conn->pkt_tx_last = NULL; } } if (conn->pkt_tx_last) { conn->pkt_tx_last->next = node_tx_new; } conn->pkt_tx_last = node_tx_new; } else { struct pdu_data *pdu_data_tx; pdu_data_tx = (struct pdu_data *)node_tx_new->pdu_data; /* By setting it resv, when app gets num cmplt, no * num cmplt is counted, but the buffer is released */ pdu_data_tx->ll_id = PDU_DATA_LLID_RESV; pdu_node_tx_release(pdu_data_q_tx->handle, node_tx_new); } first = _radio.packet_tx_first + 1; if (first == _radio.packet_tx_count) { first = 0; } _radio.packet_tx_first = first; } } static struct pdu_data *empty_tx_enqueue(struct connection *conn) { struct pdu_data *pdu_data_tx; conn->empty = 1; pdu_data_tx = (struct pdu_data *)radio_pkt_empty_get(); pdu_data_tx->ll_id = PDU_DATA_LLID_DATA_CONTINUE; pdu_data_tx->len = 0; if (conn->pkt_tx_head) { pdu_data_tx->md = 1; } else { pdu_data_tx->md = 0; } return pdu_data_tx; } static void ctrl_tx_last_enqueue(struct connection *conn, struct radio_pdu_node_tx *node_tx) { node_tx->next = conn->pkt_tx_ctrl_last->next; conn->pkt_tx_ctrl_last->next = node_tx; conn->pkt_tx_ctrl_last = node_tx; } static void ctrl_tx_enqueue(struct connection *conn, struct radio_pdu_node_tx *node_tx) { /* check if a packet was tx-ed and not acked by peer */ if ( /* An explicit empty PDU is not enqueued */ !conn->empty && /* and data/ctrl packet is in the head */ conn->pkt_tx_head && /* data PDU tx is not paused */ !conn->pause_tx) { /* data or ctrl may have been transmitted once, but not acked * by peer, hence place this new ctrl after head */ /* if data transmited once, keep it at head of the tx list, * as we will insert a ctrl after it, hence advance the * data pointer */ if (conn->pkt_tx_head == conn->pkt_tx_data) { conn->pkt_tx_data = conn->pkt_tx_data->next; } /* if no ctrl packet already queued, new ctrl added will be * the ctrl pointer and is inserted after head. */ if (!conn->pkt_tx_ctrl) { node_tx->next = conn->pkt_tx_head->next; conn->pkt_tx_head->next = node_tx; conn->pkt_tx_ctrl = node_tx; conn->pkt_tx_ctrl_last = node_tx; } else { ctrl_tx_last_enqueue(conn, node_tx); } } else { /* No packet needing ACK. */ /* If first ctrl packet then add it as head else add it to the * tail of the ctrl packets. */ if (!conn->pkt_tx_ctrl) { node_tx->next = conn->pkt_tx_head; conn->pkt_tx_head = node_tx; conn->pkt_tx_ctrl = node_tx; conn->pkt_tx_ctrl_last = node_tx; } else { ctrl_tx_last_enqueue(conn, node_tx); } } /* Update last pointer if ctrl added at end of tx list */ if (node_tx->next == 0) { conn->pkt_tx_last = node_tx; } } static void ctrl_tx_sec_enqueue(struct connection *conn, struct radio_pdu_node_tx *node_tx) { if (conn->pause_tx) { if (!conn->pkt_tx_ctrl) { node_tx->next = conn->pkt_tx_head; conn->pkt_tx_head = node_tx; } else { node_tx->next = conn->pkt_tx_ctrl_last->next; conn->pkt_tx_ctrl_last->next = node_tx; } } else { ctrl_tx_enqueue(conn, node_tx); } } static void pdu_node_tx_release(u16_t handle, struct radio_pdu_node_tx *node_tx) { u8_t last; last = _radio.packet_release_last + 1; if (last == _radio.packet_tx_count) { last = 0; } LL_ASSERT(last != _radio.packet_release_first); /* Enqueue app mem for release */ _radio.pkt_release[_radio.packet_release_last].handle = handle; _radio.pkt_release[_radio.packet_release_last].node_tx = node_tx; _radio.packet_release_last = last; /* callback to trigger application action */ packet_rx_callback(); } static void connection_release(struct connection *conn) { u32_t ticker_status; /* Enable Ticker Job, we are in a radio event which disabled it if * worker0 and job0 priority where same. */ mayfly_enable(RADIO_TICKER_USER_ID_WORKER, RADIO_TICKER_USER_ID_JOB, 1); /** @todo correctly stop tickers ensuring crystal and radio active are * placed in right states */ /* Stop Master/Slave role ticker */ ticker_status = ticker_stop(RADIO_TICKER_INSTANCE_ID_RADIO, RADIO_TICKER_USER_ID_WORKER, (RADIO_TICKER_ID_FIRST_CONNECTION + conn->handle), ticker_success_assert, (void *)__LINE__); LL_ASSERT((ticker_status == TICKER_STATUS_SUCCESS) || (ticker_status == TICKER_STATUS_BUSY)); /* Stop Marker 0 and event single-shot tickers */ if ((_radio.state == STATE_ABORT) && (_radio.ticker_id_prepare == (RADIO_TICKER_ID_FIRST_CONNECTION + conn->handle))) { /* Reset the stored ticker id in prepare phase that * initiated the connection event abort (pre-emption). */ _radio.ticker_id_prepare = 0; ticker_status = ticker_stop(RADIO_TICKER_INSTANCE_ID_RADIO, RADIO_TICKER_USER_ID_WORKER, RADIO_TICKER_ID_MARKER_0, ticker_success_assert, (void *)__LINE__); LL_ASSERT((ticker_status == TICKER_STATUS_SUCCESS) || (ticker_status == TICKER_STATUS_BUSY)); ticker_status = ticker_stop(RADIO_TICKER_INSTANCE_ID_RADIO, RADIO_TICKER_USER_ID_WORKER, RADIO_TICKER_ID_EVENT, ticker_success_assert, (void *)__LINE__); LL_ASSERT((ticker_status == TICKER_STATUS_SUCCESS) || (ticker_status == TICKER_STATUS_BUSY)); } /* flush and release, data packet before ctrl */ while ((conn->pkt_tx_head != conn->pkt_tx_ctrl) && (conn->pkt_tx_head != conn->pkt_tx_data)) { struct radio_pdu_node_tx *node_tx; struct pdu_data *pdu_data_tx; /* By setting it resv, when app gets num cmplt, no num cmplt * is counted, but the buffer is released */ node_tx = conn->pkt_tx_head; pdu_data_tx = (struct pdu_data *)node_tx->pdu_data; pdu_data_tx->ll_id = PDU_DATA_LLID_RESV; conn->pkt_tx_head = conn->pkt_tx_head->next; pdu_node_tx_release(conn->handle, node_tx); } /* flush and release, ctrl packet before data */ while ((conn->pkt_tx_head) && (conn->pkt_tx_head != conn->pkt_tx_data)) { void *release; release = conn->pkt_tx_head; conn->pkt_tx_head = conn->pkt_tx_head->next; conn->pkt_tx_ctrl = conn->pkt_tx_head; mem_release(release, &_radio.pkt_tx_ctrl_free); } conn->pkt_tx_ctrl = NULL; conn->pkt_tx_ctrl_last = NULL; /* flush and release, rest of data */ while (conn->pkt_tx_head) { struct radio_pdu_node_tx *node_tx; struct pdu_data *pdu_data_tx; /* By setting it resv, when app gets num cmplt, no num cmplt * is counted, but the buffer is released */ node_tx = conn->pkt_tx_head; pdu_data_tx = (struct pdu_data *)node_tx->pdu_data; pdu_data_tx->ll_id = PDU_DATA_LLID_RESV; conn->pkt_tx_head = conn->pkt_tx_head->next; conn->pkt_tx_data = conn->pkt_tx_head; pdu_node_tx_release(conn->handle, node_tx); } conn->handle = 0xffff; /* reset mutex */ if (_radio.conn_upd == conn) { _radio.conn_upd = NULL; } } static void terminate_ind_rx_enqueue(struct connection *conn, u8_t reason) { struct radio_pdu_node_rx *radio_pdu_node_rx; void *link; /* Prepare the rx packet structure */ radio_pdu_node_rx = (void *)&conn->llcp_terminate.radio_pdu_node_rx; LL_ASSERT(radio_pdu_node_rx->hdr.onion.link); radio_pdu_node_rx->hdr.handle = conn->handle; radio_pdu_node_rx->hdr.type = NODE_RX_TYPE_TERMINATE; *((u8_t *)radio_pdu_node_rx->pdu_data) = reason; /* Get the link mem reserved in the connection context */ link = radio_pdu_node_rx->hdr.onion.link; /* Serialize release queue with rx queue by storing reference to * last element in release queue */ radio_pdu_node_rx->hdr.onion.packet_release_last = _radio.packet_release_last; /* Enqueue into event-cum-data queue */ link = memq_enqueue(radio_pdu_node_rx, link, (void *)&_radio.link_rx_tail); LL_ASSERT(link); /* callback to trigger application action */ packet_rx_callback(); } static u32_t conn_update(struct connection *conn, struct pdu_data *pdu_data_rx) { if (((pdu_data_rx->payload.llctrl.ctrldata.conn_update_ind.instant - conn->event_counter) & 0xFFFF) > 0x7FFF) { return 1; } LL_ASSERT((conn->llcp_req == conn->llcp_ack) || ((conn->llcp_type == LLCP_CONNECTION_UPDATE) && (conn->llcp.connection_update.state == LLCP_CONN_STATE_RSP_WAIT))); /* set mutex, if only not already set. As a master the mutex shall * be set, but a slave we accept it as new 'set' of mutex. */ if (_radio.conn_upd == 0) { LL_ASSERT(conn->role); _radio.conn_upd = conn; } conn->llcp.connection_update.win_size = pdu_data_rx->payload.llctrl.ctrldata.conn_update_ind.win_size; conn->llcp.connection_update.win_offset_us = pdu_data_rx->payload.llctrl.ctrldata.conn_update_ind.win_offset * 1250; conn->llcp.connection_update.interval = pdu_data_rx->payload.llctrl.ctrldata.conn_update_ind.interval; conn->llcp.connection_update.latency = pdu_data_rx->payload.llctrl.ctrldata.conn_update_ind.latency; conn->llcp.connection_update.timeout = pdu_data_rx->payload.llctrl.ctrldata.conn_update_ind.timeout; conn->llcp.connection_update.instant = pdu_data_rx->payload.llctrl.ctrldata.conn_update_ind.instant; conn->llcp.connection_update.state = LLCP_CONN_STATE_INPROG; conn->llcp.connection_update.is_internal = 0; conn->llcp_type = LLCP_CONNECTION_UPDATE; conn->llcp_ack--; return 0; } static u32_t is_peer_compatible(struct connection *conn) { return ((conn->llcp_version.rx) && (conn->llcp_version.version_number >= RADIO_BLE_VERSION_NUMBER) && (conn->llcp_version.company_id == RADIO_BLE_COMPANY_ID) && (conn->llcp_version.sub_version_number >= RADIO_BLE_SUB_VERSION_NUMBER)); } static u32_t conn_update_req(struct connection *conn) { if (conn->llcp_req != conn->llcp_ack) { return 1; } if (!conn->role || (is_peer_compatible(conn))) { /** Perform slave intiated conn param req */ conn->llcp.connection_update.win_size = 1; conn->llcp.connection_update.win_offset_us = 0; conn->llcp.connection_update.interval = conn->conn_interval; conn->llcp.connection_update.latency = conn->latency; conn->llcp.connection_update.timeout = conn->conn_interval * conn->supervision_reload * 125 / 1000; /* conn->llcp.connection_update.instant = 0; */ conn->llcp.connection_update.state = !conn->role ? LLCP_CONN_STATE_INITIATE : LLCP_CONN_STATE_REQ; conn->llcp.connection_update.is_internal = 1; conn->llcp_type = LLCP_CONNECTION_UPDATE; conn->llcp_ack--; return 0; } return 2; } static u32_t chan_map_update(struct connection *conn, struct pdu_data *pdu_data_rx) { if (((pdu_data_rx->payload.llctrl.ctrldata.chan_map_ind.instant - conn->event_counter) & 0xffff) > 0x7fff) { return 1; } LL_ASSERT(conn->llcp_req == conn->llcp_ack); memcpy(&conn->llcp.chan_map.chm[0], &pdu_data_rx->payload.llctrl.ctrldata.chan_map_ind.chm[0], sizeof(conn->llcp.chan_map.chm)); conn->llcp.chan_map.instant = pdu_data_rx->payload.llctrl.ctrldata.chan_map_ind.instant; conn->llcp.chan_map.initiate = 0; conn->llcp_type = LLCP_CHAN_MAP; conn->llcp_ack--; return 0; } #if defined(CONFIG_BLUETOOTH_CONTROLLER_PHY) static inline u32_t phy_upd_ind(struct radio_pdu_node_rx *radio_pdu_node_rx, u8_t *rx_enqueue) { struct connection *conn = _radio.conn_curr; struct pdu_data_llctrl_phy_upd_ind *p; struct pdu_data *pdu_data_rx; pdu_data_rx = (struct pdu_data *)radio_pdu_node_rx->pdu_data; p = &pdu_data_rx->payload.llctrl.ctrldata.phy_upd_ind; /* Both tx and rx PHY unchanged */ if (!((p->m_to_s_phy | p->s_to_m_phy) & 0x07)) { struct radio_le_phy_upd_cmplt *p; /* Ignore event generation if not local cmd initiated */ if ((conn->llcp_phy.ack == conn->llcp_phy.req) || (conn->llcp_phy.state != LLCP_PHY_STATE_RSP_WAIT) || (!conn->llcp_phy.cmd)) { return 0; } /* Procedure complete */ conn->llcp_phy.ack = conn->llcp_phy.req; conn->procedure_expire = 0; /* generate phy update complete event */ radio_pdu_node_rx->hdr.type = NODE_RX_TYPE_PHY_UPDATE; p = (struct radio_le_phy_upd_cmplt *)&pdu_data_rx->payload; p->status = 0; p->tx = conn->phy_tx; p->rx = conn->phy_rx; /* enqueue the phy update complete */ *rx_enqueue = 1; return 0; } /* instant passed */ if (((p->instant - conn->event_counter) & 0xffff) > 0x7fff) { return 1; } LL_ASSERT(conn->llcp_req == conn->llcp_ack); if ((conn->llcp_phy.ack != conn->llcp_phy.req) && (conn->llcp_phy.state == LLCP_PHY_STATE_RSP_WAIT)) { conn->llcp_phy.ack = conn->llcp_phy.req; conn->llcp.phy_upd_ind.cmd = conn->llcp_phy.cmd; /* Procedure complete, just wait for instant */ conn->procedure_expire = 0; } conn->llcp.phy_upd_ind.tx = p->s_to_m_phy; conn->llcp.phy_upd_ind.rx = p->m_to_s_phy; conn->llcp.phy_upd_ind.instant = p->instant; conn->llcp.phy_upd_ind.initiate = 0; conn->llcp_type = LLCP_PHY_UPD; conn->llcp_ack--; return 0; } #endif /* CONFIG_BLUETOOTH_CONTROLLER_PHY */ #if defined(CONFIG_BLUETOOTH_CONTROLLER_LE_ENC) static void enc_req_reused_send(struct connection *conn, struct radio_pdu_node_tx *node_tx) { struct pdu_data *pdu_ctrl_tx; pdu_ctrl_tx = (struct pdu_data *)node_tx->pdu_data; pdu_ctrl_tx->ll_id = PDU_DATA_LLID_CTRL; pdu_ctrl_tx->len = offsetof(struct pdu_data_llctrl, ctrldata) + sizeof(struct pdu_data_llctrl_enc_req); pdu_ctrl_tx->payload.llctrl.opcode = PDU_DATA_LLCTRL_TYPE_ENC_REQ; memcpy(&pdu_ctrl_tx->payload.llctrl.ctrldata.enc_req.rand[0], &conn->llcp.encryption.rand[0], sizeof(pdu_ctrl_tx->payload.llctrl.ctrldata.enc_req.rand)); pdu_ctrl_tx->payload.llctrl.ctrldata.enc_req.ediv[0] = conn->llcp.encryption.ediv[0]; pdu_ctrl_tx->payload.llctrl.ctrldata.enc_req.ediv[1] = conn->llcp.encryption.ediv[1]; /** @todo */ memset(&pdu_ctrl_tx->payload.llctrl.ctrldata.enc_req.skdm[0], 0xcc, sizeof(pdu_ctrl_tx->payload.llctrl.ctrldata.enc_req.skdm)); /** @todo */ memset(&pdu_ctrl_tx->payload.llctrl.ctrldata.enc_req.ivm[0], 0xdd, sizeof(pdu_ctrl_tx->payload.llctrl.ctrldata.enc_req.ivm)); } static void enc_rsp_send(struct connection *conn) { struct radio_pdu_node_tx *node_tx; struct pdu_data *pdu_ctrl_tx; /* acquire tx mem */ node_tx = mem_acquire(&_radio.pkt_tx_ctrl_free); LL_ASSERT(node_tx); pdu_ctrl_tx = (struct pdu_data *)node_tx->pdu_data; pdu_ctrl_tx->ll_id = PDU_DATA_LLID_CTRL; pdu_ctrl_tx->len = offsetof(struct pdu_data_llctrl, ctrldata) + sizeof(struct pdu_data_llctrl_enc_rsp); pdu_ctrl_tx->payload.llctrl.opcode = PDU_DATA_LLCTRL_TYPE_ENC_RSP; /** @todo */ memset(&pdu_ctrl_tx->payload.llctrl.ctrldata.enc_rsp.skds[0], 0xaa, sizeof(pdu_ctrl_tx->payload.llctrl.ctrldata.enc_rsp.skds)); /** @todo */ memset(&pdu_ctrl_tx->payload.llctrl.ctrldata.enc_rsp.ivs[0], 0xbb, sizeof(pdu_ctrl_tx->payload.llctrl.ctrldata.enc_rsp.ivs)); /* things from slave stored for session key calculation */ memcpy(&conn->llcp.encryption.skd[8], &pdu_ctrl_tx->payload.llctrl.ctrldata.enc_rsp.skds[0], 8); memcpy(&conn->ccm_rx.iv[4], &pdu_ctrl_tx->payload.llctrl.ctrldata.enc_rsp.ivs[0], 4); ctrl_tx_enqueue(conn, node_tx); } static void start_enc_rsp_send(struct connection *conn, struct pdu_data *pdu_ctrl_tx) { struct radio_pdu_node_tx *node_tx = NULL; if (!pdu_ctrl_tx) { /* acquire tx mem */ node_tx = mem_acquire(&_radio.pkt_tx_ctrl_free); LL_ASSERT(node_tx); pdu_ctrl_tx = (struct pdu_data *)node_tx->pdu_data; } pdu_ctrl_tx->ll_id = PDU_DATA_LLID_CTRL; pdu_ctrl_tx->len = offsetof(struct pdu_data_llctrl, ctrldata); pdu_ctrl_tx->payload.llctrl.opcode = PDU_DATA_LLCTRL_TYPE_START_ENC_RSP; if (node_tx) { ctrl_tx_enqueue(conn, node_tx); } } static void pause_enc_rsp_send(struct connection *conn) { struct radio_pdu_node_tx *node_tx; struct pdu_data *pdu_ctrl_tx; /* acquire tx mem */ node_tx = mem_acquire(&_radio.pkt_tx_ctrl_free); LL_ASSERT(node_tx); pdu_ctrl_tx = (struct pdu_data *)node_tx->pdu_data; pdu_ctrl_tx->ll_id = PDU_DATA_LLID_CTRL; pdu_ctrl_tx->len = offsetof(struct pdu_data_llctrl, ctrldata); pdu_ctrl_tx->payload.llctrl.opcode = PDU_DATA_LLCTRL_TYPE_PAUSE_ENC_RSP; ctrl_tx_enqueue(conn, node_tx); } #endif /* CONFIG_BLUETOOTH_CONTROLLER_LE_ENC */ static void unknown_rsp_send(struct connection *conn, u8_t type) { struct radio_pdu_node_tx *node_tx; struct pdu_data *pdu_ctrl_tx; /* acquire tx mem */ node_tx = mem_acquire(&_radio.pkt_tx_ctrl_free); LL_ASSERT(node_tx); pdu_ctrl_tx = (struct pdu_data *)node_tx->pdu_data; pdu_ctrl_tx->ll_id = PDU_DATA_LLID_CTRL; pdu_ctrl_tx->len = offsetof(struct pdu_data_llctrl, ctrldata) + sizeof(struct pdu_data_llctrl_unknown_rsp); pdu_ctrl_tx->payload.llctrl.opcode = PDU_DATA_LLCTRL_TYPE_UNKNOWN_RSP; pdu_ctrl_tx->payload.llctrl.ctrldata.unknown_rsp.type = type; ctrl_tx_enqueue(conn, node_tx); } static void feature_rsp_send(struct connection *conn) { struct radio_pdu_node_tx *node_tx; struct pdu_data *pdu_ctrl_tx; /* acquire tx mem */ node_tx = mem_acquire(&_radio.pkt_tx_ctrl_free); LL_ASSERT(node_tx); pdu_ctrl_tx = (struct pdu_data *)node_tx->pdu_data; pdu_ctrl_tx->ll_id = PDU_DATA_LLID_CTRL; pdu_ctrl_tx->len = offsetof(struct pdu_data_llctrl, ctrldata) + sizeof(struct pdu_data_llctrl_feature_rsp); pdu_ctrl_tx->payload.llctrl.opcode = PDU_DATA_LLCTRL_TYPE_FEATURE_RSP; memset(&pdu_ctrl_tx->payload.llctrl.ctrldata.feature_rsp.features[0], 0x00, sizeof(pdu_ctrl_tx->payload.llctrl.ctrldata.feature_rsp.features)); pdu_ctrl_tx->payload.llctrl.ctrldata.feature_req.features[0] = conn->llcp_features & 0xFF; pdu_ctrl_tx->payload.llctrl.ctrldata.feature_req.features[1] = (conn->llcp_features >> 8) & 0xFF; pdu_ctrl_tx->payload.llctrl.ctrldata.feature_req.features[2] = (conn->llcp_features >> 16) & 0xFF; ctrl_tx_sec_enqueue(conn, node_tx); } static void version_ind_send(struct connection *conn) { struct radio_pdu_node_tx *node_tx; struct pdu_data *pdu_ctrl_tx; /* acquire tx mem */ node_tx = mem_acquire(&_radio.pkt_tx_ctrl_free); LL_ASSERT(node_tx); pdu_ctrl_tx = (struct pdu_data *)node_tx->pdu_data; pdu_ctrl_tx->ll_id = PDU_DATA_LLID_CTRL; pdu_ctrl_tx->len = offsetof(struct pdu_data_llctrl, ctrldata) + sizeof(struct pdu_data_llctrl_version_ind); pdu_ctrl_tx->payload.llctrl.opcode = PDU_DATA_LLCTRL_TYPE_VERSION_IND; pdu_ctrl_tx->payload.llctrl.ctrldata.version_ind.version_number = RADIO_BLE_VERSION_NUMBER; pdu_ctrl_tx->payload.llctrl.ctrldata.version_ind.company_id = RADIO_BLE_COMPANY_ID; pdu_ctrl_tx->payload.llctrl.ctrldata.version_ind.sub_version_number = RADIO_BLE_SUB_VERSION_NUMBER; ctrl_tx_sec_enqueue(conn, node_tx); /* Apple work-around, add empty packet before version_ind */ empty_tx_enqueue(conn); } #if defined(CONFIG_BLUETOOTH_CONTROLLER_LE_PING) static void ping_resp_send(struct connection *conn) { struct radio_pdu_node_tx *node_tx; struct pdu_data *pdu_ctrl_tx; /* acquire tx mem */ node_tx = mem_acquire(&_radio.pkt_tx_ctrl_free); LL_ASSERT(node_tx); pdu_ctrl_tx = (struct pdu_data *)node_tx->pdu_data; pdu_ctrl_tx->ll_id = PDU_DATA_LLID_CTRL; pdu_ctrl_tx->len = offsetof(struct pdu_data_llctrl, ctrldata); pdu_ctrl_tx->payload.llctrl.opcode = PDU_DATA_LLCTRL_TYPE_PING_RSP; ctrl_tx_enqueue(conn, node_tx); } #endif /* CONFIG_BLUETOOTH_CONTROLLER_LE_PING */ static void reject_ind_ext_send(struct connection *conn, u8_t reject_opcode, u8_t error_code) { struct radio_pdu_node_tx *node_tx; struct pdu_data *pdu_ctrl_tx; /* acquire tx mem */ node_tx = mem_acquire(&_radio.pkt_tx_ctrl_free); LL_ASSERT(node_tx); pdu_ctrl_tx = (struct pdu_data *)node_tx->pdu_data; pdu_ctrl_tx->ll_id = PDU_DATA_LLID_CTRL; pdu_ctrl_tx->len = offsetof(struct pdu_data_llctrl, ctrldata) + sizeof(struct pdu_data_llctrl_reject_ext_ind); pdu_ctrl_tx->payload.llctrl.opcode = PDU_DATA_LLCTRL_TYPE_REJECT_EXT_IND; pdu_ctrl_tx->payload.llctrl.ctrldata.reject_ext_ind.reject_opcode = reject_opcode; pdu_ctrl_tx->payload.llctrl.ctrldata.reject_ext_ind.error_code = error_code; ctrl_tx_enqueue(conn, node_tx); } #if defined(CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH) static void length_resp_send(struct connection *conn, u16_t eff_rx_octets, u16_t eff_tx_octets) { struct radio_pdu_node_tx *node_tx; struct pdu_data *pdu_ctrl_tx; node_tx = mem_acquire(&_radio.pkt_tx_ctrl_free); LL_ASSERT(node_tx); pdu_ctrl_tx = (struct pdu_data *) node_tx->pdu_data; pdu_ctrl_tx->ll_id = PDU_DATA_LLID_CTRL; pdu_ctrl_tx->len = offsetof(struct pdu_data_llctrl, ctrldata) + sizeof(struct pdu_data_llctrl_length_req_rsp); pdu_ctrl_tx->payload.llctrl.opcode = PDU_DATA_LLCTRL_TYPE_LENGTH_RSP; pdu_ctrl_tx->payload.llctrl.ctrldata.length_rsp.max_rx_octets = eff_rx_octets; pdu_ctrl_tx->payload.llctrl.ctrldata.length_rsp.max_rx_time = ((eff_rx_octets + 14) << 3); pdu_ctrl_tx->payload.llctrl.ctrldata.length_rsp.max_tx_octets = eff_tx_octets; pdu_ctrl_tx->payload.llctrl.ctrldata.length_rsp.max_tx_time = ((eff_tx_octets + 14) << 3); ctrl_tx_enqueue(conn, node_tx); } #endif /* CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH */ #if defined(CONFIG_BLUETOOTH_CONTROLLER_PHY) static void phy_rsp_send(struct connection *conn) { struct radio_pdu_node_tx *node_tx; struct pdu_data *pdu_ctrl_tx; /* acquire tx mem */ node_tx = mem_acquire(&_radio.pkt_tx_ctrl_free); LL_ASSERT(node_tx); pdu_ctrl_tx = (struct pdu_data *)node_tx->pdu_data; pdu_ctrl_tx->ll_id = PDU_DATA_LLID_CTRL; pdu_ctrl_tx->len = offsetof(struct pdu_data_llctrl, ctrldata) + sizeof(struct pdu_data_llctrl_phy_req_rsp); pdu_ctrl_tx->payload.llctrl.opcode = PDU_DATA_LLCTRL_TYPE_PHY_RSP; pdu_ctrl_tx->payload.llctrl.ctrldata.phy_rsp.tx_phys = conn->phy_pref_tx; pdu_ctrl_tx->payload.llctrl.ctrldata.phy_rsp.rx_phys = conn->phy_pref_rx; ctrl_tx_enqueue(conn, node_tx); } #endif /* CONFIG_BLUETOOTH_CONTROLLER_PHY */ void ll_radio_state_abort(void) { event_stop(0, 0, 0, (void *)STATE_ABORT); } u32_t ll_radio_state_is_idle(void) { return radio_is_idle(); } void radio_ticks_active_to_start_set(u32_t ticks_active_to_start) { _radio.ticks_active_to_start = ticks_active_to_start; } struct radio_adv_data *radio_adv_data_get(void) { return &_radio.advertiser.adv_data; } struct radio_adv_data *radio_scan_data_get(void) { return &_radio.advertiser.scan_data; } static struct connection *connection_get(u16_t handle) { struct connection *conn; if (handle < _radio.connection_count) { conn = mem_get(_radio.conn_pool, CONNECTION_T_SIZE, handle); if ((conn) && (conn->handle == handle)) { return conn; } } return 0; } static inline void role_active_disable(u8_t ticker_id_stop, u32_t ticks_xtal_to_start, u32_t ticks_active_to_start) { static void *s_link[2]; static struct mayfly s_mfy_radio_inactive = {0, 0, s_link, NULL, mayfly_radio_inactive}; u32_t volatile ret_cb = TICKER_STATUS_BUSY; u32_t ret; /* Step 2: Is caller before Event? Stop Event */ ret = ticker_stop(RADIO_TICKER_INSTANCE_ID_RADIO, RADIO_TICKER_USER_ID_APP, RADIO_TICKER_ID_EVENT, ticker_if_done, (void *)&ret_cb); if (ret == TICKER_STATUS_BUSY) { mayfly_enable(RADIO_TICKER_USER_ID_APP, RADIO_TICKER_USER_ID_JOB, 1); LL_ASSERT(ret_cb != TICKER_STATUS_BUSY); } if (ret_cb == TICKER_STATUS_SUCCESS) { static void *s_link[2]; static struct mayfly s_mfy_xtal_stop = {0, 0, s_link, NULL, mayfly_xtal_stop}; u32_t volatile ret_cb = TICKER_STATUS_BUSY; u32_t ret; /* Reset the stored ticker id in prepare phase. */ LL_ASSERT(_radio.ticker_id_prepare); _radio.ticker_id_prepare = 0; /* Step 2.1: Is caller between Primary and Marker0? * Stop the Marker0 event */ ret = ticker_stop(RADIO_TICKER_INSTANCE_ID_RADIO, RADIO_TICKER_USER_ID_APP, RADIO_TICKER_ID_MARKER_0, ticker_if_done, (void *)&ret_cb); if (ret == TICKER_STATUS_BUSY) { mayfly_enable(RADIO_TICKER_USER_ID_APP, RADIO_TICKER_USER_ID_JOB, 1); LL_ASSERT(ret_cb != TICKER_STATUS_BUSY); } if (ret_cb == TICKER_STATUS_SUCCESS) { /* Step 2.1.1: Check and deassert Radio Active or XTAL * start */ if (ticks_active_to_start > ticks_xtal_to_start) { u32_t retval; /* radio active asserted, handle deasserting * here */ retval = mayfly_enqueue( RADIO_TICKER_USER_ID_APP, RADIO_TICKER_USER_ID_WORKER, 0, &s_mfy_radio_inactive); LL_ASSERT(!retval); } else { u32_t retval; /* XTAL started, handle XTAL stop here */ retval = mayfly_enqueue( RADIO_TICKER_USER_ID_APP, RADIO_TICKER_USER_ID_WORKER, 0, &s_mfy_xtal_stop); LL_ASSERT(!retval); } } else if (ret_cb == TICKER_STATUS_FAILURE) { u32_t retval; /* Step 2.1.2: Deassert Radio Active and XTAL start */ /* radio active asserted, handle deasserting here */ retval = mayfly_enqueue(RADIO_TICKER_USER_ID_APP, RADIO_TICKER_USER_ID_WORKER, 0, &s_mfy_radio_inactive); LL_ASSERT(!retval); /* XTAL started, handle XTAL stop here */ retval = mayfly_enqueue(RADIO_TICKER_USER_ID_APP, RADIO_TICKER_USER_ID_WORKER, 0, &s_mfy_xtal_stop); LL_ASSERT(!retval); } else { LL_ASSERT(0); } } else if (ret_cb == TICKER_STATUS_FAILURE) { u32_t volatile ret_cb = TICKER_STATUS_BUSY; u32_t ret; /* Step 3: Caller inside Event, handle graceful stop of Event * (role dependent) */ /* Stop ticker "may" be in use for direct adv or scanner, * hence stop may fail if ticker not used. */ ret = ticker_stop(RADIO_TICKER_INSTANCE_ID_RADIO, RADIO_TICKER_USER_ID_APP, ticker_id_stop, ticker_if_done, (void *)&ret_cb); if (ret == TICKER_STATUS_BUSY) { mayfly_enable(RADIO_TICKER_USER_ID_APP, RADIO_TICKER_USER_ID_JOB, 1); LL_ASSERT(ret_cb != TICKER_STATUS_BUSY); } LL_ASSERT((ret_cb == TICKER_STATUS_SUCCESS) || (ret_cb == TICKER_STATUS_FAILURE)); if (_radio.role != ROLE_NONE) { static void *s_link[2]; static struct mayfly s_mfy_radio_stop = {0, 0, s_link, NULL, mayfly_radio_stop}; u32_t retval; /* Radio state STOP is supplied in params */ s_mfy_radio_stop.param = (void *)STATE_STOP; /* Stop Radio Tx/Rx */ retval = mayfly_enqueue(RADIO_TICKER_USER_ID_APP, RADIO_TICKER_USER_ID_WORKER, 0, &s_mfy_radio_stop); LL_ASSERT(!retval); /* wait for radio ISR to exit */ while (_radio.role != ROLE_NONE) { cpu_sleep(); } } } else { LL_ASSERT(0); } } static u32_t role_disable(u8_t ticker_id_primary, u8_t ticker_id_stop) { u32_t volatile ret_cb = TICKER_STATUS_BUSY; u32_t ticks_active_to_start = 0; u32_t ticks_xtal_to_start = 0; u32_t ret; /* Determine xtal, active and start ticks. Stop directed adv stop * ticker. */ switch (ticker_id_primary) { case RADIO_TICKER_ID_ADV: ticks_xtal_to_start = _radio.advertiser.hdr.ticks_xtal_to_start; ticks_active_to_start = _radio.advertiser.hdr.ticks_active_to_start; /* Stop ticker "may" be in use for direct adv, * hence stop may fail if ticker not used. */ ret = ticker_stop(RADIO_TICKER_INSTANCE_ID_RADIO, RADIO_TICKER_USER_ID_APP, ticker_id_stop, ticker_if_done, (void *)&ret_cb); if (ret == TICKER_STATUS_BUSY) { /* wait for ticker to be stopped */ while (ret_cb == TICKER_STATUS_BUSY) { cpu_sleep(); } } LL_ASSERT((ret_cb == TICKER_STATUS_SUCCESS) || (ret_cb == TICKER_STATUS_FAILURE)); break; case RADIO_TICKER_ID_SCAN: ticks_xtal_to_start = _radio.scanner.hdr.ticks_xtal_to_start; ticks_active_to_start = _radio.scanner.hdr.ticks_active_to_start; break; default: if (ticker_id_primary >= RADIO_TICKER_ID_FIRST_CONNECTION) { struct connection *conn; u16_t conn_handle; conn_handle = ticker_id_primary - RADIO_TICKER_ID_FIRST_CONNECTION; conn = connection_get(conn_handle); if (!conn) { return 1; } ticks_xtal_to_start = conn->hdr.ticks_xtal_to_start; ticks_active_to_start = conn->hdr.ticks_active_to_start; } else { LL_ASSERT(0); } break; } LL_ASSERT(!_radio.ticker_id_stop); _radio.ticker_id_stop = ticker_id_primary; /* Step 1: Is Primary started? Stop the Primary ticker */ ret_cb = TICKER_STATUS_BUSY; ret = ticker_stop(RADIO_TICKER_INSTANCE_ID_RADIO, RADIO_TICKER_USER_ID_APP, ticker_id_primary, ticker_if_done, (void *)&ret_cb); if (ret == TICKER_STATUS_BUSY) { /* if inside our event, enable Job. */ if (_radio.ticker_id_event == ticker_id_primary) { mayfly_enable(RADIO_TICKER_USER_ID_APP, RADIO_TICKER_USER_ID_JOB, 1); } /* wait for ticker to be stopped */ while (ret_cb == TICKER_STATUS_BUSY) { cpu_sleep(); } } if (ret_cb != TICKER_STATUS_SUCCESS) { goto role_disable_cleanup; } /* Inside our event, gracefully handle XTAL and Radio actives */ if ((_radio.ticker_id_prepare == ticker_id_primary) || (_radio.ticker_id_event == ticker_id_primary)) { role_active_disable(ticker_id_stop, ticks_xtal_to_start, ticks_active_to_start); } if (!_radio.ticker_id_stop) { ret_cb = TICKER_STATUS_FAILURE; } role_disable_cleanup: _radio.ticker_id_stop = 0; return ret_cb; } #if defined(CONFIG_BLUETOOTH_CONTROLLER_ADV_EXT) u32_t radio_adv_enable(u8_t phy_p, u16_t interval, u8_t chan_map, u8_t filter_policy, u8_t rl_idx) #else /* !CONFIG_BLUETOOTH_CONTROLLER_ADV_EXT */ u32_t radio_adv_enable(u16_t interval, u8_t chan_map, u8_t filter_policy, u8_t rl_idx) #endif /* !CONFIG_BLUETOOTH_CONTROLLER_ADV_EXT */ { u32_t volatile ret_cb = TICKER_STATUS_BUSY; u32_t ticks_slot_offset; struct connection *conn; struct pdu_adv *pdu_adv; u16_t ticks_slot; u8_t chan_cnt; u32_t ret; if (_radio.advertiser.is_enabled) { return BT_HCI_ERR_CMD_DISALLOWED; } pdu_adv = (struct pdu_adv *) &_radio.advertiser.adv_data.data[_radio.advertiser.adv_data.last][0]; if ((pdu_adv->type == PDU_ADV_TYPE_ADV_IND) || (pdu_adv->type == PDU_ADV_TYPE_DIRECT_IND)) { void *link; if (_radio.advertiser.conn) { return BT_HCI_ERR_CMD_DISALLOWED; } link = mem_acquire(&_radio.link_rx_free); if (!link) { return BT_HCI_ERR_MEM_CAPACITY_EXCEEDED; } conn = mem_acquire(&_radio.conn_free); if (!conn) { mem_release(link, &_radio.link_rx_free); return BT_HCI_ERR_MEM_CAPACITY_EXCEEDED; } conn->handle = 0xFFFF; conn->llcp_features = RADIO_BLE_FEAT; conn->data_chan_sel = 0; conn->data_chan_use = 0; conn->event_counter = 0; conn->latency_prepare = 0; conn->latency_event = 0; #if defined(CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH) conn->default_tx_octets = _radio.default_tx_octets; conn->max_tx_octets = RADIO_LL_LENGTH_OCTETS_RX_MIN; conn->max_rx_octets = RADIO_LL_LENGTH_OCTETS_RX_MIN; #endif /* CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH */ #if defined(CONFIG_BLUETOOTH_CONTROLLER_PHY) conn->phy_pref_tx = _radio.default_phy_tx; conn->phy_tx = BIT(0); conn->phy_pref_flags = 0; conn->phy_flags = 0; conn->phy_pref_rx = _radio.default_phy_rx; conn->phy_rx = BIT(0); #endif /* CONFIG_BLUETOOTH_CONTROLLER_PHY */ conn->role = 1; conn->connect_expire = 6; conn->common.fex_valid = 0; conn->slave.latency_enabled = 0; conn->slave.latency_cancel = 0; conn->slave.window_widening_prepare_us = 0; conn->slave.window_widening_event_us = 0; conn->slave.ticks_to_offset = 0; conn->supervision_expire = 0; conn->procedure_expire = 0; #if defined(CONFIG_BLUETOOTH_CONTROLLER_LE_PING) conn->apto_expire = 0; conn->appto_expire = 0; #endif /* CONFIG_BLUETOOTH_CONTROLLER_LE_PING */ conn->llcp_req = 0; conn->llcp_ack = 0; conn->llcp_version.tx = 0; conn->llcp_version.rx = 0; conn->llcp_terminate.req = 0; conn->llcp_terminate.ack = 0; conn->llcp_terminate.reason_peer = 0; conn->llcp_terminate.radio_pdu_node_rx.hdr.onion.link = link; #if defined(CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH) conn->llcp_length.req = 0; conn->llcp_length.ack = 0; #endif /* CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH */ #if defined(CONFIG_BLUETOOTH_CONTROLLER_PHY) conn->llcp_phy.req = 0; conn->llcp_phy.ack = 0; #endif /* CONFIG_BLUETOOTH_CONTROLLER_PHY */ conn->sn = 0; conn->nesn = 0; conn->pause_rx = 0; conn->pause_tx = 0; conn->enc_rx = 0; conn->enc_tx = 0; conn->refresh = 0; conn->empty = 0; conn->pkt_tx_head = NULL; conn->pkt_tx_ctrl = NULL; conn->pkt_tx_ctrl_last = NULL; conn->pkt_tx_data = NULL; conn->pkt_tx_last = NULL; conn->packet_tx_head_len = 0; conn->packet_tx_head_offset = 0; #if defined(CONFIG_BLUETOOTH_CONTROLLER_CONN_RSSI) conn->rssi_latest = 0x7F; conn->rssi_reported = 0x7F; conn->rssi_sample_count = 0; #endif /* CONFIG_BLUETOOTH_CONTROLLER_CONN_RSSI */ _radio.advertiser.conn = conn; } else { conn = NULL; } #if defined(CONFIG_BLUETOOTH_CONTROLLER_ADV_EXT) _radio.advertiser.phy_p = phy_p; #endif /* CONFIG_BLUETOOTH_CONTROLLER_ADV_EXT */ _radio.advertiser.chan_map = chan_map; _radio.advertiser.filter_policy = filter_policy; #if defined(CONFIG_BLUETOOTH_CONTROLLER_PRIVACY) _radio.advertiser.rl_idx = rl_idx; #else ARG_UNUSED(rl_idx); #endif /* CONFIG_BLUETOOTH_CONTROLLER_PRIVACY */ _radio.advertiser.hdr.ticks_active_to_start = _radio.ticks_active_to_start; _radio.advertiser.hdr.ticks_xtal_to_start = TICKER_US_TO_TICKS(RADIO_TICKER_XTAL_OFFSET_US); _radio.advertiser.hdr.ticks_preempt_to_start = TICKER_US_TO_TICKS(RADIO_TICKER_PREEMPT_PART_MIN_US); chan_cnt = util_ones_count_get(&chan_map, sizeof(chan_map)); if (pdu_adv->type == PDU_ADV_TYPE_DIRECT_IND) { /* Max. chain is DIRECT_IND * channels + CONNECT_IND */ ticks_slot = ((RADIO_TICKER_START_PART_US + 176 + 152 + 40) * chan_cnt) - 40 + 352; } else if (pdu_adv->type == PDU_ADV_TYPE_NONCONN_IND) { ticks_slot = (RADIO_TICKER_START_PART_US + 376) * chan_cnt; } else { /* Max. chain is ADV/SCAN_IND + SCAN_REQ + SCAN_RESP */ ticks_slot = (RADIO_TICKER_START_PART_US + 376 + 152 + 176 + 152 + 376) * chan_cnt; } _radio.advertiser.hdr.ticks_slot = TICKER_US_TO_TICKS(ticks_slot); ticks_slot_offset = (_radio.advertiser.hdr.ticks_active_to_start < _radio.advertiser.hdr.ticks_xtal_to_start) ? _radio.advertiser.hdr.ticks_xtal_to_start : _radio.advertiser.hdr.ticks_active_to_start; /* High Duty Cycle Directed Advertising if interval is 0. */ if ((pdu_adv->type == PDU_ADV_TYPE_DIRECT_IND) && !interval) { u32_t ticks_now = ticker_ticks_now_get(); ret = ticker_start(RADIO_TICKER_INSTANCE_ID_RADIO, RADIO_TICKER_USER_ID_APP, RADIO_TICKER_ID_ADV, ticks_now, 0, (ticks_slot_offset + _radio.advertiser.hdr.ticks_slot), TICKER_NULL_REMAINDER, TICKER_NULL_LAZY, (ticks_slot_offset + _radio.advertiser.hdr.ticks_slot), radio_event_adv_prepare, NULL, ticker_if_done, (void *)&ret_cb); if (ret == TICKER_STATUS_BUSY) { while (ret_cb == TICKER_STATUS_BUSY) { cpu_sleep(); } } if (ret_cb != TICKER_STATUS_SUCCESS) { goto failure_cleanup; } ret_cb = TICKER_STATUS_BUSY; ret = ticker_start(RADIO_TICKER_INSTANCE_ID_RADIO, RADIO_TICKER_USER_ID_APP, RADIO_TICKER_ID_ADV_STOP, ticks_now, TICKER_US_TO_TICKS((u64_t) (1280 * 1000) + RADIO_TICKER_XTAL_OFFSET_US), TICKER_NULL_PERIOD, TICKER_NULL_REMAINDER, TICKER_NULL_LAZY, TICKER_NULL_SLOT, event_adv_stop, NULL, ticker_if_done, (void *)&ret_cb); } else { ret = ticker_start(RADIO_TICKER_INSTANCE_ID_RADIO, RADIO_TICKER_USER_ID_APP, RADIO_TICKER_ID_ADV, ticker_ticks_now_get(), 0, TICKER_US_TO_TICKS((u64_t) interval * 625), TICKER_NULL_REMAINDER, TICKER_NULL_LAZY, (ticks_slot_offset + _radio.advertiser.hdr.ticks_slot), radio_event_adv_prepare, NULL, ticker_if_done, (void *)&ret_cb); } if (ret == TICKER_STATUS_BUSY) { while (ret_cb == TICKER_STATUS_BUSY) { cpu_sleep(); } } if (ret_cb == TICKER_STATUS_SUCCESS) { _radio.advertiser.is_enabled = 1; if (!_radio.scanner.is_enabled) { ll_adv_scan_state_cb(BIT(0)); } return 0; } failure_cleanup: if (conn) { mem_release(conn->llcp_terminate.radio_pdu_node_rx.hdr. onion.link, &_radio.link_rx_free); mem_release(conn, &_radio.conn_free); } return BT_HCI_ERR_CMD_DISALLOWED; } u32_t radio_adv_disable(void) { u32_t status; status = role_disable(RADIO_TICKER_ID_ADV, RADIO_TICKER_ID_ADV_STOP); if (!status) { struct connection *conn; _radio.advertiser.is_enabled = 0; if (!_radio.scanner.is_enabled) { ll_adv_scan_state_cb(0); } conn = _radio.advertiser.conn; if (conn) { _radio.advertiser.conn = NULL; mem_release(conn->llcp_terminate.radio_pdu_node_rx.hdr.onion.link, &_radio.link_rx_free); mem_release(conn, &_radio.conn_free); } } return status ? BT_HCI_ERR_CMD_DISALLOWED : 0; } u32_t radio_adv_is_enabled(void) { return _radio.advertiser.is_enabled; } u32_t radio_adv_filter_pol_get(void) { /* NOTE: filter_policy is only written in thread mode; if is_enabled is * unset by ISR, returning the stale filter_policy is acceptable because * the unset code path in ISR will generate a connection complete * event. */ if (_radio.advertiser.is_enabled) { return _radio.advertiser.filter_policy; } return 0; } u32_t radio_scan_enable(u8_t type, u8_t init_addr_type, u8_t *init_addr, u16_t interval, u16_t window, u8_t filter_policy, u8_t rpa_gen, u8_t rl_idx) { u32_t volatile ret_cb = TICKER_STATUS_BUSY; u32_t ticks_slot_offset; u32_t ticks_interval; u32_t ticks_anchor; u32_t us_offset; u32_t ret; if (_radio.scanner.is_enabled) { return BT_HCI_ERR_CMD_DISALLOWED; } _radio.scanner.type = type; #if defined(CONFIG_BLUETOOTH_CONTROLLER_ADV_EXT) _radio.scanner.phy = type >> 1; #endif /* CONFIG_BLUETOOTH_CONTROLLER_ADV_EXT */ #if defined(CONFIG_BLUETOOTH_CONTROLLER_PRIVACY) _radio.scanner.rpa_gen = rpa_gen; _radio.scanner.rl_idx = rl_idx; #endif /* CONFIG_BLUETOOTH_CONTROLLER_PRIVACY */ _radio.scanner.init_addr_type = init_addr_type; memcpy(&_radio.scanner.init_addr[0], init_addr, BDADDR_SIZE); _radio.scanner.ticks_window = TICKER_US_TO_TICKS((u64_t) window * 625); _radio.scanner.filter_policy = filter_policy; _radio.scanner.hdr.ticks_active_to_start = _radio.ticks_active_to_start; _radio.scanner.hdr.ticks_xtal_to_start = TICKER_US_TO_TICKS(RADIO_TICKER_XTAL_OFFSET_US); _radio.scanner.hdr.ticks_preempt_to_start = TICKER_US_TO_TICKS(RADIO_TICKER_PREEMPT_PART_MIN_US); _radio.scanner.hdr.ticks_slot = _radio.scanner.ticks_window + TICKER_US_TO_TICKS(RADIO_TICKER_START_PART_US); ticks_interval = TICKER_US_TO_TICKS((u64_t) interval * 625); if (_radio.scanner.hdr.ticks_slot > (ticks_interval - TICKER_US_TO_TICKS(RADIO_TICKER_XTAL_OFFSET_US))) { _radio.scanner.hdr.ticks_slot = (ticks_interval - TICKER_US_TO_TICKS(RADIO_TICKER_XTAL_OFFSET_US)); } ticks_slot_offset = (_radio.scanner.hdr.ticks_active_to_start < _radio.scanner.hdr.ticks_xtal_to_start) ? _radio.scanner.hdr.ticks_xtal_to_start : _radio.scanner.hdr.ticks_active_to_start; ticks_anchor = ticker_ticks_now_get(); if ((_radio.scanner.conn) || !IS_ENABLED(CONFIG_BLUETOOTH_CONTROLLER_SCHED_ADVANCED)) { us_offset = 0; } #if defined(CONFIG_BLUETOOTH_CONTROLLER_SCHED_ADVANCED) else { u32_t ticks_ref = 0; sched_after_mstr_free_slot_get(RADIO_TICKER_USER_ID_APP, (ticks_slot_offset + _radio.scanner.hdr.ticks_slot), &ticks_ref, &us_offset); /* Use the ticks_ref as scanner's anchor if a free time space * after any master role is available (indicated by a non-zero * us_offset value). */ if (us_offset) { ticks_anchor = ticks_ref; } } #endif /* CONFIG_BLUETOOTH_CONTROLLER_SCHED_ADVANCED */ ret = ticker_start(RADIO_TICKER_INSTANCE_ID_RADIO, RADIO_TICKER_USER_ID_APP, RADIO_TICKER_ID_SCAN, (ticks_anchor + TICKER_US_TO_TICKS(us_offset)), 0, ticks_interval, TICKER_REMAINDER((u64_t) interval * 625), TICKER_NULL_LAZY, (ticks_slot_offset + _radio.scanner.hdr.ticks_slot), event_scan_prepare, NULL, ticker_if_done, (void *)&ret_cb); if (ret == TICKER_STATUS_BUSY) { while (ret_cb == TICKER_STATUS_BUSY) { cpu_sleep(); } } if (ret_cb != TICKER_STATUS_SUCCESS) { return BT_HCI_ERR_CMD_DISALLOWED; } _radio.scanner.is_enabled = 1; if (!_radio.advertiser.is_enabled) { ll_adv_scan_state_cb(BIT(1)); } return 0; } u32_t radio_scan_disable(void) { u32_t status; status = role_disable(RADIO_TICKER_ID_SCAN, RADIO_TICKER_ID_SCAN_STOP); if (!status) { struct connection *conn; _radio.scanner.is_enabled = 0; if (!_radio.advertiser.is_enabled) { ll_adv_scan_state_cb(0); } conn = _radio.scanner.conn; if (conn) { _radio.scanner.conn = NULL; mem_release(conn->llcp_terminate. radio_pdu_node_rx.hdr.onion.link, &_radio.link_rx_free); mem_release(conn, &_radio.conn_free); } } return status ? BT_HCI_ERR_CMD_DISALLOWED : 0; } u32_t radio_scan_is_enabled(void) { return _radio.scanner.is_enabled; } u32_t radio_scan_filter_pol_get(void) { /* NOTE: filter_policy is only written in thread mode; if is_enabled is * unset by ISR, returning the stale filter_policy is acceptable because * the unset code path in ISR will generate a connection complete * event. */ if (_radio.scanner.is_enabled) { return _radio.scanner.filter_policy; } return 0; } u32_t radio_connect_enable(u8_t adv_addr_type, u8_t *adv_addr, u16_t interval, u16_t latency, u16_t timeout) { struct connection *conn; u32_t conn_interval_us; u32_t access_addr; void *link; if (_radio.scanner.conn) { return BT_HCI_ERR_CMD_DISALLOWED; } link = mem_acquire(&_radio.link_rx_free); if (!link) { return BT_HCI_ERR_MEM_CAPACITY_EXCEEDED; } conn = mem_acquire(&_radio.conn_free); if (!conn) { mem_release(link, &_radio.link_rx_free); return BT_HCI_ERR_MEM_CAPACITY_EXCEEDED; } _radio.scanner.adv_addr_type = adv_addr_type; memcpy(&_radio.scanner.adv_addr[0], adv_addr, BDADDR_SIZE); _radio.scanner.conn_interval = interval; _radio.scanner.conn_latency = latency; _radio.scanner.conn_timeout = timeout; _radio.scanner.ticks_conn_slot = TICKER_US_TO_TICKS(RADIO_TICKER_START_PART_US + radio_tx_ready_delay_get(0, 0) + 328 + RADIO_TIFS + 328); conn->handle = 0xFFFF; conn->llcp_features = RADIO_BLE_FEAT; access_addr = access_addr_get(); memcpy(&conn->access_addr[0], &access_addr, sizeof(conn->access_addr)); memcpy(&conn->crc_init[0], &conn, 3); memcpy(&conn->data_chan_map[0], &_radio.data_chan_map[0], sizeof(conn->data_chan_map)); conn->data_chan_count = _radio.data_chan_count; conn->data_chan_sel = 0; conn->data_chan_hop = 6; conn->data_chan_use = 0; conn->event_counter = 0; conn->conn_interval = _radio.scanner.conn_interval; conn->latency_prepare = 0; conn->latency_event = 0; conn->latency = _radio.scanner.conn_latency; #if defined(CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH) conn->default_tx_octets = _radio.default_tx_octets; conn->max_tx_octets = RADIO_LL_LENGTH_OCTETS_RX_MIN; conn->max_rx_octets = RADIO_LL_LENGTH_OCTETS_RX_MIN; #endif /* CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH */ #if defined(CONFIG_BLUETOOTH_CONTROLLER_PHY) conn->phy_pref_tx = _radio.default_phy_tx; conn->phy_tx = BIT(0); conn->phy_pref_flags = 0; conn->phy_flags = 0; conn->phy_pref_rx = _radio.default_phy_rx; conn->phy_rx = BIT(0); #endif /* CONFIG_BLUETOOTH_CONTROLLER_PHY */ conn->role = 0; conn->connect_expire = 6; conn->common.fex_valid = 0; conn->master.terminate_ack = 0; conn_interval_us = (u32_t)_radio.scanner.conn_interval * 1250; conn->supervision_reload = RADIO_CONN_EVENTS((_radio.scanner.conn_timeout * 10 * 1000), conn_interval_us); conn->supervision_expire = 0; conn->procedure_reload = RADIO_CONN_EVENTS((40 * 1000 * 1000), conn_interval_us); conn->procedure_expire = 0; #if defined(CONFIG_BLUETOOTH_CONTROLLER_LE_PING) /* APTO in no. of connection events */ conn->apto_reload = RADIO_CONN_EVENTS((30 * 1000 * 1000), conn_interval_us); /* Dispatch LE Ping PDU 6 connection events (that peer would listen to) * before 30s timeout * TODO: "peer listens to" is greater than 30s due to latency */ conn->appto_reload = (conn->apto_reload > (conn->latency + 6)) ? (conn->apto_reload - (conn->latency + 6)) : conn->apto_reload; conn->apto_expire = 0; conn->appto_expire = 0; #endif /* CONFIG_BLUETOOTH_CONTROLLER_LE_PING */ conn->llcp_req = 0; conn->llcp_ack = 0; conn->llcp_version.tx = 0; conn->llcp_version.rx = 0; conn->llcp_terminate.req = 0; conn->llcp_terminate.ack = 0; conn->llcp_terminate.reason_peer = 0; conn->llcp_terminate.radio_pdu_node_rx.hdr.onion.link = link; #if defined(CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH) conn->llcp_length.req = 0; conn->llcp_length.ack = 0; #endif /* CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH */ #if defined(CONFIG_BLUETOOTH_CONTROLLER_PHY) conn->llcp_phy.req = 0; conn->llcp_phy.ack = 0; #endif /* CONFIG_BLUETOOTH_CONTROLLER_PHY */ conn->sn = 0; conn->nesn = 0; conn->pause_rx = 0; conn->pause_tx = 0; conn->enc_rx = 0; conn->enc_tx = 0; conn->refresh = 0; conn->empty = 0; conn->pkt_tx_head = NULL; conn->pkt_tx_ctrl = NULL; conn->pkt_tx_ctrl_last = NULL; conn->pkt_tx_data = NULL; conn->pkt_tx_last = NULL; conn->packet_tx_head_len = 0; conn->packet_tx_head_offset = 0; #if defined(CONFIG_BLUETOOTH_CONTROLLER_CONN_RSSI) conn->rssi_latest = 0x7F; conn->rssi_reported = 0x7F; conn->rssi_sample_count = 0; #endif /* CONFIG_BLUETOOTH_CONTROLLER_CONN_RSSI */ _radio.scanner.conn = conn; return 0; } u32_t ll_connect_disable(void) { u32_t status; if (_radio.scanner.conn == 0) { return BT_HCI_ERR_CMD_DISALLOWED; } status = radio_scan_disable(); return status; } u32_t ll_conn_update(u16_t handle, u8_t cmd, u8_t status, u16_t interval, u16_t latency, u16_t timeout) { struct connection *conn; ARG_UNUSED(status); conn = connection_get(handle); if ((!conn) || ((conn->llcp_req != conn->llcp_ack) && ((conn->llcp_type != LLCP_CONNECTION_UPDATE) || (conn->llcp.connection_update.state != LLCP_CONN_STATE_APP_WAIT)))) { if ((conn) && (conn->llcp_type == LLCP_CONNECTION_UPDATE)) { /* controller busy (mockup requirement) */ return 2; } return 1; } conn->llcp.connection_update.win_size = 1; conn->llcp.connection_update.win_offset_us = 0; conn->llcp.connection_update.interval = interval; conn->llcp.connection_update.latency = latency; conn->llcp.connection_update.timeout = timeout; /* conn->llcp.connection_update.instant = 0; */ conn->llcp.connection_update.state = cmd + 1; conn->llcp.connection_update.is_internal = 0; conn->llcp_type = LLCP_CONNECTION_UPDATE; conn->llcp_req++; return 0; } u32_t ll_chm_update(u8_t *chm) { u8_t instance; memcpy(&_radio.data_chan_map[0], chm, sizeof(_radio.data_chan_map)); _radio.data_chan_count = util_ones_count_get(&_radio.data_chan_map[0], sizeof(_radio.data_chan_map)); instance = _radio.connection_count; while (instance--) { struct connection *conn; conn = connection_get(instance); if (!conn || conn->role) { continue; } if (conn->llcp_req != conn->llcp_ack) { return 1; } memcpy(&conn->llcp.chan_map.chm[0], chm, sizeof(conn->llcp.chan_map.chm)); /* conn->llcp.chan_map.instant = 0; */ conn->llcp.chan_map.initiate = 1; conn->llcp_type = LLCP_CHAN_MAP; conn->llcp_req++; } return 0; } u32_t ll_chm_get(u16_t handle, u8_t *chm) { struct connection *conn; conn = connection_get(handle); if (!conn) { return 1; } /** @todo make reading context-safe */ memcpy(chm, conn->data_chan_map, sizeof(conn->data_chan_map)); return 0; } #if defined(CONFIG_BLUETOOTH_CONTROLLER_LE_ENC) u32_t ll_enc_req_send(u16_t handle, u8_t *rand, u8_t *ediv, u8_t *ltk) { struct connection *conn; struct radio_pdu_node_tx *node_tx; conn = connection_get(handle); if (!conn) { return 1; } node_tx = radio_tx_mem_acquire(); if (node_tx) { struct pdu_data *pdu_data_tx; pdu_data_tx = (struct pdu_data *)node_tx->pdu_data; memcpy(&conn->llcp.encryption.ltk[0], ltk, sizeof(conn->llcp.encryption.ltk)); if ((conn->enc_rx == 0) && (conn->enc_tx == 0)) { pdu_data_tx->ll_id = PDU_DATA_LLID_CTRL; pdu_data_tx->len = offsetof(struct pdu_data_llctrl, ctrldata) + sizeof(struct pdu_data_llctrl_enc_req); pdu_data_tx->payload.llctrl.opcode = PDU_DATA_LLCTRL_TYPE_ENC_REQ; memcpy(&pdu_data_tx->payload.llctrl.ctrldata. enc_req.rand[0], rand, sizeof(pdu_data_tx->payload.llctrl.ctrldata. enc_req.rand)); pdu_data_tx->payload.llctrl.ctrldata.enc_req.ediv[0] = ediv[0]; pdu_data_tx->payload.llctrl.ctrldata.enc_req.ediv[1] = ediv[1]; memset(&pdu_data_tx->payload.llctrl.ctrldata.enc_req. skdm[0], 0xcc, /** @todo */ sizeof(pdu_data_tx->payload.llctrl.ctrldata. enc_req.skdm)); memset(&pdu_data_tx->payload.llctrl.ctrldata.enc_req. ivm[0], 0xdd, /** @todo */ sizeof(pdu_data_tx->payload.llctrl.ctrldata. enc_req.ivm)); } else if ((conn->enc_rx != 0) && (conn->enc_tx != 0)) { memcpy(&conn->llcp.encryption.rand[0], rand, sizeof(conn->llcp.encryption.rand)); conn->llcp.encryption.ediv[0] = ediv[0]; conn->llcp.encryption.ediv[1] = ediv[1]; pdu_data_tx->ll_id = PDU_DATA_LLID_CTRL; pdu_data_tx->len = offsetof(struct pdu_data_llctrl, ctrldata); pdu_data_tx->payload.llctrl.opcode = PDU_DATA_LLCTRL_TYPE_PAUSE_ENC_REQ; } else { radio_tx_mem_release(node_tx); return 1; } if (radio_tx_mem_enqueue(handle, node_tx)) { radio_tx_mem_release(node_tx); return 1; } return 0; } return 1; } u32_t ll_start_enc_req_send(u16_t handle, u8_t error_code, u8_t const *const ltk) { struct connection *conn; conn = connection_get(handle); if (!conn) { return 1; } if (error_code) { if (conn->refresh == 0) { if (conn->llcp_req != conn->llcp_ack) { return 1; } conn->llcp.encryption.error_code = error_code; conn->llcp_type = LLCP_ENCRYPTION; conn->llcp_req++; } else { if (conn->llcp_terminate.ack != conn->llcp_terminate.req) { return 1; } conn->llcp_terminate.reason_own = error_code; conn->llcp_terminate.req++; } } else { memcpy(&conn->llcp.encryption.ltk[0], ltk, sizeof(conn->llcp.encryption.ltk)); if (conn->llcp_req != conn->llcp_ack) { return 1; } conn->llcp.encryption.error_code = 0; conn->llcp_type = LLCP_ENCRYPTION; conn->llcp_req++; } return 0; } #endif /* CONFIG_BLUETOOTH_CONTROLLER_LE_ENC */ u32_t ll_feature_req_send(u16_t handle) { struct connection *conn; conn = connection_get(handle); if (!conn || (conn->llcp_req != conn->llcp_ack)) { return 1; } conn->llcp_type = LLCP_FEATURE_EXCHANGE; conn->llcp_req++; return 0; } u32_t ll_version_ind_send(u16_t handle) { struct connection *conn; conn = connection_get(handle); if (!conn || (conn->llcp_req != conn->llcp_ack)) { return 1; } conn->llcp_type = LLCP_VERSION_EXCHANGE; conn->llcp_req++; return 0; } u32_t ll_terminate_ind_send(u16_t handle, u8_t reason) { struct connection *conn; conn = connection_get(handle); if (!conn || (conn->llcp_terminate.ack != conn->llcp_terminate.req)) { return 1; } conn->llcp_terminate.reason_own = reason; conn->llcp_terminate.req++; return 0; } #if defined(CONFIG_BLUETOOTH_CONTROLLER_LE_PING) u32_t ll_apto_get(u16_t handle, u16_t *apto) { struct connection *conn; conn = connection_get(handle); if (!conn) { return 1; } *apto = conn->apto_reload * conn->conn_interval * 125 / 1000; return 0; } u32_t ll_apto_set(u16_t handle, u16_t apto) { struct connection *conn; conn = connection_get(handle); if (!conn) { return 1; } conn->apto_reload = RADIO_CONN_EVENTS(apto * 10 * 1000, conn->conn_interval * 1250); return 0; } #endif /* CONFIG_BLUETOOTH_CONTROLLER_LE_PING */ #if defined(CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH) u32_t ll_length_req_send(u16_t handle, u16_t tx_octets) { struct connection *conn; conn = connection_get(handle); if (!conn || (conn->llcp_req != conn->llcp_ack) || (conn->llcp_length.req != conn->llcp_length.ack)) { return 1; } /* TODO: parameter check tx_octets */ conn->llcp_length.state = LLCP_LENGTH_STATE_REQ; conn->llcp_length.tx_octets = tx_octets; conn->llcp_length.req++; return 0; } void ll_length_default_get(u16_t *max_tx_octets, u16_t *max_tx_time) { *max_tx_octets = _radio.default_tx_octets; *max_tx_time = _radio.default_tx_time; } u32_t ll_length_default_set(u16_t max_tx_octets, u16_t max_tx_time) { /* TODO: parameter check (for BT 5.0 compliance) */ _radio.default_tx_octets = max_tx_octets; _radio.default_tx_time = max_tx_time; return 0; } void ll_length_max_get(u16_t *max_tx_octets, u16_t *max_tx_time, u16_t *max_rx_octets, u16_t *max_rx_time) { *max_tx_octets = RADIO_LL_LENGTH_OCTETS_RX_MAX; *max_tx_time = RADIO_LL_LENGTH_TIME_RX_MAX; *max_rx_octets = RADIO_LL_LENGTH_OCTETS_RX_MAX; *max_rx_time = RADIO_LL_LENGTH_TIME_RX_MAX; } #endif /* CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH */ #if defined(CONFIG_BLUETOOTH_CONTROLLER_PHY) u32_t ll_phy_get(u16_t handle, u8_t *tx, u8_t *rx) { struct connection *conn; conn = connection_get(handle); if (!conn) { return 1; } /* TODO: context safe read */ *tx = conn->phy_tx; *rx = conn->phy_rx; return 0; } u32_t ll_phy_default_set(u8_t tx, u8_t rx) { /* TODO: validate against supported phy */ _radio.default_phy_tx = tx; _radio.default_phy_rx = rx; return 0; } u32_t ll_phy_req_send(u16_t handle, u8_t tx, u8_t flags, u8_t rx) { struct connection *conn; conn = connection_get(handle); if (!conn || (conn->llcp_req != conn->llcp_ack) || (conn->llcp_phy.req != conn->llcp_phy.ack)) { return 1; } conn->llcp_phy.state = LLCP_PHY_STATE_REQ; conn->llcp_phy.cmd = 1; conn->llcp_phy.tx = tx; conn->llcp_phy.flags = flags; conn->llcp_phy.rx = rx; conn->llcp_phy.req++; return 0; } #endif /* CONFIG_BLUETOOTH_CONTROLLER_PHY */ static u8_t tx_cmplt_get(u16_t *handle, u8_t *first, u8_t last) { u8_t _first; u8_t cmplt; _first = *first; if (_first == last) { return 0; } cmplt = 0; *handle = _radio.pkt_release[_first].handle; do { struct radio_pdu_node_tx *node_tx; struct pdu_data *pdu_data_tx; if (*handle != _radio.pkt_release[_first].handle) { break; } node_tx = _radio.pkt_release[_first].node_tx; /*@FIXME: assign before first 3 if conditions */ pdu_data_tx = (struct pdu_data *)node_tx->pdu_data; if ((!node_tx) || (node_tx == (struct radio_pdu_node_tx *)1) || ((((u32_t)node_tx & ~(0x00000003)) != 0) && (pdu_data_tx) && (pdu_data_tx->len != 0) && ((pdu_data_tx->ll_id == PDU_DATA_LLID_DATA_START) || (pdu_data_tx->ll_id == PDU_DATA_LLID_DATA_CONTINUE)))) { /* data packet, hence count num cmplt */ _radio.pkt_release[_first].node_tx = (struct radio_pdu_node_tx *)1; cmplt++; } else { /* ctrl packet, hence not num cmplt */ _radio.pkt_release[_first].node_tx = (struct radio_pdu_node_tx *)2; } if (((u32_t)node_tx & ~(0x00000003)) != 0) { mem_release(node_tx, &_radio.pkt_tx_data_free); } _first = _first + 1; if (_first == _radio.packet_tx_count) { _first = 0; } } while (_first != last); *first = _first; return cmplt; } u8_t radio_rx_get(struct radio_pdu_node_rx **radio_pdu_node_rx, u16_t *handle) { u8_t cmplt; cmplt = 0; if (_radio.link_rx_head != _radio.link_rx_tail) { struct radio_pdu_node_rx *_radio_pdu_node_rx; _radio_pdu_node_rx = *((void **)_radio.link_rx_head + 1); cmplt = tx_cmplt_get(handle, &_radio.packet_release_first, _radio_pdu_node_rx->hdr.onion. packet_release_last); if (!cmplt) { u16_t handle; u8_t first, cmplt_prev, cmplt_curr; first = _radio.packet_release_first; cmplt_curr = 0; do { cmplt_prev = cmplt_curr; cmplt_curr = tx_cmplt_get(&handle, &first, _radio.packet_release_last); } while ((cmplt_prev != 0) || (cmplt_prev != cmplt_curr)); *radio_pdu_node_rx = _radio_pdu_node_rx; } else { *radio_pdu_node_rx = NULL; } } else { cmplt = tx_cmplt_get(handle, &_radio.packet_release_first, _radio.packet_release_last); *radio_pdu_node_rx = NULL; } return cmplt; } void radio_rx_dequeue(void) { struct radio_pdu_node_rx *radio_pdu_node_rx = NULL; void *link; link = memq_dequeue(_radio.link_rx_tail, &_radio.link_rx_head, (void **)&radio_pdu_node_rx); LL_ASSERT(link); mem_release(link, &_radio.link_rx_free); switch (radio_pdu_node_rx->hdr.type) { case NODE_RX_TYPE_DC_PDU: case NODE_RX_TYPE_REPORT: #if defined(CONFIG_BLUETOOTH_CONTROLLER_ADV_EXT) case NODE_RX_TYPE_EXT_1M_REPORT: case NODE_RX_TYPE_EXT_CODED_REPORT: #endif /* CONFIG_BLUETOOTH_CONTROLLER_ADV_EXT */ #if defined(CONFIG_BLUETOOTH_CONTROLLER_SCAN_REQ_NOTIFY) case NODE_RX_TYPE_SCAN_REQ: #endif /* CONFIG_BLUETOOTH_CONTROLLER_SCAN_REQ_NOTIFY */ case NODE_RX_TYPE_CONNECTION: case NODE_RX_TYPE_CONN_UPDATE: case NODE_RX_TYPE_ENC_REFRESH: #if defined(CONFIG_BLUETOOTH_CONTROLLER_LE_PING) case NODE_RX_TYPE_APTO: #endif /* CONFIG_BLUETOOTH_CONTROLLER_LE_PING */ case NODE_RX_TYPE_CHAN_SEL_ALGO: #if defined(CONFIG_BLUETOOTH_CONTROLLER_PHY) case NODE_RX_TYPE_PHY_UPDATE: #endif /* CONFIG_BLUETOOTH_CONTROLLER_PHY */ #if defined(CONFIG_BLUETOOTH_CONTROLLER_CONN_RSSI) case NODE_RX_TYPE_RSSI: #endif /* CONFIG_BLUETOOTH_CONTROLLER_CONN_RSSI */ #if defined(CONFIG_BLUETOOTH_CONTROLLER_PROFILE_ISR) case NODE_RX_TYPE_PROFILE: #endif /* CONFIG_BLUETOOTH_CONTROLLER_PROFILE_ISR */ #if defined(CONFIG_BLUETOOTH_CONTROLLER_ADV_INDICATION) case NODE_RX_TYPE_ADV_INDICATION: #endif /* CONFIG_BLUETOOTH_CONTROLLER_ADV_INDICATION */ /* release data link credit quota */ LL_ASSERT(_radio.link_rx_data_quota < (_radio.packet_rx_count - 1)); _radio.link_rx_data_quota++; break; case NODE_RX_TYPE_TERMINATE: /* did not use data link quota */ break; default: LL_ASSERT(0); break; } if (radio_pdu_node_rx->hdr.type == NODE_RX_TYPE_CONNECTION) { struct radio_le_conn_cmplt *radio_le_conn_cmplt; struct connection *conn = NULL; struct pdu_data *pdu_data_rx; u8_t bm; pdu_data_rx = (void *)radio_pdu_node_rx->pdu_data; radio_le_conn_cmplt = (void *)&pdu_data_rx->payload; if ((radio_le_conn_cmplt->status == 0x3c) || radio_le_conn_cmplt->role) { if (radio_le_conn_cmplt->status == 0x3c) { conn = _radio.advertiser.conn; _radio.advertiser.conn = NULL; } LL_ASSERT(_radio.advertiser.is_enabled); _radio.advertiser.is_enabled = 0; } else { LL_ASSERT(_radio.scanner.is_enabled); _radio.scanner.is_enabled = 0; } if (conn) { struct radio_pdu_node_rx *node_rx = (void *) &conn->llcp_terminate.radio_pdu_node_rx; mem_release(node_rx->hdr.onion.link, &_radio.link_rx_free); mem_release(conn, &_radio.conn_free); } bm = ((u8_t)_radio.scanner.is_enabled << 1) | _radio.advertiser.is_enabled; if (!bm) { ll_adv_scan_state_cb(0); } } } void radio_rx_mem_release(struct radio_pdu_node_rx **radio_pdu_node_rx) { struct radio_pdu_node_rx *_radio_pdu_node_rx; struct connection *conn; _radio_pdu_node_rx = *radio_pdu_node_rx; while (_radio_pdu_node_rx) { struct radio_pdu_node_rx *_radio_pdu_node_rx_free; _radio_pdu_node_rx_free = _radio_pdu_node_rx; _radio_pdu_node_rx = _radio_pdu_node_rx->hdr.onion.next; switch (_radio_pdu_node_rx_free->hdr.type) { case NODE_RX_TYPE_DC_PDU: case NODE_RX_TYPE_REPORT: #if defined(CONFIG_BLUETOOTH_CONTROLLER_ADV_EXT) case NODE_RX_TYPE_EXT_1M_REPORT: case NODE_RX_TYPE_EXT_CODED_REPORT: #endif /* CONFIG_BLUETOOTH_CONTROLLER_ADV_EXT */ #if defined(CONFIG_BLUETOOTH_CONTROLLER_SCAN_REQ_NOTIFY) case NODE_RX_TYPE_SCAN_REQ: #endif /* CONFIG_BLUETOOTH_CONTROLLER_SCAN_REQ_NOTIFY */ case NODE_RX_TYPE_CONNECTION: case NODE_RX_TYPE_CONN_UPDATE: case NODE_RX_TYPE_ENC_REFRESH: #if defined(CONFIG_BLUETOOTH_CONTROLLER_LE_PING) case NODE_RX_TYPE_APTO: #endif /* CONFIG_BLUETOOTH_CONTROLLER_LE_PING */ case NODE_RX_TYPE_CHAN_SEL_ALGO: #if defined(CONFIG_BLUETOOTH_CONTROLLER_PHY) case NODE_RX_TYPE_PHY_UPDATE: #endif /* CONFIG_BLUETOOTH_CONTROLLER_PHY */ #if defined(CONFIG_BLUETOOTH_CONTROLLER_CONN_RSSI) case NODE_RX_TYPE_RSSI: #endif /* CONFIG_BLUETOOTH_CONTROLLER_CONN_RSSI */ #if defined(CONFIG_BLUETOOTH_CONTROLLER_PROFILE_ISR) case NODE_RX_TYPE_PROFILE: #endif /* CONFIG_BLUETOOTH_CONTROLLER_PROFILE_ISR */ #if defined(CONFIG_BLUETOOTH_CONTROLLER_ADV_INDICATION) case NODE_RX_TYPE_ADV_INDICATION: #endif /* CONFIG_BLUETOOTH_CONTROLLER_ADV_INDICATION */ mem_release(_radio_pdu_node_rx_free, &_radio.pkt_rx_data_free); break; case NODE_RX_TYPE_TERMINATE: conn = mem_get(_radio.conn_pool, CONNECTION_T_SIZE, _radio_pdu_node_rx_free->hdr.handle); mem_release(conn, &_radio.conn_free); break; default: LL_ASSERT(0); break; } } *radio_pdu_node_rx = _radio_pdu_node_rx; packet_rx_allocate(0xff); } static void rx_fc_lock(u16_t handle) { if (_radio.fc_req == _radio.fc_ack) { u8_t req; _radio.fc_handle[_radio.fc_req] = handle; req = _radio.fc_req + 1; if (req == TRIPLE_BUFFER_SIZE) { req = 0; } _radio.fc_req = req; } } u8_t do_radio_rx_fc_set(u16_t handle, u8_t req, u8_t ack) { if (req == ack) { if (_radio.link_rx_head == _radio.link_rx_tail) { u8_t ack1 = ack; if (ack1 == 0) { ack1 = TRIPLE_BUFFER_SIZE; } _radio.fc_handle[--ack1] = handle; _radio.fc_ack = ack1; /* check if ISR updated FC by changing fc_req */ if (req != _radio.fc_req) { _radio.fc_ack = ack; return 1; } } else { return 1; } } else if (((req == 0) && (_radio.fc_handle[TRIPLE_BUFFER_SIZE - 1] != handle)) || ((req != 0) && (_radio.fc_handle[req - 1] != handle))) { return 1; } return 0; } u8_t radio_rx_fc_set(u16_t handle, u8_t fc) { if (_radio.fc_ena) { u8_t req = _radio.fc_req; u8_t ack = _radio.fc_ack; if (fc) { if (handle != 0xffff) { return do_radio_rx_fc_set(handle, req, ack); } } else if ((_radio.link_rx_head == _radio.link_rx_tail) && (req != ack) ) { _radio.fc_ack = req; if ((_radio.link_rx_head != _radio.link_rx_tail) && (req == _radio.fc_req)) { _radio.fc_ack = ack; } } } return 0; } u8_t radio_rx_fc_get(u16_t *handle) { u8_t req = _radio.fc_req; u8_t ack = _radio.fc_ack; if (req != ack) { if (handle) { *handle = _radio.fc_handle[ack]; } return 1; } return 0; } struct radio_pdu_node_tx *radio_tx_mem_acquire(void) { return mem_acquire(&_radio.pkt_tx_data_free); } void radio_tx_mem_release(struct radio_pdu_node_tx *node_tx) { mem_release(node_tx, &_radio.pkt_tx_data_free); } static void ticker_op_latency_cancelled(u32_t ticker_status, void *params) { struct connection *conn; LL_ASSERT(ticker_status == TICKER_STATUS_SUCCESS); conn = (struct connection *)params; conn->slave.latency_cancel = 0; } u32_t radio_tx_mem_enqueue(u16_t handle, struct radio_pdu_node_tx *node_tx) { u8_t last; struct connection *conn; struct pdu_data *pdu_data; last = _radio.packet_tx_last + 1; if (last == _radio.packet_tx_count) { last = 0; } pdu_data = (struct pdu_data *)node_tx->pdu_data; conn = connection_get(handle); if (!conn || (last == _radio.packet_tx_first)) { return 1; } LL_ASSERT(pdu_data->len <= (_radio.packet_tx_data_size - offsetof(struct radio_pdu_node_tx, pdu_data) - offsetof(struct pdu_data, payload))); _radio.pkt_tx[_radio.packet_tx_last].handle = handle; _radio.pkt_tx[_radio.packet_tx_last]. node_tx = node_tx; _radio.packet_tx_last = last; /* break slave latency */ if (conn->role && conn->latency_event && !conn->slave.latency_cancel) { u32_t ticker_status; conn->slave.latency_cancel = 1; ticker_status = ticker_update(RADIO_TICKER_INSTANCE_ID_RADIO, RADIO_TICKER_USER_ID_APP, RADIO_TICKER_ID_FIRST_CONNECTION + conn->handle, 0, 0, 0, 0, 1, 0, ticker_op_latency_cancelled, (void *)conn); LL_ASSERT((ticker_status == TICKER_STATUS_SUCCESS) || (ticker_status == TICKER_STATUS_BUSY)); } return 0; } void __weak ll_adv_scan_state_cb(u8_t bm) { }