Bluetooth: Controller: A full, hardware-agnostic BLE Link Layer

The ll/ folder contains a full implementation of a BLE Link Layer
interfacing with a baseband and a radio through radio.h. The
current code implements most Bluetooth 4.2 features and is
currently functional and tested with nRF5x ICs from Nordic
Semiconductor.

Jira: ZEP-702

Origin: Original
Change-Id: Ib79cd97142d1a72c99dcf2a88116ac97ddd90a2b
Signed-off-by: Vinayak Chettimada <vinayak.kariappa.chettimada@nordicsemi.no>
Signed-off-by: Carles Cufi <carles.cufi@nordicsemi.no>
This commit is contained in:
Vinayak Chettimada 2016-08-15 12:28:31 +02:00 committed by Johan Hedberg
parent c160a0548d
commit 48c48711e6
9 changed files with 10400 additions and 0 deletions

View file

@ -1,5 +1,6 @@
ccflags-$(CONFIG_BLUETOOTH_CONTROLLER) += -I$(srctree)/drivers/bluetooth/controller/util
ccflags-$(CONFIG_BLUETOOTH_CONTROLLER) += -I$(srctree)/drivers/bluetooth/controller/hal
ccflags-$(CONFIG_BLUETOOTH_CONTROLLER) += -I$(srctree)/drivers/bluetooth/controller/ll
obj-$(CONFIG_BLUETOOTH_CONTROLLER) += util/mem.o
obj-$(CONFIG_BLUETOOTH_CONTROLLER) += util/memq.o
obj-$(CONFIG_BLUETOOTH_CONTROLLER) += util/work.o
@ -10,4 +11,7 @@ obj-$(CONFIG_BLUETOOTH_CONTROLLER) += hal/rtc.o
obj-$(CONFIG_BLUETOOTH_CONTROLLER) += hal/rand.o
obj-$(CONFIG_BLUETOOTH_CONTROLLER) += hal/ecb.o
obj-$(CONFIG_BLUETOOTH_CONTROLLER) += hal/radio.o
obj-$(CONFIG_BLUETOOTH_CONTROLLER) += ll/ticker.o
obj-$(CONFIG_BLUETOOTH_CONTROLLER) += ll/ctrl.o
obj-$(CONFIG_BLUETOOTH_CONTROLLER) += ll/ll.o
obj-$(CONFIG_BLUETOOTH_CONTROLLER) += hci/hci.o

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,255 @@
/*
* Copyright (c) 2016 Nordic Semiconductor ASA
* Copyright (c) 2016 Vinayak Kariappa Chettimada
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _CTRL_H_
#define _CTRL_H_
/*****************************************************************************
* Zephyr Kconfig defined
****************************************************************************/
#ifdef CONFIG_BLUETOOTH_MAX_CONN
#define RADIO_CONNECTION_CONTEXT_MAX CONFIG_BLUETOOTH_MAX_CONN
#else
#define RADIO_CONNECTION_CONTEXT_MAX 0
#endif
#ifdef CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH
#define RADIO_LL_LENGTH_OCTETS_RX_MAX \
CONFIG_BLUETOOTH_CONTROLLER_DATA_LENGTH
#endif
#ifdef CONFIG_BLUETOOTH_CONTROLLER_RX_BUFFERS
#define RADIO_PACKET_COUNT_RX_MAX \
CONFIG_BLUETOOTH_CONTROLLER_RX_BUFFERS
#endif
#ifdef CONFIG_BLUETOOTH_CONTROLLER_TX_BUFFERS
#define RADIO_PACKET_COUNT_TX_MAX \
CONFIG_BLUETOOTH_CONTROLLER_TX_BUFFERS
#endif
/*****************************************************************************
* Timer Resources (Controller defined)
****************************************************************************/
#define RADIO_TICKER_ID_EVENT 0
#define RADIO_TICKER_ID_MARKER_0 1
#define RADIO_TICKER_ID_PRE_EMPT 2
#define RADIO_TICKER_ID_ADV_STOP 3
#define RADIO_TICKER_ID_OBS_STOP 4
#define RADIO_TICKER_ID_ADV 5
#define RADIO_TICKER_ID_OBS 6
#define RADIO_TICKER_ID_FIRST_CONNECTION 7
#define RADIO_TICKER_INSTANCE_ID_RADIO 0
#define RADIO_TICKER_INSTANCE_ID_APP 1
#define RADIO_TICKER_USERS 3
#define RADIO_TICKER_USER_ID_WORKER 0
#define RADIO_TICKER_USER_ID_JOB 1
#define RADIO_TICKER_USER_ID_APP 2
#define RADIO_TICKER_USER_WORKER_OPS (7 + 1)
#define RADIO_TICKER_USER_JOB_OPS (2 + 1)
#define RADIO_TICKER_USER_APP_OPS (1 + 1)
#define RADIO_TICKER_USER_OPS (RADIO_TICKER_USER_WORKER_OPS \
+ RADIO_TICKER_USER_JOB_OPS \
+ RADIO_TICKER_USER_APP_OPS \
)
#define RADIO_TICKER_NODES (RADIO_TICKER_ID_FIRST_CONNECTION \
+ RADIO_CONNECTION_CONTEXT_MAX \
)
/*****************************************************************************
* Controller Interface Defines
****************************************************************************/
#define RADIO_BLE_VERSION_NUMBER (0x08)
#define RADIO_BLE_COMPANY_ID (0xFFFF)
#define RADIO_BLE_SUB_VERSION_NUMBER (0xFFFF)
#define RADIO_BLE_FEATURES (0x1F) /* LE Ping, Slave Initiated
* Feature request, Extended
* Reject Indication, Conn Param
* Req Procedure, LE encryption.
*/
/*****************************************************************************
* Controller Reference Defines (compile time override-able)
****************************************************************************/
/* Minimum LL Payload support (Dont change). */
#define RADIO_LL_LENGTH_OCTETS_RX_MIN 27
#define RADIO_LL_LENGTH_TIME_RX_MIN (((RADIO_LL_LENGTH_OCTETS_RX_MIN) \
+ 14) * 8 \
)
/* Maximum LL Payload support (27 to 251). */
#ifndef RADIO_LL_LENGTH_OCTETS_RX_MAX
#define RADIO_LL_LENGTH_OCTETS_RX_MAX 251
#endif
#define RADIO_LL_LENGTH_TIME_RX_MAX (((RADIO_LL_LENGTH_OCTETS_RX_MAX) \
+ 14) * 8 \
)
/* Implementation default L2CAP MTU */
#ifndef RADIO_L2CAP_MTU_MAX
#define RADIO_L2CAP_MTU_MAX (RADIO_LL_LENGTH_OCTETS_RX_MAX - 4)
#endif
/* Maximise L2CAP MTU to LL data PDU size */
#if (RADIO_L2CAP_MTU_MAX < (RADIO_LL_LENGTH_OCTETS_RX_MAX - 4))
#undef RADIO_L2CAP_MTU_MAX
#define RADIO_L2CAP_MTU_MAX (RADIO_LL_LENGTH_OCTETS_RX_MAX - 4)
#endif
/* Maximum LL PDU Receive pool size. */
#ifndef RADIO_PACKET_COUNT_RX_MAX
#define RADIO_PACKET_COUNT_RX ((RADIO_L2CAP_MTU_MAX + \
RADIO_LL_LENGTH_OCTETS_RX_MAX \
+ 3) \
/ \
RADIO_LL_LENGTH_OCTETS_RX_MAX \
)
#define RADIO_PACKET_COUNT_RX_MAX (RADIO_PACKET_COUNT_RX + \
((RADIO_CONNECTION_CONTEXT_MAX - 1) * \
(RADIO_PACKET_COUNT_RX - 1)) \
)
#endif /* RADIO_PACKET_COUNT_RX_MAX */
/* Maximum LL PDU Transmit pool size and application tx count. */
#ifndef RADIO_PACKET_COUNT_TX_MAX
#define RADIO_PACKET_COUNT_APP_TX_MAX (RADIO_CONNECTION_CONTEXT_MAX)
#define RADIO_PACKET_COUNT_TX_MAX (RADIO_PACKET_COUNT_RX_MAX + \
RADIO_PACKET_COUNT_APP_TX_MAX \
)
#else
#define RADIO_PACKET_COUNT_APP_TX_MAX (RADIO_PACKET_COUNT_TX_MAX)
#endif
/*****************************************************************************
* Controller Interface Structures
****************************************************************************/
struct radio_adv_data {
uint8_t data[DOUBLE_BUFFER_SIZE][RADIO_ACPDU_SIZE_MAX];
uint8_t first;
uint8_t last;
};
struct __packed radio_le_conn_cmplt {
uint8_t status;
uint8_t role;
uint8_t peer_addr_type;
uint8_t peer_addr[BDADDR_SIZE];
uint8_t own_addr_type;
uint8_t own_addr[BDADDR_SIZE];
uint8_t peer_irk_index;
uint16_t interval;
uint16_t latency;
uint16_t timeout;
uint8_t mca;
};
struct __packed radio_le_conn_update_cmplt {
uint8_t status;
uint16_t interval;
uint16_t latency;
uint16_t timeout;
};
struct radio_pdu_node_tx {
void *next;
uint8_t pdu_data[1];
};
enum radio_pdu_node_rx_type {
NODE_RX_TYPE_NONE,
NODE_RX_TYPE_DC_PDU,
NODE_RX_TYPE_PROFILE,
NODE_RX_TYPE_REPORT,
NODE_RX_TYPE_CONNECTION,
NODE_RX_TYPE_TERMINATE,
NODE_RX_TYPE_CONN_UPDATE,
NODE_RX_TYPE_ENC_REFRESH,
NODE_RX_TYPE_APTO,
NODE_RX_TYPE_RSSI,
};
struct radio_pdu_node_rx_hdr {
enum radio_pdu_node_rx_type type;
uint16_t handle;
union {
void *next;
void *link;
uint8_t packet_release_last;
} onion;
};
struct radio_pdu_node_rx {
struct radio_pdu_node_rx_hdr hdr;
uint8_t pdu_data[1];
};
/*****************************************************************************
* Controller Interface Functions
****************************************************************************/
uint32_t radio_init(uint8_t sca, uint8_t connection_count_max,
uint8_t rx_count_max, uint8_t tx_count_max,
uint16_t data_octets_max, uint8_t *mem_radio,
uint16_t mem_size);
void radio_ticks_active_to_start_set(uint32_t ticks_active_to_start);
struct radio_adv_data *radio_adv_data_get(void);
struct radio_adv_data *radio_scan_data_get(void);
void radio_filter_clear(void);
uint32_t radio_filter_add(uint8_t addr_type, uint8_t *addr);
void radio_irk_clear(void);
uint32_t radio_irk_add(uint8_t *irk);
uint32_t radio_adv_enable(uint16_t interval, uint8_t chl_map,
uint8_t filter_policy);
uint32_t radio_adv_disable(void);
uint32_t radio_scan_enable(uint8_t scan_type, uint8_t init_addr_type,
uint8_t *init_addr, uint16_t interval,
uint16_t window, uint8_t filter_policy);
uint32_t radio_scan_disable(void);
uint32_t radio_connect_enable(uint8_t adv_addr_type, uint8_t *adv_addr,
uint16_t interval, uint16_t latency,
uint16_t timeout);
uint32_t radio_connect_disable(void);
uint32_t radio_conn_update(uint16_t handle, uint8_t cmd, uint8_t status,
uint16_t interval, uint16_t latency,
uint16_t timeout);
uint32_t radio_chm_update(uint8_t *chm);
uint32_t radio_chm_get(uint16_t handle, uint8_t *chm);
uint32_t radio_enc_req_send(uint16_t handle, uint8_t *rand, uint8_t *ediv,
uint8_t *ltk);
uint32_t radio_start_enc_req_send(uint16_t handle, uint8_t err_code,
uint8_t const *const ltk);
uint32_t radio_feature_req_send(uint16_t handle);
uint32_t radio_version_ind_send(uint16_t handle);
uint32_t radio_terminate_ind_send(uint16_t handle, uint8_t reason);
uint32_t radio_length_req_send(uint16_t handle, uint16_t tx_octets);
uint8_t radio_rx_get(struct radio_pdu_node_rx **radio_pdu_node_rx,
uint16_t *handle);
void radio_rx_dequeue(void);
void radio_rx_mem_release(struct radio_pdu_node_rx **radio_pdu_node_rx);
uint8_t radio_rx_fc_set(uint16_t handle, uint8_t fc);
struct radio_pdu_node_tx *radio_tx_mem_acquire(void);
void radio_tx_mem_release(struct radio_pdu_node_tx *pdu_data_node_tx);
uint32_t radio_tx_mem_enqueue(uint16_t handle,
struct radio_pdu_node_tx *pdu_data_node_tx);
extern void radio_active_callback(uint8_t active);
extern void radio_event_callback(void);
#endif

View file

@ -0,0 +1,240 @@
/*
* Copyright (c) 2016 Nordic Semiconductor ASA
* Copyright (c) 2016 Vinayak Kariappa Chettimada
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stddef.h>
#include "util/defines.h"
#include "pdu.h"
#include "ctrl.h"
enum llcp {
LLCP_NONE,
LLCP_CONNECTION_UPDATE,
LLCP_CHANNEL_MAP,
LLCP_ENCRYPTION,
LLCP_FEATURE_EXCHANGE,
LLCP_VERSION_EXCHANGE,
/* LLCP_TERMINATE, */
LLCP_PING,
/* LLCP_LENGTH, */
};
struct shdr {
uint32_t ticks_xtal_to_start;
uint32_t ticks_active_to_start;
uint32_t ticks_preempt_to_start;
uint32_t ticks_slot;
};
struct connection {
struct shdr hdr;
uint8_t access_addr[4];
uint8_t crc_init[3];
uint8_t data_channel_map[5];
uint8_t data_channel_count;
uint8_t data_channel_hop;
uint8_t data_channel_use;
uint16_t handle;
uint16_t event_counter;
uint16_t conn_interval;
uint16_t latency;
uint16_t latency_prepare;
uint16_t latency_event;
uint16_t sug_tx_octets;
uint16_t max_tx_octets;
uint16_t max_rx_octets;
uint16_t supervision_reload;
uint16_t supervision_expire;
uint16_t procedure_reload;
uint16_t procedure_expire;
uint16_t appto_reload;
uint16_t appto_expire;
uint16_t apto_reload;
uint16_t apto_expire;
union {
struct {
uint8_t role:1;
uint8_t connect_expire;
} master;
struct {
uint8_t role:1;
uint8_t sca:3;
uint8_t latency_cancel:1;
uint32_t window_widening_periodic_us;
uint32_t window_widening_max_us;
uint32_t window_widening_prepare_us;
uint32_t window_widening_event_us;
uint32_t window_size_prepare_us;
uint32_t window_size_event_us;
uint32_t force;
uint32_t ticks_to_offset;
} slave;
} role;
uint8_t llcp_req;
uint8_t llcp_ack;
enum llcp llcp_type;
union {
struct {
uint16_t interval;
uint16_t latency;
uint16_t timeout;
uint8_t preferred_periodicity;
uint16_t instant;
uint16_t offset0;
uint16_t offset1;
uint16_t offset2;
uint16_t offset3;
uint16_t offset4;
uint16_t offset5;
uint32_t ticks_ref;
uint32_t ticks_to_offset_next;
uint32_t win_offset_us;
uint16_t *pdu_win_offset;
uint8_t win_size;
uint8_t state:3;
#define LLCP_CONN_STATE_INPROG 0 /* master + slave proc in progress
* until instant
*/
#define LLCP_CONN_STATE_INITIATE 1 /* master sends conn_update */
#define LLCP_CONN_STATE_REQ 2 /* master / slave send req */
#define LLCP_CONN_STATE_RSP 3 /* master rej / slave rej/rsp */
#define LLCP_CONN_STATE_APP_WAIT 4 /* app resp */
#define LLCP_CONN_STATE_RSP_WAIT 5 /* master rsp or slave conn_update
* or rej
*/
uint8_t is_internal:2;
} connection_update;
struct {
uint8_t initiate;
uint8_t chm[5];
uint16_t instant;
} channel_map;
struct {
uint8_t error_code;
uint8_t rand[8];
uint8_t ediv[2];
uint8_t ltk[16];
uint8_t skd[16];
} encryption;
} llcp;
uint8_t llcp_features;
struct {
uint8_t tx:1;
uint8_t rx:1;
uint8_t version_number;
uint16_t company_id;
uint16_t sub_version_number;
} llcp_version;
struct {
uint8_t req;
uint8_t ack;
uint8_t reason_own;
uint8_t reason_peer;
struct {
struct radio_pdu_node_rx_hdr hdr;
uint8_t reason;
} radio_pdu_node_rx;
} llcp_terminate;
struct {
uint8_t req;
uint8_t ack;
uint8_t state:2;
#define LLCP_LENGTH_STATE_REQ 0
#define LLCP_LENGTH_STATE_ACK_WAIT 1
#define LLCP_LENGTH_STATE_RSP_WAIT 2
#define LLCP_LENGTH_STATE_RESIZE 3
uint16_t rx_octets;
uint16_t tx_octets;
} llcp_length;
uint8_t sn:1;
uint8_t nesn:1;
uint8_t pause_rx:1;
uint8_t pause_tx:1;
uint8_t enc_rx:1;
uint8_t enc_tx:1;
uint8_t refresh:1;
uint8_t empty:1;
struct ccm ccm_rx;
struct ccm ccm_tx;
struct radio_pdu_node_tx *pkt_tx_head;
struct radio_pdu_node_tx *pkt_tx_ctrl;
struct radio_pdu_node_tx *pkt_tx_data;
struct radio_pdu_node_tx *pkt_tx_last;
uint8_t packet_tx_head_len;
uint8_t packet_tx_head_offset;
uint8_t rssi_latest;
uint8_t rssi_reported;
uint8_t rssi_sample_count;
};
#define CONNECTION_T_SIZE ALIGN4(sizeof(struct connection))
struct pdu_data_q_tx {
uint16_t handle;
struct radio_pdu_node_tx *node_tx;
};
/** @todo fix starvation when ctrl rx in radio ISR
* for multiple connections needs to tx back to peer.
*/
#define PACKET_MEM_COUNT_TX_CTRL 2
#define LL_MEM_CONN (sizeof(struct connection) * RADIO_CONNECTION_CONTEXT_MAX)
#define LL_MEM_RXQ (sizeof(void *) * (RADIO_PACKET_COUNT_RX_MAX + 4))
#define LL_MEM_TXQ (sizeof(struct pdu_data_q_tx) * \
(RADIO_PACKET_COUNT_TX_MAX + 2))
#define LL_MEM_RX_POOL_SZ (ALIGN4(__builtin_offsetof(struct radio_pdu_node_rx,\
pdu_data) + (\
((RADIO_ACPDU_SIZE_MAX + 1) < \
(__builtin_offsetof(struct pdu_data, payload) + \
RADIO_LL_LENGTH_OCTETS_RX_MAX)) ? \
((__builtin_offsetof(struct pdu_data, payload) + \
RADIO_LL_LENGTH_OCTETS_RX_MAX) * \
(RADIO_PACKET_COUNT_RX_MAX + 3)) \
: \
((RADIO_ACPDU_SIZE_MAX + 1) * \
(RADIO_PACKET_COUNT_RX_MAX + 3)) \
)))
#define LL_MEM_RX_LINK_POOL (sizeof(void *) * 2 * ((RADIO_PACKET_COUNT_RX_MAX +\
4) + RADIO_CONNECTION_CONTEXT_MAX))
#define LL_MEM_TX_CTRL_POOL ((ALIGN4(__builtin_offsetof( \
struct radio_pdu_node_tx, pdu_data) + \
__builtin_offsetof(struct pdu_data, payload) + 27)) * \
PACKET_MEM_COUNT_TX_CTRL)
#define LL_MEM_RX_DATA_POOL ((ALIGN4(__builtin_offsetof( \
struct radio_pdu_node_tx, pdu_data) + \
__builtin_offsetof(struct pdu_data, payload) + \
RADIO_LL_LENGTH_OCTETS_RX_MAX)) \
* (RADIO_PACKET_COUNT_TX_MAX + 2))
#define LL_MEM_TOTAL (LL_MEM_CONN + LL_MEM_RXQ + (LL_MEM_TXQ * 2) + \
LL_MEM_RX_POOL_SZ + \
LL_MEM_RX_LINK_POOL + LL_MEM_TX_CTRL_POOL + LL_MEM_RX_DATA_POOL)

View file

@ -0,0 +1,295 @@
/*
* Copyright (c) 2016 Nordic Semiconductor ASA
* Copyright (c) 2016 Vinayak Kariappa Chettimada
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stdint.h>
#include <string.h>
#include "defines.h"
#include "mem.h"
#include "ticker.h"
#include "ccm.h"
#include "radio.h"
#include "pdu.h"
#include "ctrl.h"
#include "ll.h"
#include "debug.h"
static struct {
uint8_t pub_addr[BDADDR_SIZE];
uint8_t rnd_addr[BDADDR_SIZE];
} _ll_context;
static struct {
uint16_t interval;
uint8_t adv_type:4;
uint8_t tx_addr:1;
uint8_t rx_addr:1;
uint8_t filter_policy:2;
uint8_t chl_map:3;
uint8_t adv_addr[BDADDR_SIZE];
uint8_t direct_addr[BDADDR_SIZE];
} _ll_adv_params;
static struct {
uint16_t interval;
uint16_t window;
uint8_t scan_type:1;
uint8_t tx_addr:1;
uint8_t filter_policy:1;
} _ll_scan_params;
void ll_address_get(uint8_t addr_type, uint8_t *bdaddr)
{
if (addr_type) {
memcpy(bdaddr, &_ll_context.rnd_addr[0], BDADDR_SIZE);
} else {
memcpy(bdaddr, &_ll_context.pub_addr[0], BDADDR_SIZE);
}
}
void ll_address_set(uint8_t addr_type, uint8_t const *const bdaddr)
{
if (addr_type) {
memcpy(&_ll_context.rnd_addr[0], bdaddr, BDADDR_SIZE);
} else {
memcpy(&_ll_context.pub_addr[0], bdaddr, BDADDR_SIZE);
}
}
void ll_adv_params_set(uint16_t interval, uint8_t adv_type,
uint8_t own_addr_type, uint8_t direct_addr_type,
uint8_t const *const direct_addr, uint8_t chl_map,
uint8_t filter_policy)
{
struct radio_adv_data *radio_adv_data;
struct pdu_adv *pdu;
/** @todo check and fail if adv role active else
* update (implemented below) current index elements for
* both adv and scan data.
*/
/* remember params so that set adv/scan data and adv enable
* interface can correctly update adv/scan data in the
* double buffer between caller and controller context.
*/
_ll_adv_params.interval = interval;
_ll_adv_params.chl_map = chl_map;
_ll_adv_params.filter_policy = filter_policy;
_ll_adv_params.adv_type = adv_type;
_ll_adv_params.tx_addr = own_addr_type;
_ll_adv_params.rx_addr = 0;
/* update the current adv data */
radio_adv_data = radio_adv_data_get();
pdu = (struct pdu_adv *)&radio_adv_data->data[radio_adv_data->last][0];
pdu->type = _ll_adv_params.adv_type;
pdu->tx_addr = _ll_adv_params.tx_addr;
if (adv_type == PDU_ADV_TYPE_DIRECT_IND) {
_ll_adv_params.rx_addr = direct_addr_type;
memcpy(&_ll_adv_params.direct_addr[0], direct_addr,
BDADDR_SIZE);
memcpy(&pdu->payload.direct_ind.init_addr[0],
direct_addr, BDADDR_SIZE);
pdu->len = sizeof(struct pdu_adv_payload_direct_ind);
} else if (pdu->len == 0) {
pdu->len = BDADDR_SIZE;
}
pdu->rx_addr = _ll_adv_params.rx_addr;
/* update the current scan data */
radio_adv_data = radio_scan_data_get();
pdu = (struct pdu_adv *)&radio_adv_data->data[radio_adv_data->last][0];
pdu->type = PDU_ADV_TYPE_SCAN_RESP;
pdu->tx_addr = _ll_adv_params.tx_addr;
pdu->rx_addr = 0;
if (pdu->len == 0) {
pdu->len = BDADDR_SIZE;
}
}
void ll_adv_data_set(uint8_t len, uint8_t const *const data)
{
struct radio_adv_data *radio_adv_data;
struct pdu_adv *pdu;
uint8_t last;
/** @todo dont update data if directed adv type. */
/* use the last index in double buffer, */
radio_adv_data = radio_adv_data_get();
if (radio_adv_data->first == radio_adv_data->last) {
last = radio_adv_data->last + 1;
if (last == DOUBLE_BUFFER_SIZE) {
last = 0;
}
} else {
last = radio_adv_data->last;
}
/* update adv pdu fields. */
pdu = (struct pdu_adv *)&radio_adv_data->data[last][0];
pdu->type = _ll_adv_params.adv_type;
pdu->tx_addr = _ll_adv_params.tx_addr;
pdu->rx_addr = _ll_adv_params.rx_addr;
memcpy(&pdu->payload.adv_ind.addr[0],
&_ll_adv_params.adv_addr[0], BDADDR_SIZE);
if (_ll_adv_params.adv_type == PDU_ADV_TYPE_DIRECT_IND) {
memcpy(&pdu->payload.direct_ind.init_addr[0],
&_ll_adv_params.direct_addr[0], BDADDR_SIZE);
pdu->len = sizeof(struct pdu_adv_payload_direct_ind);
} else {
memcpy(&pdu->payload.adv_ind.data[0], data, len);
pdu->len = BDADDR_SIZE + len;
}
/* commit the update so controller picks it. */
radio_adv_data->last = last;
}
void ll_scan_data_set(uint8_t len, uint8_t const *const data)
{
struct radio_adv_data *radio_scan_data;
struct pdu_adv *pdu;
uint8_t last;
/* use the last index in double buffer, */
radio_scan_data = radio_scan_data_get();
if (radio_scan_data->first == radio_scan_data->last) {
last = radio_scan_data->last + 1;
if (last == DOUBLE_BUFFER_SIZE) {
last = 0;
}
} else {
last = radio_scan_data->last;
}
/* update scan pdu fields. */
pdu = (struct pdu_adv *)&radio_scan_data->data[last][0];
pdu->type = PDU_ADV_TYPE_SCAN_RESP;
pdu->tx_addr = _ll_adv_params.tx_addr;
pdu->rx_addr = 0;
pdu->len = BDADDR_SIZE + len;
memcpy(&pdu->payload.scan_resp.addr[0],
&_ll_adv_params.adv_addr[0], BDADDR_SIZE);
memcpy(&pdu->payload.scan_resp.data[0], data, len);
/* commit the update so controller picks it. */
radio_scan_data->last = last;
}
uint32_t ll_adv_enable(uint8_t enable)
{
uint32_t status;
if (enable) {
struct radio_adv_data *radio_adv_data;
struct radio_adv_data *radio_scan_data;
struct pdu_adv *pdu_adv;
struct pdu_adv *pdu_scan;
/** @todo move the addr remembered into controller
* this way when implementing Privacy 1.2, generated
* new resolvable addresses can be used instantly.
*/
/* remember addr to use and also update the addr in
* both adv and scan PDUs.
*/
radio_adv_data = radio_adv_data_get();
radio_scan_data = radio_scan_data_get();
pdu_adv = (struct pdu_adv *)&radio_adv_data->data
[radio_adv_data->last][0];
pdu_scan = (struct pdu_adv *)&radio_scan_data->data
[radio_scan_data->last][0];
if (_ll_adv_params.tx_addr) {
memcpy(&_ll_adv_params.adv_addr[0],
&_ll_context.rnd_addr[0], BDADDR_SIZE);
memcpy(&pdu_adv->payload.adv_ind.addr[0],
&_ll_context.rnd_addr[0], BDADDR_SIZE);
memcpy(&pdu_scan->payload.scan_resp.addr[0],
&_ll_context.rnd_addr[0], BDADDR_SIZE);
} else {
memcpy(&_ll_adv_params.adv_addr[0],
&_ll_context.pub_addr[0], BDADDR_SIZE);
memcpy(&pdu_adv->payload.adv_ind.addr[0],
&_ll_context.pub_addr[0], BDADDR_SIZE);
memcpy(&pdu_scan->payload.scan_resp.addr[0],
&_ll_context.pub_addr[0], BDADDR_SIZE);
}
status = radio_adv_enable(_ll_adv_params.interval,
_ll_adv_params.chl_map,
_ll_adv_params.filter_policy);
} else {
status = radio_adv_disable();
}
return status;
}
void ll_scan_params_set(uint8_t scan_type, uint16_t interval, uint16_t window,
uint8_t own_addr_type, uint8_t filter_policy)
{
_ll_scan_params.scan_type = scan_type;
_ll_scan_params.interval = interval;
_ll_scan_params.window = window;
_ll_scan_params.tx_addr = own_addr_type;
_ll_scan_params.filter_policy = filter_policy;
}
uint32_t ll_scan_enable(uint8_t enable)
{
uint32_t status;
if (enable) {
status = radio_scan_enable(_ll_scan_params.scan_type,
_ll_scan_params.tx_addr,
(_ll_scan_params.tx_addr) ?
&_ll_context.rnd_addr[0] :
&_ll_context.pub_addr[0],
_ll_scan_params.interval,
_ll_scan_params.window,
_ll_scan_params.filter_policy);
} else {
status = radio_scan_disable();
}
return status;
}
uint32_t ll_create_connection(uint16_t scan_interval, uint16_t scan_window,
uint8_t filter_policy, uint8_t peer_addr_type,
uint8_t *peer_addr, uint8_t own_addr_type,
uint16_t interval, uint16_t latency,
uint16_t timeout)
{
uint32_t status;
status = radio_connect_enable(peer_addr_type, peer_addr, interval,
latency, timeout);
if (status) {
return status;
}
return radio_scan_enable(0, own_addr_type, (own_addr_type) ?
&_ll_context.rnd_addr[0] :
&_ll_context.pub_addr[0],
scan_interval, scan_window, filter_policy);
}

View file

@ -0,0 +1,39 @@
/*
* Copyright (c) 2016 Nordic Semiconductor ASA
* Copyright (c) 2016 Vinayak Kariappa Chettimada
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _LL_H_
#define _LL_H_
void ll_address_get(uint8_t addr_type, uint8_t *p_bdaddr);
void ll_address_set(uint8_t addr_type, uint8_t const *const p_bdaddr);
void ll_adv_params_set(uint16_t interval, uint8_t adv_type,
uint8_t own_addr_type, uint8_t direct_addr_type,
uint8_t const *const p_direct_addr, uint8_t chl_map,
uint8_t filter_policy);
void ll_adv_data_set(uint8_t len, uint8_t const *const p_data);
void ll_scan_data_set(uint8_t len, uint8_t const *const p_data);
uint32_t ll_adv_enable(uint8_t enable);
void ll_scan_params_set(uint8_t scan_type, uint16_t interval, uint16_t window,
uint8_t own_addr_type, uint8_t filter_policy);
uint32_t ll_scan_enable(uint8_t enable);
uint32_t ll_create_connection(uint16_t scan_interval, uint16_t scan_window,
uint8_t filter_policy, uint8_t peer_addr_type,
uint8_t *p_peer_addr, uint8_t own_addr_type,
uint16_t interval, uint16_t latency,
uint16_t timeout);
#endif /* _LL_H_ */

View file

@ -0,0 +1,260 @@
/*
* Copyright (c) 2016 Nordic Semiconductor ASA
* Copyright (c) 2016 Vinayak Kariappa Chettimada
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _PDU_H_
#define _PDU_H_
#include <toolchain.h>
struct __packed pdu_adv_payload_adv_ind {
uint8_t addr[BDADDR_SIZE];
uint8_t data[31];
};
struct __packed pdu_adv_payload_direct_ind {
uint8_t adv_addr[BDADDR_SIZE];
uint8_t init_addr[BDADDR_SIZE];
};
struct __packed pdu_adv_payload_scan_resp {
uint8_t addr[BDADDR_SIZE];
uint8_t data[31];
};
struct __packed pdu_adv_payload_scan_req {
uint8_t scan_addr[BDADDR_SIZE];
uint8_t adv_addr[BDADDR_SIZE];
};
struct __packed pdu_adv_payload_connect_req {
uint8_t init_addr[BDADDR_SIZE];
uint8_t adv_addr[BDADDR_SIZE];
struct __packed {
uint8_t access_addr[4];
uint8_t crc_init[3];
uint8_t win_size;
uint16_t win_offset;
uint16_t interval;
uint16_t latency;
uint16_t timeout;
uint8_t channel_map[5];
uint8_t hop:5;
uint8_t sca:3;
} lldata;
};
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_SCAN_RESP = 0x04,
PDU_ADV_TYPE_CONNECT_REQ = 0x05,
PDU_ADV_TYPE_SCAN_IND = 0x06,
};
struct __packed pdu_adv {
uint8_t type:4;
uint8_t rfu0:2;
uint8_t tx_addr:1;
uint8_t rx_addr:1;
uint8_t len:6;
uint8_t rfu1:2;
uint8_t nrf_radio_s1;
union __packed {
struct pdu_adv_payload_adv_ind adv_ind;
struct pdu_adv_payload_direct_ind direct_ind;
struct pdu_adv_payload_scan_req scan_req;
struct pdu_adv_payload_scan_resp scan_resp;
struct pdu_adv_payload_connect_req connect_req;
} payload;
};
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,
};
enum pdu_data_llctrl_type {
PDU_DATA_LLCTRL_TYPE_CONN_UPDATE_REQ = 0x00,
PDU_DATA_LLCTRL_TYPE_CHANNEL_MAP_REQ = 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_IND_EXT = 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,
};
struct __packed pdu_data_llctrl_conn_update_req {
uint8_t win_size;
uint16_t win_offset;
uint16_t interval;
uint16_t latency;
uint16_t timeout;
uint16_t instant;
};
struct __packed pdu_data_llctrl_channel_map_req {
uint8_t chm[5];
uint16_t instant;
};
struct __packed pdu_data_llctrl_terminate_ind {
uint8_t error_code;
};
struct __packed pdu_data_llctrl_enc_req {
uint8_t rand[8];
uint8_t ediv[2];
uint8_t skdm[8];
uint8_t ivm[4];
};
struct __packed pdu_data_llctrl_enc_rsp {
uint8_t skds[8];
uint8_t ivs[4];
};
struct __packed pdu_data_llctrl_unknown_rsp {
uint8_t type;
};
struct __packed pdu_data_llctrl_feature_req {
uint8_t features[8];
};
struct __packed pdu_data_llctrl_feature_rsp {
uint8_t features[8];
};
struct __packed pdu_data_llctrl_version_ind {
uint8_t version_number;
uint16_t company_id;
uint16_t sub_version_number;
};
struct __packed pdu_data_llctrl_reject_ind {
uint8_t error_code;
};
struct __packed pdu_data_llctrl_conn_param_req {
uint16_t interval_min;
uint16_t interval_max;
uint16_t latency;
uint16_t timeout;
uint8_t preferred_periodicity;
uint16_t reference_conn_event_count;
uint16_t offset0;
uint16_t offset1;
uint16_t offset2;
uint16_t offset3;
uint16_t offset4;
uint16_t offset5;
};
struct __packed pdu_data_llctrl_conn_param_rsp {
uint16_t interval_min;
uint16_t interval_max;
uint16_t latency;
uint16_t timeout;
uint8_t preferred_periodicity;
uint16_t reference_conn_event_count;
uint16_t offset0;
uint16_t offset1;
uint16_t offset2;
uint16_t offset3;
uint16_t offset4;
uint16_t offset5;
};
struct __packed pdu_data_llctrl_reject_ind_ext {
uint8_t reject_opcode;
uint8_t error_code;
};
struct __packed pdu_data_llctrl_length_req_rsp {
uint16_t max_rx_octets;
uint16_t max_rx_time;
uint16_t max_tx_octets;
uint16_t max_tx_time;
};
struct __packed pdu_data_llctrl {
uint8_t opcode;
union __packed {
struct pdu_data_llctrl_conn_update_req conn_update_req;
struct pdu_data_llctrl_channel_map_req channel_map_req;
struct pdu_data_llctrl_terminate_ind terminate_ind;
struct pdu_data_llctrl_enc_req enc_req;
struct pdu_data_llctrl_enc_rsp enc_rsp;
struct pdu_data_llctrl_unknown_rsp unknown_rsp;
struct pdu_data_llctrl_feature_req feature_req;
struct pdu_data_llctrl_feature_rsp feature_rsp;
struct pdu_data_llctrl_version_ind version_ind;
struct pdu_data_llctrl_reject_ind reject_ind;
struct pdu_data_llctrl_feature_req slave_feature_req;
struct pdu_data_llctrl_conn_param_req conn_param_req;
struct pdu_data_llctrl_conn_param_rsp conn_param_rsp;
struct pdu_data_llctrl_reject_ind_ext reject_ind_ext;
struct pdu_data_llctrl_length_req_rsp length_req;
struct pdu_data_llctrl_length_req_rsp length_rsp;
} ctrldata;
};
struct __packed profile {
uint32_t min;
uint32_t avg;
uint32_t max;
};
struct __packed pdu_data {
uint8_t ll_id:2;
uint8_t nesn:1;
uint8_t sn:1;
uint8_t md:1;
uint8_t rfu0:3;
uint8_t len:8;
uint8_t resv:8;
union __packed {
uint8_t lldata[1];
struct pdu_data_llctrl llctrl;
uint8_t rssi;
struct profile profile;
} payload;
};
#endif /* _PDU_H_ */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,138 @@
/*
* Copyright (c) 2016 Nordic Semiconductor ASA
* Copyright (c) 2016 Vinayak Kariappa Chettimada
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _TICKER_H_
#define _TICKER_H_
/** \brief Macro to translate microseconds to tick units.
*
* \note This returns the floor value.
*/
#define TICKER_US_TO_TICKS(x) \
( \
((uint32_t)(((uint64_t) (x) * 1000000000UL) / 30517578125UL)) \
& 0x00FFFFFF \
)
/** \brief Macro returning remainder in nanoseconds over-and-above a tick unit.
*/
#define TICKER_REMAINDER(x) \
( \
( \
((uint64_t) (x) * 1000000000UL) \
- ((uint64_t) TICKER_US_TO_TICKS(x) * 30517578125UL) \
) \
/ 1000UL \
)
/** \brief Macro to translate tick units to microseconds.
*/
#define TICKER_TICKS_TO_US(x) \
((uint32_t)(((uint64_t) (x) * 30517578125UL) / 1000000000UL))
/** \defgroup Timer API return codes.
*
* @{
*/
#define TICKER_STATUS_SUCCESS 0 /**< Success. */
#define TICKER_STATUS_FAILURE 1 /**< Failure. */
#define TICKER_STATUS_BUSY 2 /**< Busy, requested feature will
* complete later in time as job is
* disabled or at lower execution
* priority than the caller.
*/
/**
* @}
*/
/** \defgroup Timer API common defaults parameter values.
*
* @{
*/
#define TICKER_NULL ((uint8_t)((uint8_t)0 - 1))
#define TICKER_NULL_REMAINDER 0
#define TICKER_NULL_PERIOD 0
#define TICKER_NULL_SLOT 0
#define TICKER_NULL_LAZY 0
/**
* @}
*/
/** \brief Timer node type size.
*/
#define TICKER_NODE_T_SIZE 36
/** \brief Timer user type size.
*/
#define TICKER_USER_T_SIZE 8
/** \brief Timer user operation type size.
*/
#define TICKER_USER_OP_T_SIZE 44
/** \brief Timer timeout function type.
*/
typedef void (*ticker_timeout_func) (uint32_t ticks_at_expire,
uint32_t remainder, uint16_t lazy,
void *context);
/** \brief Timer operation complete function type.
*/
typedef void (*ticker_op_func) (uint32_t status, void *op_context);
/** \brief Timer module initialization.
*
* \param[in] instance_index Timer mode instance 0 or 1 (uses RTC0 CMP0 or
* CMP1 respectively).
* \param[in] count_node Max. no. of ticker nodes to initialise.
* \param[in] node
* \param[in] count_user
* \param[in] user
* \param[in] count_op
* \param[in] user_op
*/
uint32_t ticker_init(uint8_t instance_index, uint8_t count_node, void *node,
uint8_t count_user, void *user, uint8_t count_op,
void *user_op);
void ticker_trigger(uint8_t instance_index);
uint32_t ticker_start(uint8_t instance_index, uint8_t user_id,
uint8_t ticker_id, uint32_t ticks_anchor,
uint32_t ticks_first, uint32_t ticks_periodic,
uint32_t remainder_periodic, uint16_t lazy,
uint16_t ticks_slot,
ticker_timeout_func ticker_timeout_func, void *context,
ticker_op_func fp_op_func, void *op_context);
uint32_t ticker_update(uint8_t instance_index, uint8_t user_id,
uint8_t ticker_id, uint16_t ticks_drift_plus,
uint16_t ticks_drift_minus, uint16_t ticks_slot_plus,
uint16_t ticks_slot_minus, uint16_t lazy, uint8_t force,
ticker_op_func fp_op_func, void *op_context);
uint32_t ticker_stop(uint8_t instance_index, uint8_t user_id,
uint8_t ticker_id, ticker_op_func fp_op_func,
void *op_context);
uint32_t ticker_next_slot_get(uint8_t instance_index, uint8_t user_id,
uint8_t *ticker_id_head,
uint32_t *ticks_current,
uint32_t *ticks_to_expire,
ticker_op_func fp_op_func, void *op_context);
uint32_t ticker_job_idle_get(uint8_t instance_index, uint8_t user_id,
ticker_op_func fp_op_func, void *op_context);
void ticker_job_sched(uint8_t instance_index);
uint32_t ticker_ticks_now_get(void);
uint32_t ticker_ticks_diff_get(uint32_t ticks_now, uint32_t ticks_old);
#endif