btstack/chipset/sx128x/ll_sx1280.c

1406 lines
46 KiB
C

/*
* Copyright (C) 2020 BlueKitchen GmbH
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
* 4. Any redistribution, use, or modification is done solely for
* personal benefit and not for any commercial purpose or for
* monetary gain.
*
* THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN
* GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Please inquire about commercial licensing options at
* contact@bluekitchen-gmbh.com
*
*/
#define BTSTACK_FILE__ "ll_sx1280.c"
#define DEBUG
#include <string.h>
#include "ll.h"
#include "hw.h"
#include "radio.h"
#include "sx1280.h"
#include "debug.h"
#include "btstack_config.h"
#include "btstack_debug.h"
#include "btstack_memory.h"
#include "btstack_memory_pool.h"
#include "btstack_linked_queue.h"
#include "bluetooth_company_id.h"
#include "hal_cpu.h"
#include "hci_event.h"
#include "hopping.h"
#include "hal_timer.h"
//
// configuration
//
#define AUTO_RX_TX_TIME_US 86
#define TX_PARAMS_RAMP_TIME RADIO_RAMP_02_US
// set output power in dBM, range [-18..+13] dBm - Bluetooth LE max is 10 dBM
#define TX_PARAMS_OUTPUT_POWER 10
#define ACL_LE_MAX_PAYLOAD 31
#define ADV_MAX_PAYLOAD (6+6+22)
#define LL_MAX_PAYLOAD 37
// split 256 bytes data buffer into 2 rx and 2 tx buffers
#define SX1280_RX0_OFFSET 0
#define SX1280_RX1_OFFSET 64
#define SX1280_TX0_OFFSET 128
#define SX1280_TX1_OFFSET 192
// Mask of IRQs to listen in tx and rx mode
#define RX_TX_IRQ_MASK (IRQ_RX_DONE | IRQ_TX_DONE | IRQ_RX_TX_TIMEOUT | IRQ_CRC_ERROR)
// sync hop delay - time we prepare for next connection event
#define SYNC_HOP_DELAY_US 600
// num tx buffers for use by link layer
#define HCI_NUM_TX_BUFFERS_LL 4
// num rx buffers
#define HCI_NUM_RX_BUFFERS 16
// total number PDU buffers
#define MAX_NUM_LL_PDUS (HCI_NUM_TX_BUFFERS_STACK + HCI_NUM_TX_BUFFERS_LL + HCI_NUM_RX_BUFFERS)
// HCI Connection Handle used for all HCI events/connections
#define HCI_CON_HANDLE 0x0001
// convert us to ticks, rounding to the closest tick count
// @note us must be <= 1000000 us = 1 s
#define US_TO_TICKS(US) (((((uint32_t)(US)) * 4096) + 6125) / 125000L)
// ADV PDU Types
enum pdu_adv_type {
PDU_ADV_TYPE_ADV_IND = 0x00,
PDU_ADV_TYPE_DIRECT_IND = 0x01,
PDU_ADV_TYPE_NONCONN_IND = 0x02,
PDU_ADV_TYPE_SCAN_REQ = 0x03,
PDU_ADV_TYPE_AUX_SCAN_REQ = PDU_ADV_TYPE_SCAN_REQ,
PDU_ADV_TYPE_SCAN_RSP = 0x04,
PDU_ADV_TYPE_CONNECT_IND = 0x05,
PDU_ADV_TYPE_AUX_CONNECT_REQ = PDU_ADV_TYPE_CONNECT_IND,
PDU_ADV_TYPE_SCAN_IND = 0x06,
PDU_ADV_TYPE_EXT_IND = 0x07,
PDU_ADV_TYPE_AUX_ADV_IND = PDU_ADV_TYPE_EXT_IND,
PDU_ADV_TYPE_AUX_SCAN_RSP = PDU_ADV_TYPE_EXT_IND,
PDU_ADV_TYPE_AUX_SYNC_IND = PDU_ADV_TYPE_EXT_IND,
PDU_ADV_TYPE_AUX_CHAIN_IND = PDU_ADV_TYPE_EXT_IND,
PDU_ADV_TYPE_AUX_CONNECT_RSP = 0x08,
};
// DATA PDU Types
enum pdu_data_llid {
PDU_DATA_LLID_RESV = 0x00,
PDU_DATA_LLID_DATA_CONTINUE = 0x01,
PDU_DATA_LLID_DATA_START = 0x02,
PDU_DATA_LLID_CTRL = 0x03,
};
// DATA Link Layer Control Types
enum pdu_data_llctrl_type {
PDU_DATA_LLCTRL_TYPE_CONN_UPDATE_IND = 0x00,
PDU_DATA_LLCTRL_TYPE_CHAN_MAP_IND = 0x01,
PDU_DATA_LLCTRL_TYPE_TERMINATE_IND = 0x02,
PDU_DATA_LLCTRL_TYPE_ENC_REQ = 0x03,
PDU_DATA_LLCTRL_TYPE_ENC_RSP = 0x04,
PDU_DATA_LLCTRL_TYPE_START_ENC_REQ = 0x05,
PDU_DATA_LLCTRL_TYPE_START_ENC_RSP = 0x06,
PDU_DATA_LLCTRL_TYPE_UNKNOWN_RSP = 0x07,
PDU_DATA_LLCTRL_TYPE_FEATURE_REQ = 0x08,
PDU_DATA_LLCTRL_TYPE_FEATURE_RSP = 0x09,
PDU_DATA_LLCTRL_TYPE_PAUSE_ENC_REQ = 0x0A,
PDU_DATA_LLCTRL_TYPE_PAUSE_ENC_RSP = 0x0B,
PDU_DATA_LLCTRL_TYPE_VERSION_IND = 0x0C,
PDU_DATA_LLCTRL_TYPE_REJECT_IND = 0x0D,
PDU_DATA_LLCTRL_TYPE_SLAVE_FEATURE_REQ = 0x0E,
PDU_DATA_LLCTRL_TYPE_CONN_PARAM_REQ = 0x0F,
PDU_DATA_LLCTRL_TYPE_CONN_PARAM_RSP = 0x10,
PDU_DATA_LLCTRL_TYPE_REJECT_EXT_IND = 0x11,
PDU_DATA_LLCTRL_TYPE_PING_REQ = 0x12,
PDU_DATA_LLCTRL_TYPE_PING_RSP = 0x13,
PDU_DATA_LLCTRL_TYPE_LENGTH_REQ = 0x14,
PDU_DATA_LLCTRL_TYPE_LENGTH_RSP = 0x15,
PDU_DATA_LLCTRL_TYPE_PHY_REQ = 0x16,
PDU_DATA_LLCTRL_TYPE_PHY_RSP = 0x17,
PDU_DATA_LLCTRL_TYPE_PHY_UPD_IND = 0x18,
PDU_DATA_LLCTRL_TYPE_MIN_USED_CHAN_IND = 0x19,
};
// Radio State
typedef enum {
RADIO_LOWPOWER,
RADIO_RX_ERROR,
RADIO_TX_TIMEOUT,
RADIO_W4_TX_DONE_TO_RX,
RADIO_W4_TX_ONLY_DONE,
RADIO_W4_TIMER,
} radio_state_t;
// Link Layer State
typedef enum {
LL_STATE_STANDBY,
LL_STATE_SCANNING,
LL_STATE_ADVERTISING,
LL_STATE_INITIATING,
LL_STATE_CONNECTED
} ll_state_t;
// Link Layer PDU Flags
typedef enum {
LL_PDU_FLAG_DATA_PDU = 1,
} ll_pdu_flags;
// Link Layer PDU, used in linked list
typedef struct {
// header
void * item;
hci_con_handle_t con_handle;
uint8_t flags;
// over the air data
uint8_t header;
uint8_t len;
uint8_t payload[LL_MAX_PAYLOAD];
} ll_pdu_t;
// channel table: freq in hertz and whitening seed
static const struct {
uint32_t freq_hz;
uint8_t whitening;
} channel_table[] = {
{ 2404000000, 0x01 /* 00000001 */ },
{ 2406000000, 0x41 /* 01000001 */ },
{ 2408000000, 0x21 /* 00100001 */ },
{ 2410000000, 0x61 /* 01100001 */ },
{ 2412000000, 0x11 /* 00010001 */ },
{ 2414000000, 0x51 /* 01010001 */ },
{ 2416000000, 0x31 /* 00110001 */ },
{ 2418000000, 0x71 /* 01110001 */ },
{ 2420000000, 0x09 /* 00001001 */ },
{ 2422000000, 0x49 /* 01001001 */ },
{ 2424000000, 0x29 /* 00101001 */ },
{ 2428000000, 0x69 /* 01101001 */ },
{ 2430000000, 0x19 /* 00011001 */ },
{ 2432000000, 0x59 /* 01011001 */ },
{ 2434000000, 0x39 /* 00111001 */ },
{ 2436000000, 0x79 /* 01111001 */ },
{ 2438000000, 0x05 /* 00000101 */ },
{ 2440000000, 0x45 /* 01000101 */ },
{ 2442000000, 0x25 /* 00100101 */ },
{ 2444000000, 0x65 /* 01100101 */ },
{ 2446000000, 0x15 /* 00010101 */ },
{ 2448000000, 0x55 /* 01010101 */ },
{ 2450000000, 0x35 /* 00110101 */ },
{ 2452000000, 0x75 /* 01110101 */ },
{ 2454000000, 0x0d /* 00001101 */ },
{ 2456000000, 0x4d /* 01001101 */ },
{ 2458000000, 0x2d /* 00101101 */ },
{ 2460000000, 0x6d /* 01101101 */ },
{ 2462000000, 0x1d /* 00011101 */ },
{ 2464000000, 0x5d /* 01011101 */ },
{ 2466000000, 0x3d /* 00111101 */ },
{ 2468000000, 0x7d /* 01111101 */ },
{ 2470000000, 0x03 /* 00000011 */ },
{ 2472000000, 0x43 /* 01000011 */ },
{ 2474000000, 0x23 /* 00100011 */ },
{ 2476000000, 0x63 /* 01100011 */ },
{ 2478000000, 0x13 /* 00010011 */ },
{ 2402000000, 0x53 /* 01010011 */ },
{ 2426000000, 0x33 /* 00110011 */ },
{ 2480000000, 0x73 /* 01110011 */ },
};
// tx buffer offset
static uint8_t tx_buffer_offset[] = {
SX1280_TX0_OFFSET,
SX1280_TX1_OFFSET
};
// hopping context
static hopping_t h;
static struct {
volatile bool synced;
volatile uint16_t packet_nr_in_connection_event;
volatile uint16_t conn_interval_1250us;
volatile uint32_t conn_interval_us;
volatile uint16_t conn_interval_ticks;
volatile uint16_t conn_latency;
volatile uint16_t supervision_timeout_10ms;
volatile uint32_t supervision_timeout_us;
//
volatile uint32_t time_without_any_packets_us;
// access address
volatile uint32_t aa;
// start of current connection event
volatile uint16_t anchor_ticks;
// latest time to send tx packet before sync hop
volatile uint16_t conn_latest_tx_ticks;
// timeout for sync relative to anchor
volatile uint16_t conn_sync_hop_ticks;
// current channel
volatile uint8_t channel;
// CSA #2 supported
uint8_t csa2_support;
// channels selection algorithm index (1 for csa #2)
volatile uint8_t channel_selection_algorithm;
// current connection event, first one starts with 0
// - needed for connection param and channel map updates as well as encryption
volatile uint16_t connection_event;
// pending channel map update
volatile bool channel_map_update_pending;
volatile uint16_t channel_map_update_instant;
volatile uint8_t channel_map_update_map[5];
// pending connection param update
volatile bool conn_param_update_pending;
volatile uint16_t conn_param_update_instant;
volatile uint8_t conn_param_update_win_size;
volatile uint16_t conn_param_update_win_offset;
volatile uint16_t conn_param_update_interval_1250us;
volatile uint16_t conn_param_update_latency;
volatile uint32_t conn_param_update_timeout_us;
// our bd_addr as little endian
uint8_t bd_addr_le[6];
// peer addr
uint8_t peer_addr_type;
uint8_t peer_addr[6];
// adv data
uint8_t adv_len;
uint8_t adv_data[31];
// adv param
uint8_t adv_map;
uint32_t adv_interval_us;
uint8_t adv_type;
// adv data
uint8_t scan_resp_len;
uint8_t scan_resp_data[31];
// next expected sequence number
volatile uint8_t next_expected_sequence_number;
// transmit sequence number
volatile uint8_t transmit_sequence_number;
// num completed packets
volatile uint8_t num_completed;
// rx queue
btstack_linked_queue_t rx_queue;
// current incoming packet
ll_pdu_t * rx_pdu;
// rx packet ready
bool rx_pdu_received;
// tx queue of outgoing pdus
btstack_linked_queue_t tx_queue;
// pdus transferred into controller tx buffers
ll_pdu_t * tx_buffer_pdu[2];
// manage tx packets on controller
uint8_t num_tx_pdus_on_controller;
// index of next tx buffer to send
uint8_t next_tx_buffer;
} ctx;
static radio_state_t radio_state = RADIO_LOWPOWER;
// Buffer pool
static ll_pdu_t ll_pdu_pool_storage[MAX_NUM_LL_PDUS];
static btstack_memory_pool_t ll_pdu_pool;
// single ll control response
static ll_pdu_t ll_tx_packet;
static ll_pdu_t ll_empty_packet;
// Link Layer State
static ll_state_t ll_state;
static uint32_t ll_scan_interval_us;
static uint32_t ll_scan_window_us;
static ll_pdu_t * ll_reserved_acl_buffer;
static void (*controller_packet_handler)(uint8_t packet_type, uint8_t * packet, uint16_t size);
static uint8_t ll_outgoing_hci_event[258];
static bool ll_send_disconnected;
static bool ll_send_connection_complete;
// prototypes
static void radio_set_timer_ticks(uint32_t anchor_offset_ticks);
// memory pool for acl-le pdus
static ll_pdu_t * btstack_memory_ll_pdu_get(void){
void * buffer = btstack_memory_pool_get(&ll_pdu_pool);
if (buffer){
memset(buffer, 0, sizeof(ll_pdu_t));
}
return (ll_pdu_t *) buffer;
}
static void btstack_memory_ll_pdu_free(ll_pdu_t *acl_le_pdu){
btstack_memory_pool_free(&ll_pdu_pool, acl_le_pdu);
}
static void radio_auto_tx_on(void){
// SetAutoTX(150 ms) - direct write / ignore compensation
uint8_t buf[2];
big_endian_store_16(buf, 0, AUTO_RX_TX_TIME_US);
SX1280HalWriteCommand( RADIO_SET_AUTOTX, buf, 2 );
}
static void radio_auto_tx_off(void){
// SetAutoTX(0) - direct write / ignore compensation
uint8_t buf[2] = { 0, 0 };
SX1280HalWriteCommand( RADIO_SET_AUTOTX, buf, 2 );
}
static bool receive_prepare_rx_bufffer(void){
if (ctx.rx_pdu == NULL){
ctx.rx_pdu = btstack_memory_ll_pdu_get();
}
if (ctx.rx_pdu == NULL){
printf("No free RX buffer\n");
return false;
} else {
return true;
}
}
static void receive_response(void){
if (receive_prepare_rx_bufffer()) {
// 150 us would be enough, but the timeout seems to apply for AutoTx as well, so we use 250 us
Radio.SetRx( ( TickTime_t ) { RADIO_TICK_SIZE_0015_US, 16 } );
}
}
static void receive_first_master(void){
if (receive_prepare_rx_bufffer()){
Radio.SetRx( ( TickTime_t ) { RADIO_TICK_SIZE_1000_US, 1000 } );
}
}
static void receive_master(void){
if (receive_prepare_rx_bufffer()) {
Radio.SetRx((TickTime_t) {RADIO_TICK_SIZE_1000_US, 1});
}
}
static void setup_adv_pdu(uint8_t offset, uint8_t header, uint8_t len, const uint8_t * data){
uint8_t buffer[39];
buffer[0] = header;
buffer[1] = 6 + len;
memcpy(&buffer[2], ctx.bd_addr_le, 6);
memcpy(&buffer[8], data, len);
uint16_t packet_size = 2 + buffer[1];
SX1280HalWriteBuffer( offset, buffer, packet_size );
}
static void send_adv(void){
// enable AutoTX for potential Scan Response
// TODO: only if adv type allows for scanning
radio_auto_tx_on();
SX1280SetBufferBaseAddresses( SX1280_TX0_OFFSET, SX1280_RX0_OFFSET);
SX1280SetTx( ( TickTime_t ){ RADIO_TICK_SIZE_1000_US, 1 } );
}
static void select_channel(uint8_t channel){
// Set Whitening seed
Radio.SetWhiteningSeed( channel_table[channel].whitening );
// Sel Frequency
Radio.SetRfFrequency( channel_table[channel].freq_hz );
}
static void next_channel(void){
switch (ctx.channel_selection_algorithm){
case 0:
ctx.channel = hopping_csa1_get_next_channel( &h );
break;
case 1:
ctx.channel = hopping_csa2_get_channel_for_counter( &h, ctx.connection_event);
break;
default:
break;
}
select_channel(ctx.channel);
}
static void ll_advertising_statemachine(void){
switch ( radio_state) {
case RADIO_RX_ERROR:
case RADIO_LOWPOWER:
// find next channel
while (ctx.channel < 40){
ctx.channel++;
if ((ctx.adv_map & (1 << (ctx.channel - 37))) != 0) {
// Set Channel
select_channel(ctx.channel);
if (ctx.adv_type == 3) {
// Non connectable undirected advertising (ADV_NONCONN_IND)
radio_state = RADIO_W4_TX_ONLY_DONE;
} else {
// All other are either connectable and/or scannable
radio_state = RADIO_W4_TX_DONE_TO_RX;
}
send_adv();
break;
}
if (ctx.channel >= 40){
// Set timer
radio_state = RADIO_W4_TIMER;
uint32_t adv_interval_ticks = US_TO_TICKS(ctx.adv_interval_us);
radio_set_timer_ticks(adv_interval_ticks);
}
}
break;
default:
break;
}
}
static void start_advertising(void){
Radio.StopAutoTx();
PacketParams_t packetParams;
packetParams.PacketType = PACKET_TYPE_BLE;
packetParams.Params.Ble.BlePacketType = BLE_EYELONG_1_0;
packetParams.Params.Ble.ConnectionState = BLE_PAYLOAD_LENGTH_MAX_37_BYTES;
packetParams.Params.Ble.CrcField = BLE_CRC_3B;
packetParams.Params.Ble.Whitening = RADIO_WHITENING_ON;
Radio.SetPacketParams( &packetParams );
// Set CRC init value 0x555555
Radio.WriteRegister(0x9c7, 0x55 );
Radio.WriteRegister(0x9c8, 0x55 );
Radio.WriteRegister(0x9c9, 0x55 );
// Set AccessAddress for ADV packets
Radio.SetBleAdvertizerAccessAddress( );
// prepare adv and scan data in tx0 and tx1
setup_adv_pdu(SX1280_TX0_OFFSET, PDU_ADV_TYPE_ADV_IND, ctx.adv_len, ctx.adv_data);
setup_adv_pdu(SX1280_TX1_OFFSET, PDU_ADV_TYPE_SCAN_RSP, ctx.scan_resp_len, ctx.scan_resp_data);
radio_state = RADIO_LOWPOWER;
ll_state = LL_STATE_ADVERTISING;
// prepare
ctx.channel = 36;
ctx.anchor_ticks = hal_timer_get_ticks();
// and get started
ll_advertising_statemachine();
}
static void start_hopping(void){
PacketParams_t packetParams;
packetParams.PacketType = PACKET_TYPE_BLE;
packetParams.Params.Ble.BlePacketType = BLE_EYELONG_1_0;
packetParams.Params.Ble.ConnectionState = BLE_PAYLOAD_LENGTH_MAX_31_BYTES;
packetParams.Params.Ble.CrcField = BLE_CRC_3B;
packetParams.Params.Ble.Whitening = RADIO_WHITENING_ON;
Radio.SetPacketParams( &packetParams );
}
static void radio_stop_timer(void){
hal_timer_stop();
}
static void radio_set_timer_ticks(uint32_t anchor_offset_ticks){
radio_stop_timer();
// set timer for next radio event relative to anchor
uint16_t timeout_ticks = (uint16_t) (ctx.anchor_ticks + anchor_offset_ticks);
hal_timer_start(timeout_ticks);
}
static void ctx_set_conn_interval(uint16_t conn_interval_1250us){
ctx.conn_interval_1250us = conn_interval_1250us;
ctx.conn_interval_us = ctx.conn_interval_1250us * 1250;
ctx.conn_interval_ticks = US_TO_TICKS(ctx.conn_interval_us);
ctx.conn_sync_hop_ticks = US_TO_TICKS(ctx.conn_interval_us - SYNC_HOP_DELAY_US);
// latest time to send a packet before getting ready for next cnonection event
uint16_t max_packet_time_incl_ifs_us = 500;
ctx.conn_latest_tx_ticks = US_TO_TICKS(ctx.conn_interval_us - SYNC_HOP_DELAY_US - max_packet_time_incl_ifs_us);
}
static void ll_terminate(void){
ll_state = LL_STATE_STANDBY;
ctx.conn_param_update_pending = false;
ctx.channel_map_update_pending = false;
// stop sync hop timer
radio_stop_timer();
// free outgoing tx packets
uint8_t i;
for (i=0;i<2;i++){
ll_pdu_t * tx_pdu = ctx.tx_buffer_pdu[i];
if ((tx_pdu != NULL) && (tx_pdu != &ll_tx_packet) && (tx_pdu != &ll_empty_packet)){
btstack_memory_ll_pdu_free(tx_pdu);
ctx.tx_buffer_pdu[i] = NULL;
}
}
ctx.num_tx_pdus_on_controller = 0;
// free queued tx packets
while (true){
ll_pdu_t * tx_pdu = (ll_pdu_t *) btstack_linked_queue_dequeue(&ctx.tx_queue);
if (tx_pdu != NULL) {
btstack_memory_ll_pdu_free(tx_pdu);
} else {
break;
}
}
// disable auto tx
Radio.StopAutoTx();
// notify host stack
ll_send_disconnected = true;
}
// load queued tx pdu into next free tx buffer
static void preload_tx_buffer(void){
if (ctx.num_tx_pdus_on_controller >= 2) return;
ll_pdu_t * tx_pdu = (ll_pdu_t *) btstack_linked_queue_dequeue(&ctx.tx_queue);
if (tx_pdu == NULL) return;
const uint16_t max_packet_len = 2 + 27;
uint8_t index = (ctx.next_tx_buffer + ctx.num_tx_pdus_on_controller) & 1;
ctx.tx_buffer_pdu[index] = tx_pdu;
SX1280HalWriteBuffer( tx_buffer_offset[index], (uint8_t *) &ctx.tx_buffer_pdu[index]->header, max_packet_len);
ctx.num_tx_pdus_on_controller++;
// printf("preload %u bytes into %u\n", ctx.tx_buffer_pdu[index]->len, index);
}
static void radio_timer_handler(void){
uint16_t t0 = hal_timer_get_ticks();
switch (ll_state){
case LL_STATE_CONNECTED:
// check supervision timeout
ctx.time_without_any_packets_us += ctx.conn_interval_us;
if (ctx.time_without_any_packets_us > ctx.supervision_timeout_us) {
printf("Supervision timeout\n\n");
ll_terminate();
return;
}
// prepare next connection event
ctx.connection_event++;
ctx.anchor_ticks += ctx.conn_interval_ticks;
ctx.packet_nr_in_connection_event = 0;
next_channel();
if (ctx.channel_map_update_pending && (ctx.channel_map_update_instant == ctx.connection_event)) {
hopping_set_channel_map( &h, (const uint8_t *) &ctx.channel_map_update_map );
ctx.channel_map_update_pending = false;
}
if (ctx.conn_param_update_pending && ((ctx.conn_param_update_instant) == ctx.connection_event) ) {
ctx_set_conn_interval(ctx.conn_param_update_interval_1250us);
ctx.conn_latency = ctx.conn_param_update_latency;
ctx.supervision_timeout_us = ctx.conn_param_update_timeout_us;
ctx.conn_param_update_pending = false;
log_info("Conn param update now");
radio_stop_timer();
ctx.synced = false;
}
// preload tx pdu
preload_tx_buffer();
if (ctx.synced){
// restart radio timer (might get overwritten by first packet)
radio_set_timer_ticks(ctx.conn_sync_hop_ticks);
receive_master();
} else {
// just wait longer
receive_first_master();
}
// printf("--SYNC-Ch %02u-Event %04u - t %08u--\n", ctx.channel, ctx.connection_event, t0);
break;
case LL_STATE_ADVERTISING:
// send adv on all configured channels
ctx.channel = 36;
ctx.anchor_ticks = t0;
radio_stop_timer();
ll_advertising_statemachine();
radio_state = RADIO_LOWPOWER;
break;
default:
break;
}
}
static void radio_fetch_rx_pdu(void){
if (!ctx.rx_pdu_received) return;
ctx.rx_pdu_received = false;
// fetch reserved rx pdu
ll_pdu_t * rx_packet = ctx.rx_pdu;
btstack_assert(rx_packet != NULL);
// read max packet
uint16_t max_packet_len = 2 + 27;
SX1280HalReadBuffer( SX1280_RX0_OFFSET, &rx_packet->header, max_packet_len);
// queue if not empty
if (rx_packet->len != 0){
// packet used
ctx.rx_pdu = NULL;
// mark as data packet
rx_packet->flags |= LL_PDU_FLAG_DATA_PDU;
// queue received packet
btstack_linked_queue_enqueue(&ctx.rx_queue, (btstack_linked_item_t *) rx_packet);
}
}
/** Radio IRQ handlers */
static void radio_on_tx_done(void ){
switch (ll_state){
case LL_STATE_ADVERTISING:
switch (radio_state){
case RADIO_W4_TX_DONE_TO_RX:
receive_response();
break;
case RADIO_W4_TX_ONLY_DONE:
radio_state = RADIO_LOWPOWER;
break;
default:
break;
}
break;
case LL_STATE_CONNECTED:
btstack_assert(radio_state == RADIO_W4_TX_DONE_TO_RX);
receive_response();
radio_fetch_rx_pdu();
preload_tx_buffer();
break;
default:
break;
}
}
static void radio_prepare_auto_tx(uint16_t packet_end_ticks, uint8_t rx_len){
// restart supervision timeout
ctx.time_without_any_packets_us = 0;
// check if we can sent a full packet before sync hop
int16_t now_ticks = packet_end_ticks - ctx.anchor_ticks;
if (ctx.synced && (now_ticks > ctx.conn_latest_tx_ticks)){
// disable AutoTX to abort sending of next packet
Radio.SetFs();
log_info("Close before Sync hop: now %u > %u", now_ticks, ctx.conn_latest_tx_ticks);
// get rx pdu and
radio_fetch_rx_pdu();
return;
}
// setup empty packet in ll buffer if no tx packet was preloaded
if (ctx.num_tx_pdus_on_controller == 0) {
ctx.tx_buffer_pdu[ctx.next_tx_buffer] = &ll_empty_packet;
ctx.num_tx_pdus_on_controller++;
ll_empty_packet.header = PDU_DATA_LLID_DATA_CONTINUE;
ll_empty_packet.len = 0;
}
// setup pdu header
uint8_t packet_header[2];
uint8_t md = btstack_linked_queue_empty(&ctx.tx_queue) ? 0 : 1;
packet_header[0] = (md << 4) | (ctx.transmit_sequence_number << 3) | (ctx.next_expected_sequence_number << 2) | ctx.tx_buffer_pdu[ctx.next_tx_buffer]->header;
packet_header[1] = ctx.tx_buffer_pdu[ctx.next_tx_buffer]->len;
// select outgoing tx buffer and update pdu header
SX1280SetBufferBaseAddresses( tx_buffer_offset[ctx.next_tx_buffer], SX1280_RX0_OFFSET);
SX1280HalWriteBuffer( tx_buffer_offset[ctx.next_tx_buffer], (uint8_t *) packet_header, sizeof(packet_header));
// update operating state
SX1280AutoTxWillStart();
// set anchor on first packet in connection event
if (ctx.packet_nr_in_connection_event == 0){
// preamble (1) + aa (4) + header (1) + len (1) + payload (len) + crc (3) -- ISR handler ca. 35 us
uint16_t timestamp_delay = (10 + rx_len) * 8 - 35;
uint16_t packet_start_ticks = packet_end_ticks - US_TO_TICKS(timestamp_delay);
ctx.anchor_ticks = packet_start_ticks;
ctx.synced = true;
radio_set_timer_ticks(ctx.conn_sync_hop_ticks);
}
ctx.packet_nr_in_connection_event++;
// printf("RX %02x -- tx buffer %u, %02x %02x\n", rx_header, ctx.next_tx_buffer, packet_header[0], packet_header[1]);
}
static void radio_on_rx_done(void ){
uint16_t packet_end_ticks = hal_timer_get_ticks();
if (ll_state == LL_STATE_ADVERTISING){
// get rx pdu header
uint8_t rx_header;
SX1280HalReadBuffer( SX1280_RX0_OFFSET, &rx_header, 1);
// check for Scan Request
uint8_t pdu_type = rx_header & 0x0f;
if (pdu_type == PDU_ADV_TYPE_SCAN_REQ){
// scan request, select TX1 for active AutoTx
SX1280SetBufferBaseAddresses( SX1280_TX1_OFFSET, SX1280_RX0_OFFSET);
radio_state = RADIO_W4_TX_ONLY_DONE;
} else {
// fetch reserved rx pdu
ll_pdu_t * rx_packet = ctx.rx_pdu;
btstack_assert(rx_packet != NULL);
ctx.rx_pdu = NULL;
// no data packet
rx_packet->flags = 0;
uint16_t max_packet_len = 2 + LL_MAX_PAYLOAD;
// no scan request, disable auto tx and read complete buffer
radio_auto_tx_off();
SX1280HalReadBuffer( SX1280_RX0_OFFSET, &rx_packet->header, max_packet_len);
// queue received packet
btstack_linked_queue_enqueue(&ctx.rx_queue, (btstack_linked_item_t *) rx_packet);
}
} else if (ll_state == LL_STATE_CONNECTED){
// get and parse rx pdu header
uint8_t rx_buffer[2];
SX1280HalReadBuffer( SX1280_RX0_OFFSET, rx_buffer, 2);
uint8_t rx_header = rx_buffer[0];
uint8_t rx_len = rx_buffer[1];
uint8_t next_expected_sequence_number = (rx_header >> 2) & 1;
uint8_t sequence_number = (rx_header >> 3) & 1;
// more data field not used yet
// uint8_t more_data = (rx_packet->header >> 4) & 1;
// only accept packets with new sequence number and len <= payload size
if ((sequence_number == ctx.next_expected_sequence_number) && (rx_len <= LL_MAX_PAYLOAD)) {
bool rx_buffer_available = receive_prepare_rx_bufffer();
if (rx_buffer_available){
// update state
ctx.next_expected_sequence_number = 1 - sequence_number;
// register pdu fetch
ctx.rx_pdu_received = true;
}
}
// report outgoing packet as ack'ed and free if confirmed by peer
bool tx_acked = ctx.transmit_sequence_number != next_expected_sequence_number;
if (tx_acked){
if (ctx.num_tx_pdus_on_controller > 0){
ll_pdu_t * acked_pdu = ctx.tx_buffer_pdu[ctx.next_tx_buffer];
btstack_assert(acked_pdu != NULL);
// if non link-layer packet, free buffer and report as completed
if ((acked_pdu != &ll_tx_packet) && (acked_pdu != &ll_empty_packet)){
btstack_memory_ll_pdu_free(acked_pdu);
ctx.tx_buffer_pdu[ctx.next_tx_buffer] = NULL;
ctx.num_completed++;
}
// next buffer
ctx.num_tx_pdus_on_controller--;
ctx.next_tx_buffer = (ctx.next_tx_buffer + 1 ) & 1;
}
ctx.transmit_sequence_number = next_expected_sequence_number;
}
// packet received, now prepare for AutoTX
radio_prepare_auto_tx(packet_end_ticks, rx_len);
}
}
static void radio_on_tx_timeout(void ){
radio_state = RADIO_TX_TIMEOUT;
printf( "<>>>>>>>>TXE\n\r" );
}
static void radio_on_rx_timeout(void ){
switch (ll_state){
case LL_STATE_ADVERTISING:
radio_state = RADIO_RX_ERROR;
break;
default:
break;
}
}
static void radio_on_rx_error(IrqErrorCode_t errorCode ){
uint16_t packet_end_ticks = hal_timer_get_ticks();
uint8_t rx_buffer[2];
uint8_t rx_len;
switch (ll_state){
case LL_STATE_ADVERTISING:
radio_state = RADIO_RX_ERROR;
break;
case LL_STATE_CONNECTED:
// get len from rx pdu header
SX1280HalReadBuffer(SX1280_RX0_OFFSET, rx_buffer, 2);
rx_len = rx_buffer[1];
radio_prepare_auto_tx(packet_end_ticks, rx_len);
break;
default:
break;
}
}
const static RadioCallbacks_t Callbacks =
{
&radio_on_tx_done, // txDone
&radio_on_rx_done, // rxDone
NULL, // syncWordDone
NULL, // headerDone
&radio_on_tx_timeout, // txTimeout
&radio_on_rx_timeout, // rxTimeout
&radio_on_rx_error, // rxError
NULL, // rangingDone
NULL, // cadDone
};
// Link Layer
static void ll_emit_hci_event(const hci_event_t * event, ...){
va_list argptr;
va_start(argptr, event);
uint16_t length = hci_event_create_from_template_and_arglist(ll_outgoing_hci_event, sizeof(ll_outgoing_hci_event), event, argptr);
va_end(argptr);
controller_packet_handler(HCI_EVENT_PACKET, ll_outgoing_hci_event, length);
}
void ll_init(void){
// setup memory pools
btstack_memory_pool_create(&ll_pdu_pool, ll_pdu_pool_storage, MAX_NUM_LL_PDUS, sizeof(ll_pdu_t));
// set test bd addr 33:33:33:33:33:33
memset(ctx.bd_addr_le, 0x33, 6);
// default channels, advertising interval
ctx.adv_map = 0x7;
ctx.adv_interval_us = 1280000;
// init timer
hal_timer_init();
hal_timer_set_callback(&radio_timer_handler);
}
void ll_radio_on(void){
Radio.Init( (RadioCallbacks_t *) &Callbacks );
Radio.SetRegulatorMode( USE_DCDC ); // Can also be set in LDO mode but consume more power
Radio.SetInterruptMode( );
Radio.SetDioIrqParams( RX_TX_IRQ_MASK, RX_TX_IRQ_MASK, IRQ_RADIO_NONE, IRQ_RADIO_NONE );
ModulationParams_t modulationParams;
modulationParams.PacketType = PACKET_TYPE_BLE;
modulationParams.Params.Ble.BitrateBandwidth = GFSK_BLE_BR_1_000_BW_1_2;
modulationParams.Params.Ble.ModulationIndex = GFSK_BLE_MOD_IND_0_50;
modulationParams.Params.Ble.ModulationShaping = RADIO_MOD_SHAPING_BT_0_5;
Radio.SetStandby( STDBY_RC );
Radio.SetPacketType( modulationParams.PacketType );
Radio.SetModulationParams( &modulationParams );
Radio.SetBufferBaseAddresses( SX1280_TX0_OFFSET, SX1280_RX0_OFFSET );
Radio.SetTxParams( TX_PARAMS_OUTPUT_POWER, TX_PARAMS_RAMP_TIME );
// Go back to Frequcency Synthesis Mode, reduces transition time between Rx<->TX
Radio.SetAutoFS(1);
uint16_t fw_version = SX1280GetFirmwareVersion();
printf("FW Version: 0x%04x\n", fw_version);
// quick test
uint8_t data[] = {1, 2, 4, 8, 16, 32, 64, 128, 1, 2, 4, 8, 16, 32, 64, 128, 1, 2, 4, 8, 16, 32, 64, 128, 1, 2, 4, 8, 16, 32, 64, 128 };
Radio.WriteBuffer(0, data, sizeof(data));
uint8_t check[32];
Radio.ReadBuffer(0, check, sizeof(data));
if (memcmp(data, check, sizeof(data)) != 0) {
printf("GOOD: "); printf_hexdump(data, sizeof(data));
printf("BAD: "); printf_hexdump(check, sizeof(data));
btstack_assert(false);
}
ll_state = LL_STATE_STANDBY;
}
static void ll_handle_conn_ind(ll_pdu_t * rx_packet){
printf("Connect Req: ");
printf_hexdump(&rx_packet->header, rx_packet->len + 2);
uint8_t * init_addr = &rx_packet->payload[0];
uint8_t * adv_addr = &rx_packet->payload[6];
uint8_t chan_sel = (rx_packet->header >> 5) & 1;
// verify AdvA
if (memcmp(ctx.bd_addr_le, adv_addr, 6) != 0){
// differs, go back to adv sending
radio_state = RADIO_LOWPOWER;
return;
}
// TODO: get remote addr type
ctx.peer_addr_type = 0;
memcpy(ctx.peer_addr, init_addr, 6);
// get params for HCI event
const uint8_t * ll_data = &rx_packet->payload[12];
ctx.aa = little_endian_read_32(ll_data, 0);
uint8_t crc_init_0 = ll_data[4];
uint8_t crc_init_1 = ll_data[5];
uint8_t crc_init_2 = ll_data[6];
uint8_t win_size = ll_data[7];
uint16_t win_offset = little_endian_read_16(ll_data, 8);
uint16_t conn_interval_1250us = little_endian_read_16(ll_data, 10);
ctx.conn_latency = little_endian_read_16(ll_data, 12);
ctx.supervision_timeout_10ms = little_endian_read_16(ll_data, 14);
const uint8_t * channel_map = &ll_data[16];
uint8_t hop = ll_data[21] & 0x1f;
uint8_t sca = ll_data[21] >> 5;
UNUSED(sca);
UNUSED(win_offset);
UNUSED(win_size);
ctx_set_conn_interval(conn_interval_1250us);
// convert to us
ctx.supervision_timeout_us = ctx.supervision_timeout_10ms * 10000;
ctx.connection_event = 0;
ctx.packet_nr_in_connection_event = 0;
ctx.next_expected_sequence_number = 0;
ctx.transmit_sequence_number = 0;
// set AA
Radio.SetBleAccessAddress(ctx.aa);
// set CRC init value
Radio.WriteRegister(0x9c7, crc_init_2);
Radio.WriteRegister(0x9c8, crc_init_1);
Radio.WriteRegister(0x9c9, crc_init_0);
printf("Connection interval %u us\n", ctx.conn_interval_us);
printf("Connection timeout %u us\n", ctx.supervision_timeout_us);
printf("AA %08x\n", ctx.aa);
printf("CRC Init 0x%02x%02x%02x\n", crc_init_2, crc_init_1, crc_init_0);
// init hopping
hopping_init( &h );
hopping_set_channel_map( &h, channel_map);
ctx.channel_selection_algorithm = ctx.csa2_support & chan_sel;
switch (ctx.channel_selection_algorithm){
case 0:
hopping_csa1_set_hop_increment( &h, hop );
break;
case 1:
hopping_csa2_set_access_address( &h, ctx.aa);
break;
default:
break;
}
next_channel();
start_hopping();
radio_auto_tx_on();
// pre-load tx pdu
ctx.num_tx_pdus_on_controller = 0;
ctx.next_tx_buffer = 0;
preload_tx_buffer();
// get next packet
ll_state = LL_STATE_CONNECTED;
receive_first_master();
ll_send_connection_complete = true;
}
static void ll_queue_control_tx(void){
hal_cpu_disable_irqs();
btstack_linked_queue_enqueue(&ctx.tx_queue, (btstack_linked_item_t *) &ll_tx_packet);
hal_cpu_enable_irqs();
}
static void ll_handle_control(ll_pdu_t * rx_packet){
ll_pdu_t * tx_packet = &ll_tx_packet;
uint8_t opcode = rx_packet->payload[0];
switch (opcode){
case PDU_DATA_LLCTRL_TYPE_VERSION_IND:
tx_packet->len = 6;
tx_packet->header = PDU_DATA_LLID_CTRL;
tx_packet->payload[0] = PDU_DATA_LLCTRL_TYPE_VERSION_IND;
tx_packet->payload[1] = 0x06; // VersNr = Bluetooth Core V4.0
little_endian_store_16(tx_packet->payload, 2, BLUETOOTH_COMPANY_ID_BLUEKITCHEN_GMBH);
little_endian_store_16(tx_packet->payload, 4, 0);
ll_queue_control_tx();
printf("Queue Version Ind\n");
break;
case PDU_DATA_LLCTRL_TYPE_FEATURE_REQ:
tx_packet->len = 9;
tx_packet->header = PDU_DATA_LLID_CTRL;
tx_packet->payload[0] = PDU_DATA_LLCTRL_TYPE_FEATURE_RSP;
// TODO: set features of our controller
memset(&tx_packet->payload[1], 0, 8);
ll_queue_control_tx();
printf("Queue Feature Rsp\n");
break;
case PDU_DATA_LLCTRL_TYPE_CHAN_MAP_IND:
memcpy((uint8_t *) ctx.channel_map_update_map, &rx_packet->payload[1], 5);
ctx.channel_map_update_instant = little_endian_read_16(rx_packet->payload, 6);
ctx.channel_map_update_pending = true;
break;
case PDU_DATA_LLCTRL_TYPE_CONN_UPDATE_IND:
ctx.conn_param_update_win_size = rx_packet->payload[1];
ctx.conn_param_update_win_offset = little_endian_read_16(rx_packet->payload, 2);
ctx.conn_param_update_interval_1250us = little_endian_read_16(rx_packet->payload, 4);
ctx.conn_param_update_latency = little_endian_read_16(rx_packet->payload, 6);
ctx.conn_param_update_timeout_us = little_endian_read_16(rx_packet->payload, 8) * 10000;
ctx.conn_param_update_instant = little_endian_read_16(rx_packet->payload, 10);
ctx.conn_param_update_pending = true;
log_info("PDU_DATA_LLCTRL_TYPE_CONN_UPDATE_IND, conn interval %u 1250us at instant %u",
(unsigned int) ctx.conn_param_update_interval_1250us, ctx.conn_param_update_instant);
break;
case PDU_DATA_LLCTRL_TYPE_TERMINATE_IND:
printf("Terminate!\n");
ll_terminate();
break;
default:
btstack_assert(false);
printf("Unhandled LL Control PDU 0x%02x\n", opcode);
break;
}
}
static void ll_handle_data(ll_pdu_t * rx_packet){
if (ll_state != LL_STATE_CONNECTED) return;
btstack_assert(rx_packet->len <= LL_MAX_PAYLOAD);
uint8_t acl_packet[4 + LL_MAX_PAYLOAD];
// ACL Header
uint8_t ll_id = rx_packet->header & 3;
acl_packet[0] = 0x01;
acl_packet[1] = ll_id << 4;
little_endian_store_16(acl_packet, 2, rx_packet->len);
memcpy(&acl_packet[4], rx_packet->payload, rx_packet->len);
(*controller_packet_handler)(HCI_ACL_DATA_PACKET, acl_packet, rx_packet->len + 4);
}
void ll_set_scan_parameters(uint8_t le_scan_type, uint16_t le_scan_interval, uint16_t le_scan_window, uint8_t own_address_type, uint8_t scanning_filter_policy){
// TODO .. store other params
ll_scan_interval_us = ((uint32_t) le_scan_interval) * 625;
ll_scan_window_us = ((uint32_t) le_scan_window) * 625;
log_info("LE Scan Params: window %lu, interval %lu ms", ll_scan_interval_us, ll_scan_window_us);
}
static uint8_t ll_start_scanning(uint8_t filter_duplicates){
#if 0
// COMMAND DISALLOWED if wrong state.
if (ll_state != LL_STATE_STANDBY) return 0x0c;
ll_state = LL_STATE_SCANNING;
log_info("LE Scan Start: window %lu, interval %lu ms", ll_scan_interval_us, ll_scan_window_us);
// reset timer and capature events
NRF_TIMER0->TASKS_CLEAR = 1;
NRF_TIMER0->TASKS_STOP = 1;
NRF_TIMER0->EVENTS_COMPARE[0] = 0;
NRF_TIMER0->EVENTS_COMPARE[1] = 0;
// limit scanning
if (ll_scan_window_us < ll_scan_interval_us){
// setup PPI to disable radio after end of scan_window
NRF_TIMER0->CC[1] = ll_scan_window_us;
NRF_PPI->CHENSET = 1 << 22; // TIMER0->EVENTS_COMPARE[1] -> RADIO->TASKS_DISABLE
}
// set timer to trigger IRQ for next scan interval
NRF_TIMER0->CC[0] = ll_scan_interval_us;
NRF_TIMER0->INTENSET = TIMER_INTENSET_COMPARE0_Enabled << TIMER_INTENSET_COMPARE0_Pos;
// next channel to scan
int adv_channel = (random_generator_next() % 3) + 37;
log_debug("LE Scan Channel: %u", adv_channel);
// start receiving
NRF_TIMER0->TASKS_START = 1;
radio_receive_on_channel(adv_channel);
#endif
return 0;
}
static uint8_t ll_stop_scanning(void){
#if 0
// COMMAND DISALLOWED if wrong state.
if (ll_state != LL_STATE_SCANNING) return 0x0c;
log_info("LE Scan Stop");
ll_state = LL_STATE_STANDBY;
// stop radio
radio_disable();
#endif
return 0;
}
uint8_t ll_set_scan_enable(uint8_t le_scan_enable, uint8_t filter_duplicates){
if (le_scan_enable){
return ll_start_scanning(filter_duplicates);
} else {
return ll_stop_scanning();
}
}
static uint8_t ll_start_advertising(void){
// COMMAND DISALLOWED if wrong state.
if (ll_state != LL_STATE_STANDBY) return ERROR_CODE_COMMAND_DISALLOWED;
log_info("Start Advertising on channels 0x%0x, interval %lu us", ctx.adv_map, ctx.adv_interval_us);
start_advertising();
return ERROR_CODE_SUCCESS;
}
static uint8_t ll_stop_advertising(void){
// COMMAND DISALLOWED if wrong state.
if (ll_state != LL_STATE_ADVERTISING) return ERROR_CODE_COMMAND_DISALLOWED;
// TODO:
return ERROR_CODE_SUCCESS;
}
uint8_t ll_set_advertise_enable(uint8_t le_adv_enable){
if (le_adv_enable){
return ll_start_advertising();
} else {
return ll_stop_advertising();
}
}
uint8_t ll_set_advertising_parameters(uint16_t advertising_interval_min, uint16_t advertising_interval_max,
uint8_t advertising_type, uint8_t own_address_type, uint8_t peer_address_types, uint8_t * peer_address,
uint8_t advertising_channel_map, uint8_t advertising_filter_policy){
// validate channel map
if (advertising_channel_map == 0) return ERROR_CODE_INVALID_HCI_COMMAND_PARAMETERS;
if ((advertising_channel_map & 0xf8) != 0) return ERROR_CODE_INVALID_HCI_COMMAND_PARAMETERS;
// validate advertising interval
if (advertising_interval_min < 0x20) return ERROR_CODE_INVALID_HCI_COMMAND_PARAMETERS;
if (advertising_interval_min > 0x4000) return ERROR_CODE_INVALID_HCI_COMMAND_PARAMETERS;
if (advertising_interval_max < 0x20) return ERROR_CODE_INVALID_HCI_COMMAND_PARAMETERS;
if (advertising_interval_max > 0x4000) return ERROR_CODE_INVALID_HCI_COMMAND_PARAMETERS;
if (advertising_interval_min > advertising_interval_max) return ERROR_CODE_INVALID_HCI_COMMAND_PARAMETERS;
ctx.adv_map = advertising_channel_map;
ctx.adv_interval_us = advertising_interval_max * 625;
ctx.adv_type= advertising_type;
// TODO: validate other params
// TODO: process other params
return ERROR_CODE_SUCCESS;
}
uint8_t ll_set_advertising_data(uint8_t adv_len, const uint8_t * adv_data){
// COMMAND DISALLOWED if wrong state.
if (ll_state == LL_STATE_ADVERTISING) return ERROR_CODE_COMMAND_DISALLOWED;
if (adv_len > 31) return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
ctx.adv_len = adv_len;
memcpy(ctx.adv_data, adv_data, adv_len);
return ERROR_CODE_SUCCESS;
}
uint8_t ll_set_scan_response_data(uint8_t adv_len, const uint8_t * adv_data){
// COMMAND DISALLOWED if wrong state.
if (ll_state == LL_STATE_ADVERTISING) return ERROR_CODE_COMMAND_DISALLOWED;
if (adv_len > 31) return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
ctx.scan_resp_len = adv_len;
memcpy(ctx.scan_resp_data, adv_data, adv_len);
return ERROR_CODE_SUCCESS;
}
void ll_execute_once(void){
// process received packets
while (1){
ll_pdu_t * rx_packet = (ll_pdu_t *) btstack_linked_queue_dequeue(&ctx.rx_queue);
if (rx_packet == NULL) break;
if (rx_packet->len > 0){
if ((rx_packet->flags & LL_PDU_FLAG_DATA_PDU) == 0){
// ADV PDU
// connect ind?
if ((rx_packet->header & 0x0f) == PDU_ADV_TYPE_CONNECT_IND){
ll_handle_conn_ind(rx_packet);
}
else {
radio_state = RADIO_LOWPOWER;
}
} else {
// DATA PDU
uint8_t ll_id = rx_packet->header & 3;
if (ll_id == PDU_DATA_LLID_CTRL) {
ll_handle_control(rx_packet);
} else {
ll_handle_data(rx_packet);
}
}
}
// free packet
btstack_memory_ll_pdu_free(rx_packet);
}
switch ( ll_state ){
case LL_STATE_ADVERTISING:
ll_advertising_statemachine();
break;
default:
break;
}
// generate HCI events
// report num complete packets
/** critical section start */
hal_cpu_disable_irqs();
uint8_t num_completed = ctx.num_completed;
ctx.num_completed = 0;
hal_cpu_enable_irqs();
/** critical section end */
if (num_completed > 0){
ll_emit_hci_event(&hci_event_number_of_completed_packets_1, 1, HCI_CON_HANDLE, num_completed);
}
// report connection event
if (ll_send_connection_complete){
ll_send_connection_complete = false;
ll_emit_hci_event(&hci_subevent_le_connection_complete,
ERROR_CODE_SUCCESS, HCI_CON_HANDLE, 0x01 /* slave */, ctx.peer_addr_type, ctx.peer_addr,
ctx.conn_interval_1250us, ctx.conn_latency, ctx.supervision_timeout_10ms, 0 /* master clock accuracy */);
}
// report disconnection event
if (ll_send_disconnected){
ll_send_disconnected = false;
ll_emit_hci_event(&hci_event_disconnection_complete, ERROR_CODE_SUCCESS, HCI_CON_HANDLE, 0);
}
}
bool ll_reserve_acl_packet(void){
if (ll_reserved_acl_buffer == NULL){
ll_reserved_acl_buffer = btstack_memory_ll_pdu_get();
}
return ll_reserved_acl_buffer != NULL;
}
void ll_queue_acl_packet(const uint8_t * packet, uint16_t size){
btstack_assert(ll_reserved_acl_buffer != NULL);
ll_pdu_t * tx_packet = ll_reserved_acl_buffer;
ll_reserved_acl_buffer = NULL;
switch ((packet[1] >> 4) & 0x03){
case 0:
case 2:
tx_packet->header = PDU_DATA_LLID_DATA_START;
break;
case 1:
tx_packet->header = PDU_DATA_LLID_DATA_CONTINUE;
break;
case 3:
while(1);
break;
default:
break;
}
tx_packet->len = size - 4;
memcpy(tx_packet->payload, &packet[4], size - 4);
btstack_linked_queue_enqueue(&ctx.tx_queue, (btstack_linked_item_t *) tx_packet);
}
void ll_register_packet_handler(void (*packet_handler)(uint8_t packet_type, uint8_t * packet, uint16_t size)){
controller_packet_handler = packet_handler;
}