Bluetooth: controller: push topic branch to main

Pushes all work done in the topic-ble-llcp branch into main branch
This is a refactoring of the LL control procedures; the refactored
control procedures are hidden behind a KConfig option and
per default disabled

Goal of the refactoring:

close issue Link Layer Control Procedure overhaul #15256
make it easier to add/update control procedures
Refactoring consists in principal of writing explicit state machines
for the control procedures.
To reduce the risk of regression errors unit-tests have been added

Following control procedures are implemented:

Connection update procedure
Channel map update procedure
Encryption procedure
Feature exchange procedure
Version exchange procedure
ACL termination procedure
Connection parameters request procedure
LE Ping procedure
Data Length Update procedure
PHY update procedure
Min. nr. Of channels used procedure
Constant Tone extension request procedure

This is a joined work by the people listed in the signed-off-by
list (in alphabetical order)

Signed-off-by: Andries Kruithof Andries.Kruithof@nordicsemi.no
Signed-off-by: Erik Brockhoff erbr@oticon.com
Signed-off-by: Piotr Pryga piotr.pryga@nordicsemi.no
Signed-off-by: Szymon Janc szymon.janc@codecoup.pl
Signed-off-by: Thomas Ebert Hansen thoh@oticon.com
Signed-off-by: Tommie Skriver tosk@demant.com

Signed-off-by: Andries Kruithof <Andries.Kruithof@nordicsemi.no>
This commit is contained in:
Andries Kruithof 2021-11-15 09:41:12 +01:00 committed by Anas Nashif
commit f023b5f611
161 changed files with 24482 additions and 81 deletions

View file

@ -201,6 +201,14 @@ struct bt_hci_cp_vs_set_usb_transport_mode {
uint8_t mode;
} __packed;
#define BT_HCI_OP_VS_SET_MIN_NUM_USED_CHANS BT_OP(BT_OGF_VS, 0x0012)
struct bt_hci_cp_vs_set_min_num_used_chans {
uint16_t handle;
uint8_t phys;
uint8_t min_used_chans;
} __packed;
/* Events */
struct bt_hci_evt_vs {

View file

@ -71,6 +71,27 @@ if(CONFIG_BT_LL_SW_SPLIT)
zephyr_library_sources(
ll_sw/ull_conn.c
)
if(CONFIG_BT_LL_SW_LLCP_LEGACY)
else()
zephyr_library_sources_ifdef(
CONFIG_BT_PHY_UPDATE
ll_sw/ull_llcp_phy.c
)
zephyr_library_sources_ifdef(
CONFIG_BT_CTLR_LE_ENC
ll_sw/ull_llcp_enc.c
)
zephyr_library_sources(
ll_sw/ull_tx_queue.c
ll_sw/ull_llcp.c
ll_sw/ull_llcp_common.c
ll_sw/ull_llcp_local.c
ll_sw/ull_llcp_pdu.c
ll_sw/ull_llcp_conn_upd.c
ll_sw/ull_llcp_chmu.c
ll_sw/ull_llcp_remote.c
)
endif()
if(CONFIG_BT_PERIPHERAL)
zephyr_library_sources(
ll_sw/ull_peripheral.c

View file

@ -74,6 +74,27 @@ config BT_CTLR_TIFS_HW_SUPPORT
config BT_CTLR_ULL_LLL_PRIO_SUPPORT
bool
choice BT_LL_SW_LLCP_IMPL
prompt "Bluetooth Low Energy Software Link Layer Control Procedure Implementation"
default BT_LL_SW_LLCP_LEGACY
help
Select the Bluetooth Low Energy Software Link Layer Control Procedure implementation.
config BT_LL_SW_LLCP_LEGACY
bool "Legacy implementation"
help
Use the Bluetooth Low Energy Software Link Layer Legacy Control Procedure implementation.
config BT_LL_SW_LLCP
bool "New implementation, replacing the legacy one [EXPERIMENTAL]"
select EXPERIMENTAL
help
Use the new Bluetooth Low Energy Software Link Layer Control Procedure implementation.
It is considered experimental because it is still under development and is not qualifiable yet.
endchoice
config BT_CTLR_RX_PRIO_STACK_SIZE
# Controller's Co-Operative high priority Rx thread stack size.
int "High priority Rx thread stack size"
@ -461,6 +482,49 @@ config BT_CTLR_LLCP_CONN
more than this number of connections simultaneously may cause
instabilities.
if !BT_LL_SW_LLCP_LEGACY
config BT_CTLR_LLCP_TX_PER_CONN_TX_CTRL_BUF_NUM_MAX
int
default 4
help
The theoretical maximum number of tx ctrl buffers needed for any connection, is 4.
two for active encryption procedure plus one for rejecting a remote request
and one for a local terminate
config BT_CTLR_LLCP_PER_CONN_TX_CTRL_BUF_NUM
int "Number of tx control buffers to be reserved per connection"
default BT_CTLR_LLCP_TX_PER_CONN_TX_CTRL_BUF_NUM_MAX
range 0 BT_CTLR_LLCP_TX_PER_CONN_TX_CTRL_BUF_NUM_MAX
help
Set the number control buffers that is to be pre allocated per connection
This defines the minimum number of buffers available for any connection
Setting this to non zero will ensure a connection will always have access
to buffer(s) for control procedure TX
config BT_CTLR_LLCP_COMMON_TX_CTRL_BUF_NUM
int "Number of tx control buffers to be available across all connections"
default 0
range 0 255
help
Set the number control buffers that is to be available for tx.
This defines the size of the pool of tx buffers available
for control procedure tx. This pool is shared across all
procedures/connections with allocation through a fifo queue.
Configure between 0 and (4 - BT_CTLR_LLCP_PER_CONN_TX_CTRL_BUF_NUM) * BT_CTLR_LLCP_CONN
config BT_CTLR_LLCP_PROC_CTX_BUF_NUM
int "Number of control procedure contexts to be available across all connections"
default BT_CTLR_LLCP_CONN
range 1 255
help
Set the number control procedure contexts that is to be available.
This defines the size of the pool of control procedure contexts available
for handlign control procedures. This pool is shared across all
connections (local vs remote initiate), with allocation through a queue
endif #!BT_LL_SW_LLCP_LEGACY
config BT_CTLR_LLID_DATA_START_EMPTY
bool "Handle zero length L2CAP start frame"
default y if BT_HCI_RAW

View file

@ -47,6 +47,9 @@
#include "ll_sw/ull_adv_types.h"
#include "ll_sw/ull_scan_types.h"
#include "ll_sw/ull_sync_types.h"
#if !defined(CONFIG_BT_LL_SW_LLCP_LEGACY)
#include "ull_tx_queue.h"
#endif
#include "ll_sw/ull_sync_internal.h"
#include "ll_sw/ull_conn_types.h"
#include "ll_sw/ull_conn_internal.h"
@ -2325,6 +2328,7 @@ static void le_reject_cis(struct net_buf *buf, struct net_buf **evt)
#endif /* CONFIG_BT_PERIPHERAL */
#if defined(CONFIG_BT_CENTRAL) || defined(CONFIG_BT_CTLR_PER_INIT_FEAT_XCHG)
static void le_read_remote_features(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_le_read_remote_features *cmd = (void *)buf->data;
@ -2336,6 +2340,7 @@ static void le_read_remote_features(struct net_buf *buf, struct net_buf **evt)
*evt = cmd_status(status);
}
#endif /* CONFIG_BT_CENTRAL || CONFIG_BT_CTLR_PER_INIT_FEAT_XCHG */
static void le_read_chan_map(struct net_buf *buf, struct net_buf **evt)
{
@ -3995,9 +4000,11 @@ static int controller_cmd_handle(uint16_t ocf, struct net_buf *cmd,
le_read_chan_map(cmd, evt);
break;
#if defined(CONFIG_BT_CENTRAL) || defined(CONFIG_BT_CTLR_PER_INIT_FEAT_XCHG)
case BT_OCF(BT_HCI_OP_LE_READ_REMOTE_FEATURES):
le_read_remote_features(cmd, evt);
break;
#endif /* CONFIG_BT_CENTRAL || CONFIG_BT_CTLR_PER_INIT_FEAT_XCHG */
case BT_OCF(BT_HCI_OP_LE_CONN_UPDATE):
le_conn_update(cmd, evt);
@ -4380,7 +4387,20 @@ static void vs_read_key_hierarchy_roots(struct net_buf *buf,
rp->status = 0x00;
hci_vendor_read_key_hierarchy_roots(rp->ir, rp->er);
}
#if !defined(CONFIG_BT_LL_SW_LLCP_LEGACY)
#if defined(CONFIG_BT_CTLR_MIN_USED_CHAN) && defined(CONFIG_BT_PERIPHERAL)
static void vs_set_min_used_chans(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_vs_set_min_num_used_chans *cmd = (void *)buf->data;
uint16_t handle = sys_le16_to_cpu(cmd->handle);
uint8_t status;
status = ll_set_min_used_chans(handle, cmd->phys, cmd->min_used_chans);
*evt = cmd_complete_status(status);
}
#endif /* CONFIG_BT_CTLR_MIN_USED_CHAN && CONFIG_BT_PERIPHERAL */
#endif /* !CONFIG_BT_LL_SW_LLCP_LEGACY */
#if defined(CONFIG_BT_CTLR_TX_PWR_DYNAMIC_CONTROL)
static void vs_write_tx_power_level(struct net_buf *buf, struct net_buf **evt)
{
@ -4635,6 +4655,14 @@ int hci_vendor_cmd_handle_common(uint16_t ocf, struct net_buf *cmd,
break;
#endif /* CONFIG_BT_HCI_MESH_EXT */
#if !defined(CONFIG_BT_LL_SW_LLCP_LEGACY)
#if defined(CONFIG_BT_CTLR_MIN_USED_CHAN) && defined(CONFIG_BT_PERIPHERAL)
case BT_OCF(BT_HCI_OP_VS_SET_MIN_NUM_USED_CHANS):
vs_set_min_used_chans(cmd, evt);
break;
#endif /* CONFIG_BT_CTLR_MIN_USED_CHAN && CONFIG_BT_PERIPHERAL */
#endif /* !CONFIG_BT_LL_SW_LLCP_LEGACY */
default:
return -EINVAL;
}

View file

@ -255,8 +255,8 @@ uint8_t ll_conn_update(uint16_t handle, uint8_t cmd, uint8_t status, uint16_t in
uint16_t interval_max, uint16_t latency, uint16_t timeout);
uint8_t ll_chm_update(uint8_t const *const chm);
uint8_t ll_chm_get(uint16_t handle, uint8_t *const chm);
uint8_t ll_enc_req_send(uint16_t handle, uint8_t const *const rand,
uint8_t const *const ediv, uint8_t const *const ltk);
uint8_t ll_enc_req_send(uint16_t handle, uint8_t const *const rand_num, uint8_t const *const ediv,
uint8_t const *const ltk);
uint8_t ll_start_enc_req_send(uint16_t handle, uint8_t err_code,
uint8_t const *const ltk);
uint8_t ll_feature_req_send(uint16_t handle);
@ -285,6 +285,9 @@ uint8_t ll_phy_get(uint16_t handle, uint8_t *const tx, uint8_t *const rx);
uint8_t ll_phy_default_set(uint8_t tx, uint8_t rx);
uint8_t ll_phy_req_send(uint16_t handle, uint8_t tx, uint8_t flags, uint8_t rx);
uint8_t ll_set_min_used_chans(uint16_t handle, uint8_t const phys,
uint8_t const min_used_chans);
/* Direction Finding */
/* Sets CTE transmission parameters for periodic advertising */
uint8_t ll_df_set_cl_cte_tx_params(uint8_t adv_handle, uint8_t cte_len,

View file

@ -192,7 +192,10 @@
/* All defined feature bits */
#define LL_FEAT_BIT_MASK 0xFFFFFFFFFULL
/* Feature bits that are valid from controller to controller */
/*
* LL_FEAT_BIT_MASK_VALID is defined as per
* Core Spec V5.2 Volume 6, Part B, chapter 4.6
*/
#define LL_FEAT_BIT_MASK_VALID 0xFF787CF2FULL
/* Mask to filter away octet 0 for feature exchange */

View file

@ -87,5 +87,5 @@ uint8_t *ll_addr_read(uint8_t addr_type, uint8_t *const bdaddr)
void bt_ctlr_set_public_addr(const uint8_t *addr)
{
(void)memcpy(pub_addr, addr, sizeof(pub_addr));
(void)memcpy(pub_addr, addr, sizeof(pub_addr));
}

View file

@ -28,6 +28,10 @@
#include "lll/lll_df_types.h"
#include "lll_conn.h"
#if !defined(CONFIG_BT_LL_SW_LLCP_LEGACY)
#include "ull_tx_queue.h"
#endif
#include "ull_adv_types.h"
#include "ull_scan_types.h"
#include "ull_conn_types.h"

View file

@ -143,6 +143,9 @@ enum done_result {
DONE_LATE
};
/* Forward declaration data type to store CTE IQ samples report related data */
struct cte_conn_iq_report;
struct ull_hdr {
uint8_t volatile ref; /* Number of ongoing (between Prepare and Done)
* events
@ -279,6 +282,8 @@ struct node_rx_ftr {
*/
void *aux_ptr;
uint8_t aux_phy;
uint8_t aux_sched;
struct cte_conn_iq_report *iq_report;
};
uint32_t ticks_anchor;
uint32_t radio_end_us;

View file

@ -28,6 +28,15 @@ struct node_tx {
uint8_t pdu[];
};
#if defined(CONFIG_BT_CTLR_DATA_LENGTH)
struct data_pdu_length {
uint16_t max_tx_octets;
uint16_t max_rx_octets;
uint16_t max_tx_time;
uint16_t max_rx_time;
};
#endif /* CONFIG_BT_CTLR_DATA_LENGTH */
struct lll_conn {
struct lll_hdr hdr;
@ -78,6 +87,8 @@ struct lll_conn {
};
#if defined(CONFIG_BT_CTLR_DATA_LENGTH)
#ifdef CONFIG_BT_LL_SW_LLCP_LEGACY
uint16_t max_tx_octets;
uint16_t max_rx_octets;
@ -85,7 +96,16 @@ struct lll_conn {
uint16_t max_tx_time;
uint16_t max_rx_time;
#endif /* CONFIG_BT_CTLR_PHY */
#else /* CONFIG_BT_LL_SW_LLCP_LEGACY */
struct {
struct data_pdu_length local;
struct data_pdu_length remote;
struct data_pdu_length eff;
uint8_t update;
} dle;
#endif /* CONFIG_BT_CTLR_DATA_LENGTH */
#endif/* CONFIG_BT_LL_SW_LLCP_LEGACY */
#if defined(CONFIG_BT_CTLR_PHY)
uint8_t phy_tx:3;

View file

@ -34,10 +34,10 @@
#include "lll_adv_pdu.h"
#include "lll_adv_aux.h"
#include "lll_adv_sync.h"
#include "lll_df_types.h"
#include "lll_conn.h"
#include "lll_chan.h"
#include "lll_filter.h"
#include "lll_df_types.h"
#include "lll_internal.h"
#include "lll_tim_internal.h"

View file

@ -26,6 +26,7 @@
#include "lll_vendor.h"
#include "lll_clock.h"
#include "lll_chan.h"
#include "lll/lll_df_types.h"
#include "lll_conn.h"
#include "lll_adv_types.h"
#include "lll_adv.h"

View file

@ -480,7 +480,11 @@ void lll_conn_rx_pkt_set(struct lll_conn *lll)
LL_ASSERT(node_rx);
#if defined(CONFIG_BT_CTLR_DATA_LENGTH)
#ifdef CONFIG_BT_LL_SW_LLCP_LEGACY
max_rx_octets = lll->max_rx_octets;
#else
max_rx_octets = lll->dle.eff.max_rx_octets;
#endif
#else /* !CONFIG_BT_CTLR_DATA_LENGTH */
max_rx_octets = PDU_DC_PAYLOAD_SIZE_MIN;
#endif /* !CONFIG_BT_CTLR_DATA_LENGTH */
@ -524,7 +528,11 @@ void lll_conn_tx_pkt_set(struct lll_conn *lll, struct pdu_data *pdu_data_tx)
uint8_t phy, flags;
#if defined(CONFIG_BT_CTLR_DATA_LENGTH)
#ifdef CONFIG_BT_LL_SW_LLCP_LEGACY
max_tx_octets = lll->max_tx_octets;
#else
max_tx_octets = lll->dle.eff.max_tx_octets;
#endif
#else /* !CONFIG_BT_CTLR_DATA_LENGTH */
max_tx_octets = PDU_DC_PAYLOAD_SIZE_MIN;
#endif /* !CONFIG_BT_CTLR_DATA_LENGTH */

View file

@ -57,7 +57,7 @@ struct lll_df_adv_cfg {
#endif
#define IQ_SAMPLE_TOTAL_CNT ((IQ_SAMPLE_REF_CNT) + (IQ_SAMPLE_SWITCH_CNT))
#define IQ_SAMPLE_CNT (PDU_DC_LL_HEADER_SIZE + LL_LENGTH_OCTETS_RX_MAX)
#define IQ_SAMPLE_CNT (PDU_DC_LL_HEADER_SIZE + LL_LENGTH_OCTETS_RX_MAX)
#define RSSI_DBM_TO_DECI_DBM(x) (-(x) * 10)
#define IQ_SHIFT_12_TO_8_BIT(x) ((x) >> 4)
@ -116,3 +116,21 @@ struct lll_df_conn_rx_params {
uint8_t ant_sw_len : 7;
uint8_t ant_ids[BT_CTLR_DF_MAX_ANT_SW_PATTERN_LEN];
};
/* @brief Structure to store data required to prepare LE Connection IQ Report event or LE
* Connectionless IQ Report event.
*
* TODO (ppryga): use struct cte_conn_iq_report in connected mode. Members are exactly the same as
* members of node_rx_iq_report except hdr.
*/
struct cte_conn_iq_report {
struct pdu_cte_info cte_info;
uint8_t local_slot_durations;
uint8_t packet_status;
uint8_t sample_count;
uint8_t rssi_ant_id;
union {
uint8_t pdu[0] __aligned(4);
struct iq_sample sample[0];
};
};

View file

@ -15,6 +15,7 @@
#include "hal/radio.h"
#include "hal/ticker.h"
#include "util/util.h"
#include "util/memq.h"
#include "pdu.h"
@ -22,6 +23,7 @@
#include "lll.h"
#include "lll_vendor.h"
#include "lll_clock.h"
#include "lll_df_types.h"
#include "lll_conn.h"
#include "lll_peripheral.h"
#include "lll_chan.h"

View file

@ -47,6 +47,7 @@
#define LOG_MODULE_NAME bt_ctlr_lll_scan_aux
#include "common/log.h"
#include <soc.h>
#include <ull_scan_types.h>
#include "hal/debug.h"
static int init_reset(void);
@ -453,6 +454,12 @@ static int prepare_cb(struct lll_prepare_param *p)
}
#endif /* CONFIG_BT_CENTRAL */
/* Initialize scanning state */
lll->state = 0U;
/* Reset Tx/rx count */
trx_cnt = 0U;
/* Start setting up Radio h/w */
radio_reset();
@ -693,7 +700,6 @@ static void isr_rx(struct lll_scan *lll, struct lll_scan_aux *lll_aux,
irkmatch_ok = radio_ar_has_match();
irkmatch_id = radio_ar_match_get();
rssi_ready = radio_rssi_is_ready();
phy_aux_flags_rx = radio_phy_flags_rx_get();
} else {
crc_ok = devmatch_ok = irkmatch_ok = rssi_ready =
phy_aux_flags_rx = 0U;

View file

@ -31,6 +31,7 @@
#include "lll_adv_types.h"
#include "lll_adv.h"
#include "lll_adv_pdu.h"
#include "lll_df_types.h"
#include "lll_conn.h"
#include "lll_chan.h"
#include "lll_filter.h"

View file

@ -14,12 +14,14 @@
#include "hal/radio.h"
#include "hal/ticker.h"
#include "util/util.h"
#include "util/memq.h"
#include "pdu.h"
#include "lll.h"
#include "lll_vendor.h"
#include "lll_df_types.h"
#include "lll_conn.h"
#include "lll_central.h"
#include "lll_chan.h"

View file

@ -15,6 +15,7 @@
#include "hal/ccm.h"
#include "hal/radio.h"
#include "util/util.h"
#include "util/mem.h"
#include "util/memq.h"
#include "util/mfifo.h"
@ -22,6 +23,7 @@
#include "pdu.h"
#include "lll.h"
#include "lll_df_types.h"
#include "lll_conn.h"
#include "lll_internal.h"
@ -427,7 +429,11 @@ void lll_conn_rx_pkt_set(struct lll_conn *lll)
LL_ASSERT(node_rx);
#if defined(CONFIG_BT_CTLR_DATA_LENGTH)
#ifdef CONFIG_BT_LL_SW_LLCP_LEGACY
max_rx_octets = lll->max_rx_octets;
#else
max_rx_octets = lll->dle.eff.max_rx_octets;
#endif
#else /* !CONFIG_BT_CTLR_DATA_LENGTH */
max_rx_octets = PDU_DC_PAYLOAD_SIZE_MIN;
#endif /* !CONFIG_BT_CTLR_DATA_LENGTH */
@ -461,7 +467,11 @@ void lll_conn_tx_pkt_set(struct lll_conn *lll, struct pdu_data *pdu_data_tx)
uint8_t phy, flags;
#if defined(CONFIG_BT_CTLR_DATA_LENGTH)
#ifdef CONFIG_BT_LL_SW_LLCP_LEGACY
max_tx_octets = lll->max_tx_octets;
#else
max_tx_octets = lll->dle.eff.max_tx_octets;
#endif
#else /* !CONFIG_BT_CTLR_DATA_LENGTH */
max_tx_octets = PDU_DC_PAYLOAD_SIZE_MIN;
#endif /* !CONFIG_BT_CTLR_DATA_LENGTH */

View file

@ -14,12 +14,14 @@
#include "hal/radio.h"
#include "hal/ticker.h"
#include "util/util.h"
#include "util/memq.h"
#include "pdu.h"
#include "lll.h"
#include "lll_vendor.h"
#include "lll_df_types.h"
#include "lll_conn.h"
#include "lll_peripheral.h"
#include "lll_chan.h"

View file

@ -26,6 +26,7 @@
#include "lll_vendor.h"
#include "lll_clock.h"
#include "lll_scan.h"
#include "lll_df_types.h"
#include "lll_conn.h"
#include "lll_chan.h"
#include "lll_filter.h"

View file

@ -214,6 +214,33 @@
#define PKT_BIS_US(octets, mic, phy) PDU_MAX_US((octets), (mic), (phy))
/* TODO: verify if the following lines are correct */
/* Extra bytes for enqueued node_rx metadata: rssi (always), resolving
* index, directed adv report, and mesh channel and instant.
*/
#define PDU_AC_SIZE_RSSI 1
#if defined(CONFIG_BT_CTLR_PRIVACY)
#define PDU_AC_SIZE_PRIV 1
#else
#define PDU_AC_SIZE_PRIV 0
#endif /* CONFIG_BT_CTLR_PRIVACY */
#if defined(CONFIG_BT_CTLR_EXT_SCAN_FP)
#define PDU_AC_SIZE_SCFP 1
#else
#define PDU_AC_SIZE_SCFP 0
#endif /* CONFIG_BT_CTLR_EXT_SCAN_FP */
#if defined(CONFIG_BT_HCI_MESH_EXT)
#define PDU_AC_SIZE_MESH 5
#else
#define PDU_AC_SIZE_MESH 0
#endif /* CONFIG_BT_HCI_MESH_EXT */
#define PDU_AC_LL_SIZE_EXTRA (PDU_AC_SIZE_RSSI + \
PDU_AC_SIZE_PRIV + \
PDU_AC_SIZE_SCFP + \
PDU_AC_SIZE_MESH)
struct pdu_adv_adv_ind {
uint8_t addr[BDADDR_SIZE];
uint8_t data[PDU_AC_DATA_SIZE_MAX];
@ -485,10 +512,13 @@ enum pdu_data_llctrl_type {
PDU_DATA_LLCTRL_TYPE_PHY_RSP = 0x17,
PDU_DATA_LLCTRL_TYPE_PHY_UPD_IND = 0x18,
PDU_DATA_LLCTRL_TYPE_MIN_USED_CHAN_IND = 0x19,
PDU_DATA_LLCTRL_TYPE_CTE_REQ = 0x1A,
PDU_DATA_LLCTRL_TYPE_CTE_RSP = 0x1B,
PDU_DATA_LLCTRL_TYPE_CIS_REQ = 0x1F,
PDU_DATA_LLCTRL_TYPE_CIS_RSP = 0x20,
PDU_DATA_LLCTRL_TYPE_CIS_IND = 0x21,
PDU_DATA_LLCTRL_TYPE_CIS_TERMINATE_IND = 0x22,
PDU_DATA_LLCTRL_TYPE_UNUSED = 0xFF
};
struct pdu_data_llctrl_conn_update_ind {
@ -641,6 +671,24 @@ struct pdu_data_llctrl_min_used_chans_ind {
uint8_t min_used_chans;
} __packed;
struct pdu_data_llctrl_cte_req {
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
uint8_t min_cte_len_req : 5;
uint8_t rfu : 1;
uint8_t cte_type_req : 2;
#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
uint8_t cte_type_req : 2;
uint8_t rfu : 1;
uint8_t min_cte_len_req : 5;
#else
#error "Unsupported endianness"
#endif
} __packed;
struct pdu_data_llctrl_cte_rsp {
/* no members */
} __packed;
struct pdu_data_llctrl_cis_req {
uint8_t cig_id;
uint8_t cis_id;
@ -733,6 +781,8 @@ struct pdu_data_llctrl {
struct pdu_data_llctrl_phy_rsp phy_rsp;
struct pdu_data_llctrl_phy_upd_ind phy_upd_ind;
struct pdu_data_llctrl_min_used_chans_ind min_used_chans_ind;
struct pdu_data_llctrl_cte_req cte_req;
struct pdu_data_llctrl_cte_rsp cte_rsp;
struct pdu_data_llctrl_cis_req cis_req;
struct pdu_data_llctrl_cis_rsp cis_rsp;
struct pdu_data_llctrl_cis_ind cis_ind;

View file

@ -45,6 +45,9 @@
#include "ull_adv_types.h"
#include "ull_scan_types.h"
#include "ull_sync_types.h"
#if !defined(CONFIG_BT_LL_SW_LLCP_LEGACY)
#include "ull_tx_queue.h"
#endif
#include "ull_conn_types.h"
#include "ull_filter.h"
#include "ull_df_types.h"

View file

@ -38,6 +38,10 @@
#include "lll_filter.h"
#include "lll/lll_df_types.h"
#if !defined(CONFIG_BT_LL_SW_LLCP_LEGACY)
#include "ll_sw/ull_tx_queue.h"
#endif /* !CONFIG_BT_LL_SW_LLCP_LEGACY */
#include "ull_adv_types.h"
#include "ull_scan_types.h"
#include "ull_conn_types.h"
@ -52,6 +56,10 @@
#include "ll_feat.h"
#include "ll_settings.h"
#if !defined(CONFIG_BT_LL_SW_LLCP_LEGACY)
#include "ll_sw/ull_llcp.h"
#endif /* !CONFIG_BT_LL_SW_LLCP_LEGACY */
#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_DEBUG_HCI_DRIVER)
#define LOG_MODULE_NAME bt_ctlr_ull_adv
#include "common/log.h"
@ -938,6 +946,7 @@ uint8_t ll_adv_enable(uint8_t enable)
conn_lll->nesn = 0;
conn_lll->empty = 0;
#if defined(CONFIG_BT_LL_SW_LLCP_LEGACY)
#if defined(CONFIG_BT_CTLR_DATA_LENGTH)
conn_lll->max_tx_octets = PDU_DC_PAYLOAD_SIZE_MIN;
conn_lll->max_rx_octets = PDU_DC_PAYLOAD_SIZE_MIN;
@ -957,7 +966,17 @@ uint8_t ll_adv_enable(uint8_t enable)
lll->phy_s));
#endif /* CONFIG_BT_CTLR_ADV_EXT */
#endif /* CONFIG_BT_CTLR_PHY */
#endif
#else /* CONFIG_BT_LL_SW_LLCP_LEGACY */
#if defined(CONFIG_BT_CTLR_DATA_LENGTH)
#if defined(CONFIG_BT_CTLR_PHY) && defined(CONFIG_BT_CTLR_ADV_EXT)
const uint8_t phy = lll->phy_s;
#else
const uint8_t phy = PHY_1M;
#endif
ull_dle_init(conn, phy);
#endif /* CONFIG_BT_CTLR_DATA_LENGTH */
#endif /* CONFIG_BT_LL_SW_LLCP_LEGACY */
#if defined(CONFIG_BT_CTLR_PHY)
conn_lll->phy_flags = 0;
@ -1024,6 +1043,7 @@ uint8_t ll_adv_enable(uint8_t enable)
sizeof(conn->peer_id_addr));
#endif /* CONFIG_BT_CTLR_CHECK_SAME_PEER_CONN */
#if defined(CONFIG_BT_LL_SW_LLCP_LEGACY)
conn->common.fex_valid = 0;
conn->common.txn_lock = 0;
conn->periph.latency_cancel = 0;
@ -1063,7 +1083,6 @@ uint8_t ll_adv_enable(uint8_t enable)
conn->llcp_length.disabled = 0U;
conn->llcp_length.cache.tx_octets = 0U;
conn->default_tx_octets = ull_conn_default_tx_octets_get();
#if defined(CONFIG_BT_CTLR_PHY)
conn->default_tx_time = ull_conn_default_tx_time_get();
#endif /* CONFIG_BT_CTLR_PHY */
@ -1079,6 +1098,24 @@ uint8_t ll_adv_enable(uint8_t enable)
conn->tx_head = conn->tx_ctrl = conn->tx_ctrl_last =
conn->tx_data = conn->tx_data_last = 0;
#else /* CONFIG_BT_LL_SW_LLCP_LEGACY */
/* Re-initialize the control procedure data structures */
ull_llcp_init(conn);
conn->llcp_terminate.reason_final = 0;
/* NOTE: use allocated link for generating dedicated
* terminate ind rx node
*/
conn->llcp_terminate.node_rx.hdr.link = link;
#if defined(CONFIG_BT_CTLR_PHY)
conn->phy_pref_tx = ull_conn_default_phy_tx_get();
conn->phy_pref_rx = ull_conn_default_phy_rx_get();
#endif /* CONFIG_BT_CTLR_PHY */
/* Re-initialize the Tx Q */
ull_tx_q_init(&conn->tx_q);
#endif /* CONFIG_BT_LL_SW_LLCP_LEGACY */
/* NOTE: using same link as supplied for terminate ind */
adv->link_cc_free = link;
@ -2455,7 +2492,8 @@ static void ext_disabled_cb(void *param)
struct node_rx_hdr *rx_hdr = (void *)lll->node_rx_adv_term;
/* Under race condition, if a connection has been established then
* node_rx is already utilized to send terminate event on connection */
* node_rx is already utilized to send terminate event on connection
*/
if (!rx_hdr) {
return;
}

View file

@ -10,6 +10,7 @@
#include <sys/byteorder.h>
#include "hal/cpu.h"
#include "hal/ccm.h"
#include "hal/ticker.h"
#include "util/util.h"
@ -30,6 +31,7 @@
#include "lll/lll_adv_pdu.h"
#include "lll_adv_aux.h"
#include "lll/lll_df_types.h"
#include "lll_conn.h"
#include "ull_adv_types.h"

View file

@ -10,6 +10,7 @@
#include <sys/byteorder.h>
#include "hal/cpu.h"
#include "hal/ccm.h"
#include "hal/ticker.h"
#include "util/util.h"
@ -29,6 +30,7 @@
#include "lll/lll_adv_pdu.h"
#include "lll_adv_sync.h"
#include "lll/lll_df_types.h"
#include "lll_conn.h"
#include "lll_chan.h"
#include "ull_adv_types.h"

View file

@ -36,6 +36,10 @@
#include "lll_central.h"
#include "lll_filter.h"
#if !defined(CONFIG_BT_LL_SW_LLCP_LEGACY)
#include "ull_tx_queue.h"
#endif /* !CONFIG_BT_LL_SW_LLCP_LEGACY */
#include "ull_adv_types.h"
#include "ull_scan_types.h"
#include "ull_conn_types.h"
@ -51,6 +55,10 @@
#include "ll_feat.h"
#include "ll_settings.h"
#if !defined(CONFIG_BT_LL_SW_LLCP_LEGACY)
#include "ll_sw/ull_llcp.h"
#endif /* !CONFIG_BT_LL_SW_LLCP_LEGACY */
#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_DEBUG_HCI_DRIVER)
#define LOG_MODULE_NAME bt_ctlr_ull_central
#include "common/log.h"
@ -202,6 +210,7 @@ uint8_t ll_create_connection(uint16_t scan_interval, uint16_t scan_window,
conn_lll->nesn = 0;
conn_lll->empty = 0;
#if defined(CONFIG_BT_LL_SW_LLCP_LEGACY)
#if defined(CONFIG_BT_CTLR_DATA_LENGTH)
conn_lll->max_tx_octets = PDU_DC_PAYLOAD_SIZE_MIN;
conn_lll->max_rx_octets = PDU_DC_PAYLOAD_SIZE_MIN;
@ -214,6 +223,11 @@ uint8_t ll_create_connection(uint16_t scan_interval, uint16_t scan_window,
conn_lll->max_rx_time = PDU_DC_MAX_US(PDU_DC_PAYLOAD_SIZE_MIN, PHY_1M);
#endif /* CONFIG_BT_CTLR_PHY */
#endif /* CONFIG_BT_CTLR_DATA_LENGTH */
#else /* CONFIG_BT_LL_SW_LLCP_LEGACY */
#if defined(CONFIG_BT_CTLR_DATA_LENGTH)
ull_dle_init(conn, PHY_1M);
#endif /* CONFIG_BT_CTLR_DATA_LENGTH */
#endif /* CONFIG_BT_LL_SW_LLCP_LEGACY */
#if defined(CONFIG_BT_CTLR_PHY)
/* Use the default 1M PHY, extended connection initiation in LLL will
@ -279,6 +293,7 @@ uint8_t ll_create_connection(uint16_t scan_interval, uint16_t scan_window,
conn->apto_reload;
#endif /* CONFIG_BT_CTLR_LE_PING */
#if defined(CONFIG_BT_LL_SW_LLCP_LEGACY)
conn->common.fex_valid = 0U;
conn->common.txn_lock = 0U;
conn->central.terminate_ack = 0U;
@ -332,6 +347,26 @@ uint8_t ll_create_connection(uint16_t scan_interval, uint16_t scan_window,
conn->tx_head = conn->tx_ctrl = conn->tx_ctrl_last =
conn->tx_data = conn->tx_data_last = 0;
#else /* CONFIG_BT_LL_SW_LLCP_LEGACY */
/* Re-initialize the control procedure data structures */
ull_llcp_init(conn);
conn->central.terminate_ack = 0U;
conn->llcp_terminate.reason_final = 0U;
/* NOTE: use allocated link for generating dedicated
* terminate ind rx node
*/
conn->llcp_terminate.node_rx.hdr.link = link;
#if defined(CONFIG_BT_CTLR_PHY)
conn->phy_pref_tx = ull_conn_default_phy_tx_get();
conn->phy_pref_rx = ull_conn_default_phy_rx_get();
#endif /* CONFIG_BT_CTLR_PHY */
/* Re-initialize the Tx Q */
ull_tx_q_init(&conn->tx_q);
#endif /* CONFIG_BT_LL_SW_LLCP_LEGACY */
/* TODO: active_to_start feature port */
conn->ull.ticks_active_to_start = 0U;
@ -361,6 +396,7 @@ conn_is_valid:
ready_delay_us = lll_radio_tx_ready_delay_get(0, 0);
#endif
#if defined(CONFIG_BT_LL_SW_LLCP_LEGACY)
#if defined(CONFIG_BT_CTLR_DATA_LENGTH)
#if defined(CONFIG_BT_CTLR_PHY)
#if defined(CONFIG_BT_CTLR_ADV_EXT)
@ -387,6 +423,15 @@ conn_is_valid:
PDU_DC_MAX_US(PDU_DC_PAYLOAD_SIZE_MIN, lll->phy));
#endif /* CONFIG_BT_CTLR_ADV_EXT */
#endif /* !CONFIG_BT_CTLR_DATA_LENGTH */
#else /* CONFIG_BT_LL_SW_LLCP_LEGACY */
/* TODO(thoh-ot): Not entirely sure this is correct */
#if defined(CONFIG_BT_CTLR_DATA_LENGTH)
ull_dle_max_time_get(conn, &max_rx_time, &max_tx_time);
#else /* CONFIG_BT_CTLR_DATA_LENGTH */
max_tx_time = PDU_DC_MAX_US(PDU_DC_PAYLOAD_SIZE_MIN, PHY_1M);
max_rx_time = PDU_DC_MAX_US(PDU_DC_PAYLOAD_SIZE_MIN, PHY_1M);
#endif /* CONFIG_BT_CTLR_DATA_LENGTH */
#endif /* CONFIG_BT_LL_SW_LLCP_LEGACY */
conn->ull.ticks_slot =
HAL_TICKER_US_TO_TICKS(EVENT_OVERHEAD_START_US +
@ -572,7 +617,7 @@ uint8_t ll_connect_disable(void **rx)
}
#if defined(CONFIG_BT_CTLR_LE_ENC)
uint8_t ll_enc_req_send(uint16_t handle, uint8_t const *const rand,
uint8_t ll_enc_req_send(uint16_t handle, uint8_t const *const rand_num,
uint8_t const *const ediv, uint8_t const *const ltk)
{
struct ll_conn *conn;
@ -583,6 +628,7 @@ uint8_t ll_enc_req_send(uint16_t handle, uint8_t const *const rand,
return BT_HCI_ERR_UNKNOWN_CONN_ID;
}
#if defined(CONFIG_BT_LL_SW_LLCP_LEGACY)
if ((conn->llcp_enc.req != conn->llcp_enc.ack) ||
((conn->llcp_req != conn->llcp_ack) &&
(conn->llcp_type == LLCP_ENCRYPTION))) {
@ -608,13 +654,13 @@ uint8_t ll_enc_req_send(uint16_t handle, uint8_t const *const rand,
PDU_DATA_LLCTRL_TYPE_ENC_REQ;
enc_req = (void *)
&pdu_data_tx->llctrl.enc_req;
memcpy(enc_req->rand, rand, sizeof(enc_req->rand));
memcpy(enc_req->rand, rand_num, sizeof(enc_req->rand));
enc_req->ediv[0] = ediv[0];
enc_req->ediv[1] = ediv[1];
lll_csrand_get(enc_req->skdm, sizeof(enc_req->skdm));
lll_csrand_get(enc_req->ivm, sizeof(enc_req->ivm));
} else if (conn->lll.enc_rx && conn->lll.enc_tx) {
memcpy(&conn->llcp_enc.rand[0], rand,
memcpy(&conn->llcp_enc.rand[0], rand_num,
sizeof(conn->llcp_enc.rand));
conn->llcp_enc.ediv[0] = ediv[0];
@ -641,6 +687,17 @@ uint8_t ll_enc_req_send(uint16_t handle, uint8_t const *const rand,
return 0;
}
#else /* CONFIG_BT_LL_SW_LLCP_LEGACY */
ARG_UNUSED(tx);
if (!conn->lll.enc_tx && !conn->lll.enc_rx) {
/* Encryption is fully disabled */
return ull_cp_encryption_start(conn, rand_num, ediv, ltk);
} else if (conn->lll.enc_tx && conn->lll.enc_rx) {
/* Encryption is fully enabled */
return ull_cp_encryption_pause(conn, rand_num, ediv, ltk);
}
#endif /* CONFIG_BT_LL_SW_LLCP_LEGACY */
return BT_HCI_ERR_CMD_DISALLOWED;
}
@ -807,6 +864,11 @@ void ull_central_setup(struct node_rx_hdr *rx, struct node_rx_ftr *ftr,
lll->handle = ll_conn_handle_get(conn);
rx->handle = lll->handle;
#if (!defined(CONFIG_BT_LL_SW_LLCP_LEGACY))
/* Set LLCP as connection-wise connected */
ull_cp_state_set(conn, ULL_CP_CONNECTED);
#endif /* CONFIG_BT_LL_SW_LLCP_LEGACY */
#if defined(CONFIG_BT_CTLR_TX_PWR_DYNAMIC_CONTROL)
lll->tx_pwr_lvl = RADIO_TXP_DEFAULT;
#endif /* CONFIG_BT_CTLR_TX_PWR_DYNAMIC_CONTROL */
@ -1042,6 +1104,7 @@ uint8_t ull_central_chm_update(void)
continue;
}
#if defined(CONFIG_BT_LL_SW_LLCP_LEGACY)
ret = ull_conn_llcp_req(conn);
if (ret) {
return ret;
@ -1053,6 +1116,15 @@ uint8_t ull_central_chm_update(void)
conn->llcp_type = LLCP_CHAN_MAP;
conn->llcp_req++;
#else /* CONFIG_BT_LL_SW_LLCP_LEGACY */
uint8_t chm[5];
ull_chan_map_get(chm);
ret = ull_cp_chan_map_update(conn, chm);
if (ret) {
return ret;
}
#endif /* CONFIG_BT_LL_SW_LLCP_LEGACY */
}
return 0;

View file

@ -23,6 +23,7 @@
#include "lll/lll_adv_types.h"
#include "lll_adv.h"
#include "lll/lll_adv_pdu.h"
#include "lll/lll_df_types.h"
#include "lll_conn.h"
#include "ull_adv_types.h"

View file

@ -6,3 +6,6 @@
int ull_chan_reset(void);
uint8_t ull_chan_map_get(uint8_t *const chan_map);
#if !defined(CONFIG_BT_LL_SW_LLCP_LEGACY)
void ull_chan_map_set(uint8_t const *const chan_map);
#endif

File diff suppressed because it is too large Load diff

View file

@ -13,7 +13,6 @@ uint16_t ll_conn_free_count_get(void);
void ll_tx_ack_put(uint16_t handle, struct node_tx *node_tx);
int ull_conn_init(void);
int ull_conn_reset(void);
void ull_conn_chan_map_set(uint8_t *chan_map);
uint16_t ull_conn_default_tx_octets_get(void);
uint16_t ull_conn_default_tx_time_get(void);
uint8_t ull_conn_default_phy_tx_get(void);
@ -37,3 +36,63 @@ memq_link_t *ull_conn_ack_by_last_peek(uint8_t last, uint16_t *handle,
void *ull_conn_ack_dequeue(void);
void ull_conn_tx_ack(uint16_t handle, memq_link_t *link, struct node_tx *tx);
uint8_t ull_conn_llcp_req(void *conn);
#if !defined(CONFIG_BT_LL_SW_LLCP_LEGACY)
uint16_t ull_conn_event_counter(struct ll_conn *conn);
void ull_conn_update_parameters(struct ll_conn *conn, uint8_t is_cu_proc,
uint8_t win_size, uint16_t win_offset_us,
uint16_t interval, uint16_t latency,
uint16_t timeout, uint16_t instant);
void ull_conn_default_tx_octets_set(uint16_t tx_octets);
void ull_conn_default_tx_time_set(uint16_t tx_time);
uint8_t ull_conn_lll_phy_active(struct ll_conn *conn, uint8_t phy);
void ull_dle_init(struct ll_conn *conn, uint8_t phy);
void ull_dle_max_time_get(struct ll_conn *conn, uint16_t *max_rx_time,
uint16_t *max_tx_time);
uint8_t ull_dle_update_eff(struct ll_conn *conn);
void ull_dle_local_tx_update(struct ll_conn *conn, uint16_t tx_octets, uint16_t tx_time);
void ull_conn_default_phy_tx_set(uint8_t tx);
void ull_conn_default_phy_rx_set(uint8_t rx);
void ull_conn_chan_map_set(struct ll_conn *conn, const uint8_t chm[5]);
void *ull_conn_tx_mem_acquire(void);
void ull_conn_tx_mem_release(void *tx);
uint8_t ull_conn_mfifo_get_tx(void **lll_tx);
void ull_conn_mfifo_enqueue_tx(uint8_t idx);
/**
* @brief Pause the data path of a rx queue.
*/
void ull_conn_pause_rx_data(struct ll_conn *conn);
/**
* @brief Resume the data path of a rx queue.
*/
void ull_conn_resume_rx_data(struct ll_conn *conn);
/**
* @brief Restart procedure timeout timer
*/
void ull_conn_prt_reload(struct ll_conn *conn, uint16_t procedure_reload);
/**
* @brief Clear procedure timeout timer
*/
void ull_conn_prt_clear(struct ll_conn *conn);
#endif /* CONFIG_BT_LL_SW_LLCP_LEGACY */

View file

@ -32,6 +32,7 @@ enum llcp {
#endif /* CONFIG_BT_CTLR_PHY */
};
#if defined(CONFIG_BT_LL_SW_LLCP_LEGACY)
struct ll_conn {
struct ull_hdr ull;
struct lll_conn lll;
@ -345,6 +346,194 @@ struct ll_conn {
struct lll_df_conn_rx_params df_rx_params;
#endif /* CONFIG_BT_CTLR_DF_CONN_CTE_REQ */
};
#else
/*
* This is for the refactored LLCP
*
* to reduce length and unreadability of the ll_conn struct the
* structures inside it have been defined first
*/
struct llcp_struct {
/* Local Request */
struct {
sys_slist_t pend_proc_list;
uint8_t state;
} local;
/* Remote Request */
struct {
sys_slist_t pend_proc_list;
uint8_t state;
uint8_t collision;
uint8_t incompat;
uint8_t reject_opcode;
} remote;
/* Prepare parameters */
struct {
uint32_t ticks_at_expire;
uint16_t lazy;
} prep;
/* Version Exchange Procedure State */
struct {
uint8_t sent;
uint8_t valid;
struct pdu_data_llctrl_version_ind cached;
} vex;
/*
* As of today only 36 feature bits are in use,
* so some optimisation is possible
* we also need to keep track of the features of the
* other node, so that we can send a proper
* reply over HCI to the host
* see BT Core spec 5.2 Vol 6, Part B, sec. 5.1.4
*/
struct {
uint8_t sent;
uint8_t valid;
uint64_t features_peer;
uint64_t features_used;
} fex;
/* Minimum used channels procedure state */
struct {
uint8_t phys;
uint8_t min_used_chans;
} muc;
/* TODO: we'll need the next few structs eventually,
* Thomas and Szymon please comment on names etc.
*/
struct {
uint16_t *pdu_win_offset;
uint32_t ticks_anchor;
} conn_upd;
#if defined(CONFIG_BT_CTLR_DF_CONN_CTE_REQ)
/* @brief Constant Tone Extension configuration for CTE request control procedure. */
struct llcp_df_req_cfg {
/* Procedure may be active periodically, active state must be stored.
* If procedure is active, request parameters update may not be issued.
*/
uint8_t is_enabled;
uint8_t cte_type;
/* Minimum requested CTE length in 8us units */
uint8_t min_cte_len;
uint16_t req_interval;
} cte_req;
#endif /* CONFIG_BT_CTLR_DF_CONN_CTE_REQ */
#if defined(CONFIG_BT_CTLR_DF_CONN_CTE_RSP)
/* TODO (ppryga): Consided move of the type of structure to ull_df_types.h or
* lll_df_types.h. To have single definition of the type and share it wish LLL.
*/
struct llcp_df_rsp_cfg {
uint8_t is_enabled;
uint8_t cte_types;
uint8_t max_cte_len;
uint8_t ant_sw_len;
/* TODO (ppryga): Update to use the same macro as in lll_df_types.h */
uint8_t ant_ids[CONFIG_BT_CTLR_DF_MAX_ANT_SW_PATTERN_LEN];
} cte_rsp;
#endif /* CONFIG_BT_CTLR_DF_CONN_CTE_RSP */
#if (CONFIG_BT_CTLR_LLCP_PER_CONN_TX_CTRL_BUF_NUM > 0) &&\
(CONFIG_BT_CTLR_LLCP_PER_CONN_TX_CTRL_BUF_NUM <\
CONFIG_BT_CTLR_LLCP_TX_PER_CONN_TX_CTRL_BUF_NUM_MAX)
uint8_t tx_buffer_alloc;
#endif /* (CONFIG_BT_CTLR_LLCP_PER_CONN_TX_CTRL_BUF_NUM > 0) */
}; /* struct llcp_struct */
struct ll_conn {
struct ull_hdr ull;
struct lll_conn lll;
struct ull_tx_q tx_q;
struct llcp_struct llcp;
struct {
uint8_t reason_final;
/* node rx type with memory aligned storage for terminate
* reason.
* HCI will reference the value using the pdu member of
* struct node_rx_pdu.
*/
struct {
struct node_rx_hdr hdr;
uint8_t reason __aligned(4);
} node_rx;
} llcp_terminate;
/*
* TODO: all the following comes from the legacy LL llcp structure
* and/or needs to be properly integrated in the control procedures
*/
union {
#if defined(CONFIG_BT_PERIPHERAL)
struct {
uint8_t latency_cancel:1;
uint8_t sca:3;
uint32_t force;
uint32_t ticks_to_offset;
} periph;
#endif /* CONFIG_BT_PERIPHERAL */
#if defined(CONFIG_BT_CENTRAL)
struct {
uint8_t terminate_ack:1;
} central;
#endif /* CONFIG_BT_CENTRAL */
};
/* Cancel the prepare in the instant a Connection Update takes place */
uint8_t cancel_prepare:1;
#if defined(CONFIG_BT_CTLR_LE_ENC)
/* Pause Rx data PDU's */
uint8_t pause_rx_data:1;
#endif /* CONFIG_BT_CTLR_LE_ENC */
#if defined(CONFIG_BT_CTLR_LE_PING)
uint16_t appto_reload;
uint16_t appto_expire;
uint16_t apto_reload;
uint16_t apto_expire;
#endif /* CONFIG_BT_CTLR_LE_PING */
uint16_t connect_expire;
uint16_t supervision_reload;
uint16_t supervision_expire;
uint16_t procedure_reload;
uint16_t procedure_expire;
#if defined(CONFIG_BT_CTLR_PHY)
uint8_t phy_pref_tx:3;
uint8_t phy_pref_rx:3;
#endif /* CONFIG_BT_CTLR_PHY */
#if defined(CONFIG_BT_CTLR_DATA_LENGTH)
uint16_t default_tx_octets;
#if defined(CONFIG_BT_CTLR_PHY)
uint16_t default_tx_time;
#endif /* CONFIG_BT_CTLR_PHY */
#endif /* CONFIG_BT_CTLR_DATA_LENGTH */
#if defined(CONFIG_BT_CTLR_CHECK_SAME_PEER_CONN)
uint8_t own_id_addr_type:1;
uint8_t peer_id_addr_type:1;
uint8_t own_id_addr[BDADDR_SIZE];
uint8_t peer_id_addr[BDADDR_SIZE];
#endif /* CONFIG_BT_CTLR_CHECK_SAME_PEER_CONN */
#if defined(CONFIG_BT_CTLR_LLID_DATA_START_EMPTY)
/* Detect empty L2CAP start frame */
uint8_t start_empty:1;
#endif /* CONFIG_BT_CTLR_LLID_DATA_START_EMPTY */
}; /* struct ll_conn */
#endif /* BT_LL_SW_SPLIT_LEGACY */
struct node_rx_cc {
uint8_t status;

View file

@ -30,6 +30,10 @@
#include "lll_conn.h"
#include "lll_filter.h"
#if (!defined(CONFIG_BT_LL_SW_LLCP_LEGACY))
#include "ll_sw/ull_tx_queue.h"
#endif
#include "ull_adv_types.h"
#include "ull_scan_types.h"
#include "ull_conn_types.h"

View file

@ -31,6 +31,7 @@ static inline void ull_hdr_init(struct ull_hdr *hdr)
hdr->disabled_cb = hdr->disabled_param = NULL;
}
void ll_tx_ack_put(uint16_t handle, struct node_tx *node_tx);
void *ll_rx_link_alloc(void);
void ll_rx_link_release(void *link);
void *ll_rx_alloc(void);

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,160 @@
/*
* Copyright (c) 2020 Demant
*
* SPDX-License-Identifier: Apache-2.0
*/
/* Temporary data structure to avoid change ll_conn/lll_conn to much
* and having to update all the dependencies
*/
enum { ULL_CP_CONNECTED, ULL_CP_DISCONNECTED };
/**
* @brief Initialize the LL Control Procedure system.
*/
void ull_cp_init(void);
/**
* @brief Initialize the LL Control Procedure connection data.
*/
void ull_llcp_init(struct ll_conn *conn);
/**
* @brief XXX
*/
void ull_cp_state_set(struct ll_conn *conn, uint8_t state);
/**
*
*/
void ull_cp_release_tx(struct ll_conn *conn, struct node_tx *tx);
/**
*
*/
void ull_cp_release_ntf(struct node_rx_pdu *ntf);
/**
* @brief Run pending LL Control Procedures.
*/
void ull_cp_run(struct ll_conn *conn);
/**
* @brief Handle TX ack PDU.
*/
void ull_cp_tx_ack(struct ll_conn *conn, struct node_tx *tx);
/**
* @brief Handle received LL Control PDU.
*/
void ull_cp_rx(struct ll_conn *conn, struct node_rx_pdu *rx);
#if defined(CONFIG_BT_CTLR_LE_PING)
/**
* @brief Initiate a LE Ping Procedure.
*/
uint8_t ull_cp_le_ping(struct ll_conn *conn);
#endif /* CONFIG_BT_CTLR_LE_PING */
/**
* @brief Initiate a Version Exchange Procedure.
*/
uint8_t ull_cp_version_exchange(struct ll_conn *conn);
/**
* @brief Initiate a Feature Exchange Procedure.
*/
uint8_t ull_cp_feature_exchange(struct ll_conn *conn);
#if defined(CONFIG_BT_CTLR_MIN_USED_CHAN)
/**
* @brief Initiate a Minimum used channels Procedure.
*/
uint8_t ull_cp_min_used_chans(struct ll_conn *conn, uint8_t phys, uint8_t min_used_chans);
#endif /* CONFIG_BT_CTLR_MIN_USED_CHAN */
/**
* @brief Initiate a Encryption Start Procedure.
*/
uint8_t ull_cp_encryption_start(struct ll_conn *conn, const uint8_t rand[8], const uint8_t ediv[2],
const uint8_t ltk[16]);
/**
* @brief Initiate a Encryption Pause Procedure.
*/
uint8_t ull_cp_encryption_pause(struct ll_conn *conn, const uint8_t rand[8], const uint8_t ediv[2],
const uint8_t ltk[16]);
/**
* @brief Check if an encryption pause procedure is active.
*/
uint8_t ull_cp_encryption_paused(struct ll_conn *conn);
/**
*/
void ull_cp_ltk_req_reply(struct ll_conn *conn, const uint8_t ltk[16]);
/**
*/
void ull_cp_ltk_req_neq_reply(struct ll_conn *conn);
/**
* @brief Initiate a PHY Update Procedure.
*/
uint8_t ull_cp_phy_update(struct ll_conn *conn, uint8_t tx, uint8_t flags, uint8_t rx,
uint8_t host_initiated);
/**
* @brief Initiate a Connection Parameter Request Procedure or Connection Update Procedure
*/
uint8_t ull_cp_conn_update(struct ll_conn *conn, uint16_t interval_min, uint16_t interval_max,
uint16_t latency, uint16_t timeout);
/**
* @brief Accept the remote devices request to change connection parameters.
*/
void ull_cp_conn_param_req_reply(struct ll_conn *conn);
/**
* @brief Reject the remote devices request to change connection parameters.
*/
void ull_cp_conn_param_req_neg_reply(struct ll_conn *conn, uint8_t error_code);
/**
* @brief Check if a remote data length update is in the works.
*/
uint8_t ull_cp_remote_dle_pending(struct ll_conn *conn);
/**
* @brief Initiate a Termination Procedure.
*/
uint8_t ull_cp_terminate(struct ll_conn *conn, uint8_t error_code);
/**
* @brief Initiate a Channel Map Update Procedure.
*/
uint8_t ull_cp_chan_map_update(struct ll_conn *conn, const uint8_t chm[5]);
/**
* @brief Check if Channel Map Update Procedure is pending
*/
const uint8_t *ull_cp_chan_map_update_pending(struct ll_conn *conn);
#if defined(CONFIG_BT_CTLR_DATA_LENGTH)
/**
* @brief Initiate a Data Length Update Procedure.
*/
uint8_t ull_cp_data_length_update(struct ll_conn *conn, uint16_t max_tx_octets,
uint16_t max_tx_time);
#endif /* CONFIG_BT_CTLR_DATA_LENGTH */
/**
* @brief Initiate a CTE Request Procedure.
*/
uint8_t ull_cp_cte_req(struct ll_conn *conn, uint8_t min_cte_len, uint8_t cte_type);
/**
* @brief Enable or disable response to CTE Request Procedure.
*/
void ull_cp_cte_rsp_enable(struct ll_conn *conn, bool enable, uint8_t max_cte_len,
uint8_t cte_types);

View file

@ -0,0 +1,322 @@
/*
* Copyright (c) 2020 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/types.h>
#include <bluetooth/hci.h>
#include <sys/byteorder.h>
#include <sys/slist.h>
#include <sys/util.h>
#include "hal/ccm.h"
#include "util/util.h"
#include "util/mem.h"
#include "util/memq.h"
#include "pdu.h"
#include "ll.h"
#include "ll_settings.h"
#include "lll.h"
#include "lll/lll_df_types.h"
#include "lll_conn.h"
#include "ull_tx_queue.h"
#include "ull_conn_types.h"
#include "ull_llcp.h"
#include "ull_llcp_internal.h"
#include "ull_conn_internal.h"
#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_DEBUG_HCI_DRIVER)
#define LOG_MODULE_NAME bt_ctlr_ull_llcp_chmu
#include "common/log.h"
#include <soc.h>
#include "hal/debug.h"
/* Hardcoded instant delta +6 */
#define CHMU_INSTANT_DELTA 6U
/* LLCP Local Procedure Channel Map Update FSM states */
enum {
LP_CHMU_STATE_IDLE,
LP_CHMU_STATE_WAIT_TX_CHAN_MAP_IND,
LP_CHMU_STATE_WAIT_INSTANT,
};
/* LLCP Local Procedure Channel Map Update FSM events */
enum {
/* Procedure run */
LP_CHMU_EVT_RUN,
};
/* LLCP Remote Procedure Channel Map Update FSM states */
enum {
RP_CHMU_STATE_IDLE,
RP_CHMU_STATE_WAIT_RX_CHAN_MAP_IND,
RP_CHMU_STATE_WAIT_INSTANT,
};
/* LLCP Remote Procedure Channel Map Update FSM events */
enum {
/* Procedure run */
RP_CHMU_EVT_RUN,
/* Indication received */
RP_CHMU_EVT_RX_CHAN_MAP_IND,
};
#if defined(CONFIG_BT_CENTRAL)
/*
* LLCP Local Procedure Channel Map Update FSM
*/
/* TODO should go into some utils file */
static uint16_t lp_event_counter(struct ll_conn *conn)
{
struct lll_conn *lll = &conn->lll;
/* Calculate current event counter */
return lll->event_counter + lll->latency_prepare;
}
static void lp_chmu_tx(struct ll_conn *conn, struct proc_ctx *ctx)
{
struct node_tx *tx;
struct pdu_data *pdu;
/* Allocate tx node */
tx = llcp_tx_alloc(conn, ctx);
LL_ASSERT(tx);
pdu = (struct pdu_data *)tx->pdu;
/* Encode LL Control PDU */
llcp_pdu_encode_chan_map_update_ind(ctx, pdu);
ctx->tx_opcode = pdu->llctrl.opcode;
/* Enqueue LL Control PDU towards LLL */
llcp_tx_enqueue(conn, tx);
}
static void lp_chmu_complete(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, void *param)
{
ull_conn_chan_map_set(conn, ctx->data.chmu.chm);
llcp_lr_complete(conn);
ctx->state = LP_CHMU_STATE_IDLE;
}
static void lp_chmu_send_channel_map_update_ind(struct ll_conn *conn, struct proc_ctx *ctx,
uint8_t evt, void *param)
{
if (llcp_rr_get_collision(conn) || !llcp_tx_alloc_peek(conn, ctx)) {
ctx->state = LP_CHMU_STATE_WAIT_TX_CHAN_MAP_IND;
} else {
llcp_rr_set_incompat(conn, INCOMPAT_RESOLVABLE);
/* TODO Hardcoded instant delta */
ctx->data.chmu.instant = lp_event_counter(conn) + CHMU_INSTANT_DELTA;
lp_chmu_tx(conn, ctx);
ctx->state = LP_CHMU_STATE_WAIT_INSTANT;
}
}
static void lp_chmu_st_idle(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, void *param)
{
switch (evt) {
case LP_CHMU_EVT_RUN:
lp_chmu_send_channel_map_update_ind(conn, ctx, evt, param);
break;
default:
/* Ignore other evts */
break;
}
}
static void lp_chmu_st_wait_tx_chan_map_ind(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt,
void *param)
{
switch (evt) {
case LP_CHMU_EVT_RUN:
lp_chmu_send_channel_map_update_ind(conn, ctx, evt, param);
break;
default:
/* Ignore other evts */
break;
}
}
static void lp_chmu_check_instant(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt,
void *param)
{
uint16_t event_counter = lp_event_counter(conn);
if (is_instant_reached_or_passed(ctx->data.chmu.instant, event_counter)) {
llcp_rr_set_incompat(conn, INCOMPAT_NO_COLLISION);
lp_chmu_complete(conn, ctx, evt, param);
}
}
static void lp_chmu_st_wait_instant(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt,
void *param)
{
switch (evt) {
case LP_CHMU_EVT_RUN:
lp_chmu_check_instant(conn, ctx, evt, param);
break;
default:
/* Ignore other evts */
break;
}
}
static void lp_chmu_execute_fsm(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt,
void *param)
{
switch (ctx->state) {
case LP_CHMU_STATE_IDLE:
lp_chmu_st_idle(conn, ctx, evt, param);
break;
case LP_CHMU_STATE_WAIT_TX_CHAN_MAP_IND:
lp_chmu_st_wait_tx_chan_map_ind(conn, ctx, evt, param);
break;
case LP_CHMU_STATE_WAIT_INSTANT:
lp_chmu_st_wait_instant(conn, ctx, evt, param);
break;
default:
/* Unknown state */
LL_ASSERT(0);
}
}
void llcp_lp_chmu_init_proc(struct proc_ctx *ctx)
{
ctx->state = LP_CHMU_STATE_IDLE;
}
void llcp_lp_chmu_run(struct ll_conn *conn, struct proc_ctx *ctx, void *param)
{
lp_chmu_execute_fsm(conn, ctx, LP_CHMU_EVT_RUN, param);
}
#endif /* CONFIG_BT_CENTRAL */
#if defined(CONFIG_BT_PERIPHERAL)
/*
* LLCP Remote Procedure Channel Map Update FSM
*/
/* TODO should go into some utils file */
static uint16_t rp_event_counter(struct ll_conn *conn)
{
struct lll_conn *lll = &conn->lll;
/* Calculate current event counter */
return lll->event_counter + lll->latency_prepare;
}
static void rp_chmu_complete(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, void *param)
{
ull_conn_chan_map_set(conn, ctx->data.chmu.chm);
llcp_rr_complete(conn);
ctx->state = RP_CHMU_STATE_IDLE;
}
static void rp_chmu_st_idle(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, void *param)
{
/* TODO */
switch (evt) {
case RP_CHMU_EVT_RUN:
ctx->state = RP_CHMU_STATE_WAIT_RX_CHAN_MAP_IND;
break;
default:
/* Ignore other evts */
break;
}
}
static void rp_chmu_st_wait_rx_channel_map_update_ind(struct ll_conn *conn, struct proc_ctx *ctx,
uint8_t evt, void *param)
{
switch (evt) {
case RP_CHMU_EVT_RX_CHAN_MAP_IND:
llcp_pdu_decode_chan_map_update_ind(ctx, param);
ctx->state = RP_CHMU_STATE_WAIT_INSTANT;
break;
default:
/* Ignore other evts */
break;
}
}
static void rp_chmu_check_instant(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt,
void *param)
{
uint16_t event_counter = rp_event_counter(conn);
if (((event_counter - ctx->data.chmu.instant) & 0xFFFF) <= 0x7FFF) {
rp_chmu_complete(conn, ctx, evt, param);
}
}
static void rp_chmu_st_wait_instant(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt,
void *param)
{
switch (evt) {
case RP_CHMU_EVT_RUN:
rp_chmu_check_instant(conn, ctx, evt, param);
break;
default:
/* Ignore other evts */
break;
}
}
static void rp_chmu_execute_fsm(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt,
void *param)
{
switch (ctx->state) {
case RP_CHMU_STATE_IDLE:
rp_chmu_st_idle(conn, ctx, evt, param);
break;
case RP_CHMU_STATE_WAIT_RX_CHAN_MAP_IND:
rp_chmu_st_wait_rx_channel_map_update_ind(conn, ctx, evt, param);
break;
case RP_CHMU_STATE_WAIT_INSTANT:
rp_chmu_st_wait_instant(conn, ctx, evt, param);
break;
default:
/* Unknown state */
LL_ASSERT(0);
}
}
void llcp_rp_chmu_rx(struct ll_conn *conn, struct proc_ctx *ctx, struct node_rx_pdu *rx)
{
struct pdu_data *pdu = (struct pdu_data *)rx->pdu;
switch (pdu->llctrl.opcode) {
case PDU_DATA_LLCTRL_TYPE_CHAN_MAP_IND:
rp_chmu_execute_fsm(conn, ctx, RP_CHMU_EVT_RX_CHAN_MAP_IND, pdu);
break;
default:
/* Unknown opcode */
LL_ASSERT(0);
}
}
void llcp_rp_chmu_init_proc(struct proc_ctx *ctx)
{
ctx->state = RP_CHMU_STATE_IDLE;
}
void llcp_rp_chmu_run(struct ll_conn *conn, struct proc_ctx *ctx, void *param)
{
rp_chmu_execute_fsm(conn, ctx, RP_CHMU_EVT_RUN, param);
}
#endif /* CONFIG_BT_PERIPHERAL */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,994 @@
/*
* Copyright (c) 2020 Demant
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/types.h>
#include <bluetooth/hci.h>
#include <sys/byteorder.h>
#include <sys/slist.h>
#include <sys/util.h>
#include "hal/ccm.h"
#include "util/util.h"
#include "util/mem.h"
#include "util/memq.h"
#include "pdu.h"
#include "ll.h"
#include "ll_feat.h"
#include "ll_settings.h"
#include "lll.h"
#include "lll/lll_df_types.h"
#include "lll_conn.h"
#include "ull_tx_queue.h"
#include "ull_conn_internal.h"
#include "ull_conn_types.h"
#include "ull_internal.h"
#include "ull_llcp.h"
#include "ull_llcp_features.h"
#include "ull_llcp_internal.h"
#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_DEBUG_HCI_DRIVER)
#define LOG_MODULE_NAME bt_ctlr_ull_llcp_conn_upd
#include "common/log.h"
#include <soc.h>
#include "hal/debug.h"
/* Hardcoded instant delta +6 */
#define CONN_UPDATE_INSTANT_DELTA 6U
/* TODO: Known, missing items (missing implementation):
* LL/CON/MAS/BV-34-C [Accepting Connection Parameter Request event masked]
*/
/* LLCP Local Procedure Connection Update FSM states */
enum {
LP_CU_STATE_IDLE,
LP_CU_STATE_WAIT_TX_CONN_PARAM_REQ,
LP_CU_STATE_WAIT_RX_CONN_PARAM_RSP,
LP_CU_STATE_WAIT_TX_CONN_UPDATE_IND,
LP_CU_STATE_WAIT_RX_CONN_UPDATE_IND,
LP_CU_STATE_WAIT_INSTANT,
LP_CU_STATE_WAIT_NTF,
};
/* LLCP Local Procedure Connection Update FSM events */
enum {
/* Procedure run */
LP_CU_EVT_RUN,
/* Response received */
LP_CU_EVT_CONN_PARAM_RSP,
/* Indication received */
LP_CU_EVT_CONN_UPDATE_IND,
/* Reject response received */
LP_CU_EVT_REJECT,
/* Unknown response received */
LP_CU_EVT_UNKNOWN,
};
/* LLCP Remote Procedure Connection Update FSM states */
enum {
RP_CU_STATE_IDLE,
RP_CU_STATE_WAIT_RX_CONN_PARAM_REQ,
RP_CU_STATE_WAIT_NTF_CONN_PARAM_REQ,
RP_CU_STATE_WAIT_CONN_PARAM_REQ_REPLY,
RP_CU_STATE_WAIT_CONN_PARAM_REQ_REPLY_CONTINUE,
RP_CU_STATE_WAIT_TX_REJECT_EXT_IND,
RP_CU_STATE_WAIT_TX_CONN_PARAM_RSP,
RP_CU_STATE_WAIT_TX_CONN_UPDATE_IND,
RP_CU_STATE_WAIT_RX_CONN_UPDATE_IND,
RP_CU_STATE_WAIT_INSTANT,
RP_CU_STATE_WAIT_NTF,
RP_CU_STATE_WAIT_TX_UNKNOWN_RSP
};
/* LLCP Remote Procedure Connection Update FSM events */
enum {
/* Procedure run */
RP_CU_EVT_RUN,
/* Request received */
RP_CU_EVT_CONN_PARAM_REQ,
/* Indication received */
RP_CU_EVT_CONN_UPDATE_IND,
/* CONN_PARAM_REQ reply */
RP_CU_EVT_CONN_PARAM_REQ_REPLY,
/* CONN_PARAM_REQ negative reply */
RP_CU_EVT_CONN_PARAM_REQ_NEG_REPLY,
};
/*
* LLCP Local Procedure Connection Update FSM
*/
static bool cu_have_params_changed(struct ll_conn *conn, uint16_t interval, uint16_t latency,
uint16_t timeout)
{
struct lll_conn *lll = &conn->lll;
if ((interval != lll->interval) || (latency != lll->latency) ||
(RADIO_CONN_EVENTS(timeout * 10000U, lll->interval * CONN_INT_UNIT_US) !=
conn->supervision_reload)) {
return true;
}
return false;
}
static void cu_update_conn_parameters(struct ll_conn *conn, struct proc_ctx *ctx)
{
ctx->data.cu.params_changed = cu_have_params_changed(
conn, ctx->data.cu.interval_max, ctx->data.cu.latency, ctx->data.cu.timeout);
ull_conn_update_parameters(conn, (ctx->proc == PROC_CONN_UPDATE), ctx->data.cu.win_size,
ctx->data.cu.win_offset_us, ctx->data.cu.interval_max,
ctx->data.cu.latency, ctx->data.cu.timeout,
ctx->data.cu.instant);
}
static bool cu_should_notify_host(struct proc_ctx *ctx)
{
return (((ctx->proc == PROC_CONN_PARAM_REQ) && (ctx->data.cu.error != 0U)) ||
(ctx->data.cu.params_changed != 0U));
}
static void lp_cu_tx(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t opcode)
{
struct node_tx *tx;
struct pdu_data *pdu;
/* Allocate tx node */
tx = llcp_tx_alloc(conn, ctx);
LL_ASSERT(tx);
pdu = (struct pdu_data *)tx->pdu;
/* Encode LL Control PDU */
switch (opcode) {
#if defined(CONFIG_BT_CTLR_CONN_PARAM_REQ)
case PDU_DATA_LLCTRL_TYPE_CONN_PARAM_REQ:
llcp_pdu_encode_conn_param_req(ctx, pdu);
break;
#endif /* CONFIG_BT_CTLR_CONN_PARAM_REQ */
#if defined(CONFIG_BT_CENTRAL)
case PDU_DATA_LLCTRL_TYPE_CONN_UPDATE_IND:
llcp_pdu_encode_conn_update_ind(ctx, pdu);
break;
#endif /* CONFIG_BT_CENTRAL */
default:
LL_ASSERT(0);
break;
}
ctx->tx_opcode = pdu->llctrl.opcode;
/* Enqueue LL Control PDU towards LLL */
llcp_tx_enqueue(conn, tx);
}
static void lp_cu_ntf(struct ll_conn *conn, struct proc_ctx *ctx)
{
struct node_rx_pdu *ntf;
struct node_rx_cu *pdu;
/* Allocate ntf node */
ntf = llcp_ntf_alloc();
LL_ASSERT(ntf);
ntf->hdr.type = NODE_RX_TYPE_CONN_UPDATE;
ntf->hdr.handle = conn->lll.handle;
pdu = (struct node_rx_cu *)ntf->pdu;
pdu->status = ctx->data.cu.error;
pdu->interval = ctx->data.cu.interval_max;
pdu->latency = ctx->data.cu.latency;
pdu->timeout = ctx->data.cu.timeout;
/* Enqueue notification towards LL */
ll_rx_put(ntf->hdr.link, ntf);
ll_rx_sched();
}
static void lp_cu_complete(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, void *param)
{
if (!llcp_ntf_alloc_is_available()) {
ctx->state = LP_CU_STATE_WAIT_NTF;
} else {
lp_cu_ntf(conn, ctx);
llcp_lr_complete(conn);
ctx->state = LP_CU_STATE_IDLE;
}
}
#if defined(CONFIG_BT_CTLR_CONN_PARAM_REQ)
static void lp_cu_send_conn_param_req(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt,
void *param)
{
if (llcp_rr_get_collision(conn) || !llcp_tx_alloc_peek(conn, ctx)) {
ctx->state = LP_CU_STATE_WAIT_TX_CONN_PARAM_REQ;
} else {
uint16_t event_counter = ull_conn_event_counter(conn);
llcp_rr_set_incompat(conn, INCOMPAT_RESOLVABLE);
ctx->data.cu.reference_conn_event_count = event_counter;
ctx->data.cu.preferred_periodicity = 0U;
ctx->data.cu.offset0 = 0x0000U;
ctx->data.cu.offset1 = 0xffffU;
ctx->data.cu.offset2 = 0xffffU;
ctx->data.cu.offset3 = 0xffffU;
ctx->data.cu.offset4 = 0xffffU;
ctx->data.cu.offset5 = 0xffffU;
lp_cu_tx(conn, ctx, PDU_DATA_LLCTRL_TYPE_CONN_PARAM_REQ);
switch (conn->lll.role) {
#if defined(CONFIG_BT_CENTRAL)
case BT_HCI_ROLE_CENTRAL:
ctx->state = LP_CU_STATE_WAIT_RX_CONN_PARAM_RSP;
ctx->rx_opcode = PDU_DATA_LLCTRL_TYPE_CONN_PARAM_RSP;
break;
#endif /* CONFIG_BT_CENTRAL */
#if defined(CONFIG_BT_PERIPHERAL)
case BT_HCI_ROLE_PERIPHERAL:
ctx->state = LP_CU_STATE_WAIT_RX_CONN_UPDATE_IND;
ctx->rx_opcode = PDU_DATA_LLCTRL_TYPE_CONN_UPDATE_IND;
break;
#endif /* CONFIG_BT_PERIPHERAL */
default:
/* Unknown role */
LL_ASSERT(0);
break;
}
}
}
#endif /* CONFIG_BT_CTLR_CONN_PARAM_REQ */
#if defined(CONFIG_BT_CENTRAL)
static void lp_cu_send_conn_update_ind(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt,
void *param)
{
if (!llcp_tx_alloc_peek(conn, ctx)) {
ctx->state = LP_CU_STATE_WAIT_TX_CONN_UPDATE_IND;
} else {
ctx->data.cu.win_size = 1U;
ctx->data.cu.win_offset_us = 0U;
ctx->data.cu.instant = ull_conn_event_counter(conn) + conn->lll.latency +
CONN_UPDATE_INSTANT_DELTA;
lp_cu_tx(conn, ctx, PDU_DATA_LLCTRL_TYPE_CONN_UPDATE_IND);
ctx->rx_opcode = PDU_DATA_LLCTRL_TYPE_UNUSED;
ctx->state = LP_CU_STATE_WAIT_INSTANT;
}
}
#endif /* CONFIG_BT_CENTRAL */
static void lp_cu_st_idle(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, void *param)
{
switch (evt) {
case LP_CU_EVT_RUN:
switch (ctx->proc) {
#if defined(CONFIG_BT_CTLR_CONN_PARAM_REQ)
case PROC_CONN_PARAM_REQ:
lp_cu_send_conn_param_req(conn, ctx, evt, param);
break;
#endif /* CONFIG_BT_CTLR_CONN_PARAM_REQ */
#if defined(CONFIG_BT_CENTRAL)
case PROC_CONN_UPDATE:
lp_cu_send_conn_update_ind(conn, ctx, evt, param);
break;
#endif /* CONFIG_BT_CENTRAL */
default:
/* Unknown procedure */
LL_ASSERT(0);
break;
}
break;
default:
/* Ignore other evts */
break;
}
}
#if defined(CONFIG_BT_CTLR_CONN_PARAM_REQ)
static void lp_cu_st_wait_tx_conn_param_req(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt,
void *param)
{
switch (evt) {
case LP_CU_EVT_RUN:
lp_cu_send_conn_param_req(conn, ctx, evt, param);
break;
default:
/* Ignore other evts */
break;
}
}
#endif /* CONFIG_BT_CTLR_CONN_PARAM_REQ */
#if defined(CONFIG_BT_CENTRAL)
#if defined(CONFIG_BT_CTLR_CONN_PARAM_REQ)
static void lp_cu_st_wait_rx_conn_param_rsp(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt,
void *param)
{
struct pdu_data *pdu = (struct pdu_data *)param;
switch (evt) {
case LP_CU_EVT_CONN_PARAM_RSP:
llcp_rr_set_incompat(conn, INCOMPAT_RESERVED);
lp_cu_send_conn_update_ind(conn, ctx, evt, param);
break;
case LP_CU_EVT_UNKNOWN:
llcp_rr_set_incompat(conn, INCOMPAT_RESERVED);
/* Unsupported in peer, so disable locally for this connection */
feature_unmask_features(conn, LL_FEAT_BIT_CONN_PARAM_REQ);
lp_cu_send_conn_update_ind(conn, ctx, evt, param);
break;
case LP_CU_EVT_REJECT:
/* TODO(tosk): Select between LL_REJECT_IND and LL_REJECT_EXT_IND */
if (pdu->llctrl.reject_ext_ind.error_code == BT_HCI_ERR_UNSUPP_REMOTE_FEATURE) {
/* Remote legacy Host */
llcp_rr_set_incompat(conn, INCOMPAT_RESERVED);
/* Unsupported in peer, so disable locally for this connection */
feature_unmask_features(conn, LL_FEAT_BIT_CONN_PARAM_REQ);
lp_cu_send_conn_update_ind(conn, ctx, evt, param);
} else {
llcp_rr_set_incompat(conn, INCOMPAT_NO_COLLISION);
ctx->data.cu.error = pdu->llctrl.reject_ext_ind.error_code;
lp_cu_complete(conn, ctx, evt, param);
}
break;
default:
/* Ignore other evts */
break;
}
}
#endif /* CONFIG_BT_CTLR_CONN_PARAM_REQ */
static void lp_cu_st_wait_tx_conn_update_ind(struct ll_conn *conn, struct proc_ctx *ctx,
uint8_t evt, void *param)
{
switch (evt) {
case LP_CU_EVT_RUN:
lp_cu_send_conn_update_ind(conn, ctx, evt, param);
break;
default:
/* Ignore other evts */
break;
}
}
#endif /* CONFIG_BT_CENTRAL */
#if defined(CONFIG_BT_PERIPHERAL)
static void lp_cu_st_wait_rx_conn_update_ind(struct ll_conn *conn, struct proc_ctx *ctx,
uint8_t evt, void *param)
{
struct pdu_data *pdu = (struct pdu_data *)param;
switch (evt) {
case LP_CU_EVT_CONN_UPDATE_IND:
llcp_pdu_decode_conn_update_ind(ctx, param);
ctx->state = LP_CU_STATE_WAIT_INSTANT;
break;
case LP_CU_EVT_UNKNOWN:
ctx->data.cu.error = BT_HCI_ERR_UNSUPP_REMOTE_FEATURE;
lp_cu_complete(conn, ctx, evt, param);
break;
case LP_CU_EVT_REJECT:
ctx->data.cu.error = pdu->llctrl.reject_ext_ind.error_code;
lp_cu_complete(conn, ctx, evt, param);
break;
default:
/* Ignore other evts */
break;
}
}
#endif /* CONFIG_BT_PERIPHERAL */
static void lp_cu_check_instant(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt,
void *param)
{
uint16_t event_counter = ull_conn_event_counter(conn);
if (is_instant_reached_or_passed(ctx->data.cu.instant, event_counter)) {
bool notify;
/* Procedure is complete when the instant has passed, and the
* new connection event parameters have been applied.
*/
cu_update_conn_parameters(conn, ctx);
notify = cu_should_notify_host(ctx);
if (notify) {
llcp_rr_set_incompat(conn, INCOMPAT_NO_COLLISION);
ctx->data.cu.error = BT_HCI_ERR_SUCCESS;
lp_cu_complete(conn, ctx, evt, param);
} else {
llcp_lr_complete(conn);
ctx->state = LP_CU_STATE_IDLE;
}
}
}
static void lp_cu_st_wait_instant(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt,
void *param)
{
/* TODO */
switch (evt) {
case LP_CU_EVT_RUN:
lp_cu_check_instant(conn, ctx, evt, param);
break;
default:
/* Ignore other evts */
break;
}
}
static void lp_cu_st_wait_ntf(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, void *param)
{
switch (evt) {
case LP_CU_EVT_RUN:
lp_cu_complete(conn, ctx, evt, param);
break;
default:
/* Ignore other evts */
break;
}
}
static void lp_cu_execute_fsm(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, void *param)
{
switch (ctx->state) {
case LP_CU_STATE_IDLE:
lp_cu_st_idle(conn, ctx, evt, param);
break;
#if defined(CONFIG_BT_CTLR_CONN_PARAM_REQ)
case LP_CU_STATE_WAIT_TX_CONN_PARAM_REQ:
lp_cu_st_wait_tx_conn_param_req(conn, ctx, evt, param);
break;
#endif /* CONFIG_BT_CTLR_CONN_PARAM_REQ */
#if defined(CONFIG_BT_CENTRAL)
#if defined(CONFIG_BT_CTLR_CONN_PARAM_REQ)
case LP_CU_STATE_WAIT_RX_CONN_PARAM_RSP:
lp_cu_st_wait_rx_conn_param_rsp(conn, ctx, evt, param);
break;
#endif /* CONFIG_BT_CTLR_CONN_PARAM_REQ */
case LP_CU_STATE_WAIT_TX_CONN_UPDATE_IND:
lp_cu_st_wait_tx_conn_update_ind(conn, ctx, evt, param);
break;
#endif /* CONFIG_BT_CENTRAL */
#if defined(CONFIG_BT_PERIPHERAL)
case LP_CU_STATE_WAIT_RX_CONN_UPDATE_IND:
lp_cu_st_wait_rx_conn_update_ind(conn, ctx, evt, param);
break;
#endif /* CONFIG_BT_PERIPHERAL */
case LP_CU_STATE_WAIT_INSTANT:
lp_cu_st_wait_instant(conn, ctx, evt, param);
break;
case LP_CU_STATE_WAIT_NTF:
lp_cu_st_wait_ntf(conn, ctx, evt, param);
break;
default:
/* Unknown state */
LL_ASSERT(0);
break;
}
}
void llcp_lp_cu_rx(struct ll_conn *conn, struct proc_ctx *ctx, struct node_rx_pdu *rx)
{
struct pdu_data *pdu = (struct pdu_data *)rx->pdu;
switch (pdu->llctrl.opcode) {
#if defined(CONFIG_BT_CTLR_CONN_PARAM_REQ)
case PDU_DATA_LLCTRL_TYPE_CONN_PARAM_RSP:
lp_cu_execute_fsm(conn, ctx, LP_CU_EVT_CONN_PARAM_RSP, pdu);
break;
#endif /* CONFIG_BT_CTLR_CONN_PARAM_REQ */
case PDU_DATA_LLCTRL_TYPE_CONN_UPDATE_IND:
lp_cu_execute_fsm(conn, ctx, LP_CU_EVT_CONN_UPDATE_IND, pdu);
break;
case PDU_DATA_LLCTRL_TYPE_UNKNOWN_RSP:
lp_cu_execute_fsm(conn, ctx, LP_CU_EVT_UNKNOWN, pdu);
break;
case PDU_DATA_LLCTRL_TYPE_REJECT_EXT_IND:
lp_cu_execute_fsm(conn, ctx, LP_CU_EVT_REJECT, pdu);
break;
default:
/* Unknown opcode */
LL_ASSERT(0);
break;
}
}
void llcp_lp_cu_init_proc(struct proc_ctx *ctx)
{
ctx->state = LP_CU_STATE_IDLE;
}
void llcp_lp_cu_run(struct ll_conn *conn, struct proc_ctx *ctx, void *param)
{
lp_cu_execute_fsm(conn, ctx, LP_CU_EVT_RUN, param);
}
/*
* LLCP Remote Procedure Connection Update FSM
*/
static void rp_cu_tx(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t opcode)
{
struct node_tx *tx;
struct pdu_data *pdu;
/* Allocate tx node */
tx = llcp_tx_alloc(conn, ctx);
LL_ASSERT(tx);
pdu = (struct pdu_data *)tx->pdu;
/* Encode LL Control PDU */
switch (opcode) {
#if defined(CONFIG_BT_CTLR_CONN_PARAM_REQ)
case PDU_DATA_LLCTRL_TYPE_CONN_PARAM_RSP:
llcp_pdu_encode_conn_param_rsp(ctx, pdu);
break;
#endif /* CONFIG_BT_CTLR_CONN_PARAM_REQ */
case PDU_DATA_LLCTRL_TYPE_CONN_UPDATE_IND:
llcp_pdu_encode_conn_update_ind(ctx, pdu);
break;
#if defined(CONFIG_BT_CTLR_CONN_PARAM_REQ)
case PDU_DATA_LLCTRL_TYPE_REJECT_EXT_IND:
/* TODO(thoh): Select between LL_REJECT_IND and LL_REJECT_EXT_IND */
llcp_pdu_encode_reject_ext_ind(pdu, PDU_DATA_LLCTRL_TYPE_CONN_PARAM_REQ,
ctx->data.cu.error);
break;
#endif /* CONFIG_BT_CTLR_CONN_PARAM_REQ */
case PDU_DATA_LLCTRL_TYPE_UNKNOWN_RSP:
llcp_pdu_encode_unknown_rsp(ctx, pdu);
break;
default:
LL_ASSERT(0);
break;
}
ctx->tx_opcode = pdu->llctrl.opcode;
/* Enqueue LL Control PDU towards LLL */
llcp_tx_enqueue(conn, tx);
}
static void rp_cu_ntf(struct ll_conn *conn, struct proc_ctx *ctx)
{
struct node_rx_pdu *ntf;
struct node_rx_cu *pdu;
/* Allocate ntf node */
ntf = llcp_ntf_alloc();
LL_ASSERT(ntf);
ntf->hdr.type = NODE_RX_TYPE_CONN_UPDATE;
ntf->hdr.handle = conn->lll.handle;
pdu = (struct node_rx_cu *)ntf->pdu;
pdu->status = ctx->data.cu.error;
pdu->interval = ctx->data.cu.interval_max;
pdu->latency = ctx->data.cu.latency;
pdu->timeout = ctx->data.cu.timeout;
/* Enqueue notification towards LL */
ll_rx_put(ntf->hdr.link, ntf);
ll_rx_sched();
}
#if defined(CONFIG_BT_CTLR_CONN_PARAM_REQ)
static void rp_cu_conn_param_req_ntf(struct ll_conn *conn, struct proc_ctx *ctx)
{
struct node_rx_pdu *ntf;
struct pdu_data *pdu;
/* Allocate ntf node */
ntf = llcp_ntf_alloc();
LL_ASSERT(ntf);
ntf->hdr.type = NODE_RX_TYPE_DC_PDU;
ntf->hdr.handle = conn->lll.handle;
pdu = (struct pdu_data *)ntf->pdu;
llcp_pdu_encode_conn_param_req(ctx, pdu);
/* Enqueue notification towards LL */
ll_rx_put(ntf->hdr.link, ntf);
ll_rx_sched();
}
#endif /* CONFIG_BT_CTLR_CONN_PARAM_REQ */
static void rp_cu_complete(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, void *param)
{
if (!llcp_ntf_alloc_is_available()) {
ctx->state = RP_CU_STATE_WAIT_NTF;
} else {
rp_cu_ntf(conn, ctx);
llcp_rr_complete(conn);
ctx->state = RP_CU_STATE_IDLE;
}
}
static void rp_cu_send_conn_update_ind(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt,
void *param)
{
if (!llcp_tx_alloc_peek(conn, ctx)) {
ctx->state = RP_CU_STATE_WAIT_TX_CONN_UPDATE_IND;
} else {
ctx->data.cu.win_size = 1U;
ctx->data.cu.win_offset_us = 0U;
ctx->data.cu.instant = ull_conn_event_counter(conn) + conn->lll.latency +
CONN_UPDATE_INSTANT_DELTA;
rp_cu_tx(conn, ctx, PDU_DATA_LLCTRL_TYPE_CONN_UPDATE_IND);
ctx->rx_opcode = PDU_DATA_LLCTRL_TYPE_UNUSED;
ctx->state = RP_CU_STATE_WAIT_INSTANT;
}
}
#if defined(CONFIG_BT_CTLR_CONN_PARAM_REQ)
static void rp_cu_send_reject_ext_ind(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt,
void *param)
{
if (!llcp_tx_alloc_peek(conn, ctx)) {
ctx->state = RP_CU_STATE_WAIT_TX_REJECT_EXT_IND;
} else {
rp_cu_tx(conn, ctx, PDU_DATA_LLCTRL_TYPE_REJECT_EXT_IND);
llcp_rr_complete(conn);
ctx->state = RP_CU_STATE_IDLE;
}
}
static void rp_cu_send_conn_param_rsp(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt,
void *param)
{
if (!llcp_tx_alloc_peek(conn, ctx)) {
ctx->state = RP_CU_STATE_WAIT_TX_CONN_PARAM_RSP;
} else {
rp_cu_tx(conn, ctx, PDU_DATA_LLCTRL_TYPE_CONN_PARAM_RSP);
ctx->rx_opcode = PDU_DATA_LLCTRL_TYPE_CONN_UPDATE_IND;
ctx->state = RP_CU_STATE_WAIT_RX_CONN_UPDATE_IND;
}
}
static void rp_cu_send_conn_param_req_ntf(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt,
void *param)
{
if (!llcp_ntf_alloc_is_available()) {
ctx->state = RP_CU_STATE_WAIT_NTF_CONN_PARAM_REQ;
} else {
rp_cu_conn_param_req_ntf(conn, ctx);
ctx->state = RP_CU_STATE_WAIT_CONN_PARAM_REQ_REPLY;
}
}
#endif /* CONFIG_BT_CTLR_CONN_PARAM_REQ */
static void rp_cu_send_unknown_rsp(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt,
void *param)
{
if (!llcp_tx_alloc_peek(conn, ctx)) {
ctx->state = RP_CU_STATE_WAIT_TX_UNKNOWN_RSP;
} else {
rp_cu_tx(conn, ctx, PDU_DATA_LLCTRL_TYPE_UNKNOWN_RSP);
llcp_rr_complete(conn);
ctx->state = RP_CU_STATE_IDLE;
}
}
static void rp_cu_st_idle(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, void *param)
{
switch (evt) {
case RP_CU_EVT_RUN:
switch (ctx->proc) {
#if defined(CONFIG_BT_CTLR_CONN_PARAM_REQ)
case PROC_CONN_PARAM_REQ:
ctx->state = RP_CU_STATE_WAIT_RX_CONN_PARAM_REQ;
break;
#endif /* CONFIG_BT_CTLR_CONN_PARAM_REQ */
case PROC_CONN_UPDATE:
ctx->state = RP_CU_STATE_WAIT_RX_CONN_UPDATE_IND;
break;
default:
/* Unknown proceduce */
LL_ASSERT(0);
break;
}
break;
default:
/* Ignore other evts */
break;
}
}
#if defined(CONFIG_BT_CTLR_CONN_PARAM_REQ)
static void rp_cu_st_wait_rx_conn_param_req(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt,
void *param)
{
switch (evt) {
case RP_CU_EVT_CONN_PARAM_REQ:
llcp_pdu_decode_conn_param_req(ctx, param);
bool params_changed =
cu_have_params_changed(conn, ctx->data.cu.interval_max,
ctx->data.cu.latency, ctx->data.cu.timeout);
/* notify Host if conn parameters changed, else respond */
if (params_changed) {
rp_cu_send_conn_param_req_ntf(conn, ctx, evt, param);
} else {
ctx->state = RP_CU_STATE_WAIT_CONN_PARAM_REQ_REPLY;
}
break;
default:
/* Ignore other evts */
break;
}
}
static void rp_cu_state_wait_ntf_conn_param_req(struct ll_conn *conn, struct proc_ctx *ctx,
uint8_t evt, void *param)
{
switch (evt) {
case RP_CU_EVT_RUN:
rp_cu_send_conn_param_req_ntf(conn, ctx, evt, param);
break;
default:
/* Ignore other evts */
break;
}
}
static void rp_cu_state_wait_conn_param_req_reply(struct ll_conn *conn, struct proc_ctx *ctx,
uint8_t evt, void *param)
{
switch (evt) {
case RP_CU_EVT_CONN_PARAM_REQ_REPLY:
/* Continue procedure in next prepare run */
ctx->state = RP_CU_STATE_WAIT_CONN_PARAM_REQ_REPLY_CONTINUE;
break;
case RP_CU_EVT_CONN_PARAM_REQ_NEG_REPLY:
/* Send reject in next prepare run */
ctx->state = RP_CU_STATE_WAIT_TX_REJECT_EXT_IND;
break;
default:
/* Ignore other evts */
break;
}
}
static void rp_cu_state_wait_conn_param_req_reply_continue(struct ll_conn *conn,
struct proc_ctx *ctx, uint8_t evt,
void *param)
{
switch (evt) {
case RP_CU_EVT_RUN:
if (conn->lll.role == BT_HCI_ROLE_CENTRAL) {
rp_cu_send_conn_update_ind(conn, ctx, evt, param);
} else if (conn->lll.role == BT_HCI_ROLE_PERIPHERAL) {
rp_cu_send_conn_param_rsp(conn, ctx, evt, param);
} else {
/* Unknown role */
LL_ASSERT(0);
}
break;
default:
/* Ignore other evts */
break;
}
}
static void rp_cu_state_wait_tx_reject_ext_ind(struct ll_conn *conn, struct proc_ctx *ctx,
uint8_t evt, void *param)
{
switch (evt) {
case RP_CU_EVT_RUN:
rp_cu_send_reject_ext_ind(conn, ctx, evt, param);
break;
default:
/* Ignore other evts */
break;
}
}
static void rp_cu_st_wait_tx_conn_param_rsp(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt,
void *param)
{
switch (evt) {
case RP_CU_EVT_RUN:
rp_cu_send_conn_param_rsp(conn, ctx, evt, param);
break;
default:
/* Ignore other evts */
break;
}
}
#endif /* CONFIG_BT_CTLR_CONN_PARAM_REQ */
static void rp_cu_st_wait_tx_conn_update_ind(struct ll_conn *conn, struct proc_ctx *ctx,
uint8_t evt, void *param)
{
switch (evt) {
case RP_CU_EVT_RUN:
rp_cu_send_conn_update_ind(conn, ctx, evt, param);
break;
default:
/* Ignore other evts */
break;
}
}
static void rp_cu_st_wait_rx_conn_update_ind(struct ll_conn *conn, struct proc_ctx *ctx,
uint8_t evt, void *param)
{
switch (evt) {
case RP_CU_EVT_CONN_UPDATE_IND:
switch (conn->lll.role) {
case BT_HCI_ROLE_CENTRAL:
ctx->unknown_response.type = PDU_DATA_LLCTRL_TYPE_CONN_UPDATE_IND;
rp_cu_send_unknown_rsp(conn, ctx, evt, param);
break;
case BT_HCI_ROLE_PERIPHERAL:
llcp_pdu_decode_conn_update_ind(ctx, param);
/* TODO(tosk): skip/terminate if instant passed? */
#if defined(CONFIG_BT_CTLR_CONN_PARAM_REQ)
/* conn param req procedure, if any, is complete */
ull_conn_prt_clear(conn);
#endif /* CONFIG_BT_CTLR_CONN_PARAM_REQ */
ctx->state = RP_CU_STATE_WAIT_INSTANT;
break;
default:
/* Unknown role */
LL_ASSERT(0);
}
default:
/* Ignore other evts */
break;
}
}
static void rp_cu_check_instant(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt,
void *param)
{
uint16_t event_counter = ull_conn_event_counter(conn);
if (is_instant_reached_or_passed(ctx->data.cu.instant, event_counter)) {
bool notify;
/* Procedure is complete when the instant has passed, and the
* new connection event parameters have been applied.
*/
cu_update_conn_parameters(conn, ctx);
notify = cu_should_notify_host(ctx);
if (notify) {
ctx->data.cu.error = BT_HCI_ERR_SUCCESS;
rp_cu_complete(conn, ctx, evt, param);
} else {
llcp_rr_complete(conn);
ctx->state = RP_CU_STATE_IDLE;
}
}
}
static void rp_cu_st_wait_instant(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt,
void *param)
{
switch (evt) {
case RP_CU_EVT_RUN:
rp_cu_check_instant(conn, ctx, evt, param);
break;
default:
/* Ignore other evts */
break;
}
}
static void rp_cu_st_wait_ntf(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, void *param)
{
switch (evt) {
case RP_CU_EVT_RUN:
rp_cu_complete(conn, ctx, evt, param);
break;
default:
/* Ignore other evts */
break;
}
}
static void rp_cu_execute_fsm(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, void *param)
{
switch (ctx->state) {
case RP_CU_STATE_IDLE:
rp_cu_st_idle(conn, ctx, evt, param);
break;
#if defined(CONFIG_BT_CTLR_CONN_PARAM_REQ)
case RP_CU_STATE_WAIT_RX_CONN_PARAM_REQ:
rp_cu_st_wait_rx_conn_param_req(conn, ctx, evt, param);
break;
case RP_CU_STATE_WAIT_NTF_CONN_PARAM_REQ:
rp_cu_state_wait_ntf_conn_param_req(conn, ctx, evt, param);
break;
case RP_CU_STATE_WAIT_CONN_PARAM_REQ_REPLY:
rp_cu_state_wait_conn_param_req_reply(conn, ctx, evt, param);
break;
case RP_CU_STATE_WAIT_CONN_PARAM_REQ_REPLY_CONTINUE:
rp_cu_state_wait_conn_param_req_reply_continue(conn, ctx, evt, param);
break;
case RP_CU_STATE_WAIT_TX_REJECT_EXT_IND:
rp_cu_state_wait_tx_reject_ext_ind(conn, ctx, evt, param);
break;
case RP_CU_STATE_WAIT_TX_CONN_PARAM_RSP:
rp_cu_st_wait_tx_conn_param_rsp(conn, ctx, evt, param);
break;
#endif /* CONFIG_BT_CTLR_CONN_PARAM_REQ */
case RP_CU_STATE_WAIT_TX_CONN_UPDATE_IND:
rp_cu_st_wait_tx_conn_update_ind(conn, ctx, evt, param);
break;
case RP_CU_STATE_WAIT_RX_CONN_UPDATE_IND:
rp_cu_st_wait_rx_conn_update_ind(conn, ctx, evt, param);
break;
case RP_CU_STATE_WAIT_INSTANT:
rp_cu_st_wait_instant(conn, ctx, evt, param);
break;
case RP_CU_STATE_WAIT_NTF:
rp_cu_st_wait_ntf(conn, ctx, evt, param);
break;
default:
/* Unknown state */
LL_ASSERT(0);
break;
}
}
void llcp_rp_cu_rx(struct ll_conn *conn, struct proc_ctx *ctx, struct node_rx_pdu *rx)
{
struct pdu_data *pdu = (struct pdu_data *)rx->pdu;
switch (pdu->llctrl.opcode) {
#if defined(CONFIG_BT_CTLR_CONN_PARAM_REQ)
case PDU_DATA_LLCTRL_TYPE_CONN_PARAM_REQ:
rp_cu_execute_fsm(conn, ctx, RP_CU_EVT_CONN_PARAM_REQ, pdu);
break;
#endif /* CONFIG_BT_CTLR_CONN_PARAM_REQ */
case PDU_DATA_LLCTRL_TYPE_CONN_UPDATE_IND:
rp_cu_execute_fsm(conn, ctx, RP_CU_EVT_CONN_UPDATE_IND, pdu);
break;
default:
/* Unknown opcode */
LL_ASSERT(0);
break;
}
}
void llcp_rp_cu_init_proc(struct proc_ctx *ctx)
{
ctx->state = RP_CU_STATE_IDLE;
}
void llcp_rp_cu_run(struct ll_conn *conn, struct proc_ctx *ctx, void *param)
{
rp_cu_execute_fsm(conn, ctx, RP_CU_EVT_RUN, param);
}
#if defined(CONFIG_BT_CTLR_CONN_PARAM_REQ)
void llcp_rp_conn_param_req_reply(struct ll_conn *conn, struct proc_ctx *ctx)
{
rp_cu_execute_fsm(conn, ctx, RP_CU_EVT_CONN_PARAM_REQ_REPLY, NULL);
}
void llcp_rp_conn_param_req_neg_reply(struct ll_conn *conn, struct proc_ctx *ctx)
{
rp_cu_execute_fsm(conn, ctx, RP_CU_EVT_CONN_PARAM_REQ_NEG_REPLY, NULL);
}
#endif /* CONFIG_BT_CTLR_CONN_PARAM_REQ */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,170 @@
/*
* Copyright (c) 2018-2019 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
static inline void feature_unmask_features(struct ll_conn *conn, uint64_t ll_feat_mask)
{
conn->llcp.fex.features_used &= ~ll_feat_mask;
}
static inline bool feature_le_encryption(struct ll_conn *conn)
{
#if defined(CONFIG_BT_CTLR_LE_ENC)
return conn->llcp.fex.features_used & LL_FEAT_BIT_ENC;
#else
return 0;
#endif
}
static inline bool feature_conn_param_req(struct ll_conn *conn)
{
#if defined(CONFIG_BT_CTLR_CONN_PARAM_REQ)
return conn->llcp.fex.features_used & LL_FEAT_BIT_CONN_PARAM_REQ;
#else
return 0;
#endif
}
static inline bool feature_ext_rej_ind(struct ll_conn *conn)
{
#if defined(CONFIG_BT_CTLR_EXT_REJ_IND)
return conn->llcp.fex.features_used & LL_FEAT_BIT_EXT_REJ_IND;
#else
return 0;
#endif
}
static inline bool feature_periph_feat_req(struct ll_conn *conn)
{
#if defined(CONFIG_BT_CTLR_PER_INIT_FEAT_XCHG)
return conn->llcp.fex.features_used & LL_FEAT_BIT_PER_INIT_FEAT_XCHG;
#else
return 0;
#endif
}
static inline bool feature_le_ping(struct ll_conn *conn)
{
#if defined(CONFIG_BT_CTLR_LE_PING)
return conn->llcp.fex.features_used & LL_FEAT_BIT_PING;
#else
return 0;
#endif
}
static inline bool feature_dle(struct ll_conn *conn)
{
#if defined(CONFIG_BT_CTLR_DATA_LENGTH)
return conn->llcp.fex.features_used & LL_FEAT_BIT_DLE;
#else
return 0;
#endif
}
static inline bool feature_privacy(struct ll_conn *conn)
{
#if defined(CONFIG_BT_CTLR_PRIVACY)
return conn->llcp.fex.features_used & LL_FEAT_BIT_PRIVACY;
#else
return 0;
#endif
}
static inline bool feature_ext_scan(struct ll_conn *conn)
{
#if defined(CONFIG_BT_CTLR_EXT_SCAN_FP)
return conn->llcp.fex.features_used & LL_FEAT_BIT_EXT_SCAN;
#else
return 0;
#endif
}
static inline bool feature_chan_sel_2(struct ll_conn *conn)
{
#if defined(CONFIG_BT_CTLR_CHAN_SEL_2)
return conn->llcp.fex.features_used & LL_FEAT_BIT_CHAN_SEL_2;
#else
return 0;
#endif
}
static inline bool feature_min_used_chan(struct ll_conn *conn)
{
#if defined(CONFIG_BT_CTLR_MIN_USED_CHAN)
return conn->llcp.fex.features_used & LL_FEAT_BIT_MIN_USED_CHAN;
#else
return 0;
#endif
}
static inline bool feature_phy_2m(struct ll_conn *conn)
{
#if defined(CONFIG_BT_CTLR_PHY_2M)
return conn->llcp.fex.features_used & LL_FEAT_BIT_PHY_2M;
#else
return 0;
#endif
}
static inline bool feature_phy_coded(struct ll_conn *conn)
{
#if defined(CONFIG_BT_CTLR_PHY_CODED)
return conn->llcp.fex.features_used & LL_FEAT_BIT_PHY_CODED;
#else
return 0;
#endif
}
/*
* for assymetric features we can check either if we support it
* or if the peer supports it
*/
static inline bool feature_smi_rx(struct ll_conn *conn)
{
return LL_FEAT_BIT_SMI_RX;
}
static inline bool feature_peer_smi_rx(struct ll_conn *conn)
{
return conn->llcp.fex.features_peer & BIT64(BT_LE_FEAT_BIT_SMI_RX);
}
static inline bool feature_smi_tx(struct ll_conn *conn)
{
return LL_FEAT_BIT_SMI_TX;
}
static inline bool feature_peer_smi_tx(struct ll_conn *conn)
{
return conn->llcp.fex.features_peer & BIT64(BT_LE_FEAT_BIT_SMI_TX);
}
/*
* The following features are not yet defined in KConfig and do
* not have a bitfield defined in ll_feat.h
* ext_adv
* per_adv
* pwr_class1
* min_chann
* CTE_req
* CTE_rsp
* CTE_tx
* CTE_rx
* ant_sw_CTE_tx
* ant_sw_CTE_rx
* tone_ext
* per_adv_sync_tx
* per_adv_sync_rx
* sleep_upd
* rpk_valid
* iso_central
* iso_periph
* iso_broadcast
* iso_receiver
* iso_channels
* le_pwr_req
* le_pwr_ind
* le_path_loss
*/

View file

@ -0,0 +1,592 @@
/*
* Copyright (c) 2020 Demant
*
* SPDX-License-Identifier: Apache-2.0
*/
/* LLCP Procedure */
enum llcp_proc {
PROC_UNKNOWN,
PROC_LE_PING,
PROC_FEATURE_EXCHANGE,
PROC_MIN_USED_CHANS,
PROC_VERSION_EXCHANGE,
PROC_ENCRYPTION_START,
PROC_ENCRYPTION_PAUSE,
PROC_PHY_UPDATE,
PROC_CONN_UPDATE,
PROC_CONN_PARAM_REQ,
PROC_TERMINATE,
PROC_CHAN_MAP_UPDATE,
PROC_DATA_LENGTH_UPDATE,
PROC_CTE_REQ,
};
#if ((CONFIG_BT_CTLR_LLCP_COMMON_TX_CTRL_BUF_NUM <\
(CONFIG_BT_CTLR_LLCP_TX_PER_CONN_TX_CTRL_BUF_NUM_MAX *\
CONFIG_BT_CTLR_LLCP_CONN)) &&\
(CONFIG_BT_CTLR_LLCP_PER_CONN_TX_CTRL_BUF_NUM <\
CONFIG_BT_CTLR_LLCP_TX_PER_CONN_TX_CTRL_BUF_NUM_MAX))
#define LLCP_TX_CTRL_BUF_QUEUE_ENABLE
#define LLCP_TX_CTRL_BUF_COUNT ((CONFIG_BT_CTLR_LLCP_COMMON_TX_CTRL_BUF_NUM +\
(CONFIG_BT_CTLR_LLCP_CONN * CONFIG_BT_CTLR_LLCP_PER_CONN_TX_CTRL_BUF_NUM)))
#else
#define LLCP_TX_CTRL_BUF_COUNT (CONFIG_BT_CTLR_LLCP_TX_PER_CONN_TX_CTRL_BUF_NUM_MAX *\
CONFIG_BT_CTLR_LLCP_CONN)
#endif
#if defined(LLCP_TX_CTRL_BUF_QUEUE_ENABLE)
enum llcp_wait_reason {
WAITING_FOR_NOTHING,
WAITING_FOR_TX_BUFFER,
};
#endif /* LLCP_TX_CTRL_BUF_QUEUE_ENABLE */
#if defined(CONFIG_BT_CTLR_LE_ENC)
struct llcp_enc {
uint8_t error;
/* NOTE: To save memory, SKD(m|s) and IV(m|s) are
* generated just-in-time for PDU enqueuing and are
* therefore not present in this structure.
*/
/* TODO(thoh): Do we want a version without JIT vector
* generation?
*/
/* TODO(thoh): Optimize memory layout.
* * Overlay memory?
* * Repurpose memory used by lll.ccm_tx/rx?
*/
/* Master: Rand and EDIV are input copies from
* HCI that only live until the LL_ENC_REQ has
* been enqueued.
*
* Slave: Rand and EDIV are input copies from
* the LL_ENC_REQ that only live until host
* notification has been enqueued.
*/
/* 64 bit random number. */
uint8_t rand[8];
/* 16 bit encrypted diversifier.*/
uint8_t ediv[2];
/* 128 bit long term key. */
uint8_t ltk[16];
/* SKD is the concatenation of SKDm and SKDs and
* is used to calculate the session key, SK.
*
* Lifetime:
* M : Generate SKDm and IVm
* M->S : LL_ENC_REQ(Rand, EDIV, SKDm, IVm)
* S : Notify host (Rand, EDIV)
* S : Generate SKDs and IVs
* S : Calculate SK = e(LTK, SKD)
* M<-S : LL_ENC_RSP(SKDs, IVs)
* M : Calculate SK = e(LTK, SKD)
*
* where security function e generates 128-bit
* encryptedData from a 128-bit key and 128-bit
* plaintextData using the AES-128-bit block
* cypher as defined in FIPS-1971:
* encryptedData = e(key, plaintextData)
*/
union {
/* 128-bit session key diversifier */
uint8_t skd[16];
struct {
uint8_t skdm[8];
uint8_t skds[8];
};
};
};
#endif /* CONFIG_BT_CTLR_LE_ENC */
/* LLCP Procedure Context */
struct proc_ctx {
/* Must be the first for sys_slist to work */
sys_snode_t node;
/* PROC_ */
enum llcp_proc proc;
enum pdu_data_llctrl_type response_opcode;
/* Procedure FSM */
uint8_t state;
/* Expected opcode to be received next */
enum pdu_data_llctrl_type rx_opcode;
/* Last transmitted opcode used for unknown/reject */
enum pdu_data_llctrl_type tx_opcode;
/* Instant collision */
int collision;
/* Procedure pause */
int pause;
#if defined(LLCP_TX_CTRL_BUF_QUEUE_ENABLE)
/* Wait list next pointer */
sys_snode_t wait_node;
/* Procedure wait reason */
enum llcp_wait_reason wait_reason;
#endif /* LLCP_TX_CTRL_BUF_QUEUE_ENABLE */
/* TX node awaiting ack */
struct node_tx *tx_ack;
/*
* This flag is set to 1 when we are finished with the control
* procedure and it is safe to release the context ctx
*/
int done;
/* Procedure data */
union {
/* Used by Minimum Used Channels Procedure */
#if defined(CONFIG_BT_CTLR_MIN_USED_CHAN)
struct {
uint8_t phys;
uint8_t min_used_chans;
} muc;
#endif /* CONFIG_BT_CTLR_MIN_USED_CHAN */
#if defined(CONFIG_BT_CTLR_LE_ENC)
/* Used by Encryption Procedure */
struct llcp_enc enc;
#endif /* CONFIG_BT_CTLR_LE_ENC */
#if defined(CONFIG_BT_CTLR_PHY)
/* PHY Update */
struct {
uint8_t tx:3;
uint8_t rx:3;
uint8_t flags:1;
uint8_t host_initiated:1;
uint8_t ntf_pu:1;
#if defined(CONFIG_BT_CTLR_DATA_LENGTH)
uint8_t ntf_dle:1;
#endif /* CONFIG_BT_CTLR_DATA_LENGTH */
uint8_t error;
uint16_t instant;
uint8_t c_to_p_phy;
uint8_t p_to_c_phy;
} pu;
#endif /* CONFIG_BT_CTLR_PHY */
/* TODO(tosk): leave out some params below if !CONFIG_BT_CTLR_CONN_PARAM_REQ */
/* Connection Update & Connection Parameter Request */
struct {
uint8_t error;
uint8_t params_changed;
uint16_t instant;
uint8_t win_size;
uint16_t win_offset_us;
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;
} cu;
/* Use by ACL Termination Procedure */
struct {
uint8_t error_code;
} term;
/* Use by Channel Map Update Procedure */
struct {
uint16_t instant;
uint8_t chm[5];
} chmu;
#if defined(CONFIG_BT_CTLR_DF_CONN_CTE_REQ)
/* Use by CTE Request Procedure */
struct {
uint8_t type:2;
uint8_t min_len:5;
} cte_req;
#endif /* CONFIG_BT_CTLR_DF_CONN_CTE_REQ */
} data;
struct {
uint8_t type;
} unknown_response;
struct {
uint8_t reject_opcode;
uint8_t error_code;
} reject_ext_ind;
};
/* Procedure Incompatibility */
enum proc_incompat {
/* Local procedure has not sent first PDU */
INCOMPAT_NO_COLLISION,
/* Local incompatible procedure has sent first PDU */
INCOMPAT_RESOLVABLE,
/* Local incompatible procedure has received first PDU */
INCOMPAT_RESERVED,
};
/* Invalid LL Control PDU Opcode */
#define ULL_LLCP_INVALID_OPCODE (0xFFU)
static inline bool is_instant_passed(uint16_t instant, uint16_t event_count)
{
/*
* BLUETOOTH CORE SPECIFICATION Version 5.2 | Vol 6, Part B
* 5.5.1 ACL Control Procedures
* When a periph receives such a PDU where (Instant connEventCount) modulo
* 65536 is less than 32767 and Instant is not equal to connEventCount, the periph
* shall listen to all the connection events until it has confirmation that the central
* has received its acknowledgment of the PDU or connEventCount equals
* Instant.
*
* x % 2^n == x & (2^n - 1)
*
* 65535 = 2^16 - 1
* 65536 = 2^16
*/
return ((instant - event_count) & 0xFFFFU) > 0x7FFFU;
}
static inline bool is_instant_not_passed(uint16_t instant, uint16_t event_count)
{
/*
* BLUETOOTH CORE SPECIFICATION Version 5.2 | Vol 6, Part B
* 5.5.1 ACL Control Procedures
* When a periph receives such a PDU where (Instant connEventCount) modulo
* 65536 is less than 32767 and Instant is not equal to connEventCount, the periph
* shall listen to all the connection events until it has confirmation that the central
* has received its acknowledgment of the PDU or connEventCount equals
* Instant.
*
* x % 2^n == x & (2^n - 1)
*
* 65535 = 2^16 - 1
* 65536 = 2^16
*/
return ((instant - event_count) & 0xFFFFU) < 0x7FFFU;
}
static inline bool is_instant_reached_or_passed(uint16_t instant, uint16_t event_count)
{
/*
* BLUETOOTH CORE SPECIFICATION Version 5.2 | Vol 6, Part B
* 5.5.1 ACL Control Procedures
* When a periph receives such a PDU where (Instant connEventCount) modulo
* 65536 is less than 32767 and Instant is not equal to connEventCount, the periph
* shall listen to all the connection events until it has confirmation that the central
* has received its acknowledgment of the PDU or connEventCount equals
* Instant.
*
* x % 2^n == x & (2^n - 1)
*
* 65535 = 2^16 - 1
* 65536 = 2^16
*/
return ((event_count - instant) & 0xFFFFU) <= 0x7FFFU;
}
/*
* LLCP Resource Management
*/
bool llcp_ntf_alloc_is_available(void);
bool llcp_ntf_alloc_num_available(uint8_t count);
struct node_rx_pdu *llcp_ntf_alloc(void);
struct proc_ctx *llcp_create_local_procedure(enum llcp_proc proc);
struct proc_ctx *llcp_create_remote_procedure(enum llcp_proc proc);
bool llcp_tx_alloc_peek(struct ll_conn *conn, struct proc_ctx *ctx);
void llcp_tx_alloc_unpeek(struct proc_ctx *ctx);
struct node_tx *llcp_tx_alloc(struct ll_conn *conn, struct proc_ctx *ctx);
/*
* ULL -> LLL Interface
*/
void llcp_tx_enqueue(struct ll_conn *conn, struct node_tx *tx);
void llcp_tx_pause_data(struct ll_conn *conn);
void llcp_tx_resume_data(struct ll_conn *conn);
void llcp_tx_flush(struct ll_conn *conn);
/*
* LLCP Local Procedure Common
*/
void llcp_lp_comm_tx_ack(struct ll_conn *conn, struct proc_ctx *ctx, struct node_tx *tx);
void llcp_lp_comm_rx(struct ll_conn *conn, struct proc_ctx *ctx, struct node_rx_pdu *rx);
void llcp_lp_comm_init_proc(struct proc_ctx *ctx);
void llcp_lp_comm_run(struct ll_conn *conn, struct proc_ctx *ctx, void *param);
/*
* LLCP Remote Procedure Common
*/
void llcp_rp_comm_tx_ack(struct ll_conn *conn, struct proc_ctx *ctx, struct node_tx *tx);
void llcp_rp_comm_rx(struct ll_conn *conn, struct proc_ctx *ctx, struct node_rx_pdu *rx);
void llcp_rp_comm_init_proc(struct proc_ctx *ctx);
void llcp_rp_comm_run(struct ll_conn *conn, struct proc_ctx *ctx, void *param);
#if defined(CONFIG_BT_CTLR_LE_ENC)
/*
* LLCP Local Procedure Encryption
*/
void llcp_lp_enc_rx(struct ll_conn *conn, struct proc_ctx *ctx, struct node_rx_pdu *rx);
void llcp_lp_enc_init_proc(struct proc_ctx *ctx);
void llcp_lp_enc_run(struct ll_conn *conn, struct proc_ctx *ctx, void *param);
/*
* LLCP Remote Procedure Encryption
*/
void llcp_rp_enc_rx(struct ll_conn *conn, struct proc_ctx *ctx, struct node_rx_pdu *rx);
void llcp_rp_enc_init_proc(struct proc_ctx *ctx);
void llcp_rp_enc_ltk_req_reply(struct ll_conn *conn, struct proc_ctx *ctx);
void llcp_rp_enc_ltk_req_neg_reply(struct ll_conn *conn, struct proc_ctx *ctx);
void llcp_rp_enc_run(struct ll_conn *conn, struct proc_ctx *ctx, void *param);
#endif /* CONFIG_BT_CTLR_LE_ENC */
#if defined(CONFIG_BT_CTLR_PHY)
/*
* LLCP Local Procedure PHY Update
*/
void llcp_lp_pu_rx(struct ll_conn *conn, struct proc_ctx *ctx, struct node_rx_pdu *rx);
void llcp_lp_pu_init_proc(struct proc_ctx *ctx);
void llcp_lp_pu_run(struct ll_conn *conn, struct proc_ctx *ctx, void *param);
void llcp_lp_pu_tx_ack(struct ll_conn *conn, struct proc_ctx *ctx, void *param);
#endif /* CONFIG_BT_CTLR_PHY */
/*
* LLCP Local Procedure Connection Update
*/
void llcp_lp_cu_rx(struct ll_conn *conn, struct proc_ctx *ctx, struct node_rx_pdu *rx);
void llcp_lp_cu_init_proc(struct proc_ctx *ctx);
void llcp_lp_cu_run(struct ll_conn *conn, struct proc_ctx *ctx, void *param);
/*
* LLCP Local Channel Map Update
*/
void llcp_lp_chmu_init_proc(struct proc_ctx *ctx);
void llcp_lp_chmu_run(struct ll_conn *conn, struct proc_ctx *ctx, void *param);
#if defined(CONFIG_BT_CTLR_PHY)
/*
* LLCP Remote Procedure PHY Update
*/
void llcp_rp_pu_rx(struct ll_conn *conn, struct proc_ctx *ctx, struct node_rx_pdu *rx);
void llcp_rp_pu_init_proc(struct proc_ctx *ctx);
void llcp_rp_pu_run(struct ll_conn *conn, struct proc_ctx *ctx, void *param);
void llcp_rp_pu_tx_ack(struct ll_conn *conn, struct proc_ctx *ctx, void *param);
#endif /* CONFIG_BT_CTLR_PHY */
/*
* LLCP Remote Procedure Connection Update
*/
void llcp_rp_cu_rx(struct ll_conn *conn, struct proc_ctx *ctx, struct node_rx_pdu *rx);
void llcp_rp_cu_init_proc(struct proc_ctx *ctx);
void llcp_rp_cu_run(struct ll_conn *conn, struct proc_ctx *ctx, void *param);
void llcp_rp_conn_param_req_reply(struct ll_conn *conn, struct proc_ctx *ctx);
void llcp_rp_conn_param_req_neg_reply(struct ll_conn *conn, struct proc_ctx *ctx);
/*
* Terminate Helper
*/
void llcp_pdu_encode_terminate_ind(struct proc_ctx *ctx, struct pdu_data *pdu);
void llcp_ntf_encode_terminate_ind(struct proc_ctx *ctx, struct pdu_data *pdu);
void llcp_pdu_decode_terminate_ind(struct proc_ctx *ctx, struct pdu_data *pdu);
/*
* LLCP Local Request
*/
struct proc_ctx *llcp_lr_peek(struct ll_conn *conn);
void llcp_lr_tx_ack(struct ll_conn *conn, struct proc_ctx *ctx, struct node_tx *tx);
void llcp_lr_rx(struct ll_conn *conn, struct proc_ctx *ctx, struct node_rx_pdu *rx);
void llcp_lr_enqueue(struct ll_conn *conn, struct proc_ctx *ctx);
void llcp_lr_init(struct ll_conn *conn);
void llcp_lr_run(struct ll_conn *conn);
void llcp_lr_complete(struct ll_conn *conn);
void llcp_lr_connect(struct ll_conn *conn);
void llcp_lr_disconnect(struct ll_conn *conn);
void llcp_lr_abort(struct ll_conn *conn);
/*
* LLCP Remote Request
*/
void llcp_rr_set_incompat(struct ll_conn *conn, enum proc_incompat incompat);
bool llcp_rr_get_collision(struct ll_conn *conn);
struct proc_ctx *llcp_rr_peek(struct ll_conn *conn);
void llcp_rr_tx_ack(struct ll_conn *conn, struct proc_ctx *ctx, struct node_tx *tx);
void llcp_rr_rx(struct ll_conn *conn, struct proc_ctx *ctx, struct node_rx_pdu *rx);
void llcp_rr_init(struct ll_conn *conn);
void llcp_rr_prepare(struct ll_conn *conn, struct node_rx_pdu *rx);
void llcp_rr_run(struct ll_conn *conn);
void llcp_rr_complete(struct ll_conn *conn);
void llcp_rr_connect(struct ll_conn *conn);
void llcp_rr_disconnect(struct ll_conn *conn);
void llcp_rr_new(struct ll_conn *conn, struct node_rx_pdu *rx);
#if defined(CONFIG_BT_CTLR_LE_PING)
/*
* LE Ping Procedure Helper
*/
void llcp_pdu_encode_ping_req(struct pdu_data *pdu);
void llcp_pdu_encode_ping_rsp(struct pdu_data *pdu);
#endif /* CONFIG_BT_CTLR_LE_PING */
/*
* Unknown response helper
*/
void llcp_pdu_encode_unknown_rsp(struct proc_ctx *ctx,
struct pdu_data *pdu);
void llcp_pdu_decode_unknown_rsp(struct proc_ctx *ctx,
struct pdu_data *pdu);
void llcp_ntf_encode_unknown_rsp(struct proc_ctx *ctx,
struct pdu_data *pdu);
/*
* Feature Exchange Procedure Helper
*/
void llcp_pdu_encode_feature_req(struct ll_conn *conn,
struct pdu_data *pdu);
void llcp_pdu_encode_feature_rsp(struct ll_conn *conn,
struct pdu_data *pdu);
void llcp_ntf_encode_feature_rsp(struct ll_conn *conn,
struct pdu_data *pdu);
void llcp_ntf_encode_feature_req(struct ll_conn *conn,
struct pdu_data *pdu);
void llcp_pdu_decode_feature_req(struct ll_conn *conn,
struct pdu_data *pdu);
void llcp_pdu_decode_feature_rsp(struct ll_conn *conn,
struct pdu_data *pdu);
#if defined(CONFIG_BT_CTLR_MIN_USED_CHAN)
/*
* Minimum number of used channels Procedure Helper
*/
#if defined(CONFIG_BT_PERIPHERAL)
void llcp_pdu_encode_min_used_chans_ind(struct proc_ctx *ctx, struct pdu_data *pdu);
#endif /* CONFIG_BT_PERIPHERAL */
#if defined(CONFIG_BT_CENTRAL)
void llcp_pdu_decode_min_used_chans_ind(struct ll_conn *conn, struct pdu_data *pdu);
#endif /* CONFIG_BT_CENTRAL */
#endif /* CONFIG_BT_CTLR_MIN_USED_CHAN */
/*
* Version Exchange Procedure Helper
*/
void llcp_pdu_encode_version_ind(struct pdu_data *pdu);
void llcp_ntf_encode_version_ind(struct ll_conn *conn, struct pdu_data *pdu);
void llcp_pdu_decode_version_ind(struct ll_conn *conn, struct pdu_data *pdu);
#if defined(CONFIG_BT_CTLR_LE_ENC)
/*
* Encryption Start Procedure Helper
*/
#if defined(CONFIG_BT_CENTRAL)
void llcp_pdu_encode_enc_req(struct proc_ctx *ctx, struct pdu_data *pdu);
#endif /* CONFIG_BT_CENTRAL */
#if defined(CONFIG_BT_PERIPHERAL)
void llcp_ntf_encode_enc_req(struct proc_ctx *ctx, struct pdu_data *pdu);
void llcp_pdu_encode_enc_rsp(struct pdu_data *pdu);
void llcp_pdu_encode_start_enc_req(struct pdu_data *pdu);
#endif /* CONFIG_BT_PERIPHERAL */
void llcp_pdu_encode_start_enc_rsp(struct pdu_data *pdu);
#if defined(CONFIG_BT_CENTRAL)
void llcp_pdu_encode_pause_enc_req(struct pdu_data *pdu);
#endif /* CONFIG_BT_CENTRAL */
void llcp_pdu_encode_pause_enc_rsp(struct pdu_data *pdu);
#endif /* CONFIG_BT_CTLR_LE_ENC */
void llcp_pdu_encode_reject_ind(struct pdu_data *pdu, uint8_t error_code);
void llcp_pdu_encode_reject_ext_ind(struct pdu_data *pdu, uint8_t reject_opcode,
uint8_t error_code);
void llcp_pdu_decode_reject_ext_ind(struct proc_ctx *ctx, struct pdu_data *pdu);
void llcp_ntf_encode_reject_ext_ind(struct proc_ctx *ctx, struct pdu_data *pdu);
/*
* PHY Update Procedure Helper
*/
void llcp_pdu_encode_phy_req(struct proc_ctx *ctx, struct pdu_data *pdu);
void llcp_pdu_decode_phy_req(struct proc_ctx *ctx, struct pdu_data *pdu);
#if defined(CONFIG_BT_PERIPHERAL)
void llcp_pdu_encode_phy_rsp(struct ll_conn *conn, struct pdu_data *pdu);
void llcp_pdu_decode_phy_update_ind(struct proc_ctx *ctx, struct pdu_data *pdu);
#endif /* CONFIG_BT_PERIPHERAL */
#if defined(CONFIG_BT_CENTRAL)
void llcp_pdu_encode_phy_update_ind(struct proc_ctx *ctx, struct pdu_data *pdu);
void llcp_pdu_decode_phy_rsp(struct proc_ctx *ctx, struct pdu_data *pdu);
#endif /* CONFIG_BT_CENTRAL */
/*
* Connection Update Procedure Helper
*/
void llcp_pdu_encode_conn_param_req(struct proc_ctx *ctx, struct pdu_data *pdu);
void llcp_pdu_decode_conn_param_req(struct proc_ctx *ctx, struct pdu_data *pdu);
void llcp_pdu_encode_conn_param_rsp(struct proc_ctx *ctx, struct pdu_data *pdu);
void llcp_pdu_decode_conn_param_rsp(struct proc_ctx *ctx, struct pdu_data *pdu);
void llcp_pdu_encode_conn_update_ind(struct proc_ctx *ctx, struct pdu_data *pdu);
void llcp_pdu_decode_conn_update_ind(struct proc_ctx *ctx, struct pdu_data *pdu);
void llcp_proc_ctx_release(struct proc_ctx *ctx);
/*
* Remote Channel Map Update Procedure Helper
*/
void llcp_pdu_encode_chan_map_update_ind(struct proc_ctx *ctx, struct pdu_data *pdu);
void llcp_pdu_decode_chan_map_update_ind(struct proc_ctx *ctx, struct pdu_data *pdu);
void llcp_rp_chmu_rx(struct ll_conn *conn, struct proc_ctx *ctx, struct node_rx_pdu *rx);
void llcp_rp_chmu_init_proc(struct proc_ctx *ctx);
void llcp_rp_chmu_run(struct ll_conn *conn, struct proc_ctx *ctx, void *param);
#if defined(CONFIG_BT_CTLR_DATA_LENGTH)
/*
* Data Length Update Procedure Helper
*/
void llcp_pdu_encode_length_req(struct ll_conn *conn, struct pdu_data *pdu);
void llcp_pdu_encode_length_rsp(struct ll_conn *conn, struct pdu_data *pdu);
void llcp_pdu_decode_length_req(struct ll_conn *conn, struct pdu_data *pdu);
void llcp_pdu_decode_length_rsp(struct ll_conn *conn, struct pdu_data *pdu);
void llcp_ntf_encode_length_change(struct ll_conn *conn,
struct pdu_data *pdu);
#endif /* CONFIG_BT_CTLR_DATA_LENGTH */
#if defined(CONFIG_BT_CTLR_DF_CONN_CTE_REQ)
/*
* Constant Tone Request Procedure Helper
*/
void llcp_pdu_encode_cte_req(struct proc_ctx *ctx, struct pdu_data *pdu);
void llcp_ntf_encode_cte_req(struct ll_conn *conn, struct pdu_data *pdu);
void llcp_pdu_decode_cte_req(struct ll_conn *conn, struct pdu_data *pdu);
void llcp_pdu_encode_cte_rsp(struct pdu_data *pdu);
#endif /* CONFIG_BT_CTLR_DF_CONN_CTE_REQ */
#ifdef ZTEST_UNITTEST
bool lr_is_disconnected(struct ll_conn *conn);
bool lr_is_idle(struct ll_conn *conn);
bool rr_is_disconnected(struct ll_conn *conn);
bool rr_is_idle(struct ll_conn *conn);
int ctx_buffers_free(void);
#endif

View file

@ -0,0 +1,487 @@
/*
* Copyright (c) 2020 Demant
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/types.h>
#include <bluetooth/hci.h>
#include <sys/byteorder.h>
#include <sys/slist.h>
#include <sys/util.h>
#include "hal/ccm.h"
#include "util/util.h"
#include "util/mem.h"
#include "util/memq.h"
#include "pdu.h"
#include "ll.h"
#include "ll_settings.h"
#include "lll.h"
#include "lll/lll_df_types.h"
#include "lll_conn.h"
#include "ull_tx_queue.h"
#include "ull_conn_types.h"
#include "ull_llcp.h"
#include "ull_llcp_internal.h"
#include "ull_conn_internal.h"
#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_DEBUG_HCI_DRIVER)
#define LOG_MODULE_NAME bt_ctlr_ull_llcp_local
#include "common/log.h"
#include <soc.h>
#include "hal/debug.h"
static void lr_check_done(struct ll_conn *conn, struct proc_ctx *ctx);
static struct proc_ctx *lr_dequeue(struct ll_conn *conn);
/* LLCP Local Request FSM State */
enum lr_state {
LR_STATE_IDLE,
LR_STATE_ACTIVE,
LR_STATE_DISCONNECT,
LR_STATE_TERMINATE,
};
/* LLCP Local Request FSM Event */
enum {
/* Procedure run */
LR_EVT_RUN,
/* Procedure completed */
LR_EVT_COMPLETE,
/* Link connected */
LR_EVT_CONNECT,
/* Link disconnected */
LR_EVT_DISCONNECT,
};
static void lr_check_done(struct ll_conn *conn, struct proc_ctx *ctx)
{
if (ctx->done) {
struct proc_ctx *ctx_header;
ctx_header = llcp_lr_peek(conn);
LL_ASSERT(ctx_header == ctx);
lr_dequeue(conn);
if ((ctx->proc != PROC_CHAN_MAP_UPDATE) && (ctx->proc != PROC_CONN_UPDATE)) {
ull_conn_prt_clear(conn);
}
llcp_proc_ctx_release(ctx);
}
}
/*
* LLCP Local Request FSM
*/
static void lr_set_state(struct ll_conn *conn, enum lr_state state)
{
conn->llcp.local.state = state;
}
void llcp_lr_enqueue(struct ll_conn *conn, struct proc_ctx *ctx)
{
sys_slist_append(&conn->llcp.local.pend_proc_list, &ctx->node);
}
static struct proc_ctx *lr_dequeue(struct ll_conn *conn)
{
struct proc_ctx *ctx;
ctx = (struct proc_ctx *)sys_slist_get(&conn->llcp.local.pend_proc_list);
return ctx;
}
struct proc_ctx *llcp_lr_peek(struct ll_conn *conn)
{
struct proc_ctx *ctx;
ctx = (struct proc_ctx *)sys_slist_peek_head(&conn->llcp.local.pend_proc_list);
return ctx;
}
void llcp_lr_rx(struct ll_conn *conn, struct proc_ctx *ctx, struct node_rx_pdu *rx)
{
switch (ctx->proc) {
#if defined(CONFIG_BT_CTLR_LE_PING)
case PROC_LE_PING:
llcp_lp_comm_rx(conn, ctx, rx);
break;
#endif /* CONFIG_BT_CTLR_LE_PING */
case PROC_FEATURE_EXCHANGE:
llcp_lp_comm_rx(conn, ctx, rx);
break;
#if defined(CONFIG_BT_CTLR_MIN_USED_CHAN)
case PROC_MIN_USED_CHANS:
llcp_lp_comm_rx(conn, ctx, rx);
break;
#endif /* CONFIG_BT_CTLR_MIN_USED_CHAN */
case PROC_VERSION_EXCHANGE:
llcp_lp_comm_rx(conn, ctx, rx);
break;
#if defined(CONFIG_BT_CTLR_LE_ENC) && defined(CONFIG_BT_CENTRAL)
case PROC_ENCRYPTION_START:
case PROC_ENCRYPTION_PAUSE:
llcp_lp_enc_rx(conn, ctx, rx);
break;
#endif /* CONFIG_BT_CTLR_LE_ENC && CONFIG_BT_CENTRAL */
#ifdef CONFIG_BT_CTLR_PHY
case PROC_PHY_UPDATE:
llcp_lp_pu_rx(conn, ctx, rx);
break;
#endif /* CONFIG_BT_CTLR_PHY */
case PROC_CONN_UPDATE:
case PROC_CONN_PARAM_REQ:
llcp_lp_cu_rx(conn, ctx, rx);
break;
case PROC_TERMINATE:
llcp_lp_comm_rx(conn, ctx, rx);
break;
#if defined(CONFIG_BT_CTLR_DATA_LENGTH)
case PROC_DATA_LENGTH_UPDATE:
llcp_lp_comm_rx(conn, ctx, rx);
break;
#endif /* CONFIG_BT_CTLR_DATA_LENGTH */
#if defined(CONFIG_BT_CTLR_DF_CONN_CTE_REQ)
case PROC_CTE_REQ:
llcp_lp_comm_rx(conn, ctx, rx);
break;
#endif /* CONFIG_BT_CTLR_DF_CONN_CTE_REQ */
default:
/* Unknown procedure */
LL_ASSERT(0);
break;
}
lr_check_done(conn, ctx);
}
void llcp_lr_tx_ack(struct ll_conn *conn, struct proc_ctx *ctx, struct node_tx *tx)
{
switch (ctx->proc) {
#if defined(CONFIG_BT_CTLR_MIN_USED_CHAN)
case PROC_MIN_USED_CHANS:
llcp_lp_comm_tx_ack(conn, ctx, tx);
break;
#endif /* CONFIG_BT_CTLR_MIN_USED_CHAN */
case PROC_TERMINATE:
llcp_lp_comm_tx_ack(conn, ctx, tx);
break;
#if defined(CONFIG_BT_CTLR_DATA_LENGTH)
case PROC_DATA_LENGTH_UPDATE:
llcp_lp_comm_tx_ack(conn, ctx, tx);
break;
#endif /* CONFIG_BT_CTLR_DATA_LENGTH */
#ifdef CONFIG_BT_CTLR_PHY
case PROC_PHY_UPDATE:
llcp_lp_pu_tx_ack(conn, ctx, tx);
break;
#endif /* CONFIG_BT_CTLR_PHY */
default:
break;
/* Ignore tx_ack */
}
lr_check_done(conn, ctx);
}
static void lr_act_run(struct ll_conn *conn)
{
struct proc_ctx *ctx;
ctx = llcp_lr_peek(conn);
switch (ctx->proc) {
#if defined(CONFIG_BT_CTLR_LE_PING)
case PROC_LE_PING:
llcp_lp_comm_run(conn, ctx, NULL);
break;
#endif /* CONFIG_BT_CTLR_LE_PING */
case PROC_FEATURE_EXCHANGE:
llcp_lp_comm_run(conn, ctx, NULL);
break;
#if defined(CONFIG_BT_CTLR_MIN_USED_CHAN)
case PROC_MIN_USED_CHANS:
llcp_lp_comm_run(conn, ctx, NULL);
break;
#endif /* CONFIG_BT_CTLR_MIN_USED_CHAN */
case PROC_VERSION_EXCHANGE:
llcp_lp_comm_run(conn, ctx, NULL);
break;
#if defined(CONFIG_BT_CTLR_LE_ENC) && defined(CONFIG_BT_CENTRAL)
case PROC_ENCRYPTION_START:
case PROC_ENCRYPTION_PAUSE:
llcp_lp_enc_run(conn, ctx, NULL);
break;
#endif /* CONFIG_BT_CTLR_LE_ENC && CONFIG_BT_CENTRAL */
#ifdef CONFIG_BT_CTLR_PHY
case PROC_PHY_UPDATE:
llcp_lp_pu_run(conn, ctx, NULL);
break;
#endif /* CONFIG_BT_CTLR_PHY */
case PROC_CONN_UPDATE:
case PROC_CONN_PARAM_REQ:
llcp_lp_cu_run(conn, ctx, NULL);
break;
case PROC_TERMINATE:
llcp_lp_comm_run(conn, ctx, NULL);
break;
#if defined(CONFIG_BT_CENTRAL)
case PROC_CHAN_MAP_UPDATE:
llcp_lp_chmu_run(conn, ctx, NULL);
break;
#endif /* CONFIG_BT_CENTRAL */
#if defined(CONFIG_BT_CTLR_DATA_LENGTH)
case PROC_DATA_LENGTH_UPDATE:
llcp_lp_comm_run(conn, ctx, NULL);
break;
#endif /* CONFIG_BT_CTLR_DATA_LENGTH */
#if defined(CONFIG_BT_CTLR_DF_CONN_CTE_REQ)
case PROC_CTE_REQ:
/* 3rd partam null? */
llcp_lp_comm_run(conn, ctx, NULL);
break;
#endif /* CONFIG_BT_CTLR_DF_CONN_CTE_REQ */
default:
/* Unknown procedure */
LL_ASSERT(0);
break;
}
lr_check_done(conn, ctx);
}
static void lr_act_complete(struct ll_conn *conn)
{
struct proc_ctx *ctx;
ctx = llcp_lr_peek(conn);
ctx->done = 1U;
}
static void lr_act_connect(struct ll_conn *conn)
{
/* TODO */
}
static void lr_act_disconnect(struct ll_conn *conn)
{
struct proc_ctx *ctx;
ctx = lr_dequeue(conn);
/*
* we may have been disconnected in the
* middle of a control procedure, in
* which case we need to release context
*/
while (ctx != NULL) {
llcp_proc_ctx_release(ctx);
ctx = lr_dequeue(conn);
}
}
static void lr_st_disconnect(struct ll_conn *conn, uint8_t evt, void *param)
{
switch (evt) {
case LR_EVT_CONNECT:
lr_act_connect(conn);
lr_set_state(conn, LR_STATE_IDLE);
break;
default:
/* Ignore other evts */
break;
}
}
static void lr_st_idle(struct ll_conn *conn, uint8_t evt, void *param)
{
struct proc_ctx *ctx;
switch (evt) {
case LR_EVT_RUN:
ctx = llcp_lr_peek(conn);
if (ctx) {
lr_act_run(conn);
if (ctx->proc != PROC_TERMINATE) {
lr_set_state(conn, LR_STATE_ACTIVE);
} else {
lr_set_state(conn, LR_STATE_TERMINATE);
}
}
break;
case LR_EVT_DISCONNECT:
lr_act_disconnect(conn);
lr_set_state(conn, LR_STATE_DISCONNECT);
break;
default:
/* Ignore other evts */
break;
}
}
static void lr_st_active(struct ll_conn *conn, uint8_t evt, void *param)
{
switch (evt) {
case LR_EVT_RUN:
if (llcp_lr_peek(conn)) {
lr_act_run(conn);
}
break;
case LR_EVT_COMPLETE:
lr_act_complete(conn);
lr_set_state(conn, LR_STATE_IDLE);
break;
case LR_EVT_DISCONNECT:
lr_act_disconnect(conn);
lr_set_state(conn, LR_STATE_DISCONNECT);
break;
default:
/* Ignore other evts */
break;
}
}
static void lr_st_terminate(struct ll_conn *conn, uint8_t evt, void *param)
{
switch (evt) {
case LR_EVT_RUN:
if (llcp_lr_peek(conn)) {
lr_act_run(conn);
}
break;
case LR_EVT_COMPLETE:
lr_act_complete(conn);
lr_set_state(conn, LR_STATE_IDLE);
break;
case LR_EVT_DISCONNECT:
lr_act_disconnect(conn);
lr_set_state(conn, LR_STATE_DISCONNECT);
break;
default:
/* Ignore other evts */
break;
}
}
static void lr_execute_fsm(struct ll_conn *conn, uint8_t evt, void *param)
{
switch (conn->llcp.local.state) {
case LR_STATE_DISCONNECT:
lr_st_disconnect(conn, evt, param);
break;
case LR_STATE_IDLE:
lr_st_idle(conn, evt, param);
break;
case LR_STATE_ACTIVE:
lr_st_active(conn, evt, param);
break;
case LR_STATE_TERMINATE:
lr_st_terminate(conn, evt, param);
break;
default:
/* Unknown state */
LL_ASSERT(0);
}
}
void llcp_lr_init(struct ll_conn *conn)
{
lr_set_state(conn, LR_STATE_DISCONNECT);
}
void llcp_lr_run(struct ll_conn *conn)
{
lr_execute_fsm(conn, LR_EVT_RUN, NULL);
}
void llcp_lr_complete(struct ll_conn *conn)
{
lr_execute_fsm(conn, LR_EVT_COMPLETE, NULL);
}
void llcp_lr_connect(struct ll_conn *conn)
{
lr_execute_fsm(conn, LR_EVT_CONNECT, NULL);
}
void llcp_lr_disconnect(struct ll_conn *conn)
{
lr_execute_fsm(conn, LR_EVT_DISCONNECT, NULL);
}
void llcp_lr_abort(struct ll_conn *conn)
{
struct proc_ctx *ctx;
/* Flush all pending procedures */
ctx = lr_dequeue(conn);
while (ctx) {
llcp_proc_ctx_release(ctx);
ctx = lr_dequeue(conn);
}
/* TODO(thoh): Whats missing here ??? */
ull_conn_prt_clear(conn);
llcp_rr_set_incompat(conn, 0U);
lr_set_state(conn, LR_STATE_IDLE);
}
#ifdef ZTEST_UNITTEST
bool lr_is_disconnected(struct ll_conn *conn)
{
return conn->llcp.local.state == LR_STATE_DISCONNECT;
}
bool lr_is_idle(struct ll_conn *conn)
{
return conn->llcp.local.state == LR_STATE_IDLE;
}
void test_int_local_pending_requests(void)
{
struct ll_conn conn;
struct proc_ctx *peek_ctx;
struct proc_ctx *dequeue_ctx;
struct proc_ctx ctx;
ull_cp_init();
ull_tx_q_init(&conn.tx_q);
ull_llcp_init(&conn);
peek_ctx = llcp_lr_peek(&conn);
zassert_is_null(peek_ctx, NULL);
dequeue_ctx = lr_dequeue(&conn);
zassert_is_null(dequeue_ctx, NULL);
llcp_lr_enqueue(&conn, &ctx);
peek_ctx = (struct proc_ctx *)sys_slist_peek_head(&conn.llcp.local.pend_proc_list);
zassert_equal_ptr(peek_ctx, &ctx, NULL);
peek_ctx = llcp_lr_peek(&conn);
zassert_equal_ptr(peek_ctx, &ctx, NULL);
dequeue_ctx = lr_dequeue(&conn);
zassert_equal_ptr(dequeue_ctx, &ctx, NULL);
peek_ctx = llcp_lr_peek(&conn);
zassert_is_null(peek_ctx, NULL);
dequeue_ctx = lr_dequeue(&conn);
zassert_is_null(dequeue_ctx, NULL);
}
#endif

View file

@ -0,0 +1,752 @@
/*
* Copyright (c) 2020 Demant
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/types.h>
#include <bluetooth/hci.h>
#include <sys/byteorder.h>
#include <sys/slist.h>
#include <sys/util.h>
#include "hal/ccm.h"
#include "util/util.h"
#include "util/mem.h"
#include "util/memq.h"
#include "pdu.h"
#include "ll.h"
#include "ll_settings.h"
#include "lll.h"
#include "lll/lll_df_types.h"
#include "lll_conn.h"
#include "ull_tx_queue.h"
#include "ull_conn_types.h"
#include "ull_llcp.h"
#include "ull_llcp_internal.h"
#include "ll_feat.h"
#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_DEBUG_HCI_DRIVER)
#define LOG_MODULE_NAME bt_ctlr_ull_llcp_pdu
#include "common/log.h"
#include <soc.h>
#include "hal/debug.h"
/*
* we need to filter on octet 0; the following mask has 7 octets
* with all bits set to 1, the last octet is 0x00
*/
#define FEAT_FILT_OCTET0 0xFFFFFFFFFFFFFF00
#if defined(CONFIG_BT_CTLR_LE_PING)
/*
* LE Ping Procedure Helpers
*/
void llcp_pdu_encode_ping_req(struct pdu_data *pdu)
{
pdu->ll_id = PDU_DATA_LLID_CTRL;
pdu->len = offsetof(struct pdu_data_llctrl, ping_req) +
sizeof(struct pdu_data_llctrl_ping_req);
pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_PING_REQ;
}
void llcp_pdu_encode_ping_rsp(struct pdu_data *pdu)
{
pdu->ll_id = PDU_DATA_LLID_CTRL;
pdu->len = offsetof(struct pdu_data_llctrl, ping_rsp) +
sizeof(struct pdu_data_llctrl_ping_rsp);
pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_PING_RSP;
}
#endif /* CONFIG_BT_CTLR_LE_PING */
/*
* Unknown response helper
*/
void llcp_pdu_encode_unknown_rsp(struct proc_ctx *ctx, struct pdu_data *pdu)
{
pdu->ll_id = PDU_DATA_LLID_CTRL;
pdu->len = offsetof(struct pdu_data_llctrl, unknown_rsp) +
sizeof(struct pdu_data_llctrl_unknown_rsp);
pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_UNKNOWN_RSP;
pdu->llctrl.unknown_rsp.type = ctx->unknown_response.type;
}
void llcp_pdu_decode_unknown_rsp(struct proc_ctx *ctx, struct pdu_data *pdu)
{
ctx->unknown_response.type = pdu->llctrl.unknown_rsp.type;
}
void llcp_ntf_encode_unknown_rsp(struct proc_ctx *ctx, struct pdu_data *pdu)
{
struct pdu_data_llctrl_unknown_rsp *p;
pdu->ll_id = PDU_DATA_LLID_CTRL;
pdu->len = offsetof(struct pdu_data_llctrl, unknown_rsp) +
sizeof(struct pdu_data_llctrl_unknown_rsp);
pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_UNKNOWN_RSP;
p = &pdu->llctrl.unknown_rsp;
p->type = ctx->unknown_response.type;
}
/*
* Feature Exchange Procedure Helper
*/
static void feature_filter(uint8_t *featuresin, uint64_t *featuresout)
{
uint64_t feat;
/*
* Note that in the split controller invalid bits are set
* to 1, in LLCP we set them to 0; the spec. does not
* define which value they should have
*/
feat = sys_get_le64(featuresin);
feat &= LL_FEAT_BIT_MASK;
feat &= LL_FEAT_BIT_MASK_VALID;
*featuresout = feat;
}
void llcp_pdu_encode_feature_req(struct ll_conn *conn, struct pdu_data *pdu)
{
struct pdu_data_llctrl_feature_req *p;
pdu->ll_id = PDU_DATA_LLID_CTRL;
pdu->len = offsetof(struct pdu_data_llctrl, feature_req) +
sizeof(struct pdu_data_llctrl_feature_req);
pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_FEATURE_REQ;
#if defined(CONFIG_BT_CTLR_PER_INIT_FEAT_XCHG) && defined(CONFIG_BT_PERIPHERAL)
if (conn->lll.role == BT_HCI_ROLE_PERIPHERAL) {
pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_PER_INIT_FEAT_XCHG;
}
#endif /* CONFIG_BT_CTLR_PER_INIT_FEAT_XCHG && CONFIG_BT_PERIPHERAL */
p = &pdu->llctrl.feature_req;
sys_put_le64(LL_FEAT, p->features);
}
void llcp_pdu_encode_feature_rsp(struct ll_conn *conn, struct pdu_data *pdu)
{
struct pdu_data_llctrl_feature_rsp *p;
uint64_t feature_rsp = LL_FEAT;
pdu->ll_id = PDU_DATA_LLID_CTRL;
pdu->len = offsetof(struct pdu_data_llctrl, feature_rsp) +
sizeof(struct pdu_data_llctrl_feature_rsp);
pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_FEATURE_RSP;
p = &pdu->llctrl.feature_rsp;
/*
* we only filter on octet 0, remaining 7 octets are the features
* we support, as defined in LL_FEAT
*/
feature_rsp &= (FEAT_FILT_OCTET0 | conn->llcp.fex.features_used);
sys_put_le64(feature_rsp, p->features);
}
void llcp_ntf_encode_feature_rsp(struct ll_conn *conn, struct pdu_data *pdu)
{
struct pdu_data_llctrl_feature_rsp *p;
pdu->ll_id = PDU_DATA_LLID_CTRL;
pdu->len = offsetof(struct pdu_data_llctrl, feature_rsp) +
sizeof(struct pdu_data_llctrl_feature_rsp);
pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_FEATURE_RSP;
p = &pdu->llctrl.feature_rsp;
sys_put_le64(conn->llcp.fex.features_peer, p->features);
}
void llcp_pdu_decode_feature_req(struct ll_conn *conn, struct pdu_data *pdu)
{
uint64_t featureset;
feature_filter(pdu->llctrl.feature_req.features, &featureset);
conn->llcp.fex.features_used = LL_FEAT & featureset;
featureset &= (FEAT_FILT_OCTET0 | conn->llcp.fex.features_used);
conn->llcp.fex.features_peer = featureset;
conn->llcp.fex.valid = 1;
}
void llcp_pdu_decode_feature_rsp(struct ll_conn *conn, struct pdu_data *pdu)
{
uint64_t featureset;
feature_filter(pdu->llctrl.feature_rsp.features, &featureset);
conn->llcp.fex.features_used = LL_FEAT & featureset;
conn->llcp.fex.features_peer = featureset;
conn->llcp.fex.valid = 1;
}
#if defined(CONFIG_BT_CTLR_MIN_USED_CHAN)
/*
* Minimum used channels Procedure Helpers
*/
#if defined(CONFIG_BT_PERIPHERAL)
void llcp_pdu_encode_min_used_chans_ind(struct proc_ctx *ctx, struct pdu_data *pdu)
{
struct pdu_data_llctrl_min_used_chans_ind *p;
pdu->ll_id = PDU_DATA_LLID_CTRL;
pdu->len = offsetof(struct pdu_data_llctrl, min_used_chans_ind) +
sizeof(struct pdu_data_llctrl_min_used_chans_ind);
pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_MIN_USED_CHAN_IND;
p = &pdu->llctrl.min_used_chans_ind;
p->phys = ctx->data.muc.phys;
p->min_used_chans = ctx->data.muc.min_used_chans;
}
#endif /* CONFIG_BT_PERIPHERAL */
#if defined(CONFIG_BT_CENTRAL)
void llcp_pdu_decode_min_used_chans_ind(struct ll_conn *conn, struct pdu_data *pdu)
{
conn->llcp.muc.phys = pdu->llctrl.min_used_chans_ind.phys;
conn->llcp.muc.min_used_chans = pdu->llctrl.min_used_chans_ind.min_used_chans;
}
#endif /* CONFIG_BT_CENTRAL */
#endif /* CONFIG_BT_CTLR_MIN_USED_CHAN */
/*
* Termination Procedure Helper
*/
void llcp_pdu_encode_terminate_ind(struct proc_ctx *ctx, struct pdu_data *pdu)
{
struct pdu_data_llctrl_terminate_ind *p;
pdu->ll_id = PDU_DATA_LLID_CTRL;
pdu->len = offsetof(struct pdu_data_llctrl, terminate_ind) +
sizeof(struct pdu_data_llctrl_terminate_ind);
pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_TERMINATE_IND;
p = &pdu->llctrl.terminate_ind;
p->error_code = ctx->data.term.error_code;
}
void llcp_ntf_encode_terminate_ind(struct proc_ctx *ctx, struct pdu_data *pdu)
{
struct pdu_data_llctrl_terminate_ind *p;
pdu->ll_id = PDU_DATA_LLID_CTRL;
pdu->len = offsetof(struct pdu_data_llctrl, terminate_ind) +
sizeof(struct pdu_data_llctrl_terminate_ind);
pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_TERMINATE_IND;
p = &pdu->llctrl.terminate_ind;
p->error_code = ctx->data.term.error_code;
}
void llcp_pdu_decode_terminate_ind(struct proc_ctx *ctx, struct pdu_data *pdu)
{
ctx->data.term.error_code = pdu->llctrl.terminate_ind.error_code;
}
/*
* Version Exchange Procedure Helper
*/
void llcp_pdu_encode_version_ind(struct pdu_data *pdu)
{
uint16_t cid;
uint16_t svn;
struct pdu_data_llctrl_version_ind *p;
pdu->ll_id = PDU_DATA_LLID_CTRL;
pdu->len = offsetof(struct pdu_data_llctrl, version_ind) +
sizeof(struct pdu_data_llctrl_version_ind);
pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_VERSION_IND;
p = &pdu->llctrl.version_ind;
p->version_number = LL_VERSION_NUMBER;
cid = sys_cpu_to_le16(ll_settings_company_id());
svn = sys_cpu_to_le16(ll_settings_subversion_number());
p->company_id = cid;
p->sub_version_number = svn;
}
void llcp_ntf_encode_version_ind(struct ll_conn *conn, struct pdu_data *pdu)
{
struct pdu_data_llctrl_version_ind *p;
pdu->ll_id = PDU_DATA_LLID_CTRL;
pdu->len = offsetof(struct pdu_data_llctrl, version_ind) +
sizeof(struct pdu_data_llctrl_version_ind);
pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_VERSION_IND;
p = &pdu->llctrl.version_ind;
p->version_number = conn->llcp.vex.cached.version_number;
p->company_id = sys_cpu_to_le16(conn->llcp.vex.cached.company_id);
p->sub_version_number = sys_cpu_to_le16(conn->llcp.vex.cached.sub_version_number);
}
void llcp_pdu_decode_version_ind(struct ll_conn *conn, struct pdu_data *pdu)
{
conn->llcp.vex.valid = 1;
conn->llcp.vex.cached.version_number = pdu->llctrl.version_ind.version_number;
conn->llcp.vex.cached.company_id = sys_le16_to_cpu(pdu->llctrl.version_ind.company_id);
conn->llcp.vex.cached.sub_version_number =
sys_le16_to_cpu(pdu->llctrl.version_ind.sub_version_number);
}
#if defined(CONFIG_BT_CTLR_LE_ENC)
/*
* Encryption Start Procedure Helper
*/
static int csrand_get(void *buf, size_t len)
{
if (k_is_in_isr()) {
return lll_csrand_isr_get(buf, len);
} else {
return lll_csrand_get(buf, len);
}
}
#if defined(CONFIG_BT_CENTRAL)
void llcp_pdu_encode_enc_req(struct proc_ctx *ctx, struct pdu_data *pdu)
{
struct pdu_data_llctrl_enc_req *p;
pdu->ll_id = PDU_DATA_LLID_CTRL;
pdu->len =
offsetof(struct pdu_data_llctrl, enc_req) + sizeof(struct pdu_data_llctrl_enc_req);
pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_ENC_REQ;
p = &pdu->llctrl.enc_req;
memcpy(p->rand, ctx->data.enc.rand, sizeof(p->rand));
p->ediv[0] = ctx->data.enc.ediv[0];
p->ediv[1] = ctx->data.enc.ediv[1];
/* TODO(thoh): Optimize getting random data */
csrand_get(p->skdm, sizeof(p->skdm));
csrand_get(p->ivm, sizeof(p->ivm));
}
#endif /* CONFIG_BT_CENTRAL */
#if defined(CONFIG_BT_PERIPHERAL)
void llcp_ntf_encode_enc_req(struct proc_ctx *ctx, struct pdu_data *pdu)
{
struct pdu_data_llctrl_enc_req *p;
pdu->ll_id = PDU_DATA_LLID_CTRL;
pdu->len =
offsetof(struct pdu_data_llctrl, enc_req) + sizeof(struct pdu_data_llctrl_enc_req);
pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_ENC_REQ;
p = &pdu->llctrl.enc_req;
memcpy(p->rand, ctx->data.enc.rand, sizeof(p->rand));
p->ediv[0] = ctx->data.enc.ediv[0];
p->ediv[1] = ctx->data.enc.ediv[1];
}
void llcp_pdu_encode_enc_rsp(struct pdu_data *pdu)
{
struct pdu_data_llctrl_enc_rsp *p;
pdu->ll_id = PDU_DATA_LLID_CTRL;
pdu->len =
offsetof(struct pdu_data_llctrl, enc_rsp) + sizeof(struct pdu_data_llctrl_enc_rsp);
pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_ENC_RSP;
p = &pdu->llctrl.enc_rsp;
/* TODO(thoh): Optimize getting random data */
csrand_get(p->skds, sizeof(p->skds));
csrand_get(p->ivs, sizeof(p->ivs));
}
void llcp_pdu_encode_start_enc_req(struct pdu_data *pdu)
{
pdu->ll_id = PDU_DATA_LLID_CTRL;
pdu->len = offsetof(struct pdu_data_llctrl, start_enc_req) +
sizeof(struct pdu_data_llctrl_start_enc_req);
pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_START_ENC_REQ;
}
#endif /* CONFIG_BT_PERIPHERAL */
void llcp_pdu_encode_start_enc_rsp(struct pdu_data *pdu)
{
pdu->ll_id = PDU_DATA_LLID_CTRL;
pdu->len = offsetof(struct pdu_data_llctrl, start_enc_rsp) +
sizeof(struct pdu_data_llctrl_start_enc_rsp);
pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_START_ENC_RSP;
}
#if defined(CONFIG_BT_CENTRAL)
void llcp_pdu_encode_pause_enc_req(struct pdu_data *pdu)
{
pdu->ll_id = PDU_DATA_LLID_CTRL;
pdu->len = offsetof(struct pdu_data_llctrl, pause_enc_req) +
sizeof(struct pdu_data_llctrl_pause_enc_req);
pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_PAUSE_ENC_REQ;
}
#endif /* CONFIG_BT_CENTRAL */
void llcp_pdu_encode_pause_enc_rsp(struct pdu_data *pdu)
{
pdu->ll_id = PDU_DATA_LLID_CTRL;
pdu->len = offsetof(struct pdu_data_llctrl, pause_enc_rsp) +
sizeof(struct pdu_data_llctrl_pause_enc_rsp);
pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_PAUSE_ENC_RSP;
}
#endif /* CONFIG_BT_CTLR_LE_ENC */
void llcp_pdu_encode_reject_ind(struct pdu_data *pdu, uint8_t error_code)
{
pdu->ll_id = PDU_DATA_LLID_CTRL;
pdu->len = offsetof(struct pdu_data_llctrl, reject_ind) +
sizeof(struct pdu_data_llctrl_reject_ind);
pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_REJECT_IND;
pdu->llctrl.reject_ind.error_code = error_code;
}
void llcp_pdu_encode_reject_ext_ind(struct pdu_data *pdu, uint8_t reject_opcode, uint8_t error_code)
{
pdu->ll_id = PDU_DATA_LLID_CTRL;
pdu->len = offsetof(struct pdu_data_llctrl, reject_ext_ind) +
sizeof(struct pdu_data_llctrl_reject_ext_ind);
pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_REJECT_EXT_IND;
pdu->llctrl.reject_ext_ind.reject_opcode = reject_opcode;
pdu->llctrl.reject_ext_ind.error_code = error_code;
}
void llcp_pdu_decode_reject_ext_ind(struct proc_ctx *ctx, struct pdu_data *pdu)
{
ctx->reject_ext_ind.reject_opcode = pdu->llctrl.reject_ext_ind.reject_opcode;
ctx->reject_ext_ind.error_code = pdu->llctrl.reject_ext_ind.error_code;
}
void llcp_ntf_encode_reject_ext_ind(struct proc_ctx *ctx, struct pdu_data *pdu)
{
struct pdu_data_llctrl_reject_ext_ind *p;
pdu->ll_id = PDU_DATA_LLID_CTRL;
pdu->len = offsetof(struct pdu_data_llctrl, reject_ext_ind) +
sizeof(struct pdu_data_llctrl_reject_ext_ind);
pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_REJECT_EXT_IND;
p = (void *)&pdu->llctrl.reject_ext_ind;
p->error_code = ctx->reject_ext_ind.error_code;
p->reject_opcode = ctx->reject_ext_ind.reject_opcode;
}
#ifdef CONFIG_BT_CTLR_PHY
/*
* PHY Update Procedure Helper
*/
void llcp_pdu_encode_phy_req(struct proc_ctx *ctx, struct pdu_data *pdu)
{
pdu->ll_id = PDU_DATA_LLID_CTRL;
pdu->len =
offsetof(struct pdu_data_llctrl, phy_req) + sizeof(struct pdu_data_llctrl_phy_req);
pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_PHY_REQ;
pdu->llctrl.phy_req.rx_phys = ctx->data.pu.rx;
pdu->llctrl.phy_req.tx_phys = ctx->data.pu.tx;
}
void llcp_pdu_decode_phy_req(struct proc_ctx *ctx, struct pdu_data *pdu)
{
ctx->data.pu.rx = pdu->llctrl.phy_req.tx_phys;
ctx->data.pu.tx = pdu->llctrl.phy_req.rx_phys;
}
#if defined(CONFIG_BT_PERIPHERAL)
void llcp_pdu_encode_phy_rsp(struct ll_conn *conn, struct pdu_data *pdu)
{
pdu->ll_id = PDU_DATA_LLID_CTRL;
pdu->len =
offsetof(struct pdu_data_llctrl, phy_rsp) + sizeof(struct pdu_data_llctrl_phy_rsp);
pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_PHY_RSP;
pdu->llctrl.phy_rsp.rx_phys = conn->phy_pref_rx;
pdu->llctrl.phy_rsp.tx_phys = conn->phy_pref_tx;
}
void llcp_pdu_decode_phy_update_ind(struct proc_ctx *ctx, struct pdu_data *pdu)
{
ctx->data.pu.instant = sys_le16_to_cpu(pdu->llctrl.phy_upd_ind.instant);
ctx->data.pu.c_to_p_phy = pdu->llctrl.phy_upd_ind.c_to_p_phy;
ctx->data.pu.p_to_c_phy = pdu->llctrl.phy_upd_ind.p_to_c_phy;
}
#endif /* CONFIG_BT_PERIPHERAL */
#if defined(CONFIG_BT_CENTRAL)
void llcp_pdu_encode_phy_update_ind(struct proc_ctx *ctx, struct pdu_data *pdu)
{
pdu->ll_id = PDU_DATA_LLID_CTRL;
pdu->len = offsetof(struct pdu_data_llctrl, phy_upd_ind) +
sizeof(struct pdu_data_llctrl_phy_upd_ind);
pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_PHY_UPD_IND;
pdu->llctrl.phy_upd_ind.instant = sys_cpu_to_le16(ctx->data.pu.instant);
pdu->llctrl.phy_upd_ind.c_to_p_phy = ctx->data.pu.c_to_p_phy;
pdu->llctrl.phy_upd_ind.p_to_c_phy = ctx->data.pu.p_to_c_phy;
}
void llcp_pdu_decode_phy_rsp(struct proc_ctx *ctx, struct pdu_data *pdu)
{
ctx->data.pu.rx = pdu->llctrl.phy_rsp.tx_phys;
ctx->data.pu.tx = pdu->llctrl.phy_rsp.rx_phys;
}
#endif /* CONFIG_BT_CENTRAL */
#endif /* CONFIG_BT_CTLR_PHY */
/*
* Connection Update Procedure Helper
*/
void llcp_pdu_encode_conn_param_req(struct proc_ctx *ctx, struct pdu_data *pdu)
{
struct pdu_data_llctrl_conn_param_req *p;
pdu->ll_id = PDU_DATA_LLID_CTRL;
pdu->len = offsetof(struct pdu_data_llctrl, conn_param_req) +
sizeof(struct pdu_data_llctrl_conn_param_req);
pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_CONN_PARAM_REQ;
p = (void *)&pdu->llctrl.conn_param_req;
p->interval_min = sys_cpu_to_le16(ctx->data.cu.interval_min);
p->interval_max = sys_cpu_to_le16(ctx->data.cu.interval_max);
p->latency = sys_cpu_to_le16(ctx->data.cu.latency);
p->timeout = sys_cpu_to_le16(ctx->data.cu.timeout);
p->preferred_periodicity = ctx->data.cu.preferred_periodicity;
p->reference_conn_event_count = sys_cpu_to_le16(ctx->data.cu.reference_conn_event_count);
p->offset0 = sys_cpu_to_le16(ctx->data.cu.offset0);
p->offset1 = sys_cpu_to_le16(ctx->data.cu.offset1);
p->offset2 = sys_cpu_to_le16(ctx->data.cu.offset2);
p->offset3 = sys_cpu_to_le16(ctx->data.cu.offset3);
p->offset4 = sys_cpu_to_le16(ctx->data.cu.offset4);
p->offset5 = sys_cpu_to_le16(ctx->data.cu.offset5);
}
void llcp_pdu_encode_conn_param_rsp(struct proc_ctx *ctx, struct pdu_data *pdu)
{
struct pdu_data_llctrl_conn_param_req *p;
pdu->ll_id = PDU_DATA_LLID_CTRL;
pdu->len = offsetof(struct pdu_data_llctrl, conn_param_rsp) +
sizeof(struct pdu_data_llctrl_conn_param_rsp);
pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_CONN_PARAM_RSP;
p = (void *)&pdu->llctrl.conn_param_rsp;
p->interval_min = sys_cpu_to_le16(ctx->data.cu.interval_min);
p->interval_max = sys_cpu_to_le16(ctx->data.cu.interval_max);
p->latency = sys_cpu_to_le16(ctx->data.cu.latency);
p->timeout = sys_cpu_to_le16(ctx->data.cu.timeout);
p->preferred_periodicity = ctx->data.cu.preferred_periodicity;
p->reference_conn_event_count = sys_cpu_to_le16(ctx->data.cu.reference_conn_event_count);
p->offset0 = sys_cpu_to_le16(ctx->data.cu.offset0);
p->offset1 = sys_cpu_to_le16(ctx->data.cu.offset1);
p->offset2 = sys_cpu_to_le16(ctx->data.cu.offset2);
p->offset3 = sys_cpu_to_le16(ctx->data.cu.offset3);
p->offset4 = sys_cpu_to_le16(ctx->data.cu.offset4);
p->offset5 = sys_cpu_to_le16(ctx->data.cu.offset5);
}
void llcp_pdu_decode_conn_param_req(struct proc_ctx *ctx, struct pdu_data *pdu)
{
struct pdu_data_llctrl_conn_param_req *p;
p = (void *)&pdu->llctrl.conn_param_req;
ctx->data.cu.interval_min = sys_le16_to_cpu(p->interval_min);
ctx->data.cu.interval_max = sys_le16_to_cpu(p->interval_max);
ctx->data.cu.latency = sys_le16_to_cpu(p->latency);
ctx->data.cu.timeout = sys_le16_to_cpu(p->timeout);
ctx->data.cu.preferred_periodicity = p->preferred_periodicity;
ctx->data.cu.reference_conn_event_count = sys_le16_to_cpu(p->reference_conn_event_count);
ctx->data.cu.offset0 = sys_le16_to_cpu(p->offset0);
ctx->data.cu.offset1 = sys_le16_to_cpu(p->offset1);
ctx->data.cu.offset2 = sys_le16_to_cpu(p->offset2);
ctx->data.cu.offset3 = sys_le16_to_cpu(p->offset3);
ctx->data.cu.offset4 = sys_le16_to_cpu(p->offset4);
ctx->data.cu.offset5 = sys_le16_to_cpu(p->offset5);
}
void llcp_pdu_decode_conn_param_rsp(struct proc_ctx *ctx, struct pdu_data *pdu)
{
struct pdu_data_llctrl_conn_param_rsp *p;
p = (void *)&pdu->llctrl.conn_param_req;
ctx->data.cu.interval_min = sys_le16_to_cpu(p->interval_min);
ctx->data.cu.interval_max = sys_le16_to_cpu(p->interval_max);
ctx->data.cu.latency = sys_le16_to_cpu(p->latency);
ctx->data.cu.timeout = sys_le16_to_cpu(p->timeout);
ctx->data.cu.preferred_periodicity = p->preferred_periodicity;
ctx->data.cu.reference_conn_event_count = sys_le16_to_cpu(p->reference_conn_event_count);
ctx->data.cu.offset0 = sys_le16_to_cpu(p->offset0);
ctx->data.cu.offset1 = sys_le16_to_cpu(p->offset1);
ctx->data.cu.offset2 = sys_le16_to_cpu(p->offset2);
ctx->data.cu.offset3 = sys_le16_to_cpu(p->offset3);
ctx->data.cu.offset4 = sys_le16_to_cpu(p->offset4);
ctx->data.cu.offset5 = sys_le16_to_cpu(p->offset5);
}
void llcp_pdu_encode_conn_update_ind(struct proc_ctx *ctx, struct pdu_data *pdu)
{
struct pdu_data_llctrl_conn_update_ind *p;
pdu->ll_id = PDU_DATA_LLID_CTRL;
pdu->len = offsetof(struct pdu_data_llctrl, conn_update_ind) +
sizeof(struct pdu_data_llctrl_conn_update_ind);
pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_CONN_UPDATE_IND;
p = (void *)&pdu->llctrl.conn_update_ind;
p->win_size = ctx->data.cu.win_size;
p->win_offset = sys_cpu_to_le16(ctx->data.cu.win_offset_us / CONN_INT_UNIT_US);
p->latency = sys_cpu_to_le16(ctx->data.cu.latency);
p->interval = sys_cpu_to_le16(ctx->data.cu.interval_max);
p->timeout = sys_cpu_to_le16(ctx->data.cu.timeout);
p->instant = sys_cpu_to_le16(ctx->data.cu.instant);
}
void llcp_pdu_decode_conn_update_ind(struct proc_ctx *ctx, struct pdu_data *pdu)
{
struct pdu_data_llctrl_conn_update_ind *p;
p = (void *)&pdu->llctrl.conn_update_ind;
ctx->data.cu.win_size = p->win_size;
ctx->data.cu.win_offset_us = sys_le16_to_cpu(p->win_offset * CONN_INT_UNIT_US);
ctx->data.cu.latency = sys_le16_to_cpu(p->latency);
ctx->data.cu.interval_max = sys_le16_to_cpu(p->interval);
ctx->data.cu.timeout = sys_le16_to_cpu(p->timeout);
ctx->data.cu.instant = sys_le16_to_cpu(p->instant);
}
/*
* Channel Map Update Procedure Helpers
*/
void llcp_pdu_encode_chan_map_update_ind(struct proc_ctx *ctx, struct pdu_data *pdu)
{
struct pdu_data_llctrl_chan_map_ind *p;
pdu->ll_id = PDU_DATA_LLID_CTRL;
pdu->len = offsetof(struct pdu_data_llctrl, chan_map_ind) +
sizeof(struct pdu_data_llctrl_chan_map_ind);
pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_CHAN_MAP_IND;
p = &pdu->llctrl.chan_map_ind;
p->instant = sys_cpu_to_le16(ctx->data.chmu.instant);
memcpy(p->chm, ctx->data.chmu.chm, sizeof(p->chm));
}
void llcp_pdu_decode_chan_map_update_ind(struct proc_ctx *ctx, struct pdu_data *pdu)
{
ctx->data.chmu.instant = sys_le16_to_cpu(pdu->llctrl.chan_map_ind.instant);
memcpy(ctx->data.chmu.chm, pdu->llctrl.chan_map_ind.chm, sizeof(ctx->data.chmu.chm));
}
#if defined(CONFIG_BT_CTLR_DATA_LENGTH)
/*
* Data Length Update Procedure Helpers
*/
void llcp_pdu_encode_length_req(struct ll_conn *conn, struct pdu_data *pdu)
{
struct pdu_data_llctrl_length_req *p = &pdu->llctrl.length_req;
pdu->ll_id = PDU_DATA_LLID_CTRL;
pdu->len = offsetof(struct pdu_data_llctrl, length_req) +
sizeof(struct pdu_data_llctrl_length_req);
pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_LENGTH_REQ;
p->max_rx_octets = sys_cpu_to_le16(conn->lll.dle.local.max_rx_octets);
p->max_tx_octets = sys_cpu_to_le16(conn->lll.dle.local.max_tx_octets);
p->max_rx_time = sys_cpu_to_le16(conn->lll.dle.local.max_rx_time);
p->max_tx_time = sys_cpu_to_le16(conn->lll.dle.local.max_tx_time);
}
void llcp_pdu_encode_length_rsp(struct ll_conn *conn, struct pdu_data *pdu)
{
struct pdu_data_llctrl_length_rsp *p = &pdu->llctrl.length_rsp;
pdu->ll_id = PDU_DATA_LLID_CTRL;
pdu->len = offsetof(struct pdu_data_llctrl, length_rsp) +
sizeof(struct pdu_data_llctrl_length_rsp);
pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_LENGTH_RSP;
p->max_rx_octets = sys_cpu_to_le16(conn->lll.dle.local.max_rx_octets);
p->max_tx_octets = sys_cpu_to_le16(conn->lll.dle.local.max_tx_octets);
p->max_rx_time = sys_cpu_to_le16(conn->lll.dle.local.max_rx_time);
p->max_tx_time = sys_cpu_to_le16(conn->lll.dle.local.max_tx_time);
}
void llcp_ntf_encode_length_change(struct ll_conn *conn, struct pdu_data *pdu)
{
struct pdu_data_llctrl_length_rsp *p = &pdu->llctrl.length_rsp;
pdu->ll_id = PDU_DATA_LLID_CTRL;
pdu->len = offsetof(struct pdu_data_llctrl, length_rsp) +
sizeof(struct pdu_data_llctrl_length_rsp);
pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_LENGTH_RSP;
p->max_rx_octets = sys_cpu_to_le16(conn->lll.dle.eff.max_rx_octets);
p->max_tx_octets = sys_cpu_to_le16(conn->lll.dle.eff.max_tx_octets);
p->max_rx_time = sys_cpu_to_le16(conn->lll.dle.eff.max_rx_time);
p->max_tx_time = sys_cpu_to_le16(conn->lll.dle.eff.max_tx_time);
}
void llcp_pdu_decode_length_req(struct ll_conn *conn, struct pdu_data *pdu)
{
struct pdu_data_llctrl_length_req *p = &pdu->llctrl.length_req;
conn->lll.dle.remote.max_rx_octets = sys_le16_to_cpu(p->max_rx_octets);
conn->lll.dle.remote.max_tx_octets = sys_le16_to_cpu(p->max_tx_octets);
conn->lll.dle.remote.max_rx_time = sys_le16_to_cpu(p->max_rx_time);
conn->lll.dle.remote.max_tx_time = sys_le16_to_cpu(p->max_tx_time);
}
void llcp_pdu_decode_length_rsp(struct ll_conn *conn, struct pdu_data *pdu)
{
struct pdu_data_llctrl_length_rsp *p = &pdu->llctrl.length_rsp;
conn->lll.dle.remote.max_rx_octets = sys_le16_to_cpu(p->max_rx_octets);
conn->lll.dle.remote.max_tx_octets = sys_le16_to_cpu(p->max_tx_octets);
conn->lll.dle.remote.max_rx_time = sys_le16_to_cpu(p->max_rx_time);
conn->lll.dle.remote.max_tx_time = sys_le16_to_cpu(p->max_tx_time);
}
#endif /* CONFIG_BT_CTLR_DATA_LENGTH */
#if defined(CONFIG_BT_CTLR_DF_CONN_CTE_REQ)
/*
* Constant Tone Request Procedure Helper
*/
void llcp_pdu_encode_cte_req(struct proc_ctx *ctx, struct pdu_data *pdu)
{
struct pdu_data_llctrl_cte_req *p;
pdu->ll_id = PDU_DATA_LLID_CTRL;
pdu->len =
offsetof(struct pdu_data_llctrl, cte_req) + sizeof(struct pdu_data_llctrl_cte_req);
pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_CTE_REQ;
p = &pdu->llctrl.cte_req;
p->min_cte_len_req = ctx->data.cte_req.min_len;
p->rfu = 0; /* Reserved for future use */
p->cte_type_req = ctx->data.cte_req.type;
}
void llcp_ntf_encode_cte_req(struct ll_conn *conn, struct pdu_data *pdu)
{
pdu->ll_id = PDU_DATA_LLID_CTRL;
pdu->len =
offsetof(struct pdu_data_llctrl, cte_rsp) + sizeof(struct pdu_data_llctrl_cte_rsp);
pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_CTE_RSP;
/* TODO add handling of IQ samples forwarding */
}
void llcp_pdu_decode_cte_req(struct ll_conn *conn, struct pdu_data *pdu)
{
conn->llcp.cte_req.min_cte_len = pdu->llctrl.cte_req.min_cte_len_req;
conn->llcp.cte_req.cte_type = pdu->llctrl.cte_req.cte_type_req;
}
void llcp_pdu_encode_cte_rsp(struct pdu_data *pdu)
{
pdu->ll_id = PDU_DATA_LLID_CTRL;
pdu->len =
offsetof(struct pdu_data_llctrl, cte_rsp) + sizeof(struct pdu_data_llctrl_cte_rsp);
pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_CTE_RSP;
}
#endif /* CONFIG_BT_CTLR_DF_CONN_CTE_REQ */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,730 @@
/*
* Copyright (c) 2020 Demant
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/types.h>
#include <bluetooth/hci.h>
#include <sys/byteorder.h>
#include <sys/slist.h>
#include <sys/util.h>
#include "hal/ccm.h"
#include "util/util.h"
#include "util/mem.h"
#include "util/memq.h"
#include "pdu.h"
#include "ll.h"
#include "ll_settings.h"
#include "lll.h"
#include "lll/lll_df_types.h"
#include "lll_conn.h"
#include "ull_tx_queue.h"
#include "ull_conn_types.h"
#include "ull_conn_internal.h"
#include "ull_llcp.h"
#include "ull_llcp_internal.h"
#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_DEBUG_HCI_DRIVER)
#define LOG_MODULE_NAME bt_ctlr_ull_llcp_remote
#include "common/log.h"
#include <soc.h>
#include "hal/debug.h"
static void rr_check_done(struct ll_conn *conn, struct proc_ctx *ctx);
static struct proc_ctx *rr_dequeue(struct ll_conn *conn);
static void rr_abort(struct ll_conn *conn);
/* LLCP Remote Request FSM State */
enum rr_state {
RR_STATE_IDLE,
RR_STATE_REJECT,
RR_STATE_ACTIVE,
RR_STATE_DISCONNECT,
RR_STATE_TERMINATE,
};
/* LLCP Remote Request FSM Event */
enum {
/* Procedure prepare */
RR_EVT_PREPARE,
/* Procedure run */
RR_EVT_RUN,
/* Procedure completed */
RR_EVT_COMPLETE,
/* Link connected */
RR_EVT_CONNECT,
/* Link disconnected */
RR_EVT_DISCONNECT,
};
static bool proc_with_instant(struct proc_ctx *ctx)
{
/*
* TODO: should we combine all the cases that return 0
* and all the cases that return 1?
*/
switch (ctx->proc) {
case PROC_FEATURE_EXCHANGE:
return 0U;
case PROC_MIN_USED_CHANS:
return 0U;
case PROC_LE_PING:
return 0U;
case PROC_VERSION_EXCHANGE:
return 0U;
case PROC_ENCRYPTION_START:
case PROC_ENCRYPTION_PAUSE:
return 0U;
case PROC_PHY_UPDATE:
return 1U;
case PROC_CONN_UPDATE:
case PROC_CONN_PARAM_REQ:
return 1U;
case PROC_TERMINATE:
return 0U;
case PROC_CHAN_MAP_UPDATE:
return 1U;
case PROC_DATA_LENGTH_UPDATE:
return 0U;
case PROC_CTE_REQ:
return 0U;
default:
/* Unknown procedure */
LL_ASSERT(0);
break;
}
return 0U;
}
static void rr_check_done(struct ll_conn *conn, struct proc_ctx *ctx)
{
if (ctx->done) {
struct proc_ctx *ctx_header;
ctx_header = llcp_rr_peek(conn);
LL_ASSERT(ctx_header == ctx);
rr_dequeue(conn);
llcp_proc_ctx_release(ctx);
}
}
/*
* LLCP Remote Request FSM
*/
static void rr_set_state(struct ll_conn *conn, enum rr_state state)
{
conn->llcp.remote.state = state;
}
void llcp_rr_set_incompat(struct ll_conn *conn, enum proc_incompat incompat)
{
conn->llcp.remote.incompat = incompat;
}
static enum proc_incompat rr_get_incompat(struct ll_conn *conn)
{
return conn->llcp.remote.incompat;
}
static void rr_set_collision(struct ll_conn *conn, bool collision)
{
conn->llcp.remote.collision = collision;
}
bool llcp_rr_get_collision(struct ll_conn *conn)
{
return conn->llcp.remote.collision;
}
static void rr_enqueue(struct ll_conn *conn, struct proc_ctx *ctx)
{
sys_slist_append(&conn->llcp.remote.pend_proc_list, &ctx->node);
}
static struct proc_ctx *rr_dequeue(struct ll_conn *conn)
{
struct proc_ctx *ctx;
ctx = (struct proc_ctx *)sys_slist_get(&conn->llcp.remote.pend_proc_list);
return ctx;
}
struct proc_ctx *llcp_rr_peek(struct ll_conn *conn)
{
struct proc_ctx *ctx;
ctx = (struct proc_ctx *)sys_slist_peek_head(&conn->llcp.remote.pend_proc_list);
return ctx;
}
void llcp_rr_rx(struct ll_conn *conn, struct proc_ctx *ctx, struct node_rx_pdu *rx)
{
switch (ctx->proc) {
#if defined(CONFIG_BT_CTLR_LE_PING)
case PROC_LE_PING:
llcp_rp_comm_rx(conn, ctx, rx);
break;
#endif /* CONFIG_BT_CTLR_LE_PING */
case PROC_FEATURE_EXCHANGE:
llcp_rp_comm_rx(conn, ctx, rx);
break;
#if defined(CONFIG_BT_CTLR_MIN_USED_CHAN)
case PROC_MIN_USED_CHANS:
llcp_rp_comm_rx(conn, ctx, rx);
break;
#endif /* CONFIG_BT_CTLR_MIN_USED_CHAN */
case PROC_VERSION_EXCHANGE:
llcp_rp_comm_rx(conn, ctx, rx);
break;
#if defined(CONFIG_BT_CTLR_LE_ENC) && defined(CONFIG_BT_PERIPHERAL)
case PROC_ENCRYPTION_START:
case PROC_ENCRYPTION_PAUSE:
llcp_rp_enc_rx(conn, ctx, rx);
break;
#endif /* CONFIG_BT_CTLR_LE_ENC && CONFIG_BT_PERIPHERAL */
#ifdef CONFIG_BT_CTLR_PHY
case PROC_PHY_UPDATE:
llcp_rp_pu_rx(conn, ctx, rx);
break;
#endif /* CONFIG_BT_CTLR_PHY */
case PROC_CONN_UPDATE:
case PROC_CONN_PARAM_REQ:
llcp_rp_cu_rx(conn, ctx, rx);
break;
case PROC_TERMINATE:
llcp_rp_comm_rx(conn, ctx, rx);
break;
#if defined(CONFIG_BT_PERIPHERAL)
case PROC_CHAN_MAP_UPDATE:
llcp_rp_chmu_rx(conn, ctx, rx);
break;
#endif /* CONFIG_BT_PERIPHERAL */
#if defined(CONFIG_BT_CTLR_DATA_LENGTH)
case PROC_DATA_LENGTH_UPDATE:
llcp_rp_comm_rx(conn, ctx, rx);
break;
#endif /* CONFIG_BT_CTLR_DATA_LENGTH */
#if defined(CONFIG_BT_CTLR_DF_CONN_CTE_REQ)
case PROC_CTE_REQ:
llcp_rp_comm_rx(conn, ctx, rx);
break;
#endif /* CONFIG_BT_CTLR_DF_CONN_CTE_REQ */
default:
/* Unknown procedure */
LL_ASSERT(0);
break;
}
rr_check_done(conn, ctx);
}
void llcp_rr_tx_ack(struct ll_conn *conn, struct proc_ctx *ctx, struct node_tx *tx)
{
switch (ctx->proc) {
#if defined(CONFIG_BT_CTLR_DATA_LENGTH)
case PROC_DATA_LENGTH_UPDATE:
llcp_rp_comm_tx_ack(conn, ctx, tx);
break;
#endif /* CONFIG_BT_CTLR_DATA_LENGTH */
#ifdef CONFIG_BT_CTLR_PHY
case PROC_PHY_UPDATE:
llcp_rp_pu_tx_ack(conn, ctx, tx);
break;
#endif /* CONFIG_BT_CTLR_PHY */
default:
/* Ignore tx_ack */
break;
}
rr_check_done(conn, ctx);
}
static void rr_act_run(struct ll_conn *conn)
{
struct proc_ctx *ctx;
ctx = llcp_rr_peek(conn);
switch (ctx->proc) {
#if defined(CONFIG_BT_CTLR_LE_PING)
case PROC_LE_PING:
llcp_rp_comm_run(conn, ctx, NULL);
break;
#endif /* CONFIG_BT_CTLR_LE_PING */
case PROC_FEATURE_EXCHANGE:
llcp_rp_comm_run(conn, ctx, NULL);
break;
#if defined(CONFIG_BT_CTLR_MIN_USED_CHAN)
case PROC_MIN_USED_CHANS:
llcp_rp_comm_run(conn, ctx, NULL);
break;
#endif /* CONFIG_BT_CTLR_MIN_USED_CHAN */
case PROC_VERSION_EXCHANGE:
llcp_rp_comm_run(conn, ctx, NULL);
break;
#if defined(CONFIG_BT_CTLR_LE_ENC) && defined(CONFIG_BT_PERIPHERAL)
case PROC_ENCRYPTION_START:
case PROC_ENCRYPTION_PAUSE:
llcp_rp_enc_run(conn, ctx, NULL);
break;
#endif /* CONFIG_BT_CTLR_LE_ENC && CONFIG_BT_PERIPHERAL */
#ifdef CONFIG_BT_CTLR_PHY
case PROC_PHY_UPDATE:
llcp_rp_pu_run(conn, ctx, NULL);
break;
#endif /* CONFIG_BT_CTLR_PHY */
case PROC_CONN_UPDATE:
case PROC_CONN_PARAM_REQ:
llcp_rp_cu_run(conn, ctx, NULL);
break;
case PROC_TERMINATE:
llcp_rp_comm_run(conn, ctx, NULL);
break;
#if defined(CONFIG_BT_PERIPHERAL)
case PROC_CHAN_MAP_UPDATE:
llcp_rp_chmu_run(conn, ctx, NULL);
break;
#endif /* CONFIG_BT_PERIPHERAL */
#if defined(CONFIG_BT_CTLR_DATA_LENGTH)
case PROC_DATA_LENGTH_UPDATE:
llcp_rp_comm_run(conn, ctx, NULL);
break;
#endif /* CONFIG_BT_CTLR_DATA_LENGTH */
#if defined(CONFIG_BT_CTLR_DF_CONN_CTE_REQ)
case PROC_CTE_REQ:
llcp_rp_comm_run(conn, ctx, NULL);
break;
#endif /* CONFIG_BT_CTLR_DF_CONN_CTE_REQ */
default:
/* Unknown procedure */
LL_ASSERT(0);
break;
}
rr_check_done(conn, ctx);
}
static void rr_tx(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t opcode)
{
struct node_tx *tx;
struct pdu_data *pdu;
/* Allocate tx node */
tx = llcp_tx_alloc(conn, ctx);
LL_ASSERT(tx);
pdu = (struct pdu_data *)tx->pdu;
/* Encode LL Control PDU */
switch (opcode) {
case PDU_DATA_LLCTRL_TYPE_REJECT_IND:
/* TODO(thoh): Select between LL_REJECT_IND and LL_REJECT_EXT_IND */
llcp_pdu_encode_reject_ext_ind(pdu, conn->llcp.remote.reject_opcode,
BT_HCI_ERR_LL_PROC_COLLISION);
break;
default:
LL_ASSERT(0);
}
ctx->tx_opcode = pdu->llctrl.opcode;
/* Enqueue LL Control PDU towards LLL */
llcp_tx_enqueue(conn, tx);
}
static void rr_act_reject(struct ll_conn *conn)
{
struct proc_ctx *ctx = llcp_rr_peek(conn);
LL_ASSERT(ctx != NULL);
if (!llcp_tx_alloc_peek(conn, ctx)) {
rr_set_state(conn, RR_STATE_REJECT);
} else {
rr_tx(conn, ctx, PDU_DATA_LLCTRL_TYPE_REJECT_IND);
ctx->done = 1U;
rr_set_state(conn, RR_STATE_IDLE);
}
}
static void rr_act_complete(struct ll_conn *conn)
{
struct proc_ctx *ctx;
rr_set_collision(conn, 0U);
/* Dequeue pending request that just completed */
ctx = llcp_rr_peek(conn);
LL_ASSERT(ctx != NULL);
ctx->done = 1U;
}
static void rr_act_connect(struct ll_conn *conn)
{
/* TODO */
}
static void rr_act_disconnect(struct ll_conn *conn)
{
struct proc_ctx *ctx;
ctx = rr_dequeue(conn);
/*
* we may have been disconnected in the
* middle of a control procedure, in which
* case we need to release all contexts
*/
while (ctx != NULL) {
llcp_proc_ctx_release(ctx);
ctx = rr_dequeue(conn);
}
}
static void rr_st_disconnect(struct ll_conn *conn, uint8_t evt, void *param)
{
switch (evt) {
case RR_EVT_CONNECT:
rr_act_connect(conn);
rr_set_state(conn, RR_STATE_IDLE);
break;
default:
/* Ignore other evts */
break;
}
}
static void rr_st_idle(struct ll_conn *conn, uint8_t evt, void *param)
{
struct proc_ctx *ctx;
switch (evt) {
case RR_EVT_PREPARE:
ctx = llcp_rr_peek(conn);
if (ctx) {
const enum proc_incompat incompat = rr_get_incompat(conn);
const bool periph = !!(conn->lll.role == BT_HCI_ROLE_PERIPHERAL);
const bool central = !!(conn->lll.role == BT_HCI_ROLE_CENTRAL);
const bool with_instant = proc_with_instant(ctx);
if (ctx->proc == PROC_TERMINATE) {
/* Peer terminate overrides all */
/* Run remote procedure */
rr_act_run(conn);
rr_set_state(conn, RR_STATE_TERMINATE);
} else if (incompat == INCOMPAT_NO_COLLISION) {
/* No collision
* => Run procedure
*
* Local incompatible procedure request is kept pending.
*/
/* Pause local incompatible procedure */
rr_set_collision(conn, with_instant);
/* Run remote procedure */
rr_act_run(conn);
rr_set_state(conn, RR_STATE_ACTIVE);
} else if (periph && incompat == INCOMPAT_RESOLVABLE) {
/* Slave collision
* => Run procedure
*
* Local periph procedure completes with error.
*/
/* Run remote procedure */
rr_act_run(conn);
rr_set_state(conn, RR_STATE_ACTIVE);
} else if (with_instant && central && incompat == INCOMPAT_RESOLVABLE) {
/* Master collision
* => Send reject
*
* Local central incompatible procedure continues unaffected.
*/
/* Send reject */
struct node_rx_pdu *rx = (struct node_rx_pdu *)param;
struct pdu_data *pdu = (struct pdu_data *)rx->pdu;
conn->llcp.remote.reject_opcode = pdu->llctrl.opcode;
rr_act_reject(conn);
} else if (with_instant && incompat == INCOMPAT_RESERVED) {
/* Protocol violation.
* => Disconnect
*
*/
/* TODO */
LL_ASSERT(0);
}
}
break;
case RR_EVT_DISCONNECT:
rr_act_disconnect(conn);
rr_set_state(conn, RR_STATE_DISCONNECT);
break;
default:
/* Ignore other evts */
break;
}
}
static void rr_st_reject(struct ll_conn *conn, uint8_t evt, void *param)
{
/* TODO */
LL_ASSERT(0);
}
static void rr_st_active(struct ll_conn *conn, uint8_t evt, void *param)
{
switch (evt) {
case RR_EVT_RUN:
if (llcp_rr_peek(conn)) {
rr_act_run(conn);
}
break;
case RR_EVT_COMPLETE:
rr_act_complete(conn);
rr_set_state(conn, RR_STATE_IDLE);
break;
case RR_EVT_DISCONNECT:
rr_act_disconnect(conn);
rr_set_state(conn, RR_STATE_DISCONNECT);
break;
default:
/* Ignore other evts */
break;
}
}
static void rr_st_terminate(struct ll_conn *conn, uint8_t evt, void *param)
{
switch (evt) {
case RR_EVT_RUN:
if (llcp_rr_peek(conn)) {
rr_act_run(conn);
}
break;
case RR_EVT_COMPLETE:
rr_act_complete(conn);
rr_set_state(conn, RR_STATE_IDLE);
break;
case RR_EVT_DISCONNECT:
rr_act_disconnect(conn);
rr_set_state(conn, RR_STATE_DISCONNECT);
break;
default:
/* Ignore other evts */
break;
}
}
static void rr_execute_fsm(struct ll_conn *conn, uint8_t evt, void *param)
{
switch (conn->llcp.remote.state) {
case RR_STATE_DISCONNECT:
rr_st_disconnect(conn, evt, param);
break;
case RR_STATE_IDLE:
rr_st_idle(conn, evt, param);
break;
case RR_STATE_REJECT:
rr_st_reject(conn, evt, param);
break;
case RR_STATE_ACTIVE:
rr_st_active(conn, evt, param);
break;
case RR_STATE_TERMINATE:
rr_st_terminate(conn, evt, param);
break;
default:
/* Unknown state */
LL_ASSERT(0);
}
}
void llcp_rr_init(struct ll_conn *conn)
{
rr_set_state(conn, RR_STATE_DISCONNECT);
}
void llcp_rr_prepare(struct ll_conn *conn, struct node_rx_pdu *rx)
{
rr_execute_fsm(conn, RR_EVT_PREPARE, rx);
}
void llcp_rr_run(struct ll_conn *conn)
{
rr_execute_fsm(conn, RR_EVT_RUN, NULL);
}
void llcp_rr_complete(struct ll_conn *conn)
{
rr_execute_fsm(conn, RR_EVT_COMPLETE, NULL);
}
void llcp_rr_connect(struct ll_conn *conn)
{
rr_execute_fsm(conn, RR_EVT_CONNECT, NULL);
}
void llcp_rr_disconnect(struct ll_conn *conn)
{
rr_execute_fsm(conn, RR_EVT_DISCONNECT, NULL);
}
void llcp_rr_new(struct ll_conn *conn, struct node_rx_pdu *rx)
{
struct proc_ctx *ctx;
struct pdu_data *pdu;
uint8_t proc = PROC_UNKNOWN;
pdu = (struct pdu_data *)rx->pdu;
switch (pdu->llctrl.opcode) {
case PDU_DATA_LLCTRL_TYPE_CONN_UPDATE_IND:
proc = PROC_CONN_UPDATE;
break;
case PDU_DATA_LLCTRL_TYPE_FEATURE_REQ:
case PDU_DATA_LLCTRL_TYPE_PER_INIT_FEAT_XCHG:
proc = PROC_FEATURE_EXCHANGE;
break;
case PDU_DATA_LLCTRL_TYPE_PING_REQ:
proc = PROC_LE_PING;
break;
case PDU_DATA_LLCTRL_TYPE_VERSION_IND:
proc = PROC_VERSION_EXCHANGE;
break;
case PDU_DATA_LLCTRL_TYPE_ENC_REQ:
proc = PROC_ENCRYPTION_START;
break;
case PDU_DATA_LLCTRL_TYPE_PAUSE_ENC_REQ:
proc = PROC_ENCRYPTION_PAUSE;
break;
case PDU_DATA_LLCTRL_TYPE_PHY_REQ:
proc = PROC_PHY_UPDATE;
break;
case PDU_DATA_LLCTRL_TYPE_MIN_USED_CHAN_IND:
proc = PROC_MIN_USED_CHANS;
break;
case PDU_DATA_LLCTRL_TYPE_CONN_PARAM_REQ:
proc = PROC_CONN_PARAM_REQ;
break;
case PDU_DATA_LLCTRL_TYPE_TERMINATE_IND:
proc = PROC_TERMINATE;
break;
case PDU_DATA_LLCTRL_TYPE_CHAN_MAP_IND:
proc = PROC_CHAN_MAP_UPDATE;
break;
case PDU_DATA_LLCTRL_TYPE_LENGTH_REQ:
proc = PROC_DATA_LENGTH_UPDATE;
break;
case PDU_DATA_LLCTRL_TYPE_CTE_REQ:
proc = PROC_CTE_REQ;
break;
default:
/* Unknown opcode */
LL_ASSERT(0);
break;
}
if (proc == PROC_TERMINATE) {
rr_abort(conn);
}
ctx = llcp_create_remote_procedure(proc);
if (!ctx) {
return;
}
/* Enqueue procedure */
rr_enqueue(conn, ctx);
/* Prepare procedure */
llcp_rr_prepare(conn, rx);
/* Handle PDU */
ctx = llcp_rr_peek(conn);
if (ctx) {
llcp_rr_rx(conn, ctx, rx);
}
}
static void rr_abort(struct ll_conn *conn)
{
struct proc_ctx *ctx;
/* Flush all pending procedures */
ctx = rr_dequeue(conn);
while (ctx) {
llcp_proc_ctx_release(ctx);
ctx = rr_dequeue(conn);
}
/* TODO(thoh): Whats missing here ??? */
rr_set_collision(conn, 0U);
rr_set_state(conn, RR_STATE_IDLE);
}
#ifdef ZTEST_UNITTEST
bool rr_is_disconnected(struct ll_conn *conn)
{
return conn->llcp.remote.state == RR_STATE_DISCONNECT;
}
bool rr_is_idle(struct ll_conn *conn)
{
return conn->llcp.remote.state == RR_STATE_IDLE;
}
void test_int_remote_pending_requests(void)
{
struct ll_conn conn;
struct proc_ctx *peek_ctx;
struct proc_ctx *dequeue_ctx;
struct proc_ctx ctx;
ull_cp_init();
ull_tx_q_init(&conn.tx_q);
ull_llcp_init(&conn);
peek_ctx = llcp_rr_peek(&conn);
zassert_is_null(peek_ctx, NULL);
dequeue_ctx = rr_dequeue(&conn);
zassert_is_null(dequeue_ctx, NULL);
rr_enqueue(&conn, &ctx);
peek_ctx = (struct proc_ctx *)sys_slist_peek_head(&conn.llcp.remote.pend_proc_list);
zassert_equal_ptr(peek_ctx, &ctx, NULL);
peek_ctx = llcp_rr_peek(&conn);
zassert_equal_ptr(peek_ctx, &ctx, NULL);
dequeue_ctx = rr_dequeue(&conn);
zassert_equal_ptr(dequeue_ctx, &ctx, NULL);
peek_ctx = llcp_rr_peek(&conn);
zassert_is_null(peek_ctx, NULL);
dequeue_ctx = rr_dequeue(&conn);
zassert_is_null(dequeue_ctx, NULL);
}
#endif

View file

@ -30,10 +30,14 @@
#include "lll_adv.h"
#include "lll/lll_adv_pdu.h"
#include "lll_chan.h"
#include "lll/lll_df_types.h"
#include "lll_conn.h"
#include "lll_peripheral.h"
#include "lll_filter.h"
#include "lll/lll_df_types.h"
#if !defined(CONFIG_BT_LL_SW_LLCP_LEGACY)
#include "ull_tx_queue.h"
#endif
#include "ull_adv_types.h"
#include "ull_conn_types.h"
@ -46,6 +50,10 @@
#include "ll.h"
#if (!defined(CONFIG_BT_LL_SW_LLCP_LEGACY))
#include "ll_sw/ull_llcp.h"
#endif
#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_DEBUG_HCI_DRIVER)
#define LOG_MODULE_NAME bt_ctlr_ull_periph
#include "common/log.h"
@ -172,6 +180,11 @@ void ull_periph_setup(struct node_rx_hdr *rx, struct node_rx_ftr *ftr,
win_delay_us = WIN_DELAY_LEGACY;
}
#if (!defined(CONFIG_BT_LL_SW_LLCP_LEGACY))
/* Set LLCP as connection-wise connected */
ull_cp_state_set(conn, ULL_CP_CONNECTED);
#endif /* CONFIG_BT_LL_SW_LLCP_LEGACY */
/* calculate the window widening */
conn->periph.sca = pdu_adv->connect_ind.sca;
lll->periph.window_widening_periodic_us =
@ -313,8 +326,13 @@ void ull_periph_setup(struct node_rx_hdr *rx, struct node_rx_ftr *ftr,
#if defined(CONFIG_BT_CTLR_DATA_LENGTH)
#if defined(CONFIG_BT_CTLR_PHY)
#if defined(CONFIG_BT_LL_SW_LLCP_LEGACY)
max_tx_time = lll->max_tx_time;
max_rx_time = lll->max_rx_time;
#else
max_tx_time = lll->dle.local.max_tx_time;
max_rx_time = lll->dle.local.max_rx_time;
#endif
#else /* !CONFIG_BT_CTLR_PHY */
max_tx_time = PDU_DC_MAX_US(PDU_DC_PAYLOAD_SIZE_MIN, PHY_1M);
max_rx_time = PDU_DC_MAX_US(PDU_DC_PAYLOAD_SIZE_MIN, PHY_1M);
@ -544,6 +562,7 @@ uint8_t ll_start_enc_req_send(uint16_t handle, uint8_t error_code,
return BT_HCI_ERR_UNKNOWN_CONN_ID;
}
#if defined(CONFIG_BT_LL_SW_LLCP_LEGACY)
if (error_code) {
if (conn->llcp_enc.refresh == 0U) {
if ((conn->llcp_req == conn->llcp_ack) ||
@ -575,6 +594,25 @@ uint8_t ll_start_enc_req_send(uint16_t handle, uint8_t error_code,
conn->llcp.encryption.error_code = 0U;
conn->llcp.encryption.state = LLCP_ENC_STATE_INPROG;
}
#else /* CONFIG_BT_LL_SW_LLCP_LEGACY */
/*
* TODO: add info to the conn-structure
* - refresh
* - no procedure in progress
* - procedure type
* and use that info to decide if the cmd is allowed
* or if we should terminate the connection
* see BT 5.2 Vol. 6 part B chapter 5.1.3
* see also ull_periph.c line 395-439
*
* TODO: the ull_cp_ltx_req* functions should return success/fail status
*/
if (error_code) {
ull_cp_ltk_req_neq_reply(conn);
} else {
ull_cp_ltk_req_reply(conn, ltk);
}
#endif /* CONFIG_BT_LL_SW_LLCP_LEGACY */
return 0;
}
@ -639,3 +677,24 @@ static void ticker_update_latency_cancel_op_cb(uint32_t ticker_status,
conn->periph.latency_cancel = 0U;
}
#if !defined(CONFIG_BT_LL_SW_LLCP_LEGACY)
#if defined(CONFIG_BT_CTLR_MIN_USED_CHAN)
uint8_t ll_set_min_used_chans(uint16_t handle, uint8_t const phys,
uint8_t const min_used_chans)
{
struct ll_conn *conn;
conn = ll_connected_get(handle);
if (!conn) {
return BT_HCI_ERR_UNKNOWN_CONN_ID;
}
if (!conn->lll.role) {
return BT_HCI_ERR_CMD_DISALLOWED;
}
return ull_cp_min_used_chans(conn, phys, min_used_chans);
}
#endif /* CONFIG_BT_CTLR_MIN_USED_CHAN */
#endif /* !CONFIG_BT_LL_SW_LLCP_LEGACY */

View file

@ -34,11 +34,11 @@
#include "lll_filter.h"
#include "ull_adv_types.h"
#include "ull_scan_types.h"
#include "ull_filter.h"
#include "ull_internal.h"
#include "ull_adv_internal.h"
#include "ull_scan_types.h"
#include "ull_scan_internal.h"
#include "ull_sched_internal.h"

View file

@ -13,6 +13,7 @@
#include "util/util.h"
#include "hal/ticker.h"
#include "hal/ccm.h"
#include "ticker/ticker.h"
@ -23,6 +24,7 @@
#include "lll_scan.h"
#include "lll_scan_aux.h"
#include "lll/lll_df_types.h"
#include "lll_conn.h"
#include "lll_sync.h"
#include "lll_sync_iso.h"

View file

@ -26,6 +26,10 @@
#include "lll/lll_df_types.h"
#include "lll_conn.h"
#if !defined(CONFIG_BT_LL_SW_LLCP_LEGACY)
#include "ull_tx_queue.h"
#endif
#include "ull_scan_types.h"
#include "ull_conn_types.h"
@ -38,10 +42,12 @@
#include "hal/debug.h"
#if defined(CONFIG_BT_CTLR_CONN_PARAM_REQ)
#if defined(CONFIG_BT_LL_SW_LLCP_LEGACY)
static void win_offset_calc(struct ll_conn *conn_curr, uint8_t is_select,
uint32_t *ticks_to_offset_next,
uint16_t conn_interval, uint8_t *offset_max,
uint8_t *win_offset);
#endif
#endif /* CONFIG_BT_CTLR_CONN_PARAM_REQ */
static void after_mstr_offset_get(uint16_t conn_interval, uint32_t ticks_slot,
uint32_t ticks_anchor,
@ -194,7 +200,13 @@ void ull_sched_mfy_win_offset_use(void *param)
{
struct ll_conn *conn = param;
uint32_t ticks_slot_overhead;
/*
* TODO: update when updating the connection update procedure
*/
#if defined(CONFIG_BT_LL_SW_LLCP_LEGACY)
uint16_t win_offset;
#endif
if (IS_ENABLED(CONFIG_BT_CTLR_LOW_LAT)) {
ticks_slot_overhead = MAX(conn->ull.ticks_active_to_start,
@ -203,6 +215,10 @@ void ull_sched_mfy_win_offset_use(void *param)
ticks_slot_overhead = 0U;
}
/*
* TODO: update when updating the connection update procedure
*/
#if defined(CONFIG_BT_LL_SW_LLCP_LEGACY)
after_mstr_offset_get(conn->lll.interval,
(ticks_slot_overhead + conn->ull.ticks_slot),
conn->llcp.conn_upd.ticks_anchor,
@ -214,6 +230,7 @@ void ull_sched_mfy_win_offset_use(void *param)
/* move to offset calculated state */
conn->llcp_cu.state = LLCP_CUI_STATE_OFFS_RDY;
#endif
}
#if defined(CONFIG_BT_CTLR_CONN_PARAM_REQ)
@ -221,11 +238,22 @@ void ull_sched_mfy_free_win_offset_calc(void *param)
{
uint32_t ticks_to_offset_default = 0U;
uint32_t *ticks_to_offset_next;
struct ll_conn *conn = param;
/*
* TODO: update when updating the connection update procedure
*/
#if defined(CONFIG_BT_LL_SW_LLCP_LEGACY)
uint8_t offset_max = 6U;
struct ll_conn *conn = param;
#endif
ticks_to_offset_next = &ticks_to_offset_default;
/*
* TODO: update when updating the connection update procedure
*/
#if defined(CONFIG_BT_LL_SW_LLCP_LEGACY)
#if defined(CONFIG_BT_PERIPHERAL)
if (conn->lll.role) {
conn->llcp_conn_param.ticks_to_offset_next =
@ -242,19 +270,28 @@ void ull_sched_mfy_free_win_offset_calc(void *param)
/* move to offset calculated state */
conn->llcp_conn_param.state = LLCP_CPR_STATE_OFFS_RDY;
#endif /* CONFIG_BT_LL_SW_LLCP_LEGACY */
}
void ull_sched_mfy_win_offset_select(void *param)
{
#define OFFSET_S_MAX 6
#define OFFSET_M_MAX 6
#if defined(CONFIG_BT_LL_SW_LLCP_LEGACY)
uint16_t win_offset_m[OFFSET_M_MAX] = {0, };
uint8_t offset_m_max = OFFSET_M_MAX;
struct ll_conn *conn = param;
uint8_t offset_index_s = 0U;
uint8_t has_offset_s = 0U;
uint32_t ticks_to_offset;
uint16_t win_offset_s;
uint32_t ticks_to_offset;
#endif
/*
* TODO: update when updating the connection update procedure
*/
#if defined(CONFIG_BT_LL_SW_LLCP_LEGACY)
ticks_to_offset = HAL_TICKER_US_TO_TICKS(conn->llcp_conn_param.offset0 *
CONN_INT_UNIT_US);
@ -323,10 +360,15 @@ void ull_sched_mfy_win_offset_select(void *param)
/* move to conn param reject */
conn->llcp_cu.state = LLCP_CUI_STATE_REJECT;
}
#endif /* CONFIG_BT_LL_SW_LLCP_LEGACY */
#undef OFFSET_S_MAX
#undef OFFSET_M_MAX
}
#if defined(CONFIG_BT_LL_SW_LLCP_LEGACY)
static void win_offset_calc(struct ll_conn *conn_curr, uint8_t is_select,
uint32_t *ticks_to_offset_next,
uint16_t conn_interval, uint8_t *offset_max,
@ -552,6 +594,8 @@ static void win_offset_calc(struct ll_conn *conn_curr, uint8_t is_select,
*offset_max = offset_index;
}
#endif /* CONFIG_BT_LL_SW_LLCP_LEGACY */
#endif /* CONFIG_BT_CTLR_CONN_PARAM_REQ */
static void after_mstr_offset_get(uint16_t conn_interval, uint32_t ticks_slot,

View file

@ -29,6 +29,7 @@
#include "lll_chan.h"
#include "lll_scan.h"
#include "lll/lll_df_types.h"
#include "lll_conn.h"
#include "lll_sync.h"
#include "lll_sync_iso.h"

View file

@ -0,0 +1,66 @@
/*
* Copyright (c) 2020 Demant
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "ull_tx_queue.h"
void ull_tx_q_init(struct ull_tx_q *queue)
{
queue->pause_data = 0;
sys_slist_init(&queue->tx_list);
sys_slist_init(&queue->data_list);
}
void ull_tx_q_pause_data(struct ull_tx_q *queue)
{
queue->pause_data = 1U;
}
void ull_tx_q_resume_data(struct ull_tx_q *queue)
{
queue->pause_data = 0U;
/* move all paused data to the tail of tx list */
sys_slist_merge_slist(&queue->tx_list, &queue->data_list);
}
void ull_tx_q_enqueue_data(struct ull_tx_q *queue, struct node_tx *tx)
{
sys_slist_t *list;
if (queue->pause_data) {
/* enqueue data pdu into paused data wait list */
list = &queue->data_list;
} else {
/* enqueue data pdu into tx list */
list = &queue->tx_list;
}
sys_slist_append(list, (sys_snode_t *)tx);
}
void ull_tx_q_enqueue_ctrl(struct ull_tx_q *queue, struct node_tx *tx)
{
/* enqueue ctrl pdu into tx list */
sys_slist_append(&queue->tx_list, (sys_snode_t *)tx);
}
struct node_tx *ull_tx_q_peek(struct ull_tx_q *queue)
{
struct node_tx *tx;
tx = (struct node_tx *)sys_slist_peek_head(&queue->tx_list);
return tx;
}
struct node_tx *ull_tx_q_dequeue(struct ull_tx_q *queue)
{
struct node_tx *tx;
tx = (struct node_tx *)sys_slist_get(&queue->tx_list);
return tx;
}

View file

@ -0,0 +1,72 @@
/*
* Copyright (c) 2020 Demant
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <sys/slist.h>
struct ull_tx_q {
uint8_t pause_data; /* Data pause state of the tx queue */
sys_slist_t tx_list; /* Data and control node_tx list */
sys_slist_t data_list; /* Data node_tx wait list */
};
/* Forward declaration of node_tx */
struct node_tx;
/**
* @brief Initialize a tx queue.
*
* @param ull_tx_q Address of tx queue.
*/
void ull_tx_q_init(struct ull_tx_q *queue);
/**
* @brief Pause the data path of a tx queue.
*
* @param ull_tx_q Address of tx queue.
*/
void ull_tx_q_pause_data(struct ull_tx_q *queue);
/**
* @brief Resume the data path of a tx queue
*
* @param ull_tx_q Address of tx queue.
*/
void ull_tx_q_resume_data(struct ull_tx_q *queue);
/**
* @brief Enqueue a tx node in the data path of a tx queue
*
* @param ull_tx_q Address of tx queue.
* @param tx Address of tx node to enqueue.
*/
void ull_tx_q_enqueue_data(struct ull_tx_q *queue, struct node_tx *tx);
/**
* @brief Enqueue a tx node in the control path of a tx queue
*
* @param ull_tx_q Address of tx queue.
* @param tx Address of tx node to enqueue.
*/
void ull_tx_q_enqueue_ctrl(struct ull_tx_q *queue, struct node_tx *tx);
/**
* @brief Peek head tx node of tx queue.
*
* @param ull_tx_q Address of tx queue.
*
* @return Head tx node of the tx queue.
*/
struct node_tx *ull_tx_q_peek(struct ull_tx_q *queue);
/**
* @brief Dequeue a tx node from a tx queue.
*
* @param ull_tx_q Address of tx queue.
*
* @return Head tx node of the tx queue.
*/
struct node_tx *ull_tx_q_dequeue(struct ull_tx_q *queue);

View file

@ -27,7 +27,7 @@
*
* @return popcnt of 'octets'
*/
uint8_t util_ones_count_get(uint8_t *octets, uint8_t octets_len)
uint8_t util_ones_count_get(const uint8_t *octets, uint8_t octets_len)
{
uint8_t one_count = 0U;

View file

@ -13,7 +13,7 @@
#define TRIPLE_BUFFER_SIZE 3
#endif
uint8_t util_ones_count_get(uint8_t *octets, uint8_t octets_len);
uint8_t util_ones_count_get(const uint8_t *octets, uint8_t octets_len);
int util_aa_le32(uint8_t *dst);
void util_saa_le32(uint8_t *dst, uint8_t handle);
void util_bis_aa_le32(uint8_t bis, uint8_t *saa, uint8_t *dst);

View file

@ -0,0 +1,91 @@
#!/usr/bin/env bash
# Copyright 2018 Oticon A/S
# SPDX-License-Identifier: Apache-2.0
# Compile with all permutations of a given set of KConfigs
# Specifically for going through possible combinations of
# optional control procedures
#set -x #uncomment this line for debugging
# set DEBUG_PERMUTATE to 'true' for extra debug output
DEBUG_PERMUTATE=false
: "${BSIM_OUT_PATH:?BSIM_OUT_PATH must be defined}"
: "${BSIM_COMPONENTS_PATH:?BSIM_COMPONENTS_PATH must be defined}"
: "${ZEPHYR_BASE:?ZEPHYR_BASE must be set to point to the zephyr root\
directory}"
WORK_DIR="${WORK_DIR:-${ZEPHYR_BASE}/bsim_bt_out}"
BOARD="${BOARD:-nrf52_bsim}"
BOARD_ROOT="${BOARD_ROOT:-${ZEPHYR_BASE}}"
mkdir -p ${WORK_DIR}
source ${ZEPHYR_BASE}/tests/bluetooth/bsim_bt/compile.source
declare -a list=(
"CONFIG_BT_CENTRAL="
"CONFIG_BT_PERIPHERAL="
"CONFIG_BT_CTLR_PER_INIT_FEAT_XCHG="
"CONFIG_BT_DATA_LEN_UPDATE="
"CONFIG_BT_PHY_UPDATE="
"CONFIG_BT_CTLR_MIN_USED_CHAN="
"CONFIG_BT_CTLR_LE_PING="
"CONFIG_BT_CTLR_LE_ENC="
"CONFIG_BT_CTLR_CONN_PARAM_REQ="
)
perm_compile() {
local -a results=()
# We set a unique exe-name, so that we don't overwrite the executables
# created by the compile scripts since that may mess up other tests
# We also delete the executable to avoid having artifacts from
# a previous run
local exe_name="bs_nrf52_bsim_tests_kconfig_perm"
local executable_name=${exe_name}
local executable_name=${BSIM_OUT_PATH}/bin/$executable_name
rm -f ${executable_name}
let idx=$2
for (( j = 0; j < $1; j++ )); do
if (( idx % 2 )); then
results=("${results[@]}" "${list[$j]}n")
else
results=("${results[@]}" "${list[$j]}y")
fi
let idx\>\>=1
done
printf '%s\n' "${results[@]}" > $3
if test "$DEBUG_PERMUTATE" = "true"; then
echo "Compile with config overlay:"
cat $3
fi
local app=tests/bluetooth/bsim_bt/edtt_ble_test_app/hci_test_app
local conf_file=prj_dut.conf
local conf_overlay=$3
compile
if [ ! -f ${executable_name} ]; then
compile_failures=$(expr $compile_failures + 1)
fi
}
let n=${#list[@]}
temp_conf_file=$(mktemp -p ${WORK_DIR})
# compile_failures will be equal to the number of failed compilations
let compile_failures=0
for (( i = 0; i < 2**n; i++ )); do
## don't compile for CENTRAL=n AND PERIPHERAL=n
if (( (i & 0x3) != 0x3 )); then
perm_compile $n $i ${temp_conf_file}
fi
done
# We set exit code based on type of failure
# 0 means all configurations compiled w/o error
trap "{ rm "${temp_conf_file}" ; exit 255; }" SIGINT
trap "{ rm "${temp_conf_file}" ; exit 254; }" SIGTERM
trap "{ rm "${temp_conf_file}" ; exit 253; }" ERR
trap "{ rm "${temp_conf_file}" ; exit ${compile_failures}; }" EXIT

View file

@ -18,40 +18,7 @@ BOARD_ROOT="${BOARD_ROOT:-${ZEPHYR_BASE}}"
mkdir -p ${WORK_DIR}
function compile(){
local app_root="${app_root:-${ZEPHYR_BASE}}"
local conf_file="${conf_file:-prj.conf}"
local cmake_args="${cmake_args:-"-DCONFIG_COVERAGE=y"}"
local ninja_args="${ninja_args:-""}"
local cc_flags="${cc_flags:-"-Werror"}"
local exe_name="${exe_name:-bs_${BOARD}_${app}_${conf_file}}"
local exe_name=${exe_name//\//_}
local exe_name=${exe_name//./_}
local exe_name=${BSIM_OUT_PATH}/bin/$exe_name
local map_file_name=${exe_name}.Tsymbols
local this_dir=${WORK_DIR}/${app}/${conf_file}
echo "Building $exe_name"
# Set INCR_BUILD when calling to only do an incremental build
if [ ! -v INCR_BUILD ] || [ ! -d "${this_dir}" ]; then
[ -d "${this_dir}" ] && rm ${this_dir} -rf
mkdir -p ${this_dir} && cd ${this_dir}
cmake -GNinja -DBOARD_ROOT=${BOARD_ROOT} -DBOARD=${BOARD} \
-DCONF_FILE=${conf_file} ${cmake_args} \
-DCMAKE_C_FLAGS="${cc_flags}" ${app_root}/${app} \
&> cmake.out || { cat cmake.out && return 0; }
else
cd ${this_dir}
fi
ninja ${ninja_args} &> ninja.out || { cat ninja.out && return 0; }
cp ${this_dir}/zephyr/zephyr.exe ${exe_name}
nm ${exe_name} | grep -v " [U|w] " | sort | cut -d" " -f1,3 > ${map_file_name}
sed -i "1i $(wc -l ${map_file_name} | cut -d" " -f1)" ${map_file_name}
}
source ${ZEPHYR_BASE}/tests/bluetooth/bsim_bt/compile.source
app=tests/bluetooth/bsim_bt/bsim_test_app conf_file=prj_split.conf \
compile
@ -63,6 +30,10 @@ app=tests/bluetooth/bsim_bt/bsim_test_multiple compile
app=tests/bluetooth/bsim_bt/bsim_test_advx compile
app=tests/bluetooth/bsim_bt/bsim_test_iso compile
app=tests/bluetooth/bsim_bt/bsim_test_audio compile
app=tests/bluetooth/bsim_bt/edtt_ble_test_app/hci_test_app \
conf_file=prj_dut_llcp.conf compile
app=tests/bluetooth/bsim_bt/edtt_ble_test_app/hci_test_app \
conf_file=prj_tst_llcp.conf compile
app=tests/bluetooth/bsim_bt/edtt_ble_test_app/hci_test_app \
conf_file=prj_dut.conf compile
app=tests/bluetooth/bsim_bt/edtt_ble_test_app/hci_test_app \

View file

@ -0,0 +1,41 @@
# Copyright 2018 Oticon A/S
# SPDX-License-Identifier: Apache-2.0
function compile(){
: "${app:?app must be defined}"
local app_root="${app_root:-${ZEPHYR_BASE}}"
local conf_file="${conf_file:-prj.conf}"
local conf_overlay="${conf_overlay:-""}"
local cmake_args="${cmake_args:-"-DCONFIG_COVERAGE=y \
-DCONFIG_DEBUG_OPTIMIZATIONS=y \
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON"}"
local ninja_args="${ninja_args:-""}"
local exe_name="${exe_name:-bs_${BOARD}_${app}_${conf_file}}"
local exe_name=${exe_name//\//_}
local exe_name=${exe_name//./_}
local exe_name=${BSIM_OUT_PATH}/bin/$exe_name
local map_file_name=${exe_name}.Tsymbols
local this_dir=${WORK_DIR}/${app}/${conf_file}
echo "Building $exe_name"
# Set INCR_BUILD when calling to only do an incremental build
if [ ! -v INCR_BUILD ] || [ ! -d "${this_dir}" ]; then
[ -d "${this_dir}" ] && rm ${this_dir} -rf
mkdir -p ${this_dir} && cd ${this_dir}
cmake -GNinja -DBOARD_ROOT=${BOARD_ROOT} -DBOARD=${BOARD} \
-DCONF_FILE=${conf_file} -DOVERLAY_CONFIG=${conf_overlay} ${cmake_args} ${app_root}/${app} \
&> cmake.out || { cat cmake.out && return 0; }
else
cd ${this_dir}
fi
ninja ${ninja_args} &> ninja.out || { cat ninja.out && return 0; }
cp ${this_dir}/zephyr/zephyr.exe ${exe_name}
nm ${exe_name} | grep -v " [U|w] " | sort | cut -d" " -f1,3 > ${map_file_name}
sed -i "1i $(wc -l ${map_file_name} | cut -d" " -f1)" ${map_file_name}
}

View file

@ -26,3 +26,5 @@ CONFIG_BT_CTLR_RX_BUFFERS=3
# To make DEVICE Name writable...
CONFIG_BT_DEVICE_NAME_DYNAMIC=y
CONFIG_BT_LL_SW_LLCP_LEGACY=y

View file

@ -14,7 +14,7 @@ CONFIG_BT_BUF_ACL_RX_SIZE=60
CONFIG_BT_BUF_ACL_TX_SIZE=60
##
## Enabling BT_CTRL_DTM_HCI requires BT_LL_SW which requires BT_CTRL
## Enabling BT_CTRL_DTM_HCI requires BT_LL_SW_SPLIT which requires BT_CTRL
##
CONFIG_BT_CTLR=y
@ -25,3 +25,5 @@ CONFIG_BT_CTLR_PRIVACY=y
CONFIG_BT_CTLR_FILTER_ACCEPT_LIST=y
CONFIG_BT_CTLR_DTM_HCI=y
CONFIG_BT_CTLR_DATA_LENGTH_MAX=60
CONFIG_BT_LL_SW_LLCP_LEGACY=y

View file

@ -0,0 +1,32 @@
# SPDX-License-Identifier: Apache-2.0
CONFIG_BT=y
CONFIG_BT_HCI_RAW=y
CONFIG_BT_PERIPHERAL=y
CONFIG_BT_BROADCASTER=y
CONFIG_BT_CENTRAL=y
CONFIG_BT_OBSERVER=y
CONFIG_BT_DEBUG_LOG=y
CONFIG_BT_ECC=y
CONFIG_BT_TINYCRYPT_ECC=y
CONFIG_BT_BUF_ACL_RX_SIZE=60
CONFIG_BT_BUF_ACL_TX_SIZE=60
##
## Enabling BT_CTRL_DTM_HCI requires BT_LL_SW_SPLIT which requires BT_CTRL
##
CONFIG_BT_CTLR=y
CONFIG_BT_LL_SW_SPLIT=y
CONFIG_BT_CTLR_CRYPTO=y
CONFIG_BT_CTLR_LE_ENC=y
CONFIG_BT_CTLR_PRIVACY=y
CONFIG_BT_CTLR_FILTER_ACCEPT_LIST=y
CONFIG_BT_CTLR_DTM_HCI=y
CONFIG_BT_CTLR_DATA_LENGTH_MAX=60
CONFIG_BT_LL_SW_LLCP_LEGACY=n
CONFIG_BT_CTLR_ADVANCED_FEATURES=y
# LLCP Refactored controller does not support Advanced Scheduling yet
CONFIG_BT_CTLR_SCHED_ADVANCED=n

View file

@ -24,5 +24,6 @@ CONFIG_BT_CTLR_FILTER_ACCEPT_LIST=y
CONFIG_BT_CTLR_DTM_HCI=y
CONFIG_BT_CTLR_DATA_LENGTH_MAX=60
CONFIG_BT_LL_SW_LLCP_LEGACY=y
CONFIG_BT_CTLR_ADVANCED_FEATURES=y
CONFIG_BT_CTLR_PARAM_CHECK=n

View file

@ -0,0 +1,30 @@
# SPDX-License-Identifier: Apache-2.0
CONFIG_BT=y
CONFIG_BT_HCI_RAW=y
CONFIG_BT_PERIPHERAL=y
CONFIG_BT_CENTRAL=y
CONFIG_BT_DEBUG_LOG=y
CONFIG_BT_ECC=y
CONFIG_BT_TINYCRYPT_ECC=y
CONFIG_BT_BUF_ACL_RX_SIZE=60
CONFIG_BT_BUF_ACL_TX_SIZE=60
##
## Enabling BT_CTRL_DTM_HCI requires BT_LL_SW_SPLIT which requires BT_CTRL
##
CONFIG_BT_CTLR=y
CONFIG_BT_LL_SW_SPLIT=y
CONFIG_BT_CTLR_CRYPTO=y
CONFIG_BT_CTLR_LE_ENC=y
CONFIG_BT_CTLR_PRIVACY=y
CONFIG_BT_CTLR_FILTER_ACCEPT_LIST=y
CONFIG_BT_CTLR_DTM_HCI=y
CONFIG_BT_CTLR_DATA_LENGTH_MAX=60
CONFIG_BT_LL_SW_LLCP_LEGACY=n
CONFIG_BT_CTLR_ADVANCED_FEATURES=y
# LLCP Refactored controller does not support Advanced Scheduling yet
CONFIG_BT_CTLR_SCHED_ADVANCED=n

View file

@ -2,18 +2,66 @@
# Copyright 2019 Oticon A/S
# SPDX-License-Identifier: Apache-2.0
#
# ENVIRONMENT CONFIGURATION
# =========================
#
# This script can be configured with a number of environment variables.
# Values in [] are the default unless overridden.
#
# PROJECT CONFIGURATION
# ---------------------
# PRJ_CONF: Default bsim device configuration [prj_dut_conf]
# PRJ_CONF_1: bsim device 1 configuration [PRJ_CONF]
# PRJ_CONF_2: bsim device 2 configuration [PRJ_CONF]
#
# VERBOSITY
# ---------
# VERBOSITY_LEVEL: Global verbosity [2]
# VERBOSITY_LEVEL_EDTT: EDTT verbosity [VERBOSITY_LEVEL]
# VERBOSITY_LEVEL_BRIDGE: EDTT bridge verbosity [VERBOSITY_LEVEL]
# VERBOSITY_LEVEL_PHY: bsim phy verbosity [VERBOSITY_LEVEL]
# VERBOSITY_LEVEL_DEVS: Global bsim device verbosity [VERBOSITY_LEVEL]
# VERBOSITY_LEVEL_DEV1: bsim device 1 verbosity [VERBOSITY_LEVEL_DEVS]
# VERBOSITY_LEVEL_DEV1: bsim device 2 verbosity [VERBOSITY_LEVEL_DEVS]
#
# RR DEBUG SUPPORT
# ----------------
# RR: Default run bsim device under rr [0]
# 0: disables; any other value enables.
# RR_1: Run bsim device 1 under rr [RR]
# RR_2: Run bsim device 2 under rr [RR]
#
# Common part of the test scripts for some of the EDTT tests
# in which 2 controller only builds of the stack are run against each other
VERBOSITY_LEVEL=2
VERBOSITY_LEVEL=${VERBOSITY_LEVEL:-2}
VERBOSITY_LEVEL_EDTT=${VERBOSITY_LEVEL_EDTT:-${VERBOSITY_LEVEL}}
VERBOSITY_LEVEL_BRIDGE=${VERBOSITY_LEVEL_BRIDGE:-${VERBOSITY_LEVEL}}
VERBOSITY_LEVEL_PHY=${VERBOSITY_LEVEL_PHY:-${VERBOSITY_LEVEL}}
VERBOSITY_LEVEL_DEVS=${VERBOSITY_LEVEL_DEVS:-${VERBOSITY_LEVEL}}
VERBOSITY_LEVEL_DEV1=${VERBOSITY_LEVEL_1:-${VERBOSITY_LEVEL_DEVS}}
VERBOSITY_LEVEL_DEV2=${VERBOSITY_LEVEL_2:-${VERBOSITY_LEVEL_DEVS}}
PROCESS_IDS=""; EXIT_CODE=0
function Execute(){
local rr=
if [ "rr" = "$1" ]; then
local devno=$2
shift 2
local exe=$(basename $1)
local out=/tmp/rr/$$-${SIMULATION_ID}-${exe}-d_${devno}
rm -rf ${out}
rr="rr record -o ${out}"
fi
if [ ! -f $1 ]; then
echo -e " \e[91m`pwd`/`basename $1` cannot be found (did you forget to\
compile it?)\e[39m"
exit 1
fi
timeout 300 $@ & PROCESS_IDS="$PROCESS_IDS $!"
timeout 300 ${rr} $@ & PROCESS_IDS="$PROCESS_IDS $!"
}
: "${BSIM_OUT_PATH:?BSIM_OUT_PATH must be defined}"
@ -22,25 +70,47 @@ function Execute(){
#Give a default value to BOARD if it does not have one yet:
BOARD="${BOARD:-nrf52_bsim}"
#Give a default value to PRJ_CONF_x if it does not have one yet:
PRJ_CONF="${PRJ_CONF:-prj_dut_conf}"
PRJ_CONF_1="${PRJ_CONF_1:-${PRJ_CONF}}"
PRJ_CONF_2="${PRJ_CONF_2:-${PRJ_CONF}}"
#Give default value to RR_x if it does not have one yet:
RR="${RR:-0}"
RR_1="${RR_1:-${RR}}"
RR_2="${RR_2:-${RR}}"
#Check if rr was requested and is available
if [ "${RR_1}" != "0" -o "${RR_2}" != "0" ]; then
if [ ! -x "$(command -v rr)" ]; then
echo 'error: rr cannot be found in $PATH.' >&2
exit 1
fi
#Set RR_ARGS_x based on RR_x
[ "${RR_1}" != "0" ] && RR_ARGS_1="rr 1"
[ "${RR_2}" != "0" ] && RR_ARGS_2="rr 2"
fi
cd ${EDTT_PATH}
Execute ./src/edttool.py -s=${SIMULATION_ID} -d=0 --transport bsim \
-T $TEST_MODULE -C $TEST_FILE -v=${VERBOSITY_LEVEL}
-T $TEST_MODULE -C $TEST_FILE -v=${VERBOSITY_LEVEL_EDTT} -S
cd ${BSIM_OUT_PATH}/bin
Execute ./bs_device_EDTT_bridge -s=${SIMULATION_ID} -d=0 -AutoTerminate \
-RxWait=2.5e3 -D=2 -dev0=1 -dev1=2 -v=${VERBOSITY_LEVEL}
-RxWait=2.5e3 -D=2 -dev0=1 -dev1=2 -v=${VERBOSITY_LEVEL_BRIDGE}
Execute \
./bs_${BOARD}_tests_bluetooth_bsim_bt_edtt_ble_test_app_hci_test_app_prj_dut_conf\
-s=${SIMULATION_ID} -d=1 -v=${VERBOSITY_LEVEL} -RealEncryption=1
${RR_ARGS_1} ./bs_${BOARD}_tests_bluetooth_bsim_bt_edtt_ble_test_app_hci_test_app_${PRJ_CONF_1}\
-s=${SIMULATION_ID} -d=1 -v=${VERBOSITY_LEVEL_DEV1} -RealEncryption=1
Execute \
./bs_${BOARD}_tests_bluetooth_bsim_bt_edtt_ble_test_app_hci_test_app_prj_tst_conf\
-s=${SIMULATION_ID} -d=2 -v=${VERBOSITY_LEVEL} -RealEncryption=1
${RR_ARGS_2} ./bs_${BOARD}_tests_bluetooth_bsim_bt_edtt_ble_test_app_hci_test_app_${PRJ_CONF_2}\
-s=${SIMULATION_ID} -d=2 -v=${VERBOSITY_LEVEL_DEV2} -RealEncryption=1
Execute ./bs_2G4_phy_v1 -v=${VERBOSITY_LEVEL} -s=${SIMULATION_ID} \
Execute ./bs_2G4_phy_v1 -v=${VERBOSITY_LEVEL_PHY} -s=${SIMULATION_ID} \
-D=3 -sim_length=3600e6 $@
for PROCESS_ID in $PROCESS_IDS; do

View file

@ -8,5 +8,7 @@ CWD="$(cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P)"
export SIMULATION_ID="edtt_gap"
export TEST_FILE=${CWD}"/gap.test_list"
export TEST_MODULE="gap_verification"
export PRJ_CONF_1="prj_dut_conf"
export PRJ_CONF_2="prj_tst_conf"
${CWD}/_controller_tests_inner.sh

View file

@ -20,7 +20,7 @@ GAP/CONN/ACEP/BV-01-C
GAP/CONN/ACEP/BV-03-C
GAP/CONN/ACEP/BV-04-C
GAP/CONN/DCON/BV-01-C
GAP/CONN/ENC
#GAP/CONN/ENC
GAP/CONN/GCEP/BV-01-C
GAP/CONN/GCEP/BV-02-C
GAP/CONN/GCEP/BV-05-C

View file

@ -0,0 +1,14 @@
#!/usr/bin/env bash
# Copyright 2019 Oticon A/S
# SPDX-License-Identifier: Apache-2.0
# HCI regression tests based on the EDTTool
CWD="$(cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P)"
export SIMULATION_ID="edtt_hci_llcp"
export TEST_FILE=${CWD}"/hci.llcp.test_list"
export TEST_MODULE="hci_verification"
export PRJ_CONF_1="prj_dut_llcp_conf"
export PRJ_CONF_2="prj_tst_llcp_conf"
${CWD}/_controller_tests_inner.sh

View file

@ -0,0 +1,31 @@
# Copyright 2019 Oticon A/S
# SPDX-License-Identifier: Apache-2.0
HCI/CCO/BV-07-C
HCI/CCO/BV-09-C # [Handling LE Set Data Length Command]
HCI/CCO/BV-10-C
HCI/CCO/BV-11-C
HCI/CCO/BV-12-C
HCI/CCO/BV-13-C
HCI/CCO/BV-14-C
HCI/CCO/BV-15-C
HCI/CCO/BV-18-C
HCI/CFC/BV-02-C
HCI/CIN/BV-01-C
HCI/CIN/BV-03-C
HCI/CIN/BV-04-C
HCI/CIN/BV-06-C
HCI/CIN/BV-09-C
HCI/CM/BV-01-C # [Handling LE Read Peer Resolvable Address Command]
HCI/CM/BV-02-C # [Handling LE Read Local Resolvable Address Command]
HCI/CM/BV-03-C # [Handling LE Read PHY Command]
HCI/DDI/BI-02-C
HCI/DDI/BV-03-C
HCI/DDI/BV-04-C
HCI/DSU/BV-02-C
HCI/DSU/BV-03-C # [Reset Command received in Slave Role]
HCI/DSU/BV-04-C
#HCI/DSU/BV-05-C
HCI/DSU/BV-06-C # [Reset Command received in Master Role]
# HCI/GEV/BV-01-C
HCI/HFC/BV-04-C # [Events enabled by LE Set Event Mask Command]

View file

@ -8,5 +8,7 @@ CWD="$(cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P)"
export SIMULATION_ID="edtt_hci"
export TEST_FILE=${CWD}"/hci.test_list"
export TEST_MODULE="hci_verification"
export PRJ_CONF_1="prj_dut_conf"
export PRJ_CONF_2="prj_tst_conf"
${CWD}/_controller_tests_inner.sh

View file

@ -27,5 +27,5 @@ HCI/DSU/BV-03-C
HCI/DSU/BV-04-C
HCI/DSU/BV-05-C
HCI/DSU/BV-06-C
HCI/GEV/BV-01-C
#HCI/GEV/BV-01-C
HCI/HFC/BV-04-C

View file

@ -0,0 +1,15 @@
#!/usr/bin/env bash
# Copyright 2019 Oticon A/S
# SPDX-License-Identifier: Apache-2.0
# LL regression tests based on the EDTTool (part 1)
CWD="$(cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P)"
export SIMULATION_ID="edtt_ll_set1_llcp"
export TEST_FILE=${CWD}"/ll.set1.llcp.test_list"
export TEST_MODULE="ll_verification"
export PRJ_CONF_1="prj_dut_llcp_conf"
export PRJ_CONF_2="prj_tst_llcp_conf"
${CWD}/_controller_tests_inner.sh

View file

@ -9,5 +9,7 @@ CWD="$(cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P)"
export SIMULATION_ID="edtt_ll_set1"
export TEST_FILE=${CWD}"/ll.set1.test_list"
export TEST_MODULE="ll_verification"
export PRJ_CONF_1="prj_dut_conf"
export PRJ_CONF_2="prj_tst_conf"
${CWD}/_controller_tests_inner.sh

View file

@ -0,0 +1,15 @@
#!/usr/bin/env bash
# Copyright 2019 Oticon A/S
# SPDX-License-Identifier: Apache-2.0
# LL regression tests based on the EDTTool (part 2)
CWD="$(cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P)"
export SIMULATION_ID="edtt_ll_set2_llcp"
export TEST_FILE=${CWD}"/ll.set2.llcp.test_list"
export TEST_MODULE="ll_verification"
export PRJ_CONF_1="prj_dut_llcp_conf"
export PRJ_CONF_2="prj_tst_llcp_conf"
${CWD}/_controller_tests_inner.sh

View file

@ -9,5 +9,7 @@ CWD="$(cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P)"
export SIMULATION_ID="edtt_ll_set2"
export TEST_FILE=${CWD}"/ll.set2.test_list"
export TEST_MODULE="ll_verification"
export PRJ_CONF_1="prj_dut_conf"
export PRJ_CONF_2="prj_tst_conf"
${CWD}/_controller_tests_inner.sh

View file

@ -0,0 +1,64 @@
# Copyright 2019 Oticon A/S
# SPDX-License-Identifier: Apache-2.0
LL/CON/ADV/BV-01-C
LL/CON/ADV/BV-04-C
LL/CON/ADV/BV-09-C
LL/CON/ADV/BV-10-C
LL/CON/INI/BV-01-C
LL/CON/INI/BV-02-C
LL/CON/INI/BV-06-C
LL/CON/INI/BV-07-C
LL/CON/INI/BV-08-C
LL/CON/INI/BV-09-C
LL/CON/INI/BV-10-C
LL/CON/INI/BV-11-C
LL/CON/INI/BV-12-C
LL/CON/INI/BV-16-C
LL/CON/INI/BV-17-C
LL/CON/INI/BV-18-C
LL/CON/INI/BV-19-C
LL/CON/INI/BV-20-C
LL/CON/INI/BV-21-C
LL/CON/INI/BV-23-C
LL/CON/INI/BV-24-C
#LL/CON/MAS/BI-06-C #currently failing, to be investigated
LL/CON/MAS/BV-03-C
LL/CON/MAS/BV-04-C
LL/CON/MAS/BV-05-C
LL/CON/MAS/BV-07-C
LL/CON/MAS/BV-08-C
LL/CON/MAS/BV-09-C
LL/CON/MAS/BV-13-C
LL/CON/MAS/BV-20-C
LL/CON/MAS/BV-21-C
LL/CON/MAS/BV-23-C
LL/CON/MAS/BV-24-C
LL/CON/MAS/BV-25-C
LL/CON/MAS/BV-26-C
#LL/CON/MAS/BV-27-C #currently failing, to be investigated
LL/CON/MAS/BV-29-C
LL/CON/MAS/BV-30-C
LL/CON/MAS/BV-34-C
LL/CON/MAS/BV-35-C
LL/CON/MAS/BV-41-C
LL/CON/MAS/BV-43-C
#LL/CON/MAS/BV-73-C # requires update of EDTT due to change in DLE algorithm
#LL/CON/MAS/BV-74-C # requires update of EDTT due to change in DLE algorithm
#LL/CON/MAS/BV-76-C # requires update of EDTT due to change in DLE algorithm
#LL/CON/MAS/BV-77-C # requires update of EDTT due to change in DLE algorithm
#LL/CON/SLA/BI-08-C # requires update of EDTT due to change in DLE algorithm
LL/CON/SLA/BV-04-C
LL/CON/SLA/BV-05-C
LL/CON/SLA/BV-06-C
LL/CON/SLA/BV-10-C
LL/CON/SLA/BV-11-C
LL/CON/SLA/BV-12-C
LL/CON/SLA/BV-14-C
LL/CON/SLA/BV-19-C
LL/CON/SLA/BV-20-C
LL/CON/SLA/BV-22-C
LL/CON/SLA/BV-24-C
LL/CON/SLA/BV-25-C
LL/CON/SLA/BV-26-C
LL/CON/SLA/BV-27-C

View file

@ -0,0 +1,62 @@
# Copyright 2019 Oticon A/S
# SPDX-License-Identifier: Apache-2.0
LL/CON/SLA/BV-29-C
LL/CON/SLA/BV-33-C
LL/CON/SLA/BV-34-C
LL/CON/SLA/BV-40-C
LL/CON/SLA/BV-42-C
#LL/CON/SLA/BV-77-C # requires update in EDTT due to change in DLE algorithm
#LL/CON/SLA/BV-78-C # requires update in EDTT due to change in DLE algorithm
#LL/CON/SLA/BV-80-C # requires update in EDTT due to change in DLE algorithm
#LL/CON/SLA/BV-81-C # requires update in EDTT due to change in DLE algorithm
LL/DDI/ADV/BV-01-C
LL/DDI/ADV/BV-02-C
LL/DDI/ADV/BV-03-C
LL/DDI/ADV/BV-04-C
LL/DDI/ADV/BV-05-C
LL/DDI/ADV/BV-06-C
LL/DDI/ADV/BV-07-C
LL/DDI/ADV/BV-08-C
LL/DDI/ADV/BV-09-C
LL/DDI/ADV/BV-11-C
LL/DDI/ADV/BV-15-C
LL/DDI/ADV/BV-16-C
LL/DDI/ADV/BV-17-C
LL/DDI/ADV/BV-18-C
LL/DDI/ADV/BV-19-C
LL/DDI/ADV/BV-20-C
LL/DDI/SCN/BV-01-C
LL/DDI/SCN/BV-02-C
LL/DDI/SCN/BV-03-C
LL/DDI/SCN/BV-04-C
LL/DDI/SCN/BV-05-C
LL/DDI/SCN/BV-10-C
LL/DDI/SCN/BV-11-C
LL/DDI/SCN/BV-12-C
LL/DDI/SCN/BV-13-C
LL/DDI/SCN/BV-14-C
LL/DDI/SCN/BV-15-C
LL/DDI/SCN/BV-16-C
LL/DDI/SCN/BV-17-C
LL/DDI/SCN/BV-18-C
LL/DDI/SCN/BV-26-C
LL/DDI/SCN/BV-28-C
LL/SEC/ADV/BV-02-C
LL/SEC/ADV/BV-03-C
LL/SEC/ADV/BV-04-C
LL/SEC/ADV/BV-05-C
LL/SEC/ADV/BV-06-C
LL/SEC/ADV/BV-08-C
LL/SEC/ADV/BV-09-C
LL/SEC/ADV/BV-10-C
LL/SEC/ADV/BV-11-C
LL/SEC/ADV/BV-12-C
LL/SEC/ADV/BV-13-C
LL/SEC/ADV/BV-14-C
LL/SEC/ADV/BV-15-C
LL/SEC/ADV/BV-16-C
LL/SEC/ADV/BV-17-C
LL/SEC/ADV/BV-18-C
LL/SEC/ADV/BV-20-C
LL/SEC/SCN/BV-01-C

View file

@ -0,0 +1,64 @@
#
# Common include directories and source files for bluetooth unit tests
#
include_directories(
src
${ZEPHYR_BASE}/tests/bluetooth/controller/mock_ctrl/include
${ZEPHYR_BASE}/tests/bluetooth/controller/common/include
${ZEPHYR_BASE}/include/bluetooth
${ZEPHYR_BASE}/subsys/bluetooth
${ZEPHYR_BASE}/subsys/bluetooth/controller
${ZEPHYR_BASE}/subsys/bluetooth/controller/util
${ZEPHYR_BASE}/subsys/bluetooth/controller/include
${ZEPHYR_BASE}/subsys/bluetooth/controller/ll_sw
${ZEPHYR_BASE}/subsys/bluetooth/controller/ll_sw/nordic
${ZEPHYR_BASE}/subsys/bluetooth/controller/ll_sw/nordic/lll
)
FILE(GLOB ll_sw_sources
${ZEPHYR_BASE}/subsys/bluetooth/controller/util/mem.c
${ZEPHYR_BASE}/subsys/bluetooth/controller/util/memq.c
${ZEPHYR_BASE}/subsys/bluetooth/controller/ll_sw/ull_chan.c
${ZEPHYR_BASE}/subsys/bluetooth/controller/ll_sw/ull_tx_queue.c
${ZEPHYR_BASE}/subsys/bluetooth/controller/ll_sw/ull_llcp_local.c
${ZEPHYR_BASE}/subsys/bluetooth/controller/ll_sw/ull_llcp_remote.c
${ZEPHYR_BASE}/subsys/bluetooth/controller/ll_sw/ull_llcp_pdu.c
${ZEPHYR_BASE}/subsys/bluetooth/controller/ll_sw/ull_llcp_common.c
${ZEPHYR_BASE}/subsys/bluetooth/controller/ll_sw/ull_llcp_phy.c
${ZEPHYR_BASE}/subsys/bluetooth/controller/ll_sw/ull_llcp_enc.c
${ZEPHYR_BASE}/subsys/bluetooth/controller/ll_sw/ull_llcp_conn_upd.c
${ZEPHYR_BASE}/subsys/bluetooth/controller/ll_sw/ull_llcp_chmu.c
${ZEPHYR_BASE}/subsys/bluetooth/controller/ll_sw/ull_llcp.c
${ZEPHYR_BASE}/subsys/bluetooth/controller/ll_sw/ull_conn.c
${ZEPHYR_BASE}/subsys/bluetooth/controller/ll_sw/ll_addr.c
)
FILE(GLOB mock_sources
${ZEPHYR_BASE}/tests/bluetooth/controller/mock_ctrl/src/kernel.c
${ZEPHYR_BASE}/tests/bluetooth/controller/mock_ctrl/src/ecb.c
${ZEPHYR_BASE}/tests/bluetooth/controller/mock_ctrl/src/mayfly.c
${ZEPHYR_BASE}/tests/bluetooth/controller/mock_ctrl/src/lll.c
${ZEPHYR_BASE}/tests/bluetooth/controller/mock_ctrl/src/lll_conn.c
${ZEPHYR_BASE}/tests/bluetooth/controller/mock_ctrl/src/ll_assert.c
${ZEPHYR_BASE}/tests/bluetooth/controller/mock_ctrl/src/util.c
${ZEPHYR_BASE}/tests/bluetooth/controller/mock_ctrl/src/ticker.c
${ZEPHYR_BASE}/tests/bluetooth/controller/mock_ctrl/src/ull.c
${ZEPHYR_BASE}/tests/bluetooth/controller/mock_ctrl/src/ull_periph.c
${ZEPHYR_BASE}/tests/bluetooth/controller/mock_ctrl/src/ull_central.c
${ZEPHYR_BASE}/tests/bluetooth/controller/mock_ctrl/src/ull_scan.c
${ZEPHYR_BASE}/tests/bluetooth/controller/mock_ctrl/src/lll_clock.c
)
FILE(GLOB common_sources
${ZEPHYR_BASE}/tests/bluetooth/controller/common/src/helper_pdu.c
${ZEPHYR_BASE}/tests/bluetooth/controller/common/src/helper_util.c
)
add_definitions(-include kconfig.h)
if(KCONFIG_OVERRIDE_FILE)
add_definitions(-include ${KCONFIG_OVERRIDE_FILE})
endif()
add_definitions(-include ztest.h)
add_definitions(-include soc.h)

View file

@ -0,0 +1,249 @@
/*
* Copyright (c) 2020 Nordic Semiconductor ASA
* Copyright (c) 2020 Demant
*
* SPDX-License-Identifier: Apache-2.0
*/
/*
* expected features on purpose defined here
* to keep implementation separate from test
*/
#if defined(CONFIG_BT_CTLR_LE_ENC)
#define FEAT_ENCODED 0x01
#else
#define FEAT_ENCODED 0x00
#endif
#if defined(CONFIG_BT_CTLR_CONN_PARAM_REQ)
#define FEAT_PARAM_REQ 0x02
#else
#define FEAT_PARAM_REQ 0x00
#endif
#if defined(CONFIG_BT_CTLR_EXT_REJ_IND)
#define FEAT_EXT_REJ 0x04
#else
#define FEAT_EXT_REJ 0x00
#endif
#if defined(CONFIG_BT_CTLR_PER_INIT_FEAT_XCHG)
#define FEAT_SLAVE_FREQ 0x08
#else
#define FEAT_SLAVE_FREQ 0x00
#endif
#if defined(CONFIG_BT_CTLR_LE_PING)
#define FEAT_PING 0x10
#else
#define FEAT_PING 0x00
#endif
#if defined(CONFIG_BT_CTLR_DATA_LENGTH_MAX)
#define FEAT_DLE 0x20
#else
#define FEAT_DLE 0x00
#endif
#if defined(CONFIG_BT_CTLR_PRIVACY)
#define FEAT_PRIVACY 0x40
#else
#define FEAT_PRIVACY 0x00
#endif
#if defined(CONFIG_BT_CTLR_EXT_SCAN_FP)
#define FEAT_EXT_SCAN 0x80
#else
#define FEAT_EXT_SCAN 0x00
#endif
#if defined(CONFIG_BT_CTLR_PHY_2M)
#define FEAT_PHY_2M 0x100
#else
#define FEAT_PHY_2M 0x00
#endif
#if defined(CONFIG_BT_CTLR_SMI_TX)
#define FEAT_SMI_TX 0x200
#else
#define FEAT_SMI_TX 0x00
#endif
#if defined(CONFIG_BT_CTLR_SMI_RX)
#define FEAT_SMI_RX 0x400
#else
#define FEAT_SMI_RX 0x00
#endif
#if defined(CONFIG_BT_CTLR_PHY_CODED)
#define FEAT_PHY_CODED 0x800
#else
#define FEAT_PHY_CODED 0x00
#endif
#if defined(CONFIG_MISSING)
#define FEAT_EXT_ADV 0x1000
#else
#define FEAT_EXT_ADV 0x00
#endif
#if defined(CONFIG_MISSING)
#define FEAT_PER_ADV 0x2000
#else
#define FEAT_PER_ADV 0x00
#endif
#if defined(CONFIG_BT_CTLR_CHAN_SEL_2)
#define FEAT_CHAN_SEL_ALGO2 0x4000
#else
#define FEAT_CHAN_SEL_ALGO2 0x00
#endif
#if defined(CONFIG_MISSING)
#define FEAT_PWR_CLASS1 0x8000
#else
#define FEAT_PWR_CLASS1 0x00
#endif
#if defined(CONFIG_BT_CTLR_MIN_USED_CHAN)
#define FEAT_MIN_CHANN 0x10000
#else
#define FEAT_MIN_CHANN 0x00
#endif
#if defined(CONFIG_BT_CTLR_DF_CONN_CTE_REQ)
#define FEAT_CONNECTION_CTE_REQ 0x20000
#else
#define FEAT_CONNECTION_CTE_REQ 0x00
#endif
#if defined(CONFIG_BT_CTLR_DF_CONN_CTE_RSP)
#define FEAT_CONNECTION_CTE_RSP 0x40000
#else
#define FEAT_CONNECTION_CTE_RSP 0x00
#endif
#if defined(CONFIG_MISSING)
#define FEAT_CONNECTIONLESS_CTE_TX 0x80000
#else
#define FEAT_CONNECTIONLESS_CTE_TX 0x00
#endif
#if defined(CONFIG_MISSING)
#define FEAT_CONNECTIONLESS_CTE_RX 0x100000
#else
#define FEAT_CONNECTIONLESS_CTE_RX 0x00
#endif
#if defined(CONFIG_BT_CTLR_DF_ANT_SWITCH_TX)
#define FEAT_ANT_SWITCH_CTE_TX 0x200000
#else
#define FEAT_ANT_SWITCH_CTE_TX 0x00
#endif
#if defined(CONFIG_BT_CTLR_DF_ANT_SWITCH_RX)
#define FEAT_ANT_SWITCH_CTE_RX 0x400000
#else
#define FEAT_ANT_SWITCH_CTE_RX 0x00
#endif
#if defined(CONFIG_BT_CTLR_DF_CTE_RX)
#define FEAT_RX_CTE 0x800000
#else
#define FEAT_RX_CTE 0x00
#endif
#if defined(CONFIG_MISSING)
#define FEAT_PER_ADV_SYNC_TX 0x1000000
#else
#define FEAT_PER_ADV_SYNC_TX 0x00
#endif
#if defined(CONFIG_MISSING)
#define FEAT_PER_ADV_SYNC_RX 0x2000000
#else
#define FEAT_PER_ADV_SYNC_RX 0x00
#endif
#if defined(CONFIG_MISSING)
#define FEAT_SLEEP_UPD 0x4000000
#else
#define FEAT_SLEEP_UPD 0x00
#endif
#if defined(CONFIG_MISSING)
#define FEAT_RPK_VALID 0x8000000
#else
#define FEAT_RPK_VALID 0x00
#endif
#if defined(CONFIG_MISSING)
#define FEAT_ISO_MASTER 0x10000000
#else
#define FEAT_ISO_MASTER 0x00
#endif
#if defined(CONFIG_MISSING)
#define FEAT_ISO_SLAVE 0x20000000
#else
#define FEAT_ISO_SLAVE 0x00
#endif
#if defined(CONFIG_MISSING)
#define FEAT_ISO_BROADCAST 0x40000000
#else
#define FEAT_ISO_BROADCAST 0x00
#endif
#if defined(CONFIG_MISSING)
#define FEAT_ISO_RECEIVER 0x80000000
#else
#define FEAT_ISO_RECEIVER 0x00
#endif
#if defined(CONFIG_MISSING)
#define FEAT_ISO_CHANNELS 0x100000000
#else
#define FEAT_ISO_CHANNELS 0x00
#endif
#if defined(CONFIG_MISSING)
#define FEAT_LE_PWR_REQ 0x200000000
#else
#define FEAT_LE_PWR_REQ 0x00
#endif
#if defined(CONFIG_MISSING)
#define FEAT_LE_PWR_IND 0x400000000
#else
#define FEAT_LE_PWR_IND 0x00
#endif
#if defined(CONFIG_MISSING)
#define FEAT_LE_PATH_LOSS 0x800000000
#else
#define FEAT_LE_PATH_LOSS 0x00
#endif
#define DEFAULT_FEATURE \
(FEAT_ENCODED | FEAT_PARAM_REQ | FEAT_EXT_REJ | FEAT_SLAVE_FREQ | FEAT_PING | FEAT_DLE | \
FEAT_PRIVACY | FEAT_EXT_SCAN | FEAT_PHY_2M | FEAT_SMI_TX | FEAT_SMI_RX | FEAT_PHY_CODED | \
FEAT_EXT_ADV | FEAT_PER_ADV | FEAT_CHAN_SEL_ALGO2 | FEAT_PWR_CLASS1 | FEAT_MIN_CHANN | \
FEAT_CONNECTION_CTE_REQ | FEAT_CONNECTION_CTE_RSP | FEAT_ANT_SWITCH_CTE_TX | \
FEAT_ANT_SWITCH_CTE_RX | FEAT_RX_CTE | FEAT_PER_ADV_SYNC_TX | FEAT_PER_ADV_SYNC_RX | \
FEAT_SLEEP_UPD | FEAT_RPK_VALID | FEAT_ISO_MASTER | FEAT_ISO_SLAVE | FEAT_ISO_BROADCAST | \
FEAT_ISO_RECEIVER | FEAT_ISO_CHANNELS | FEAT_LE_PWR_REQ | FEAT_LE_PWR_IND | \
FEAT_LE_PATH_LOSS)
/*
* The following two are defined as per
* Core Spec V5.2 Volume 6, Part B, chapter 4.6
* LL_FEAT_BIT_MASK_VALID does not account for the bits
* for the new features in V5.2
* TODO: EXPECTED_FEAT_EXCH_VALID is not used at the moment
* but probably LL_FEAT_BIT_MASK_VALID should get this
* value in ll_feat.h
*/
#define EXPECTED_FEAT_EXCH_VALID 0x0000000FF787CF2F
#define FEAT_FILTER_OCTET0 0xFFFFFFFFFFFFFF00
#define COMMON_FEAT_OCTET0(x) (FEAT_FILTER_OCTET0 | ((x) & ~FEAT_FILTER_OCTET0))

View file

@ -0,0 +1,178 @@
/*
* Copyright (c) 2020 Nordic Semiconductor ASA
* Copyright (c) 2020 Demant
*
* SPDX-License-Identifier: Apache-2.0
*/
void helper_pdu_encode_ping_req(struct pdu_data *pdu, void *param);
void helper_pdu_encode_ping_rsp(struct pdu_data *pdu, void *param);
void helper_pdu_encode_feature_req(struct pdu_data *pdu, void *param);
void helper_pdu_encode_slave_feature_req(struct pdu_data *pdu, void *param);
void helper_pdu_encode_feature_rsp(struct pdu_data *pdu, void *param);
void helper_pdu_encode_min_used_chans_ind(struct pdu_data *pdu, void *param);
void helper_pdu_encode_version_ind(struct pdu_data *pdu, void *param);
void helper_pdu_encode_enc_req(struct pdu_data *pdu, void *param);
void helper_pdu_encode_enc_rsp(struct pdu_data *pdu, void *param);
void helper_pdu_encode_start_enc_req(struct pdu_data *pdu, void *param);
void helper_pdu_encode_start_enc_rsp(struct pdu_data *pdu, void *param);
void helper_pdu_encode_pause_enc_req(struct pdu_data *pdu, void *param);
void helper_pdu_encode_pause_enc_rsp(struct pdu_data *pdu, void *param);
void helper_pdu_encode_reject_ext_ind(struct pdu_data *pdu, void *param);
void helper_pdu_encode_reject_ind(struct pdu_data *pdu, void *param);
void helper_pdu_encode_phy_req(struct pdu_data *pdu, void *param);
void helper_pdu_encode_phy_rsp(struct pdu_data *pdu, void *param);
void helper_pdu_encode_phy_update_ind(struct pdu_data *pdu, void *param);
void helper_pdu_encode_unknown_rsp(struct pdu_data *pdu, void *param);
void helper_pdu_encode_conn_param_req(struct pdu_data *pdu, void *param);
void helper_pdu_encode_conn_param_rsp(struct pdu_data *pdu, void *param);
void helper_pdu_encode_conn_update_ind(struct pdu_data *pdu, void *param);
void helper_pdu_encode_terminate_ind(struct pdu_data *pdu, void *param);
void helper_pdu_encode_channel_map_update_ind(struct pdu_data *pdu, void *param);
void helper_pdu_encode_length_req(struct pdu_data *pdu, void *param);
void helper_pdu_encode_length_rsp(struct pdu_data *pdu, void *param);
void helper_pdu_encode_cte_req(struct pdu_data *pdu, void *param);
void helper_pdu_encode_cte_rsp(struct pdu_data *pdu, void *param);
void helper_node_encode_cte_rsp(struct node_rx_pdu *rx, void *param);
void helper_pdu_verify_ping_req(const char *file, uint32_t line, struct pdu_data *pdu, void *param);
void helper_pdu_verify_ping_rsp(const char *file, uint32_t line, struct pdu_data *pdu, void *param);
void helper_pdu_verify_feature_req(const char *file, uint32_t line, struct pdu_data *pdu,
void *param);
void helper_pdu_verify_slave_feature_req(const char *file, uint32_t line, struct pdu_data *pdu,
void *param);
void helper_pdu_verify_feature_rsp(const char *file, uint32_t line, struct pdu_data *pdu,
void *param);
void helper_pdu_verify_min_used_chans_ind(const char *file, uint32_t line, struct pdu_data *pdu,
void *param);
void helper_pdu_verify_version_ind(const char *file, uint32_t line, struct pdu_data *pdu,
void *param);
void helper_pdu_verify_enc_req(const char *file, uint32_t line, struct pdu_data *pdu, void *param);
void helper_pdu_ntf_verify_enc_req(const char *file, uint32_t line, struct pdu_data *pdu,
void *param);
void helper_pdu_verify_enc_rsp(const char *file, uint32_t line, struct pdu_data *pdu, void *param);
void helper_pdu_verify_start_enc_req(const char *file, uint32_t line, struct pdu_data *pdu,
void *param);
void helper_pdu_verify_start_enc_rsp(const char *file, uint32_t line, struct pdu_data *pdu,
void *param);
void helper_pdu_verify_pause_enc_req(const char *file, uint32_t line, struct pdu_data *pdu,
void *param);
void helper_pdu_verify_pause_enc_rsp(const char *file, uint32_t line, struct pdu_data *pdu,
void *param);
void helper_node_verify_enc_refresh(const char *file, uint32_t line, struct node_rx_pdu *rx,
void *param);
void helper_pdu_verify_reject_ind(const char *file, uint32_t line, struct pdu_data *pdu,
void *param);
void helper_pdu_verify_reject_ext_ind(const char *file, uint32_t line, struct pdu_data *pdu,
void *param);
void helper_pdu_verify_phy_req(const char *file, uint32_t line, struct pdu_data *pdu, void *param);
void helper_pdu_verify_phy_rsp(const char *file, uint32_t line, struct pdu_data *pdu, void *param);
void helper_pdu_verify_phy_update_ind(const char *file, uint32_t line, struct pdu_data *pdu,
void *param);
void helper_pdu_verify_unknown_rsp(const char *file, uint32_t line, struct pdu_data *pdu,
void *param);
void helper_pdu_verify_terminate_ind(const char *file, uint32_t line, struct pdu_data *pdu,
void *param);
void helper_pdu_verify_channel_map_update_ind(const char *file, uint32_t line, struct pdu_data *pdu,
void *param);
void helper_node_verify_phy_update(const char *file, uint32_t line, struct node_rx_pdu *rx,
void *param);
void helper_pdu_verify_conn_param_req(const char *file, uint32_t line, struct pdu_data *pdu,
void *param);
void helper_pdu_verify_conn_param_rsp(const char *file, uint32_t line, struct pdu_data *pdu,
void *param);
void helper_pdu_verify_conn_update_ind(const char *file, uint32_t line, struct pdu_data *pdu,
void *param);
void helper_node_verify_conn_update(const char *file, uint32_t line, struct node_rx_pdu *rx,
void *param);
void helper_pdu_verify_length_req(const char *file, uint32_t line, struct pdu_data *pdu,
void *param);
void helper_pdu_verify_length_rsp(const char *file, uint32_t line, struct pdu_data *pdu,
void *param);
void helper_pdu_verify_cte_req(const char *file, uint32_t line, struct pdu_data *pdu, void *param);
void helper_pdu_verify_cte_rsp(const char *file, uint32_t line, struct pdu_data *pdu, void *param);
void helper_node_verify_cte_rsp(const char *file, uint32_t line, struct node_rx_pdu *rx,
void *param);
enum helper_pdu_opcode {
LL_VERSION_IND,
LL_LE_PING_REQ,
LL_LE_PING_RSP,
LL_FEATURE_REQ,
LL_PERIPH_FEAT_XCHG,
LL_FEATURE_RSP,
LL_MIN_USED_CHANS_IND,
LL_REJECT_IND,
LL_REJECT_EXT_IND,
LL_ENC_REQ,
LL_ENC_RSP,
LL_START_ENC_REQ,
LL_START_ENC_RSP,
LL_PAUSE_ENC_REQ,
LL_PAUSE_ENC_RSP,
LL_PHY_REQ,
LL_PHY_RSP,
LL_PHY_UPDATE_IND,
LL_UNKNOWN_RSP,
LL_CONNECTION_UPDATE_IND,
LL_CONNECTION_PARAM_REQ,
LL_CONNECTION_PARAM_RSP,
LL_TERMINATE_IND,
LL_CHAN_MAP_UPDATE_IND,
LL_LENGTH_REQ,
LL_LENGTH_RSP,
LL_CTE_REQ,
LL_CTE_RSP,
};
enum helper_node_opcode {
NODE_PHY_UPDATE,
NODE_CONN_UPDATE,
NODE_ENC_REFRESH,
NODE_CTE_RSP,
};
typedef void(helper_pdu_encode_func_t)(struct pdu_data *data, void *param);
typedef void(helper_pdu_verify_func_t)(const char *file, uint32_t line, struct pdu_data *data,
void *param);
typedef void(helper_pdu_ntf_verify_func_t)(const char *file, uint32_t line, struct pdu_data *data,
void *param);
typedef void(helper_node_encode_func_t)(struct node_rx_pdu *rx, void *param);
typedef void(helper_node_verify_func_t)(const char *file, uint32_t line, struct node_rx_pdu *rx,
void *param);

View file

@ -0,0 +1,37 @@
/*
* Copyright (c) 2020 Nordic Semiconductor ASA
* Copyright (c) 2020 Demant
*
* SPDX-License-Identifier: Apache-2.0
*/
void test_print_conn(struct ll_conn *conn);
void test_set_role(struct ll_conn *conn, uint8_t role);
void test_setup(struct ll_conn *conn);
void event_prepare(struct ll_conn *conn);
void event_tx_ack(struct ll_conn *conn, struct node_tx *tx);
void event_done(struct ll_conn *conn);
uint16_t event_counter(struct ll_conn *conn);
#define lt_tx(_opcode, _conn, _param) lt_tx_real(__FILE__, __LINE__, _opcode, _conn, _param)
#define lt_rx(_opcode, _conn, _tx_ref, _param) \
lt_rx_real(__FILE__, __LINE__, _opcode, _conn, _tx_ref, _param)
#define lt_rx_q_is_empty(_conn) lt_rx_q_is_empty_real(__FILE__, __LINE__, _conn)
#define ut_rx_pdu(_opcode, _ntf_ref, _param) \
ut_rx_pdu_real(__FILE__, __LINE__, _opcode, _ntf_ref, _param)
#define ut_rx_node(_opcode, _ntf_ref, _param) \
ut_rx_node_real(__FILE__, __LINE__, _opcode, _ntf_ref, _param)
#define ut_rx_q_is_empty() ut_rx_q_is_empty_real(__FILE__, __LINE__)
void lt_tx_real(const char *file, uint32_t line, enum helper_pdu_opcode opcode,
struct ll_conn *conn, void *param);
void lt_rx_real(const char *file, uint32_t line, enum helper_pdu_opcode opcode,
struct ll_conn *conn, struct node_tx **tx_ref, void *param);
void lt_rx_q_is_empty_real(const char *file, uint32_t line, struct ll_conn *conn);
void ut_rx_pdu_real(const char *file, uint32_t line, enum helper_pdu_opcode opcode,
struct node_rx_pdu **ntf_ref, void *param);
void ut_rx_node_real(const char *file, uint32_t line, enum helper_node_opcode opcode,
struct node_rx_pdu **ntf_ref, void *param);
void ut_rx_q_is_empty_real(const char *file, uint32_t line);

View file

@ -0,0 +1,951 @@
/*
* Copyright (c) 2020 Nordic Semiconductor ASA
* Copyright (c) 2020 Demant
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "zephyr/types.h"
#include "ztest.h"
#include "kconfig.h"
#include <bluetooth/hci.h>
#include <sys/byteorder.h>
#include <sys/slist.h>
#include <sys/util.h>
#include "hal/ccm.h"
#include "util/util.h"
#include "util/mem.h"
#include "util/memq.h"
#include "pdu.h"
#include "ll.h"
#include "ll_settings.h"
#include "ll_feat.h"
#include "lll.h"
#include "lll_df_types.h"
#include "lll_conn.h"
#include "ull_tx_queue.h"
#include "ull_conn_types.h"
#include "ull_llcp.h"
#include "helper_pdu.h"
#include "helper_features.h"
#define PDU_MEM_EQUAL(_f, _s, _p, _t) \
zassert_mem_equal(_s._f, _p->_f, sizeof(_p->_f), _t "\nCalled at %s:%d\n", file, line);
void helper_pdu_encode_ping_req(struct pdu_data *pdu, void *param)
{
pdu->ll_id = PDU_DATA_LLID_CTRL;
pdu->len = offsetof(struct pdu_data_llctrl, ping_req) +
sizeof(struct pdu_data_llctrl_ping_req);
pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_PING_REQ;
}
void helper_pdu_encode_ping_rsp(struct pdu_data *pdu, void *param)
{
pdu->ll_id = PDU_DATA_LLID_CTRL;
pdu->len = offsetof(struct pdu_data_llctrl, ping_rsp) +
sizeof(struct pdu_data_llctrl_ping_rsp);
pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_PING_RSP;
}
void helper_pdu_encode_feature_req(struct pdu_data *pdu, void *param)
{
struct pdu_data_llctrl_feature_req *feature_req = param;
pdu->ll_id = PDU_DATA_LLID_CTRL;
pdu->len = offsetof(struct pdu_data_llctrl, feature_req) +
sizeof(struct pdu_data_llctrl_feature_req);
pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_FEATURE_REQ;
for (int counter = 0; counter < 8; counter++) {
uint8_t expected_value = feature_req->features[counter];
pdu->llctrl.feature_req.features[counter] = expected_value;
}
}
void helper_pdu_encode_slave_feature_req(struct pdu_data *pdu, void *param)
{
struct pdu_data_llctrl_feature_req *feature_req = param;
pdu->ll_id = PDU_DATA_LLID_CTRL;
pdu->len = offsetof(struct pdu_data_llctrl, feature_req) +
sizeof(struct pdu_data_llctrl_feature_req);
pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_PER_INIT_FEAT_XCHG;
for (int counter = 0; counter < 8; counter++) {
uint8_t expected_value = feature_req->features[counter];
pdu->llctrl.feature_req.features[counter] = expected_value;
}
}
void helper_pdu_encode_feature_rsp(struct pdu_data *pdu, void *param)
{
struct pdu_data_llctrl_feature_rsp *feature_rsp = param;
pdu->ll_id = PDU_DATA_LLID_CTRL;
pdu->len = offsetof(struct pdu_data_llctrl, feature_rsp) +
sizeof(struct pdu_data_llctrl_feature_rsp);
pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_FEATURE_RSP;
for (int counter = 0; counter < 8; counter++) {
uint8_t expected_value = feature_rsp->features[counter];
pdu->llctrl.feature_req.features[counter] = expected_value;
}
}
void helper_pdu_encode_min_used_chans_ind(struct pdu_data *pdu, void *param)
{
struct pdu_data_llctrl_min_used_chans_ind *p = param;
pdu->ll_id = PDU_DATA_LLID_CTRL;
pdu->len = offsetof(struct pdu_data_llctrl, min_used_chans_ind) +
sizeof(struct pdu_data_llctrl_min_used_chans_ind);
pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_MIN_USED_CHAN_IND;
pdu->llctrl.min_used_chans_ind.phys = p->phys;
pdu->llctrl.min_used_chans_ind.min_used_chans = p->min_used_chans;
}
void helper_pdu_encode_version_ind(struct pdu_data *pdu, void *param)
{
struct pdu_data_llctrl_version_ind *p = param;
pdu->ll_id = PDU_DATA_LLID_CTRL;
pdu->len = offsetof(struct pdu_data_llctrl, version_ind) +
sizeof(struct pdu_data_llctrl_version_ind);
pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_VERSION_IND;
pdu->llctrl.version_ind.version_number = p->version_number;
pdu->llctrl.version_ind.company_id = p->company_id;
pdu->llctrl.version_ind.sub_version_number = p->sub_version_number;
}
void helper_pdu_encode_enc_req(struct pdu_data *pdu, void *param)
{
struct pdu_data_llctrl_enc_req *p = param;
pdu->ll_id = PDU_DATA_LLID_CTRL;
pdu->len =
offsetof(struct pdu_data_llctrl, enc_req) + sizeof(struct pdu_data_llctrl_enc_req);
pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_ENC_REQ;
memcpy(pdu->llctrl.enc_req.rand, p->rand, sizeof(pdu->llctrl.enc_req.rand));
memcpy(pdu->llctrl.enc_req.ediv, p->ediv, sizeof(pdu->llctrl.enc_req.ediv));
memcpy(pdu->llctrl.enc_req.skdm, p->skdm, sizeof(pdu->llctrl.enc_req.skdm));
memcpy(pdu->llctrl.enc_req.ivm, p->ivm, sizeof(pdu->llctrl.enc_req.ivm));
}
void helper_pdu_encode_enc_rsp(struct pdu_data *pdu, void *param)
{
struct pdu_data_llctrl_enc_rsp *p = param;
pdu->ll_id = PDU_DATA_LLID_CTRL;
pdu->len =
offsetof(struct pdu_data_llctrl, enc_rsp) + sizeof(struct pdu_data_llctrl_enc_rsp);
pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_ENC_RSP;
memcpy(pdu->llctrl.enc_rsp.skds, p->skds, sizeof(pdu->llctrl.enc_rsp.skds));
memcpy(pdu->llctrl.enc_rsp.ivs, p->ivs, sizeof(pdu->llctrl.enc_rsp.ivs));
}
void helper_pdu_encode_start_enc_req(struct pdu_data *pdu, void *param)
{
pdu->ll_id = PDU_DATA_LLID_CTRL;
pdu->len = offsetof(struct pdu_data_llctrl, start_enc_req) +
sizeof(struct pdu_data_llctrl_start_enc_req);
pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_START_ENC_REQ;
}
void helper_pdu_encode_start_enc_rsp(struct pdu_data *pdu, void *param)
{
pdu->ll_id = PDU_DATA_LLID_CTRL;
pdu->len = offsetof(struct pdu_data_llctrl, start_enc_rsp) +
sizeof(struct pdu_data_llctrl_start_enc_rsp);
pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_START_ENC_RSP;
}
void helper_pdu_encode_pause_enc_req(struct pdu_data *pdu, void *param)
{
pdu->ll_id = PDU_DATA_LLID_CTRL;
pdu->len = offsetof(struct pdu_data_llctrl, pause_enc_req) +
sizeof(struct pdu_data_llctrl_pause_enc_req);
pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_PAUSE_ENC_REQ;
}
void helper_pdu_encode_pause_enc_rsp(struct pdu_data *pdu, void *param)
{
pdu->ll_id = PDU_DATA_LLID_CTRL;
pdu->len = offsetof(struct pdu_data_llctrl, pause_enc_rsp) +
sizeof(struct pdu_data_llctrl_pause_enc_rsp);
pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_PAUSE_ENC_RSP;
}
void helper_pdu_encode_reject_ext_ind(struct pdu_data *pdu, void *param)
{
struct pdu_data_llctrl_reject_ext_ind *p = param;
pdu->ll_id = PDU_DATA_LLID_CTRL;
pdu->len = offsetof(struct pdu_data_llctrl, reject_ext_ind) +
sizeof(struct pdu_data_llctrl_reject_ext_ind);
pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_REJECT_EXT_IND;
pdu->llctrl.reject_ext_ind.reject_opcode = p->reject_opcode;
pdu->llctrl.reject_ext_ind.error_code = p->error_code;
}
void helper_pdu_encode_reject_ind(struct pdu_data *pdu, void *param)
{
struct pdu_data_llctrl_reject_ind *p = param;
pdu->ll_id = PDU_DATA_LLID_CTRL;
pdu->len = offsetof(struct pdu_data_llctrl, reject_ind) +
sizeof(struct pdu_data_llctrl_reject_ind);
pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_REJECT_IND;
pdu->llctrl.reject_ind.error_code = p->error_code;
}
void helper_pdu_encode_phy_req(struct pdu_data *pdu, void *param)
{
struct pdu_data_llctrl_phy_req *p = param;
pdu->ll_id = PDU_DATA_LLID_CTRL;
pdu->len =
offsetof(struct pdu_data_llctrl, phy_req) + sizeof(struct pdu_data_llctrl_phy_req);
pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_PHY_REQ;
pdu->llctrl.phy_req.rx_phys = p->rx_phys;
pdu->llctrl.phy_req.tx_phys = p->tx_phys;
}
void helper_pdu_encode_phy_rsp(struct pdu_data *pdu, void *param)
{
struct pdu_data_llctrl_phy_rsp *p = param;
pdu->ll_id = PDU_DATA_LLID_CTRL;
pdu->len =
offsetof(struct pdu_data_llctrl, phy_rsp) + sizeof(struct pdu_data_llctrl_phy_rsp);
pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_PHY_RSP;
pdu->llctrl.phy_rsp.rx_phys = p->rx_phys;
pdu->llctrl.phy_rsp.tx_phys = p->tx_phys;
}
void helper_pdu_encode_phy_update_ind(struct pdu_data *pdu, void *param)
{
struct pdu_data_llctrl_phy_upd_ind *p = param;
pdu->ll_id = PDU_DATA_LLID_CTRL;
pdu->len = offsetof(struct pdu_data_llctrl, phy_upd_ind) +
sizeof(struct pdu_data_llctrl_phy_upd_ind);
pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_PHY_UPD_IND;
pdu->llctrl.phy_upd_ind.instant = p->instant;
pdu->llctrl.phy_upd_ind.c_to_p_phy = p->c_to_p_phy;
pdu->llctrl.phy_upd_ind.p_to_c_phy = p->p_to_c_phy;
}
void helper_pdu_encode_unknown_rsp(struct pdu_data *pdu, void *param)
{
struct pdu_data_llctrl_unknown_rsp *p = param;
pdu->ll_id = PDU_DATA_LLID_CTRL;
pdu->len = offsetof(struct pdu_data_llctrl, unknown_rsp) +
sizeof(struct pdu_data_llctrl_unknown_rsp);
pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_UNKNOWN_RSP;
pdu->llctrl.unknown_rsp.type = p->type;
}
void helper_pdu_encode_conn_param_req(struct pdu_data *pdu, void *param)
{
struct pdu_data_llctrl_conn_param_req *p = param;
pdu->ll_id = PDU_DATA_LLID_CTRL;
pdu->len = offsetof(struct pdu_data_llctrl, conn_param_req) +
sizeof(struct pdu_data_llctrl_conn_param_req);
pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_CONN_PARAM_REQ;
pdu->llctrl.conn_param_req.interval_min = sys_cpu_to_le16(p->interval_min);
pdu->llctrl.conn_param_req.interval_max = sys_cpu_to_le16(p->interval_max);
pdu->llctrl.conn_param_req.latency = sys_cpu_to_le16(p->latency);
pdu->llctrl.conn_param_req.timeout = sys_cpu_to_le16(p->timeout);
pdu->llctrl.conn_param_req.preferred_periodicity = p->preferred_periodicity;
pdu->llctrl.conn_param_req.reference_conn_event_count =
sys_cpu_to_le16(p->reference_conn_event_count);
pdu->llctrl.conn_param_req.offset0 = sys_cpu_to_le16(p->offset0);
pdu->llctrl.conn_param_req.offset1 = sys_cpu_to_le16(p->offset1);
pdu->llctrl.conn_param_req.offset2 = sys_cpu_to_le16(p->offset2);
pdu->llctrl.conn_param_req.offset3 = sys_cpu_to_le16(p->offset3);
pdu->llctrl.conn_param_req.offset4 = sys_cpu_to_le16(p->offset4);
pdu->llctrl.conn_param_req.offset5 = sys_cpu_to_le16(p->offset5);
}
void helper_pdu_encode_conn_param_rsp(struct pdu_data *pdu, void *param)
{
struct pdu_data_llctrl_conn_param_rsp *p = param;
pdu->ll_id = PDU_DATA_LLID_CTRL;
pdu->len = offsetof(struct pdu_data_llctrl, conn_param_rsp) +
sizeof(struct pdu_data_llctrl_conn_param_rsp);
pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_CONN_PARAM_RSP;
pdu->llctrl.conn_param_rsp.interval_min = sys_cpu_to_le16(p->interval_min);
pdu->llctrl.conn_param_rsp.interval_max = sys_cpu_to_le16(p->interval_max);
pdu->llctrl.conn_param_rsp.latency = sys_cpu_to_le16(p->latency);
pdu->llctrl.conn_param_rsp.timeout = sys_cpu_to_le16(p->timeout);
pdu->llctrl.conn_param_rsp.preferred_periodicity = p->preferred_periodicity;
pdu->llctrl.conn_param_rsp.reference_conn_event_count =
sys_cpu_to_le16(p->reference_conn_event_count);
pdu->llctrl.conn_param_rsp.offset0 = sys_cpu_to_le16(p->offset0);
pdu->llctrl.conn_param_rsp.offset1 = sys_cpu_to_le16(p->offset1);
pdu->llctrl.conn_param_rsp.offset2 = sys_cpu_to_le16(p->offset2);
pdu->llctrl.conn_param_rsp.offset3 = sys_cpu_to_le16(p->offset3);
pdu->llctrl.conn_param_rsp.offset4 = sys_cpu_to_le16(p->offset4);
pdu->llctrl.conn_param_rsp.offset5 = sys_cpu_to_le16(p->offset5);
}
void helper_pdu_encode_conn_update_ind(struct pdu_data *pdu, void *param)
{
struct pdu_data_llctrl_conn_update_ind *p = param;
pdu->ll_id = PDU_DATA_LLID_CTRL;
pdu->len = offsetof(struct pdu_data_llctrl, conn_update_ind) +
sizeof(struct pdu_data_llctrl_conn_update_ind);
pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_CONN_UPDATE_IND;
pdu->llctrl.conn_update_ind.win_size = p->win_size;
pdu->llctrl.conn_update_ind.win_offset = sys_cpu_to_le16(p->win_offset);
pdu->llctrl.conn_update_ind.interval = sys_cpu_to_le16(p->interval);
pdu->llctrl.conn_update_ind.latency = sys_cpu_to_le16(p->latency);
pdu->llctrl.conn_update_ind.timeout = sys_cpu_to_le16(p->timeout);
pdu->llctrl.conn_update_ind.instant = sys_cpu_to_le16(p->instant);
}
void helper_pdu_encode_terminate_ind(struct pdu_data *pdu, void *param)
{
struct pdu_data_llctrl_terminate_ind *p = param;
pdu->ll_id = PDU_DATA_LLID_CTRL;
pdu->len = offsetof(struct pdu_data_llctrl, terminate_ind) +
sizeof(struct pdu_data_llctrl_terminate_ind);
pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_TERMINATE_IND;
pdu->llctrl.terminate_ind.error_code = p->error_code;
}
void helper_pdu_encode_channel_map_update_ind(struct pdu_data *pdu, void *param)
{
struct pdu_data_llctrl_chan_map_ind *p = param;
pdu->ll_id = PDU_DATA_LLID_CTRL;
pdu->len = offsetof(struct pdu_data_llctrl, chan_map_ind) +
sizeof(struct pdu_data_llctrl_chan_map_ind);
pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_CHAN_MAP_IND;
pdu->llctrl.chan_map_ind.instant = p->instant;
memcpy(pdu->llctrl.chan_map_ind.chm, p->chm, sizeof(pdu->llctrl.chan_map_ind.chm));
}
void helper_pdu_encode_length_req(struct pdu_data *pdu, void *param)
{
struct pdu_data_llctrl_length_req *p = param;
pdu->ll_id = PDU_DATA_LLID_CTRL;
pdu->len = offsetof(struct pdu_data_llctrl, length_req) +
sizeof(struct pdu_data_llctrl_length_req);
pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_LENGTH_REQ;
pdu->llctrl.length_req.max_rx_octets = p->max_rx_octets;
pdu->llctrl.length_req.max_tx_octets = p->max_tx_octets;
pdu->llctrl.length_req.max_rx_time = p->max_rx_time;
pdu->llctrl.length_req.max_tx_time = p->max_tx_time;
}
void helper_pdu_encode_length_rsp(struct pdu_data *pdu, void *param)
{
struct pdu_data_llctrl_length_rsp *p = param;
pdu->ll_id = PDU_DATA_LLID_CTRL;
pdu->len = offsetof(struct pdu_data_llctrl, length_rsp) +
sizeof(struct pdu_data_llctrl_length_rsp);
pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_LENGTH_RSP;
pdu->llctrl.length_req.max_rx_octets = p->max_rx_octets;
pdu->llctrl.length_req.max_tx_octets = p->max_tx_octets;
pdu->llctrl.length_req.max_rx_time = p->max_rx_time;
pdu->llctrl.length_req.max_tx_time = p->max_tx_time;
}
void helper_pdu_encode_cte_req(struct pdu_data *pdu, void *param)
{
struct pdu_data_llctrl_cte_req *p = param;
pdu->ll_id = PDU_DATA_LLID_CTRL;
pdu->len =
offsetof(struct pdu_data_llctrl, cte_req) + sizeof(struct pdu_data_llctrl_cte_req);
pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_CTE_REQ;
pdu->llctrl.cte_req.min_cte_len_req = p->min_cte_len_req;
pdu->llctrl.cte_req.cte_type_req = p->cte_type_req;
}
void helper_pdu_encode_cte_rsp(struct pdu_data *pdu, void *param)
{
pdu->ll_id = PDU_DATA_LLID_CTRL;
pdu->len =
offsetof(struct pdu_data_llctrl, cte_rsp) + sizeof(struct pdu_data_llctrl_cte_rsp);
pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_CTE_RSP;
}
void helper_node_encode_cte_rsp(struct node_rx_pdu *rx, void *param)
{
rx->hdr.rx_ftr.iq_report = (struct cte_conn_iq_report *)param;
}
void helper_pdu_verify_version_ind(const char *file, uint32_t line, struct pdu_data *pdu,
void *param)
{
struct pdu_data_llctrl_version_ind *p = param;
zassert_equal(pdu->ll_id, PDU_DATA_LLID_CTRL, "Not a Control PDU.\nCalled at %s:%d\n", file,
line);
zassert_equal(pdu->llctrl.opcode, PDU_DATA_LLCTRL_TYPE_VERSION_IND,
"Not a LL_VERSION_IND.\nCalled at %s:%d\n", file, line);
zassert_equal(pdu->llctrl.version_ind.version_number, p->version_number,
"Wrong version number.\nCalled at %s:%d\n", file, line);
zassert_equal(pdu->llctrl.version_ind.company_id, p->company_id,
"Wrong company id.\nCalled at %s:%d\n", file, line);
zassert_equal(pdu->llctrl.version_ind.sub_version_number, p->sub_version_number,
"Wrong sub version number.\nCalled at %s:%d\n", file, line);
}
void helper_pdu_verify_ping_req(const char *file, uint32_t line, struct pdu_data *pdu, void *param)
{
zassert_equal(pdu->ll_id, PDU_DATA_LLID_CTRL, "Not a Control PDU.\nCalled at %s:%d\n", file,
line);
zassert_equal(pdu->llctrl.opcode, PDU_DATA_LLCTRL_TYPE_PING_REQ,
"Not a LL_PING_REQ. Called at %s:%d\n", file, line);
}
void helper_pdu_verify_ping_rsp(const char *file, uint32_t line, struct pdu_data *pdu, void *param)
{
zassert_equal(pdu->ll_id, PDU_DATA_LLID_CTRL, "Not a Control PDU.\nCalled at %s:%d\n", file,
line);
zassert_equal(pdu->llctrl.opcode, PDU_DATA_LLCTRL_TYPE_PING_RSP,
"Not a LL_PING_RSP.\nCalled at %s:%d\n", file, line);
}
void helper_pdu_verify_feature_req(const char *file, uint32_t line, struct pdu_data *pdu,
void *param)
{
struct pdu_data_llctrl_feature_req *feature_req = param;
zassert_equal(pdu->ll_id, PDU_DATA_LLID_CTRL, NULL);
zassert_equal(pdu->llctrl.opcode, PDU_DATA_LLCTRL_TYPE_FEATURE_REQ,
"Wrong opcode.\nCalled at %s:%d\n", file, line);
for (int counter = 0; counter < 8; counter++) {
uint8_t expected_value = feature_req->features[counter];
zassert_equal(pdu->llctrl.feature_req.features[counter], expected_value,
"Wrong feature exchange data.\nAt %s:%d\n", file, line);
}
}
void helper_pdu_verify_slave_feature_req(const char *file, uint32_t line, struct pdu_data *pdu,
void *param)
{
struct pdu_data_llctrl_feature_req *feature_req = param;
zassert_equal(pdu->ll_id, PDU_DATA_LLID_CTRL, NULL);
zassert_equal(pdu->llctrl.opcode, PDU_DATA_LLCTRL_TYPE_PER_INIT_FEAT_XCHG, NULL);
for (int counter = 0; counter < 8; counter++) {
uint8_t expected_value = feature_req->features[counter];
zassert_equal(pdu->llctrl.feature_req.features[counter], expected_value,
"Wrong feature data\nCalled at %s:%d\n", file, line);
}
}
void helper_pdu_verify_feature_rsp(const char *file, uint32_t line, struct pdu_data *pdu,
void *param)
{
struct pdu_data_llctrl_feature_rsp *feature_rsp = param;
zassert_equal(pdu->ll_id, PDU_DATA_LLID_CTRL, NULL);
zassert_equal(pdu->llctrl.opcode, PDU_DATA_LLCTRL_TYPE_FEATURE_RSP,
"Response: %d Expected: %d\n", pdu->llctrl.opcode,
PDU_DATA_LLCTRL_TYPE_FEATURE_RSP);
for (int counter = 0; counter < 8; counter++) {
uint8_t expected_value = feature_rsp->features[counter];
zassert_equal(pdu->llctrl.feature_rsp.features[counter], expected_value,
"Wrong feature data\nCalled at %s:%d\n", file, line);
}
}
void helper_pdu_verify_min_used_chans_ind(const char *file, uint32_t line, struct pdu_data *pdu,
void *param)
{
struct pdu_data_llctrl_min_used_chans_ind *p = param;
zassert_equal(pdu->ll_id, PDU_DATA_LLID_CTRL, "Not a Control PDU.\nCalled at %s:%d\n", file,
line);
zassert_equal(pdu->llctrl.opcode, PDU_DATA_LLCTRL_TYPE_MIN_USED_CHAN_IND,
"Not a MIN_USED_CHAN_IND.\nCalled at %s:%d\n", file, line);
zassert_equal(pdu->llctrl.min_used_chans_ind.phys, p->phys, "Wrong PHY.\nCalled at %s:%d\n",
file, line);
zassert_equal(pdu->llctrl.min_used_chans_ind.min_used_chans, p->min_used_chans,
"Channel count\nCalled at %s:%d\n", file, line);
}
void helper_pdu_verify_enc_req(const char *file, uint32_t line, struct pdu_data *pdu, void *param)
{
struct pdu_data_llctrl_enc_req *p = param;
zassert_equal(pdu->ll_id, PDU_DATA_LLID_CTRL, "Not a Control PDU.\nCalled at %s:%d\n", file,
line);
zassert_equal(pdu->llctrl.opcode, PDU_DATA_LLCTRL_TYPE_ENC_REQ,
"Not a LL_ENC_REQ. Called at %s:%d\n", file, line);
PDU_MEM_EQUAL(rand, pdu->llctrl.enc_req, p, "Rand mismatch.");
PDU_MEM_EQUAL(ediv, pdu->llctrl.enc_req, p, "EDIV mismatch.");
PDU_MEM_EQUAL(skdm, pdu->llctrl.enc_req, p, "SKDm mismatch.");
PDU_MEM_EQUAL(ivm, pdu->llctrl.enc_req, p, "IVm mismatch.");
}
void helper_pdu_ntf_verify_enc_req(const char *file, uint32_t line, struct pdu_data *pdu,
void *param)
{
struct pdu_data_llctrl_enc_req *p = param;
zassert_equal(pdu->ll_id, PDU_DATA_LLID_CTRL, "Not a Control PDU.\nCalled at %s:%d\n", file,
line);
zassert_equal(pdu->llctrl.opcode, PDU_DATA_LLCTRL_TYPE_ENC_REQ,
"Not a LL_ENC_REQ. Called at %s:%d\n", file, line);
PDU_MEM_EQUAL(rand, pdu->llctrl.enc_req, p, "Rand mismatch.");
PDU_MEM_EQUAL(ediv, pdu->llctrl.enc_req, p, "EDIV mismatch.");
}
void helper_pdu_verify_enc_rsp(const char *file, uint32_t line, struct pdu_data *pdu, void *param)
{
zassert_equal(pdu->ll_id, PDU_DATA_LLID_CTRL, "Not a Control PDU.\nCalled at %s:%d\n", file,
line);
zassert_equal(pdu->llctrl.opcode, PDU_DATA_LLCTRL_TYPE_ENC_RSP,
"Not a LL_ENC_RSP.\nCalled at %s:%d\n", file, line);
}
void helper_pdu_verify_start_enc_req(const char *file, uint32_t line, struct pdu_data *pdu,
void *param)
{
zassert_equal(pdu->ll_id, PDU_DATA_LLID_CTRL, "Not a Control PDU.\nCalled at %s:%d\n", file,
line);
zassert_equal(pdu->llctrl.opcode, PDU_DATA_LLCTRL_TYPE_START_ENC_REQ,
"Not a LL_START_ENC_REQ.\nCalled at %s:%d\n", file, line);
}
void helper_pdu_verify_start_enc_rsp(const char *file, uint32_t line, struct pdu_data *pdu,
void *param)
{
zassert_equal(pdu->ll_id, PDU_DATA_LLID_CTRL, "Not a Control PDU.\nCalled at %s:%d\n", file,
line);
zassert_equal(pdu->llctrl.opcode, PDU_DATA_LLCTRL_TYPE_START_ENC_RSP,
"Not a LL_START_ENC_RSP.\nCalled at %s:%d\n", file, line);
}
void helper_pdu_verify_pause_enc_req(const char *file, uint32_t line, struct pdu_data *pdu,
void *param)
{
zassert_equal(pdu->ll_id, PDU_DATA_LLID_CTRL, "Not a Control PDU.\nCalled at %s:%d\n", file,
line);
zassert_equal(pdu->llctrl.opcode, PDU_DATA_LLCTRL_TYPE_PAUSE_ENC_REQ,
"Not a LL_PAUSE_ENC_REQ.\nCalled at %s:%d\n", file, line);
}
void helper_pdu_verify_pause_enc_rsp(const char *file, uint32_t line, struct pdu_data *pdu,
void *param)
{
zassert_equal(pdu->ll_id, PDU_DATA_LLID_CTRL, "Not a Control PDU.\nCalled at %s:%d\n", file,
line);
zassert_equal(pdu->llctrl.opcode, PDU_DATA_LLCTRL_TYPE_PAUSE_ENC_RSP,
"Not a LL_PAUSE_ENC_RSP.\nCalled at %s:%d\n", file, line);
}
void helper_node_verify_enc_refresh(const char *file, uint32_t line, struct node_rx_pdu *rx,
void *param)
{
zassert_equal(rx->hdr.type, NODE_RX_TYPE_ENC_REFRESH,
"Not an ENC_REFRESH node.\nCalled at %s:%d\n", file, line);
}
void helper_pdu_verify_reject_ind(const char *file, uint32_t line, struct pdu_data *pdu,
void *param)
{
struct pdu_data_llctrl_reject_ind *p = param;
zassert_equal(pdu->ll_id, PDU_DATA_LLID_CTRL, "Not a Control PDU.\nCalled at %s:%d\n", file,
line);
zassert_equal(pdu->len,
offsetof(struct pdu_data_llctrl, reject_ind) +
sizeof(struct pdu_data_llctrl_reject_ind),
"Wrong length.\nCalled at %s:%d\n", file, line);
zassert_equal(pdu->llctrl.opcode, PDU_DATA_LLCTRL_TYPE_REJECT_IND,
"Not a LL_REJECT_IND.\nCalled at %s:%d\n", file, line);
zassert_equal(pdu->llctrl.reject_ind.error_code, p->error_code,
"Error code mismatch.\nCalled at %s:%d\n", file, line);
}
void helper_pdu_verify_reject_ext_ind(const char *file, uint32_t line, struct pdu_data *pdu,
void *param)
{
struct pdu_data_llctrl_reject_ext_ind *p = param;
zassert_equal(pdu->ll_id, PDU_DATA_LLID_CTRL, "Not a Control PDU.\nCalled at %s:%d\n", file,
line);
zassert_equal(pdu->len,
offsetof(struct pdu_data_llctrl, reject_ext_ind) +
sizeof(struct pdu_data_llctrl_reject_ext_ind),
"Wrong length.\nCalled at %s:%d\n", file, line);
zassert_equal(pdu->llctrl.opcode, PDU_DATA_LLCTRL_TYPE_REJECT_EXT_IND,
"Not a LL_REJECT_EXT_IND.\nCalled at %s:%d\n", file, line);
zassert_equal(pdu->llctrl.reject_ext_ind.reject_opcode, p->reject_opcode,
"Reject opcode mismatch.\nCalled at %s:%d\n", file, line);
zassert_equal(pdu->llctrl.reject_ext_ind.error_code, p->error_code,
"Error code mismatch.\nCalled at %s:%d\n", file, line);
}
void helper_pdu_verify_phy_req(const char *file, uint32_t line, struct pdu_data *pdu, void *param)
{
struct pdu_data_llctrl_phy_req *p = param;
zassert_equal(pdu->ll_id, PDU_DATA_LLID_CTRL, "Not a Control PDU.\nCalled at %s:%d\n", file,
line);
zassert_equal(pdu->len,
offsetof(struct pdu_data_llctrl, phy_req) +
sizeof(struct pdu_data_llctrl_phy_req),
"Wrong length.\nCalled at %s:%d\n", file, line);
zassert_equal(pdu->llctrl.opcode, PDU_DATA_LLCTRL_TYPE_PHY_REQ,
"Not a LL_PHY_REQ.\nCalled at %s:%d\n", file, line);
zassert_equal(pdu->llctrl.phy_req.rx_phys, p->rx_phys,
"rx phys mismatch.\nCalled at %s:%d\n", file, line);
zassert_equal(pdu->llctrl.phy_req.tx_phys, p->tx_phys,
"tx phys mismatch.\nCalled at %s:%d\n", file, line);
}
void helper_pdu_verify_phy_rsp(const char *file, uint32_t line, struct pdu_data *pdu, void *param)
{
struct pdu_data_llctrl_phy_rsp *p = param;
zassert_equal(pdu->ll_id, PDU_DATA_LLID_CTRL, "Not a Control PDU.\nCalled at %s:%d\n", file,
line);
zassert_equal(pdu->len,
offsetof(struct pdu_data_llctrl, phy_rsp) +
sizeof(struct pdu_data_llctrl_phy_rsp),
"Wrong length.\nCalled at %s:%d\n", file, line);
zassert_equal(pdu->llctrl.opcode, PDU_DATA_LLCTRL_TYPE_PHY_RSP,
"Not a LL_PHY_RSP.\nCalled at %s:%d\n", file, line);
zassert_equal(pdu->llctrl.phy_rsp.rx_phys, p->rx_phys,
"rx phys mismatch.\nCalled at %s:%d\n", file, line);
zassert_equal(pdu->llctrl.phy_rsp.tx_phys, p->tx_phys,
"tx phys mismatch.\nCalled at %s:%d\n", file, line);
}
void helper_pdu_verify_phy_update_ind(const char *file, uint32_t line, struct pdu_data *pdu,
void *param)
{
struct pdu_data_llctrl_phy_upd_ind *p = param;
zassert_equal(pdu->ll_id, PDU_DATA_LLID_CTRL, "Not a Control PDU.\nCalled at %s:%d\n", file,
line);
zassert_equal(pdu->len,
offsetof(struct pdu_data_llctrl, phy_upd_ind) +
sizeof(struct pdu_data_llctrl_phy_upd_ind),
"Wrong length.\nCalled at %s:%d\n", file, line);
zassert_equal(pdu->llctrl.opcode, PDU_DATA_LLCTRL_TYPE_PHY_UPD_IND,
"Not a LL_PHY_UPDATE_IND.\nCalled at %s:%d\n", file, line);
zassert_equal(pdu->llctrl.phy_upd_ind.instant, p->instant,
"instant mismatch.\nCalled at %s:%d\n", file, line);
zassert_equal(pdu->llctrl.phy_upd_ind.c_to_p_phy, p->c_to_p_phy,
"c_to_p_phy mismatch.\nCalled at %s:%d\n", file, line);
zassert_equal(pdu->llctrl.phy_upd_ind.p_to_c_phy, p->p_to_c_phy,
"p_to_c_phy mismatch.\nCalled at %s:%d\n", file, line);
}
void helper_node_verify_phy_update(const char *file, uint32_t line, struct node_rx_pdu *rx,
void *param)
{
struct node_rx_pu *pdu = (struct node_rx_pu *)rx->pdu;
struct node_rx_pu *p = param;
zassert_equal(rx->hdr.type, NODE_RX_TYPE_PHY_UPDATE,
"Not a PHY_UPDATE node.\nCalled at %s:%d\n", file, line);
zassert_equal(pdu->status, p->status, "Status mismatch.\nCalled at %s:%d\n", file, line);
}
void helper_pdu_verify_unknown_rsp(const char *file, uint32_t line, struct pdu_data *pdu,
void *param)
{
struct pdu_data_llctrl_unknown_rsp *p = param;
zassert_equal(pdu->ll_id, PDU_DATA_LLID_CTRL, "Not a Control PDU.\nCalled at %s:%d\n", file,
line);
zassert_equal(pdu->len,
offsetof(struct pdu_data_llctrl, unknown_rsp) +
sizeof(struct pdu_data_llctrl_unknown_rsp),
"Wrong length.\nCalled at %s:%d\n", file, line);
zassert_equal(pdu->llctrl.opcode, PDU_DATA_LLCTRL_TYPE_UNKNOWN_RSP,
"Not a LL_UNKNOWN_RSP.\nCalled at %s:%d\n", file, line);
zassert_equal(pdu->llctrl.unknown_rsp.type, p->type, "Type mismatch.\nCalled at %s:%d\n",
file, line);
}
void helper_pdu_verify_conn_param_req(const char *file, uint32_t line, struct pdu_data *pdu,
void *param)
{
struct pdu_data_llctrl_conn_param_req *p = param;
zassert_equal(pdu->ll_id, PDU_DATA_LLID_CTRL, "Not a Control PDU.\nCalled at %s:%d\n", file,
line);
zassert_equal(pdu->len,
offsetof(struct pdu_data_llctrl, conn_param_req) +
sizeof(struct pdu_data_llctrl_conn_param_req),
"Wrong length.\nCalled at %s:%d\n", file, line);
zassert_equal(pdu->llctrl.opcode, PDU_DATA_LLCTRL_TYPE_CONN_PARAM_REQ,
"Not a LL_CONNECTION_PARAM_REQ.\nCalled at %s:%d\n", file, line);
zassert_equal(pdu->llctrl.conn_param_req.interval_min, p->interval_min,
"Interval_min mismatch.\nCalled at %s:%d\n", file, line);
zassert_equal(pdu->llctrl.conn_param_req.interval_max, p->interval_max,
"Interval_max mismatch.\nCalled at %s:%d\n", file, line);
zassert_equal(pdu->llctrl.conn_param_req.latency, p->latency,
"Latency mismatch.\nCalled at %s:%d\n", file, line);
zassert_equal(pdu->llctrl.conn_param_req.timeout, p->timeout,
"Timeout mismatch.\nCalled at %s:%d\n", file, line);
zassert_equal(pdu->llctrl.conn_param_req.preferred_periodicity, p->preferred_periodicity,
"Preferred_periodicity mismatch.\nCalled at %s:%d\n", file, line);
zassert_equal(pdu->llctrl.conn_param_req.reference_conn_event_count,
p->reference_conn_event_count,
"Reference_conn_event_count mismatch.\nCalled at %s:%d\n", file, line);
zassert_equal(pdu->llctrl.conn_param_req.offset0, p->offset0,
"Offset0 mismatch.\nCalled at %s:%d\n", file, line);
zassert_equal(pdu->llctrl.conn_param_req.offset1, p->offset1,
"Offset1 mismatch.\nCalled at %s:%d\n", file, line);
zassert_equal(pdu->llctrl.conn_param_req.offset2, p->offset2,
"Offset2 mismatch.\nCalled at %s:%d\n", file, line);
zassert_equal(pdu->llctrl.conn_param_req.offset3, p->offset3,
"Offset3 mismatch.\nCalled at %s:%d\n", file, line);
zassert_equal(pdu->llctrl.conn_param_req.offset4, p->offset4,
"Offset4 mismatch.\nCalled at %s:%d\n", file, line);
zassert_equal(pdu->llctrl.conn_param_req.offset5, p->offset5,
"Offset5 mismatch.\nCalled at %s:%d\n", file, line);
}
void helper_pdu_verify_conn_param_rsp(const char *file, uint32_t line, struct pdu_data *pdu,
void *param)
{
struct pdu_data_llctrl_conn_param_rsp *p = param;
zassert_equal(pdu->ll_id, PDU_DATA_LLID_CTRL, "Not a Control PDU.\nCalled at %s:%d\n", file,
line);
zassert_equal(pdu->len,
offsetof(struct pdu_data_llctrl, conn_param_rsp) +
sizeof(struct pdu_data_llctrl_conn_param_rsp),
"Wrong length.\nCalled at %s:%d\n", file, line);
zassert_equal(pdu->llctrl.opcode, PDU_DATA_LLCTRL_TYPE_CONN_PARAM_RSP,
"Not a LL_CONNECTION_PARAM_RSP.\nCalled at %s:%d\n", file, line);
zassert_equal(pdu->llctrl.conn_param_rsp.interval_min, p->interval_min,
"Interval_min mismatch.\nCalled at %s:%d\n", file, line);
zassert_equal(pdu->llctrl.conn_param_rsp.interval_max, p->interval_max,
"Interval_max mismatch.\nCalled at %s:%d\n", file, line);
zassert_equal(pdu->llctrl.conn_param_rsp.latency, p->latency,
"Latency mismatch.\nCalled at %s:%d\n", file, line);
zassert_equal(pdu->llctrl.conn_param_rsp.timeout, p->timeout,
"Timeout mismatch.\nCalled at %s:%d\n", file, line);
zassert_equal(pdu->llctrl.conn_param_rsp.preferred_periodicity, p->preferred_periodicity,
"Preferred_periodicity mismatch.\nCalled at %s:%d\n", file, line);
zassert_equal(pdu->llctrl.conn_param_rsp.reference_conn_event_count,
p->reference_conn_event_count,
"Reference_conn_event_count mismatch.\nCalled at %s:%d\n", file, line);
zassert_equal(pdu->llctrl.conn_param_rsp.offset0, p->offset0,
"Offset0 mismatch.\nCalled at %s:%d\n", file, line);
zassert_equal(pdu->llctrl.conn_param_rsp.offset1, p->offset1,
"Offset1 mismatch.\nCalled at %s:%d\n", file, line);
zassert_equal(pdu->llctrl.conn_param_rsp.offset2, p->offset2,
"Offset2 mismatch.\nCalled at %s:%d\n", file, line);
zassert_equal(pdu->llctrl.conn_param_rsp.offset3, p->offset3,
"Offset3 mismatch.\nCalled at %s:%d\n", file, line);
zassert_equal(pdu->llctrl.conn_param_rsp.offset4, p->offset4,
"Offset4 mismatch.\nCalled at %s:%d\n", file, line);
zassert_equal(pdu->llctrl.conn_param_rsp.offset5, p->offset5,
"Offset5 mismatch.\nCalled at %s:%d\n", file, line);
}
void helper_pdu_verify_conn_update_ind(const char *file, uint32_t line, struct pdu_data *pdu,
void *param)
{
struct pdu_data_llctrl_conn_update_ind *p = param;
zassert_equal(pdu->ll_id, PDU_DATA_LLID_CTRL, "Not a Control PDU.\nCalled at %s:%d\n", file,
line);
zassert_equal(pdu->len,
offsetof(struct pdu_data_llctrl, conn_update_ind) +
sizeof(struct pdu_data_llctrl_conn_update_ind),
"Wrong length.\nCalled at %s:%d\n", file, line);
zassert_equal(pdu->llctrl.opcode, PDU_DATA_LLCTRL_TYPE_CONN_UPDATE_IND,
"Not a LL_CONNECTION_UPDATE_IND.\nCalled at %s:%d\n", file, line);
zassert_equal(pdu->llctrl.conn_update_ind.win_size, p->win_size,
"Win_size mismatch.\nCalled at %s:%d\n", file, line);
zassert_equal(pdu->llctrl.conn_update_ind.win_offset, p->win_offset,
"Win_offset mismatch.\nCalled at %s:%d\n", file, line);
zassert_equal(pdu->llctrl.conn_update_ind.latency, p->latency,
"Latency.\nCalled at %s:%d\n", file, line);
zassert_equal(pdu->llctrl.conn_update_ind.interval, p->interval,
"Interval mismatch.\nCalled at %s:%d\n", file, line);
zassert_equal(pdu->llctrl.conn_update_ind.timeout, p->timeout,
"Timeout mismatch.\nCalled at %s:%d\n", file, line);
zassert_equal(pdu->llctrl.conn_update_ind.instant, p->instant,
"Instant mismatch.\nCalled at %s:%d\n", file, line);
}
void helper_node_verify_conn_update(const char *file, uint32_t line, struct node_rx_pdu *rx,
void *param)
{
struct node_rx_pu *pdu = (struct node_rx_pu *)rx->pdu;
struct node_rx_pu *p = param;
zassert_equal(rx->hdr.type, NODE_RX_TYPE_CONN_UPDATE,
"Not a CONN_UPDATE node.\nCalled at %s:%d\n", file, line);
zassert_equal(pdu->status, p->status, "Status mismatch.\nCalled at %s:%d\n", file, line);
}
void helper_pdu_verify_terminate_ind(const char *file, uint32_t line, struct pdu_data *pdu,
void *param)
{
struct pdu_data_llctrl_terminate_ind *p = param;
zassert_equal(pdu->ll_id, PDU_DATA_LLID_CTRL, "Not a Control PDU.\nCalled at %s:%d\n", file,
line);
zassert_equal(pdu->len,
offsetof(struct pdu_data_llctrl, terminate_ind) +
sizeof(struct pdu_data_llctrl_terminate_ind),
"Wrong length.\nCalled at %s:%d\n", file, line);
zassert_equal(pdu->llctrl.opcode, PDU_DATA_LLCTRL_TYPE_TERMINATE_IND,
"Not a LL_TERMINATE_IND.\nCalled at %s:%d\n", file, line);
zassert_equal(pdu->llctrl.terminate_ind.error_code, p->error_code,
"Error code mismatch.\nCalled at %s:%d\n", file, line);
}
void helper_pdu_verify_channel_map_update_ind(const char *file, uint32_t line, struct pdu_data *pdu,
void *param)
{
struct pdu_data_llctrl_chan_map_ind *p = param;
zassert_equal(pdu->ll_id, PDU_DATA_LLID_CTRL, "Not a Control PDU.\nCalled at %s:%d\n", file,
line);
zassert_equal(pdu->llctrl.opcode, PDU_DATA_LLCTRL_TYPE_CHAN_MAP_IND,
"Not a LL_CHANNEL_MAP_UPDATE_IND.\nCalled at %s:%d ( %d %d)\n", file, line,
pdu->llctrl.opcode, PDU_DATA_LLCTRL_TYPE_CHAN_MAP_IND);
zassert_equal(pdu->len,
offsetof(struct pdu_data_llctrl, chan_map_ind) +
sizeof(struct pdu_data_llctrl_chan_map_ind),
"Wrong length.\nCalled at %s:%d\n", file, line);
zassert_equal(pdu->llctrl.chan_map_ind.instant, p->instant,
"Instant mismatch.\nCalled at %s:%d\n", file, line);
zassert_mem_equal(pdu->llctrl.chan_map_ind.chm, p->chm, sizeof(p->chm),
"Channel Map mismatch.\nCalled at %s:%d\n", file, line);
}
void helper_pdu_verify_length_req(const char *file, uint32_t line, struct pdu_data *pdu,
void *param)
{
struct pdu_data_llctrl_length_req *p = param;
zassert_equal(pdu->ll_id, PDU_DATA_LLID_CTRL, "Not a Control PDU.\nCalled at %s:%d\n", file,
line);
zassert_equal(pdu->len,
offsetof(struct pdu_data_llctrl, length_req) +
sizeof(struct pdu_data_llctrl_length_req),
"Wrong length.\nCalled at %s:%d\n", file, line);
zassert_equal(pdu->llctrl.opcode, PDU_DATA_LLCTRL_TYPE_LENGTH_REQ,
"Not a LL_LENGTH_REQ.\nCalled at %s:%d\n", file, line);
zassert_equal(pdu->llctrl.length_req.max_rx_octets, p->max_rx_octets,
"max_rx_octets mismatch.\nCalled at %s:%d\n", file, line);
zassert_equal(pdu->llctrl.length_req.max_tx_octets, p->max_tx_octets,
"max_tx_octets mismatch.\nCalled at %s:%d\n", file, line);
zassert_equal(pdu->llctrl.length_req.max_rx_time, p->max_rx_time,
"max_rx_time mismatch.\nCalled at %s:%d\n", file, line);
zassert_equal(pdu->llctrl.length_req.max_tx_time, p->max_tx_time,
"max_tx_time mismatch.\nCalled at %s:%d\n", file, line);
}
void helper_pdu_verify_length_rsp(const char *file, uint32_t line, struct pdu_data *pdu,
void *param)
{
struct pdu_data_llctrl_length_rsp *p = param;
zassert_equal(pdu->ll_id, PDU_DATA_LLID_CTRL, "Not a Control PDU.\nCalled at %s:%d\n", file,
line);
zassert_equal(pdu->len,
offsetof(struct pdu_data_llctrl, length_rsp) +
sizeof(struct pdu_data_llctrl_length_rsp),
"Wrong length.\nCalled at %s:%d\n", file, line);
zassert_equal(pdu->llctrl.opcode, PDU_DATA_LLCTRL_TYPE_LENGTH_RSP,
"Not a LL_LENGTH_RSP.\nCalled at %s:%d\n", file, line);
zassert_equal(pdu->llctrl.length_rsp.max_rx_octets, p->max_rx_octets,
"max_rx_octets mismatch.\nCalled at %s:%d\n", file, line);
zassert_equal(pdu->llctrl.length_rsp.max_tx_octets, p->max_tx_octets,
"max_tx_octets mismatch.\nCalled at %s:%d\n", file, line);
zassert_equal(pdu->llctrl.length_rsp.max_rx_time, p->max_rx_time,
"max_rx_time mismatch.\nCalled at %s:%d\n", file, line);
zassert_equal(pdu->llctrl.length_rsp.max_tx_time, p->max_tx_time,
"max_tx_time mismatch.\nCalled at %s:%d\n", file, line);
}
void helper_pdu_verify_cte_req(const char *file, uint32_t line, struct pdu_data *pdu, void *param)
{
struct pdu_data_llctrl_cte_req *p = param;
zassert_equal(pdu->ll_id, PDU_DATA_LLID_CTRL, "Not a Control PDU.\nCalled at %s:%d\n", file,
line);
zassert_equal(pdu->llctrl.opcode, PDU_DATA_LLCTRL_TYPE_CTE_REQ,
"Not a LL_CTE_REQ.\nCalled at %s:%d ( %d %d)\n", file, line,
pdu->llctrl.opcode, PDU_DATA_LLCTRL_TYPE_CTE_REQ);
zassert_equal(pdu->len,
offsetof(struct pdu_data_llctrl, cte_req) +
sizeof(struct pdu_data_llctrl_cte_req),
"Wrong length.\nCalled at %s:%d\n", file, line);
zassert_equal(pdu->llctrl.cte_req.min_cte_len_req, p->min_cte_len_req,
"Minimal CTE length request mismatch.\nCalled at %s:%d\n", file, line);
zassert_equal(pdu->llctrl.cte_req.cte_type_req, p->cte_type_req,
"CTE type request mismatch.\nCalled at %s:%d\n", file, line);
}
void helper_pdu_verify_cte_rsp(const char *file, uint32_t line, struct pdu_data *pdu, void *param)
{
zassert_equal(pdu->ll_id, PDU_DATA_LLID_CTRL, "Not a Control PDU.\nCalled at %s:%d\n", file,
line);
zassert_equal(pdu->len,
offsetof(struct pdu_data_llctrl, cte_rsp) +
sizeof(struct pdu_data_llctrl_cte_rsp),
"Wrong length.\nCalled at %s:%d\n", file, line);
zassert_equal(pdu->llctrl.opcode, PDU_DATA_LLCTRL_TYPE_CTE_RSP,
"Not a LL_CTE_RSP.\nCalled at %s:%d\n", file, line);
}
void helper_node_verify_cte_rsp(const char *file, uint32_t line, struct node_rx_pdu *rx,
void *param)
{
struct cte_conn_iq_report *p_iq_report = param;
struct cte_conn_iq_report *rx_iq_report = rx->hdr.rx_ftr.iq_report;
zassert_equal(rx_iq_report->cte_info.time, p_iq_report->cte_info.time,
"CTE Time mismatch.\nCalled at %s:%d\n", file, line);
zassert_equal(rx_iq_report->local_slot_durations, p_iq_report->local_slot_durations,
"Slot duration mismatch.\nCalled at %s:%d\n", file, line);
zassert_equal(rx_iq_report->packet_status, p_iq_report->packet_status,
"Packet status mismatch.\nCalled at %s:%d\n", file, line);
zassert_equal(rx_iq_report->rssi_ant_id, p_iq_report->rssi_ant_id,
"RSSI antenna id mismatch.\nCalled at %s:%d\n", file, line);
zassert_equal(rx_iq_report->sample_count, p_iq_report->sample_count,
"Sample count mismatch.\nCalled at %s:%d\n", file, line);
zassert_equal(memcmp(rx_iq_report->sample, p_iq_report->sample, p_iq_report->sample_count),
0, "IQ samples mismatch.\nCalled at %s:%d\n", file, line);
}

View file

@ -0,0 +1,397 @@
/*
* Copyright (c) 2020 Demant
* Copyright (c) 2020 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "zephyr/types.h"
#include "ztest.h"
#include <stdlib.h>
#include "kconfig.h"
#include <bluetooth/hci.h>
#include <sys/slist.h>
#include <sys/util.h>
#include "hal/ccm.h"
#include "util/util.h"
#include "util/mem.h"
#include "util/memq.h"
#include "pdu.h"
#include "ll.h"
#include "ll_settings.h"
#include "ll_feat.h"
#include "lll.h"
#include "lll_df_types.h"
#include "lll_conn.h"
#include "ull_tx_queue.h"
#include "ull_conn_types.h"
#include "ull_conn_internal.h"
#include "ull_llcp_internal.h"
#include "ull_llcp.h"
#include "helper_pdu.h"
#include "helper_util.h"
static uint32_t event_active;
static uint16_t lazy;
sys_slist_t ut_rx_q;
static sys_slist_t lt_tx_q;
#define PDU_DC_LL_HEADER_SIZE (offsetof(struct pdu_data, lldata))
#define NODE_RX_HEADER_SIZE (offsetof(struct node_rx_pdu, pdu))
#define NODE_RX_STRUCT_OVERHEAD (NODE_RX_HEADER_SIZE)
#define PDU_DATA_SIZE (PDU_DC_LL_HEADER_SIZE + LL_LENGTH_OCTETS_RX_MAX)
#define PDU_RX_NODE_SIZE WB_UP(NODE_RX_STRUCT_OVERHEAD + PDU_DATA_SIZE)
helper_pdu_encode_func_t *const helper_pdu_encode[] = {
[LL_VERSION_IND] = helper_pdu_encode_version_ind,
[LL_LE_PING_REQ] = helper_pdu_encode_ping_req,
[LL_LE_PING_RSP] = helper_pdu_encode_ping_rsp,
[LL_FEATURE_REQ] = helper_pdu_encode_feature_req,
[LL_PERIPH_FEAT_XCHG] = helper_pdu_encode_slave_feature_req,
[LL_FEATURE_RSP] = helper_pdu_encode_feature_rsp,
[LL_MIN_USED_CHANS_IND] = helper_pdu_encode_min_used_chans_ind,
[LL_REJECT_IND] = helper_pdu_encode_reject_ind,
[LL_REJECT_EXT_IND] = helper_pdu_encode_reject_ext_ind,
[LL_ENC_REQ] = helper_pdu_encode_enc_req,
[LL_ENC_RSP] = helper_pdu_encode_enc_rsp,
[LL_START_ENC_REQ] = helper_pdu_encode_start_enc_req,
[LL_START_ENC_RSP] = helper_pdu_encode_start_enc_rsp,
[LL_PAUSE_ENC_REQ] = helper_pdu_encode_pause_enc_req,
[LL_PAUSE_ENC_RSP] = helper_pdu_encode_pause_enc_rsp,
[LL_PHY_REQ] = helper_pdu_encode_phy_req,
[LL_PHY_RSP] = helper_pdu_encode_phy_rsp,
[LL_PHY_UPDATE_IND] = helper_pdu_encode_phy_update_ind,
[LL_UNKNOWN_RSP] = helper_pdu_encode_unknown_rsp,
[LL_CONNECTION_UPDATE_IND] = helper_pdu_encode_conn_update_ind,
[LL_CONNECTION_PARAM_REQ] = helper_pdu_encode_conn_param_req,
[LL_CONNECTION_PARAM_RSP] = helper_pdu_encode_conn_param_rsp,
[LL_TERMINATE_IND] = helper_pdu_encode_terminate_ind,
[LL_CHAN_MAP_UPDATE_IND] = helper_pdu_encode_channel_map_update_ind,
[LL_LENGTH_REQ] = helper_pdu_encode_length_req,
[LL_LENGTH_RSP] = helper_pdu_encode_length_rsp,
[LL_CTE_REQ] = helper_pdu_encode_cte_req,
[LL_CTE_RSP] = helper_pdu_encode_cte_rsp,
};
helper_pdu_verify_func_t *const helper_pdu_verify[] = {
[LL_VERSION_IND] = helper_pdu_verify_version_ind,
[LL_LE_PING_REQ] = helper_pdu_verify_ping_req,
[LL_LE_PING_RSP] = helper_pdu_verify_ping_rsp,
[LL_FEATURE_REQ] = helper_pdu_verify_feature_req,
[LL_PERIPH_FEAT_XCHG] = helper_pdu_verify_slave_feature_req,
[LL_FEATURE_RSP] = helper_pdu_verify_feature_rsp,
[LL_MIN_USED_CHANS_IND] = helper_pdu_verify_min_used_chans_ind,
[LL_REJECT_IND] = helper_pdu_verify_reject_ind,
[LL_REJECT_EXT_IND] = helper_pdu_verify_reject_ext_ind,
[LL_ENC_REQ] = helper_pdu_verify_enc_req,
[LL_ENC_RSP] = helper_pdu_verify_enc_rsp,
[LL_START_ENC_REQ] = helper_pdu_verify_start_enc_req,
[LL_START_ENC_RSP] = helper_pdu_verify_start_enc_rsp,
[LL_PAUSE_ENC_REQ] = helper_pdu_verify_pause_enc_req,
[LL_PAUSE_ENC_RSP] = helper_pdu_verify_pause_enc_rsp,
[LL_PHY_REQ] = helper_pdu_verify_phy_req,
[LL_PHY_RSP] = helper_pdu_verify_phy_rsp,
[LL_PHY_UPDATE_IND] = helper_pdu_verify_phy_update_ind,
[LL_UNKNOWN_RSP] = helper_pdu_verify_unknown_rsp,
[LL_CONNECTION_UPDATE_IND] = helper_pdu_verify_conn_update_ind,
[LL_CONNECTION_PARAM_REQ] = helper_pdu_verify_conn_param_req,
[LL_CONNECTION_PARAM_RSP] = helper_pdu_verify_conn_param_rsp,
[LL_TERMINATE_IND] = helper_pdu_verify_terminate_ind,
[LL_CHAN_MAP_UPDATE_IND] = helper_pdu_verify_channel_map_update_ind,
[LL_LENGTH_REQ] = helper_pdu_verify_length_req,
[LL_LENGTH_RSP] = helper_pdu_verify_length_rsp,
[LL_CTE_REQ] = helper_pdu_verify_cte_req,
[LL_CTE_RSP] = helper_pdu_verify_cte_rsp,
};
helper_pdu_ntf_verify_func_t *const helper_pdu_ntf_verify[] = {
[LL_VERSION_IND] = NULL,
[LL_LE_PING_REQ] = NULL,
[LL_LE_PING_RSP] = NULL,
[LL_FEATURE_REQ] = NULL,
[LL_PERIPH_FEAT_XCHG] = NULL,
[LL_FEATURE_RSP] = NULL,
[LL_MIN_USED_CHANS_IND] = NULL,
[LL_REJECT_IND] = NULL,
[LL_REJECT_EXT_IND] = NULL,
[LL_ENC_REQ] = helper_pdu_ntf_verify_enc_req,
[LL_ENC_RSP] = NULL,
[LL_START_ENC_REQ] = NULL,
[LL_START_ENC_RSP] = NULL,
[LL_PHY_REQ] = NULL,
[LL_PHY_RSP] = NULL,
[LL_PHY_UPDATE_IND] = NULL,
[LL_UNKNOWN_RSP] = NULL,
[LL_CONNECTION_UPDATE_IND] = NULL,
[LL_CONNECTION_PARAM_REQ] = NULL,
[LL_CONNECTION_PARAM_RSP] = NULL,
[LL_TERMINATE_IND] = NULL,
[LL_CHAN_MAP_UPDATE_IND] = NULL,
[LL_LENGTH_REQ] = NULL,
[LL_LENGTH_RSP] = NULL,
[LL_CTE_REQ] = NULL,
/* TODO (ppryga): Add verification for RSP notification */
[LL_CTE_RSP] = NULL,
};
helper_node_encode_func_t *const helper_node_encode[] = {
[LL_VERSION_IND] = NULL,
[LL_LE_PING_REQ] = NULL,
[LL_LE_PING_RSP] = NULL,
[LL_FEATURE_REQ] = NULL,
[LL_PERIPH_FEAT_XCHG] = NULL,
[LL_FEATURE_RSP] = NULL,
[LL_MIN_USED_CHANS_IND] = NULL,
[LL_REJECT_IND] = NULL,
[LL_REJECT_EXT_IND] = NULL,
[LL_ENC_REQ] = NULL,
[LL_ENC_RSP] = NULL,
[LL_START_ENC_REQ] = NULL,
[LL_START_ENC_RSP] = NULL,
[LL_PHY_REQ] = NULL,
[LL_PHY_RSP] = NULL,
[LL_PHY_UPDATE_IND] = NULL,
[LL_UNKNOWN_RSP] = NULL,
[LL_CONNECTION_UPDATE_IND] = NULL,
[LL_CONNECTION_PARAM_REQ] = NULL,
[LL_CONNECTION_PARAM_RSP] = NULL,
[LL_TERMINATE_IND] = NULL,
[LL_CHAN_MAP_UPDATE_IND] = NULL,
[LL_CTE_REQ] = NULL,
[LL_CTE_RSP] = helper_node_encode_cte_rsp,
};
helper_node_verify_func_t *const helper_node_verify[] = {
[NODE_PHY_UPDATE] = helper_node_verify_phy_update,
[NODE_CONN_UPDATE] = helper_node_verify_conn_update,
[NODE_ENC_REFRESH] = helper_node_verify_enc_refresh,
[NODE_CTE_RSP] = helper_node_verify_cte_rsp,
};
/*
* for debugging purpose only
*/
void test_print_conn(struct ll_conn *conn)
{
printf("------------------>\n");
printf("Mock structure\n");
printf(" Role: %d\n", conn->lll.role);
printf(" event-count: %d\n", conn->lll.event_counter);
printf("LLCP structure\n");
printf(" Local state: %d\n", conn->llcp.local.state);
printf(" Remote state: %d\n", conn->llcp.remote.state);
printf(" Collision: %d\n", conn->llcp.remote.collision);
printf(" Reject: %d\n", conn->llcp.remote.reject_opcode);
printf("--------------------->\n");
}
void test_setup(struct ll_conn *conn)
{
ull_conn_init();
/**/
memset(conn, 0x00, sizeof(*conn));
/* Initialize the upper test rx queue */
sys_slist_init(&ut_rx_q);
/* Initialize the lower tester tx queue */
sys_slist_init(&lt_tx_q);
/* Initialize the control procedure code */
ull_cp_init();
/* Initialize the ULL TX Q */
ull_tx_q_init(&conn->tx_q);
/* Initialize the connection object */
ull_llcp_init(conn);
ll_reset();
conn->lll.event_counter = 0;
event_active = 0;
lazy = 0;
}
void test_set_role(struct ll_conn *conn, uint8_t role)
{
conn->lll.role = role;
}
void event_prepare(struct ll_conn *conn)
{
struct lll_conn *lll;
/* Can only be called with no active event */
zassert_equal(event_active, 0, "Called inside an active event");
event_active = 1;
/*** ULL Prepare ***/
/* Handle any LL Control Procedures */
ull_cp_run(conn);
/*** LLL Prepare ***/
lll = &conn->lll;
/* Save the latency for use in event */
lll->latency_prepare += lll->latency;
/* Calc current event counter value */
uint16_t event_counter = lll->event_counter + lll->latency_prepare;
/* Store the next event counter value */
lll->event_counter = event_counter + 1;
lll->latency_prepare = 0;
/* Rest lazy */
lazy = 0;
}
void event_tx_ack(struct ll_conn *conn, struct node_tx *tx)
{
/* Can only be called with active event */
zassert_equal(event_active, 1, "Called outside an active event");
ull_cp_tx_ack(conn, tx);
}
void event_done(struct ll_conn *conn)
{
struct node_rx_pdu *rx;
/* Can only be called with active event */
zassert_equal(event_active, 1, "Called outside an active event");
event_active = 0;
while ((rx = (struct node_rx_pdu *)sys_slist_get(&lt_tx_q))) {
ull_cp_rx(conn, rx);
free(rx);
}
}
uint16_t event_counter(struct ll_conn *conn)
{
/* TODO(thoh): Mocked lll_conn */
struct lll_conn *lll;
uint16_t event_counter;
/**/
lll = &conn->lll;
/* Calculate current event counter */
event_counter = lll->event_counter + lll->latency_prepare + lazy;
/* If event_counter is called inside an event_prepare()/event_done() pair
* return the current event counter value (i.e. -1);
* otherwise return the next event counter value
*/
if (event_active)
event_counter--;
return event_counter;
}
void lt_tx_real(const char *file, uint32_t line, enum helper_pdu_opcode opcode,
struct ll_conn *conn, void *param)
{
struct pdu_data *pdu;
struct node_rx_pdu *rx;
rx = malloc(PDU_RX_NODE_SIZE);
zassert_not_null(rx, "Out of memory.\nCalled at %s:%d\n", file, line);
/* Encode node_rx_pdu if required by particular procedure */
if (helper_node_encode[opcode]) {
helper_node_encode[opcode](rx, param);
}
pdu = (struct pdu_data *)rx->pdu;
zassert_not_null(helper_pdu_encode[opcode], "PDU encode function cannot be NULL\n");
helper_pdu_encode[opcode](pdu, param);
sys_slist_append(&lt_tx_q, (sys_snode_t *)rx);
}
void lt_rx_real(const char *file, uint32_t line, enum helper_pdu_opcode opcode,
struct ll_conn *conn, struct node_tx **tx_ref, void *param)
{
struct node_tx *tx;
struct pdu_data *pdu;
tx = ull_tx_q_dequeue(&conn->tx_q);
zassert_not_null(tx, "Tx Q empty.\nCalled at %s:%d\n", file, line);
pdu = (struct pdu_data *)tx->pdu;
if (helper_pdu_verify[opcode]) {
helper_pdu_verify[opcode](file, line, pdu, param);
}
*tx_ref = tx;
}
void lt_rx_q_is_empty_real(const char *file, uint32_t line, struct ll_conn *conn)
{
struct node_tx *tx;
tx = ull_tx_q_dequeue(&conn->tx_q);
zassert_is_null(tx, "Tx Q not empty.\nCalled at %s:%d\n", file, line);
}
void ut_rx_pdu_real(const char *file, uint32_t line, enum helper_pdu_opcode opcode,
struct node_rx_pdu **ntf_ref, void *param)
{
struct pdu_data *pdu;
struct node_rx_pdu *ntf;
ntf = (struct node_rx_pdu *)sys_slist_get(&ut_rx_q);
zassert_not_null(ntf, "Ntf Q empty.\nCalled at %s:%d\n", file, line);
zassert_equal(ntf->hdr.type, NODE_RX_TYPE_DC_PDU,
"Ntf node is of the wrong type.\nCalled at %s:%d\n", file, line);
pdu = (struct pdu_data *)ntf->pdu;
if (helper_pdu_ntf_verify[opcode]) {
helper_pdu_ntf_verify[opcode](file, line, pdu, param);
} else if (helper_pdu_verify[opcode]) {
helper_pdu_verify[opcode](file, line, pdu, param);
}
*ntf_ref = ntf;
}
void ut_rx_node_real(const char *file, uint32_t line, enum helper_node_opcode opcode,
struct node_rx_pdu **ntf_ref, void *param)
{
struct node_rx_pdu *ntf;
ntf = (struct node_rx_pdu *)sys_slist_get(&ut_rx_q);
zassert_not_null(ntf, "Ntf Q empty.\nCalled at %s:%d\n", file, line);
zassert_not_equal(ntf->hdr.type, NODE_RX_TYPE_DC_PDU,
"Ntf node is of the wrong type.\nCalled at %s:%d\n", file, line);
if (helper_node_verify[opcode]) {
helper_node_verify[opcode](file, line, ntf, param);
}
*ntf_ref = ntf;
}
void ut_rx_q_is_empty_real(const char *file, uint32_t line)
{
struct node_rx_pdu *ntf;
ntf = (struct node_rx_pdu *)sys_slist_get(&ut_rx_q);
zassert_is_null(ntf, "Ntf Q not empty.\nCalled at %s:%d\n", file, line);
}

View file

@ -0,0 +1,16 @@
# SPDX-License-Identifier: Apache-2.0
cmake_minimum_required(VERSION 3.13.1)
if (NOT BOARD STREQUAL unit_testing)
message(FATAL_ERROR "This project can only be used with '-DBOARD=unit_testing'.")
endif()
FILE(GLOB SOURCES
src/*.c
)
project(bluetooth_ull_llcp_api)
find_package(ZephyrUnittest HINTS $ENV{ZEPHYR_BASE})
include(${ZEPHYR_BASE}/tests/bluetooth/controller/common/defaults_cmake.txt)
target_sources(testbinary PRIVATE ${ll_sw_sources} ${mock_sources} ${common_sources})

View file

@ -0,0 +1,213 @@
/*
* Copyright (c) 2020 Demant
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/types.h>
#include <ztest.h>
#include "kconfig.h"
/* Kconfig Cheats */
#include <bluetooth/hci.h>
#include <sys/byteorder.h>
#include <sys/slist.h>
#include <sys/util.h>
#include "hal/ccm.h"
#include "util/util.h"
#include "util/mem.h"
#include "util/memq.h"
#include "pdu.h"
#include "ll.h"
#include "ll_settings.h"
#include "ll_feat.h"
#include "lll.h"
#include "lll_df_types.h"
#include "lll_conn.h"
#include "ull_tx_queue.h"
#include "ull_conn_types.h"
#include "ull_llcp.h"
#include "ull_llcp_internal.h"
#include "helper_pdu.h"
#include "helper_util.h"
struct ll_conn conn;
/*
* Note API and internal test are not yet split out here
*/
void test_api_init(void)
{
ull_cp_init();
ull_tx_q_init(&conn.tx_q);
ull_llcp_init(&conn);
zassert_true(lr_is_disconnected(&conn), NULL);
zassert_true(rr_is_disconnected(&conn), NULL);
}
extern void test_int_mem_proc_ctx(void);
extern void test_int_mem_tx(void);
extern void test_int_create_proc(void);
extern void test_int_local_pending_requests(void);
extern void test_int_remote_pending_requests(void);
void test_api_connect(void)
{
ull_cp_init();
ull_tx_q_init(&conn.tx_q);
ull_llcp_init(&conn);
ull_cp_state_set(&conn, ULL_CP_CONNECTED);
zassert_true(lr_is_idle(&conn), NULL);
zassert_true(rr_is_idle(&conn), NULL);
}
void test_api_disconnect(void)
{
ull_cp_init();
ull_tx_q_init(&conn.tx_q);
ull_llcp_init(&conn);
ull_cp_state_set(&conn, ULL_CP_DISCONNECTED);
zassert_true(lr_is_disconnected(&conn), NULL);
zassert_true(rr_is_disconnected(&conn), NULL);
ull_cp_state_set(&conn, ULL_CP_CONNECTED);
zassert_true(lr_is_idle(&conn), NULL);
zassert_true(rr_is_idle(&conn), NULL);
ull_cp_state_set(&conn, ULL_CP_DISCONNECTED);
zassert_true(lr_is_disconnected(&conn), NULL);
zassert_true(rr_is_disconnected(&conn), NULL);
}
void test_int_disconnect_loc(void)
{
uint64_t err;
int nr_free_ctx;
struct node_tx *tx;
struct ll_conn conn;
struct pdu_data_llctrl_version_ind local_version_ind = {
.version_number = LL_VERSION_NUMBER,
.company_id = CONFIG_BT_CTLR_COMPANY_ID,
.sub_version_number = CONFIG_BT_CTLR_SUBVERSION_NUMBER,
};
test_setup(&conn);
test_set_role(&conn, BT_HCI_ROLE_CENTRAL);
ull_cp_state_set(&conn, ULL_CP_CONNECTED);
nr_free_ctx = ctx_buffers_free();
zassert_equal(nr_free_ctx, CONFIG_BT_CTLR_LLCP_PROC_CTX_BUF_NUM, NULL);
err = ull_cp_version_exchange(&conn);
zassert_equal(err, BT_HCI_ERR_SUCCESS, NULL);
nr_free_ctx = ctx_buffers_free();
zassert_equal(nr_free_ctx, CONFIG_BT_CTLR_LLCP_PROC_CTX_BUF_NUM - 1, NULL);
event_prepare(&conn);
lt_rx(LL_VERSION_IND, &conn, &tx, &local_version_ind);
lt_rx_q_is_empty(&conn);
event_done(&conn);
/*
* Now we disconnect before getting a response
*/
ull_cp_state_set(&conn, ULL_CP_DISCONNECTED);
nr_free_ctx = ctx_buffers_free();
zassert_equal(nr_free_ctx, CONFIG_BT_CTLR_LLCP_PROC_CTX_BUF_NUM, NULL);
ut_rx_q_is_empty();
/*
* nothing should happen when running a new event
*/
event_prepare(&conn);
event_done(&conn);
nr_free_ctx = ctx_buffers_free();
zassert_equal(nr_free_ctx, CONFIG_BT_CTLR_LLCP_PROC_CTX_BUF_NUM, NULL);
/*
* all buffers should still be empty
*/
lt_rx_q_is_empty(&conn);
ut_rx_q_is_empty();
}
void test_int_disconnect_rem(void)
{
int nr_free_ctx;
struct pdu_data_llctrl_version_ind remote_version_ind = {
.version_number = 0x55,
.company_id = 0xABCD,
.sub_version_number = 0x1234,
};
struct ll_conn conn;
test_setup(&conn);
/* Role */
test_set_role(&conn, BT_HCI_ROLE_CENTRAL);
/* Connect */
ull_cp_state_set(&conn, ULL_CP_CONNECTED);
nr_free_ctx = ctx_buffers_free();
zassert_equal(nr_free_ctx, CONFIG_BT_CTLR_LLCP_PROC_CTX_BUF_NUM, NULL);
/* Prepare */
event_prepare(&conn);
/* Rx */
lt_tx(LL_VERSION_IND, &conn, &remote_version_ind);
nr_free_ctx = ctx_buffers_free();
zassert_equal(nr_free_ctx, CONFIG_BT_CTLR_LLCP_PROC_CTX_BUF_NUM, NULL);
/* Disconnect before we reply */
/* Done */
event_done(&conn);
ull_cp_state_set(&conn, ULL_CP_DISCONNECTED);
/* Prepare */
event_prepare(&conn);
/* Done */
event_done(&conn);
nr_free_ctx = ctx_buffers_free();
zassert_equal(nr_free_ctx, CONFIG_BT_CTLR_LLCP_PROC_CTX_BUF_NUM, NULL);
/* There should not be a host notifications */
ut_rx_q_is_empty();
}
void test_main(void)
{
ztest_test_suite(internal, ztest_unit_test(test_int_mem_proc_ctx),
ztest_unit_test(test_int_mem_tx), ztest_unit_test(test_int_create_proc),
ztest_unit_test(test_int_local_pending_requests),
ztest_unit_test(test_int_remote_pending_requests),
ztest_unit_test(test_int_disconnect_loc),
ztest_unit_test(test_int_disconnect_rem));
ztest_test_suite(public, ztest_unit_test(test_api_init), ztest_unit_test(test_api_connect),
ztest_unit_test(test_api_disconnect));
ztest_run_test_suite(internal);
ztest_run_test_suite(public);
}

View file

@ -0,0 +1,5 @@
common:
tags: test_framework bluetooth bt_api bt_ull_llcp
tests:
bluetooth.controller.ctrl_api.test:
type: unit

View file

@ -0,0 +1,16 @@
# SPDX-License-Identifier: Apache-2.0
cmake_minimum_required(VERSION 3.13.1)
if (NOT BOARD STREQUAL unit_testing)
message(FATAL_ERROR "This project can only be used with '-DBOARD=unit_testing'.")
endif()
FILE(GLOB SOURCES
src/*.c
)
project(bluetooth_ctrl_ull_conn)
find_package(ZephyrUnittest HINTS $ENV{ZEPHYR_BASE})
include(${ZEPHYR_BASE}/tests/bluetooth/controller/common/defaults_cmake.txt)
target_sources(testbinary PRIVATE ${ll_sw_sources} ${mock_sources} ${common_sources})

View file

@ -0,0 +1,228 @@
/*
* Copyright (c) 2020 Demant
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/types.h>
#include <ztest.h>
#include "kconfig.h"
#define ULL_LLCP_UNITTEST
#include <bluetooth/hci.h>
#include <sys/byteorder.h>
#include <sys/slist.h>
#include <sys/util.h>
#include "hal/ccm.h"
#include "util/util.h"
#include "util/mem.h"
#include "util/memq.h"
#include "pdu.h"
#include "ll.h"
#include "ll_settings.h"
#include "lll.h"
#include "lll_df_types.h"
#include "lll_conn.h"
#include "ull_tx_queue.h"
#include "ull_conn_types.h"
#include "ull_llcp.h"
#include "ull_conn_internal.h"
#include "ull_llcp_internal.h"
#include "helper_pdu.h"
#include "helper_util.h"
static struct ll_conn conn;
static void setup(void)
{
test_setup(&conn);
}
static bool is_instant_reached(struct ll_conn *conn, uint16_t instant)
{
return ((event_counter(conn) - instant) & 0xFFFF) <= 0x7FFF;
}
void test_channel_map_update_mas_loc(void)
{
uint8_t chm[5] = { 0x00, 0x04, 0x05, 0x06, 0x00 };
/* TODO should test setup set this to valid value? */
uint8_t defchm[5] = {};
uint8_t err;
struct node_tx *tx;
struct pdu_data *pdu;
uint16_t instant;
struct pdu_data_llctrl_chan_map_ind chmu_ind = {
.instant = 6,
.chm = { 0x00, 0x04, 0x05, 0x06, 0x00 },
};
/* Role */
test_set_role(&conn, BT_HCI_ROLE_CENTRAL);
/* Connect */
ull_cp_state_set(&conn, ULL_CP_CONNECTED);
err = ull_cp_chan_map_update(&conn, chm);
zassert_equal(err, BT_HCI_ERR_SUCCESS, NULL);
/* Prepare */
event_prepare(&conn);
/* Tx Queue should have one LL Control PDU */
lt_rx(LL_CHAN_MAP_UPDATE_IND, &conn, &tx, &chmu_ind);
lt_rx_q_is_empty(&conn);
/* Done */
event_done(&conn);
/* Save Instant */
pdu = (struct pdu_data *)tx->pdu;
instant = sys_le16_to_cpu(pdu->llctrl.chan_map_ind.instant);
/* Release Tx */
ull_cp_release_tx(&conn, tx);
/* spin conn events */
while (!is_instant_reached(&conn, instant)) {
/* Prepare */
event_prepare(&conn);
/* Tx Queue should NOT have a LL Control PDU */
lt_rx_q_is_empty(&conn);
/* Done */
event_done(&conn);
/* There should NOT be a host notification */
ut_rx_q_is_empty();
/* check if using old channel map */
zassert_mem_equal(conn.lll.data_chan_map, defchm, sizeof(conn.lll.data_chan_map),
"Channel map invalid");
}
/* Prepare */
event_prepare(&conn);
/* Tx Queue should NOT have a LL Control PDU */
lt_rx_q_is_empty(&conn);
/* Done */
event_done(&conn);
/* There should be no host notification */
ut_rx_q_is_empty();
/* at this point new channel map shall be in use */
zassert_mem_equal(conn.lll.data_chan_map, chm, sizeof(conn.lll.data_chan_map),
"Channel map invalid");
zassert_equal(ctx_buffers_free(), CONFIG_BT_CTLR_LLCP_PROC_CTX_BUF_NUM,
"Free CTX buffers %d", ctx_buffers_free());
}
void test_channel_map_update_sla_rem(void)
{
uint8_t chm[5] = { 0x00, 0x04, 0x05, 0x06, 0x00 };
/* TODO should test setup set this to valid value? */
uint8_t defchm[5] = {};
struct pdu_data_llctrl_chan_map_ind chmu_ind = {
.instant = 6,
.chm = { 0x00, 0x04, 0x05, 0x06, 0x00 },
};
uint16_t instant = 6;
/* Role */
test_set_role(&conn, BT_HCI_ROLE_PERIPHERAL);
/* Connect */
ull_cp_state_set(&conn, ULL_CP_CONNECTED);
/* Prepare */
event_prepare(&conn);
/* Tx Queue should NOT have a LL Control PDU */
lt_rx_q_is_empty(&conn);
/* RX */
lt_tx(LL_CHAN_MAP_UPDATE_IND, &conn, &chmu_ind);
/* Done */
event_done(&conn);
/* spin conn events */
while (!is_instant_reached(&conn, instant)) {
/* Prepare */
event_prepare(&conn);
/* Tx Queue should NOT have a LL Control PDU */
lt_rx_q_is_empty(&conn);
/* Done */
event_done(&conn);
/* There should NOT be a host notification */
ut_rx_q_is_empty();
/* check if using old channel map */
zassert_mem_equal(conn.lll.data_chan_map, defchm, sizeof(conn.lll.data_chan_map),
"Channel map invalid");
}
/* Prepare */
event_prepare(&conn);
/* Tx Queue should NOT have a LL Control PDU */
lt_rx_q_is_empty(&conn);
/* Done */
event_done(&conn);
/* There should be no host notification */
ut_rx_q_is_empty();
/* at this point new channel map shall be in use */
zassert_mem_equal(conn.lll.data_chan_map, chm, sizeof(conn.lll.data_chan_map),
"Channel map invalid");
zassert_equal(ctx_buffers_free(), CONFIG_BT_CTLR_LLCP_PROC_CTX_BUF_NUM,
"Free CTX buffers %d", ctx_buffers_free());
}
void test_channel_map_update_sla_loc(void)
{
uint8_t err;
uint8_t chm[5] = { 0x00, 0x06, 0x06, 0x06, 0x00 };
/* Role */
test_set_role(&conn, BT_HCI_ROLE_PERIPHERAL);
/* Connect */
ull_cp_state_set(&conn, ULL_CP_CONNECTED);
err = ull_cp_chan_map_update(&conn, chm);
zassert_equal(err, BT_HCI_ERR_CMD_DISALLOWED, NULL);
zassert_equal(ctx_buffers_free(), CONFIG_BT_CTLR_LLCP_PROC_CTX_BUF_NUM,
"Free CTX buffers %d", ctx_buffers_free());
}
void test_main(void)
{
ztest_test_suite(chmu,
ztest_unit_test_setup_teardown(test_channel_map_update_mas_loc, setup,
unit_test_noop),
ztest_unit_test_setup_teardown(test_channel_map_update_sla_rem, setup,
unit_test_noop),
ztest_unit_test_setup_teardown(test_channel_map_update_sla_loc, setup,
unit_test_noop));
ztest_run_test_suite(chmu);
}

View file

@ -0,0 +1,5 @@
common:
tags: test_framework bluetooth bt_chmu bt_ull_llcp
tests:
bluetooth.controller.ctrl_chmu.test:
type: unit

View file

@ -0,0 +1,16 @@
# SPDX-License-Identifier: Apache-2.0
cmake_minimum_required(VERSION 3.13.1)
if (NOT BOARD STREQUAL unit_testing)
message(FATAL_ERROR "This project can only be used with '-DBOARD=unit_testing'.")
endif()
FILE(GLOB SOURCES
src/*.c
)
project(bluetooth_ctrl_ull_conn)
find_package(ZephyrUnittest HINTS $ENV{ZEPHYR_BASE})
include(${ZEPHYR_BASE}/tests/bluetooth/controller/common/defaults_cmake.txt)
target_sources(testbinary PRIVATE ${ll_sw_sources} ${mock_sources} ${common_sources})

View file

@ -0,0 +1,13 @@
/*
* Copyright (c) 2021 Demant
*
* SPDX-License-Identifier: Apache-2.0
*/
/*
* Override common Kconfig settings
*/
#ifdef CONFIG_BT_CTLR_CONN_PARAM_REQ
#undef CONFIG_BT_CTLR_CONN_PARAM_REQ
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,9 @@
common:
tags: test_framework bluetooth bt_conn_update bt_conn_param_req bt_ull_llcp
tests:
bluetooth.controller.ctrl_conn_update.test:
type: unit
bluetooth.controller.ctrl_conn_update.no_param_req_test:
type: unit
extra_args: KCONFIG_OVERRIDE_FILE="kconfig_override.h"

View file

@ -0,0 +1,20 @@
#
# Copyright (c) 2021 Nordic Semiconductor ASA
#
# SPDX-License-Identifier: Apache-2.0
cmake_minimum_required(VERSION 3.13.1)
if (NOT BOARD STREQUAL unit_testing)
message(FATAL_ERROR "This project can only be used with '-DBOARD=unit_testing'.")
endif()
FILE(GLOB SOURCES
src/*.c
)
project(bluetooth_ull_llcp_le_cte_req)
find_package(ZephyrUnittest HINTS $ENV{ZEPHYR_BASE})
include(${ZEPHYR_BASE}/tests/bluetooth/controller/common/defaults_cmake.txt)
target_sources(testbinary PRIVATE ${ll_sw_sources} ${mock_sources} ${common_sources})
set(CMAKE_BUILD_TYPE Debug)

View file

@ -0,0 +1,626 @@
/*
* Copyright (c) 2021 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/types.h>
#include <ztest.h>
#include "kconfig.h"
#include <bluetooth/hci.h>
#include <sys/byteorder.h>
#include <sys/slist.h>
#include <sys/util.h>
#include "hal/ccm.h"
#include "util/util.h"
#include "util/mem.h"
#include "util/memq.h"
#include "pdu.h"
#include "ll.h"
#include "ll_settings.h"
#include "lll.h"
#include "lll_df_types.h"
#include "lll_conn.h"
#include "ull_tx_queue.h"
#include "ull_conn_types.h"
#include "ull_llcp.h"
#include "ull_conn_internal.h"
#include "ull_llcp_internal.h"
#include "helper_pdu.h"
#include "helper_util.h"
struct ll_conn conn;
static void setup(void)
{
test_setup(&conn);
}
/* Tests of successful execution of CTE Request Procedure */
/* +-----+ +-------+ +-----+
* | UT | | LL_A | | LT |
* +-----+ +-------+ +-----+
* | | |
* | Start initiation | |
* | CTE Reqest Proc. | |
* |--------------------------->| |
* | | |
* | | LL_LE_CTE_REQ |
* | |------------------>|
* | | |
* | | LL_LE_CTE_RSP |
* | |<------------------|
* | | |
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* | | |
* | LE Connection IQ Report | |
* |<---------------------------| |
* | | |
* | | |
*/
void test_cte_req_central_local(void)
{
uint8_t err;
struct node_tx *tx;
struct pdu_data_llctrl_cte_req local_cte_req = {
.cte_type_req = BT_HCI_LE_AOA_CTE,
.min_cte_len_req = BT_HCI_LE_CTE_LEN_MIN,
};
struct pdu_data_llctrl_cte_rsp remote_cte_rsp = {};
struct node_rx_pdu *ntf;
/* Role */
test_set_role(&conn, BT_HCI_ROLE_PERIPHERAL);
/* Connect */
ull_cp_state_set(&conn, ULL_CP_CONNECTED);
/* Initiate an CTE Request Procedure */
err = ull_cp_cte_req(&conn, local_cte_req.min_cte_len_req, local_cte_req.cte_type_req);
zassert_equal(err, BT_HCI_ERR_SUCCESS, NULL);
/* Prepare */
event_prepare(&conn);
/* Tx Queue should have one LL Control PDU */
lt_rx(LL_CTE_REQ, &conn, &tx, &local_cte_req);
lt_rx_q_is_empty(&conn);
/* Rx */
lt_tx(LL_CTE_RSP, &conn, &remote_cte_rsp);
/* Done */
event_done(&conn);
/* Receive notification of sampled CTE response */
ut_rx_pdu(LL_CTE_RSP, &ntf, &remote_cte_rsp);
/* There should not be a host notifications */
ut_rx_q_is_empty();
/* Release tx node */
ull_cp_release_tx(&conn, tx);
zassert_equal(ctx_buffers_free(), CONFIG_BT_CTLR_LLCP_PROC_CTX_BUF_NUM,
"Free CTX buffers %d", ctx_buffers_free());
}
/* +-----+ +-------+ +-----+
* | UT | | LL_A | | LT |
* +-----+ +-------+ +-----+
* | | |
* | Start initiator | |
* | CTE Reqest Proc. | |
* |--------------------------->| |
* | | |
* | | LL_LE_CTE_REQ |
* | |------------------>|
* | | |
* | | LL_LE_CTE_RSP |
* | |<------------------|
* | | |
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* | | |
* | LE Connection IQ Report | |
* |<---------------------------| |
* | | |
* | | |
*/
void test_cte_req_peripheral_local(void)
{
uint8_t err;
struct node_tx *tx;
struct pdu_data_llctrl_cte_req local_cte_req = {
.cte_type_req = BT_HCI_LE_AOA_CTE,
.min_cte_len_req = BT_HCI_LE_CTE_LEN_MIN,
};
struct pdu_data_llctrl_cte_rsp remote_cte_rsp = {};
struct node_rx_pdu *ntf;
/* Role */
test_set_role(&conn, BT_HCI_ROLE_PERIPHERAL);
/* Connect */
ull_cp_state_set(&conn, ULL_CP_CONNECTED);
/* Initiate an CTE Request Procedure */
err = ull_cp_cte_req(&conn, local_cte_req.min_cte_len_req, local_cte_req.cte_type_req);
zassert_equal(err, BT_HCI_ERR_SUCCESS, NULL);
/* Prepare */
event_prepare(&conn);
/* Tx Queue should have one LL Control PDU */
lt_rx(LL_CTE_REQ, &conn, &tx, &local_cte_req);
lt_rx_q_is_empty(&conn);
/* Rx */
lt_tx(LL_CTE_RSP, &conn, &remote_cte_rsp);
/* Done */
event_done(&conn);
/* Receive notification of sampled CTE response */
ut_rx_pdu(LL_CTE_RSP, &ntf, &remote_cte_rsp);
/* Release tx node */
ull_cp_release_tx(&conn, tx);
/* There should not be a host notifications */
ut_rx_q_is_empty();
zassert_equal(ctx_buffers_free(), CONFIG_BT_CTLR_LLCP_PROC_CTX_BUF_NUM,
"Free CTX buffers %d", ctx_buffers_free());
}
/* +-----+ +-------+ +-----+
* | UT | | LL_A | | LT |
* +-----+ +-------+ +-----+
* | | |
* | Start responder | |
* | CTE Reqest Proc. | |
* |--------------------------->| |
* | | |
* | | LL_LE_CTE_REQ |
* | |<------------------|
* | | |
* | | LL_LE_CTE_RSP |
* | |------------------>|
* | | |
* | | |
*/
void test_cte_req_central_remote(void)
{
struct node_tx *tx;
struct pdu_data_llctrl_cte_req local_cte_req = {
.cte_type_req = BT_HCI_LE_AOA_CTE,
.min_cte_len_req = BT_HCI_LE_CTE_LEN_MIN,
};
struct pdu_data_llctrl_cte_rsp remote_cte_rsp = {};
/* Role */
test_set_role(&conn, BT_HCI_ROLE_CENTRAL);
/* Connect */
ull_cp_state_set(&conn, ULL_CP_CONNECTED);
/* Enable response for CTE request */
ull_cp_cte_rsp_enable(&conn, true, BT_HCI_LE_CTE_LEN_MAX,
(BT_HCI_LE_AOA_CTE | BT_HCI_LE_AOD_CTE_1US | BT_HCI_LE_AOD_CTE_2US));
/* Prepare */
event_prepare(&conn);
/* Tx */
lt_tx(LL_CTE_REQ, &conn, &local_cte_req);
/* Done */
event_done(&conn);
/* Prepare */
event_prepare(&conn);
/* Tx Queue should have one LL Control PDU */
lt_rx(LL_CTE_RSP, &conn, &tx, &remote_cte_rsp);
lt_rx_q_is_empty(&conn);
/* Done */
event_done(&conn);
/* Release tx node */
ull_cp_release_tx(&conn, tx);
/* There should not be a host notifications */
ut_rx_q_is_empty();
zassert_equal(ctx_buffers_free(), CONFIG_BT_CTLR_LLCP_PROC_CTX_BUF_NUM,
"Free CTX buffers %d", ctx_buffers_free());
}
/* +-----+ +-------+ +-----+
* | UT | | LL_A | | LT |
* +-----+ +-------+ +-----+
* | | |
* | Start responder | |
* | CTE Reqest Proc . | |
* |--------------------------->| |
* | | |
* | | LL_LE_CTE_REQ |
* | |<------------------|
* | | |
* | | LL_LE_CTE_RSP |
* | |------------------>|
* | | |
* | | |
*/
void test_cte_req_peripheral_remote(void)
{
struct node_tx *tx;
struct pdu_data_llctrl_cte_req local_cte_req = {
.cte_type_req = BT_HCI_LE_AOA_CTE,
.min_cte_len_req = BT_HCI_LE_CTE_LEN_MIN,
};
struct pdu_data_llctrl_cte_rsp remote_cte_rsp = {};
/* Role */
test_set_role(&conn, BT_HCI_ROLE_PERIPHERAL);
/* Connect */
ull_cp_state_set(&conn, ULL_CP_CONNECTED);
/* Enable response for CTE request */
ull_cp_cte_rsp_enable(&conn, true, BT_HCI_LE_CTE_LEN_MAX,
(BT_HCI_LE_AOA_CTE | BT_HCI_LE_AOD_CTE_1US | BT_HCI_LE_AOD_CTE_2US));
/* Prepare */
event_prepare(&conn);
/* Tx */
lt_tx(LL_CTE_REQ, &conn, &local_cte_req);
/* Done */
event_done(&conn);
/* Prepare */
event_prepare(&conn);
/* Tx Queue should have one LL Control PDU */
lt_rx(LL_CTE_RSP, &conn, &tx, &remote_cte_rsp);
lt_rx_q_is_empty(&conn);
/* Done */
event_done(&conn);
/* Release tx node */
ull_cp_release_tx(&conn, tx);
/* There should not be a host notifications */
ut_rx_q_is_empty();
zassert_equal(ctx_buffers_free(), CONFIG_BT_CTLR_LLCP_PROC_CTX_BUF_NUM,
"Free CTX buffers %d", ctx_buffers_free());
}
/* Tests of expected failures during execution of CTE Request Procedure */
/* +-----+ +-------+ +-----+
* | UT | | LL_A | | LT |
* +-----+ +-------+ +-----+
* | | |
* | Start initiation | |
* | CTE Reqest Proc. | |
* |--------------------------->| |
* | | |
* | | LL_LE_CTE_REQ |
* | |------------------------------->|
* | | |
* | | LL_REJECT_EXT_IND |
* | | BT_HCI_ERR_UNSUPP_LL_PARAM_VAL |
* | | or BT_HCI_ERR_INVALID_LL_PARAM |
* | |<-------------------------------|
* | | |
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* | | |
* | LE CTE Request Failed | |
* |<---------------------------| |
* | | |
* | | |
*/
void test_cte_req_rejected_inv_ll_param_central_local(void)
{
uint8_t err;
struct node_tx *tx;
struct pdu_data_llctrl_cte_req local_cte_req = {
.cte_type_req = BT_HCI_LE_AOD_CTE_1US,
.min_cte_len_req = BT_HCI_LE_CTE_LEN_MIN,
};
struct pdu_data_llctrl_reject_ext_ind remote_reject_ext_ind = {
.reject_opcode = PDU_DATA_LLCTRL_TYPE_CTE_REQ,
.error_code = BT_HCI_ERR_UNSUPP_LL_PARAM_VAL,
};
struct node_rx_pdu *ntf;
/* Role */
test_set_role(&conn, BT_HCI_ROLE_CENTRAL);
/* Connect */
ull_cp_state_set(&conn, ULL_CP_CONNECTED);
/* Initiate an CTE Request Procedure */
err = ull_cp_cte_req(&conn, local_cte_req.min_cte_len_req, local_cte_req.cte_type_req);
zassert_equal(err, BT_HCI_ERR_SUCCESS, NULL);
/* Prepare */
event_prepare(&conn);
/* Tx Queue should have one LL Control PDU */
lt_rx(LL_CTE_REQ, &conn, &tx, &local_cte_req);
lt_rx_q_is_empty(&conn);
/* Rx */
lt_tx(LL_REJECT_EXT_IND, &conn, &remote_reject_ext_ind);
/* Done */
event_done(&conn);
/* Receive notification of sampled CTE response */
ut_rx_pdu(LL_REJECT_EXT_IND, &ntf, &remote_reject_ext_ind);
/* There should not be a host notifications */
ut_rx_q_is_empty();
/* Release tx node */
ull_cp_release_tx(&conn, tx);
zassert_equal(ctx_buffers_free(), CONFIG_BT_CTLR_LLCP_PROC_CTX_BUF_NUM,
"Free CTX buffers %d", ctx_buffers_free());
}
/* +-----+ +-------+ +-----+
* | UT | | LL_A | | LT |
* +-----+ +-------+ +-----+
* | | |
* | Start initiation | |
* | CTE Reqest Proc. | |
* |--------------------------->| |
* | | |
* | | LL_LE_CTE_REQ |
* | |------------------------------->|
* | | |
* | | LL_REJECT_EXT_IND |
* | | BT_HCI_ERR_UNSUPP_LL_PARAM_VAL |
* | | or BT_HCI_ERR_INVALID_LL_PARAM |
* | |<-------------------------------|
* | | |
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* | | |
* | LE CTE Request Failed | |
* |<---------------------------| |
* | | |
* | | |
*/
void test_cte_req_rejected_inv_ll_param_peripheral_local(void)
{
uint8_t err;
struct node_tx *tx;
struct pdu_data_llctrl_cte_req local_cte_req = {
.cte_type_req = BT_HCI_LE_AOD_CTE_1US,
.min_cte_len_req = BT_HCI_LE_CTE_LEN_MIN,
};
struct pdu_data_llctrl_reject_ext_ind remote_reject_ext_ind = {
.reject_opcode = PDU_DATA_LLCTRL_TYPE_CTE_REQ,
.error_code = BT_HCI_ERR_UNSUPP_LL_PARAM_VAL,
};
struct node_rx_pdu *ntf;
/* Role */
test_set_role(&conn, BT_HCI_ROLE_PERIPHERAL);
/* Connect */
ull_cp_state_set(&conn, ULL_CP_CONNECTED);
/* Initiate an CTE Request Procedure */
err = ull_cp_cte_req(&conn, local_cte_req.min_cte_len_req, local_cte_req.cte_type_req);
zassert_equal(err, BT_HCI_ERR_SUCCESS, NULL);
/* Prepare */
event_prepare(&conn);
/* Tx Queue should have one LL Control PDU */
lt_rx(LL_CTE_REQ, &conn, &tx, &local_cte_req);
lt_rx_q_is_empty(&conn);
/* Rx */
lt_tx(LL_REJECT_EXT_IND, &conn, &remote_reject_ext_ind);
/* Done */
event_done(&conn);
/* Receive notification of sampled CTE response */
ut_rx_pdu(LL_REJECT_EXT_IND, &ntf, &remote_reject_ext_ind);
/* Release tx node */
ull_cp_release_tx(&conn, tx);
/* There should not be a host notifications */
ut_rx_q_is_empty();
zassert_equal(ctx_buffers_free(), CONFIG_BT_CTLR_LLCP_PROC_CTX_BUF_NUM,
"Free CTX buffers %d", ctx_buffers_free());
}
/* +-----+ +-------+ +-----+
* | UT | | LL_A | | LT |
* +-----+ +-------+ +-----+
* | | |
* | Start initiation | |
* | CTE Reqest Proc. | |
* |--------------------------->| |
* | | |
* | | LL_LE_CTE_REQ |
* | |<-------------------------------|
* | | |
* | | LL_REJECT_EXT_IND |
* | | BT_HCI_ERR_UNSUPP_LL_PARAM_VAL |
* | |------------------------------->|
* | | |
*/
void test_cte_req_reject_inv_ll_param_central_remote(void)
{
struct node_tx *tx;
struct pdu_data_llctrl_cte_req local_cte_req = {
.cte_type_req = BT_HCI_LE_AOD_CTE_2US,
.min_cte_len_req = BT_HCI_LE_CTE_LEN_MIN,
};
struct pdu_data_llctrl_reject_ext_ind remote_reject_ext_ind = {
.reject_opcode = PDU_DATA_LLCTRL_TYPE_CTE_REQ,
.error_code = BT_HCI_ERR_UNSUPP_LL_PARAM_VAL,
};
/* Role */
test_set_role(&conn, BT_HCI_ROLE_CENTRAL);
/* Connect */
ull_cp_state_set(&conn, ULL_CP_CONNECTED);
/* Enable response for CTE request */
ull_cp_cte_rsp_enable(&conn, true, BT_HCI_LE_CTE_LEN_MAX,
(BT_HCI_LE_AOA_CTE | BT_HCI_LE_AOD_CTE_1US));
/* Prepare */
event_prepare(&conn);
/* Tx */
lt_tx(LL_CTE_REQ, &conn, &local_cte_req);
/* Done */
event_done(&conn);
/* Prepare */
event_prepare(&conn);
/* Tx Queue should have one LL Control PDU */
lt_rx(LL_REJECT_EXT_IND, &conn, &tx, &remote_reject_ext_ind);
lt_rx_q_is_empty(&conn);
/* Done */
event_done(&conn);
/* Release tx node */
ull_cp_release_tx(&conn, tx);
/* There should not be a host notifications */
ut_rx_q_is_empty();
zassert_equal(ctx_buffers_free(), CONFIG_BT_CTLR_LLCP_PROC_CTX_BUF_NUM,
"Free CTX buffers %d", ctx_buffers_free());
}
/* +-----+ +-------+ +-----+
* | UT | | LL_A | | LT |
* +-----+ +-------+ +-----+
* | | |
* | Start initiation | |
* | CTE Reqest Proc. | |
* |--------------------------->| |
* | | |
* | | LL_LE_CTE_REQ |
* | |<-------------------------------|
* | | |
* | | LL_REJECT_EXT_IND |
* | | BT_HCI_ERR_UNSUPP_LL_PARAM_VAL |
* | |------------------------------->|
* | | |
*/
void test_cte_req_reject_inv_ll_param_peripheral_remote(void)
{
struct node_tx *tx;
struct pdu_data_llctrl_cte_req local_cte_req = {
.cte_type_req = BT_HCI_LE_AOD_CTE_2US,
.min_cte_len_req = BT_HCI_LE_CTE_LEN_MIN,
};
struct pdu_data_llctrl_reject_ext_ind remote_reject_ext_ind = {
.reject_opcode = PDU_DATA_LLCTRL_TYPE_CTE_REQ,
.error_code = BT_HCI_ERR_UNSUPP_LL_PARAM_VAL,
};
/* Role */
test_set_role(&conn, BT_HCI_ROLE_PERIPHERAL);
/* Connect */
ull_cp_state_set(&conn, ULL_CP_CONNECTED);
/* Enable response for CTE request */
ull_cp_cte_rsp_enable(&conn, true, BT_HCI_LE_CTE_LEN_MAX,
(BT_HCI_LE_AOA_CTE | BT_HCI_LE_AOD_CTE_1US));
/* Prepare */
event_prepare(&conn);
/* Tx */
lt_tx(LL_CTE_REQ, &conn, &local_cte_req);
/* Done */
event_done(&conn);
/* Prepare */
event_prepare(&conn);
/* Tx Queue should have one LL Control PDU */
lt_rx(LL_REJECT_EXT_IND, &conn, &tx, &remote_reject_ext_ind);
lt_rx_q_is_empty(&conn);
/* Done */
event_done(&conn);
/* Release tx node */
ull_cp_release_tx(&conn, tx);
/* There should not be a host notifications */
ut_rx_q_is_empty();
zassert_equal(ctx_buffers_free(), CONFIG_BT_CTLR_LLCP_PROC_CTX_BUF_NUM,
"Free CTX buffers %d", ctx_buffers_free());
}
void test_main(void)
{
ztest_test_suite(
cte_req,
ztest_unit_test_setup_teardown(test_cte_req_central_local, setup, unit_test_noop),
ztest_unit_test_setup_teardown(test_cte_req_peripheral_local, setup,
unit_test_noop),
ztest_unit_test_setup_teardown(test_cte_req_central_remote, setup, unit_test_noop),
ztest_unit_test_setup_teardown(test_cte_req_peripheral_remote, setup,
unit_test_noop),
ztest_unit_test_setup_teardown(test_cte_req_rejected_inv_ll_param_central_local,
setup, unit_test_noop),
ztest_unit_test_setup_teardown(test_cte_req_rejected_inv_ll_param_peripheral_local,
setup, unit_test_noop),
ztest_unit_test_setup_teardown(test_cte_req_reject_inv_ll_param_central_remote,
setup, unit_test_noop),
ztest_unit_test_setup_teardown(test_cte_req_reject_inv_ll_param_peripheral_remote,
setup, unit_test_noop));
ztest_run_test_suite(cte_req);
}

View file

@ -0,0 +1,5 @@
common:
tags: test_framework bluetooth bt_le_cte_req bt_ull_llcp
tests:
bluetooth.controller.ctrl_cte_req.test:
type: unit

View file

@ -0,0 +1,26 @@
# SPDX-License-Identifier: Apache-2.0
cmake_minimum_required(VERSION 3.13.1)
if (NOT BOARD STREQUAL unit_testing)
message(FATAL_ERROR "This project can only be used with '-DBOARD=unit_testing'.")
endif()
FILE(GLOB SOURCES
src/*.c
)
if(CONFIG_BT_CTLR_PHY_CODED)
add_compile_definitions(CONFIG_BT_CTLR_PHY_CODED)
endif(CONFIG_BT_CTLR_PHY_CODED)
if(CONFIG_BT_CTLR_PHY)
add_compile_definitions(CONFIG_BT_CTLR_PHY)
endif(CONFIG_BT_CTLR_PHY)
project(bluetooth_ull_llcp_data_length_update)
find_package(ZephyrUnittest HINTS $ENV{ZEPHYR_BASE})
include(${ZEPHYR_BASE}/tests/bluetooth/controller/common/defaults_cmake.txt)
if(NOT CONFIG_BT_CTLR_PHY)
list(REMOVE_ITEM ll_sw_sources ${ZEPHYR_BASE}/subsys/bluetooth/controller/ll_sw/ull_llcp_phy.c)
endif()
target_sources(testbinary PRIVATE ${ll_sw_sources} ${mock_sources} ${common_sources})

View file

@ -0,0 +1,14 @@
/*
* Copyright (c) 2020 Demant
* Copyright (c) 2020 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
/*
* Common Kconfig settings
*/
#ifdef CONFIG_BT_CTLR_PHY
#undef CONFIG_BT_CTLR_PHY
#endif

View file

@ -0,0 +1,673 @@
/*
* Copyright (c) 2020 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/types.h>
#include <sys/byteorder.h>
#include <ztest.h>
#define ULL_LLCP_UNITTEST
#include <bluetooth/hci.h>
#include <sys/byteorder.h>
#include <sys/slist.h>
#include <sys/util.h>
#include "hal/ccm.h"
#include "util/util.h"
#include "util/mem.h"
#include "util/memq.h"
#include "pdu.h"
#include "ll.h"
#include "ll_feat.h"
#include "ll_settings.h"
#include "lll.h"
#include "lll_df_types.h"
#include "lll_conn.h"
#include "ull_tx_queue.h"
#include "ull_internal.h"
#include "ull_conn_types.h"
#include "ull_llcp.h"
#include "ull_conn_internal.h"
#include "ull_llcp_internal.h"
#include "helper_pdu.h"
#include "helper_util.h"
#include "helper_features.h"
struct ll_conn conn;
static void setup(void)
{
test_setup(&conn);
}
/*C
* Locally triggered Data Length Update procedure
*
* +-----+ +-------+ +-----+
* | UT | | LL_A | | LT |
* +-----+ +-------+ +-----+
* | | |
* | Start | |
* | Data Length Update Proc. | |
* |--------------------------->| |
* | | (251,2120,211,1800) |
* | | LL_DATA_LENGTH_UPDATE_REQ |
* | |----------------------------->|
* | | (201,1720,251,2120) |
* | | LL_DATA_LENGTH_UPDATE_RSP |
* | |<-----------------------------|
* | (251,2120,201,1720) | |
* | Data Length Update Proc. | |
* | Complete | |
* |<---------------------------| |
* | | |
*/
void test_data_length_update_mas_loc(void)
{
uint8_t err;
struct node_tx *tx;
struct node_rx_pdu *ntf;
struct pdu_data_llctrl_length_req local_length_req = { 251, 2120, 211, 1800 };
struct pdu_data_llctrl_length_rsp remote_length_rsp = { 201, 1720, 251, 2120 };
struct pdu_data_llctrl_length_rsp length_ntf = { 251, 2120, 201, 1720 };
test_set_role(&conn, BT_HCI_ROLE_CENTRAL);
/* Connect */
ull_cp_state_set(&conn, ULL_CP_CONNECTED);
/* Init DLE data */
ull_conn_default_tx_octets_set(251);
ull_conn_default_tx_time_set(2120);
ull_dle_init(&conn, PHY_1M);
/* Steal all ntf buffers, so as to check that the wait_ntf mechanism works */
while (ll_pdu_rx_alloc_peek(1)) {
ntf = ll_pdu_rx_alloc();
/* Make sure we use a correct type or the release won't work */
ntf->hdr.type = NODE_RX_TYPE_DC_PDU;
}
/* Initiate a Data Length Update Procedure */
err = ull_cp_data_length_update(&conn, 211, 1800);
zassert_equal(err, BT_HCI_ERR_SUCCESS, NULL);
event_prepare(&conn);
/* Tx Queue should have one LL Control PDU */
lt_rx(LL_LENGTH_REQ, &conn, &tx, &local_length_req);
lt_rx_q_is_empty(&conn);
/* TX Ack */
event_tx_ack(&conn, tx);
/* Rx */
lt_tx(LL_LENGTH_RSP, &conn, &remote_length_rsp);
event_done(&conn);
ut_rx_q_is_empty();
/* Release Ntf, so next cycle will generate NTF and complete procedure */
ull_cp_release_ntf(ntf);
event_prepare(&conn);
event_done(&conn);
/* There should be one host notification */
ut_rx_pdu(LL_LENGTH_RSP, &ntf, &length_ntf);
ut_rx_q_is_empty();
zassert_equal(conn.lll.event_counter, 2, "Wrong event-count %d\n",
conn.lll.event_counter);
}
/*
* Locally triggered Data Length Update procedure - with no update to eff and thus no ntf
*
* +-----+ +-------+ +-----+
* | UT | | LL_A | | LT |
* +-----+ +-------+ +-----+
* | | |
* | Start | |
* | Data Length Update Proc. | |
* |--------------------------->| |
* | | (251,2120,211,1800) |
* | | LL_DATA_LENGTH_UPDATE_REQ |
* | |----------------------------->|
* | | (27,328,27,328) |
* | | LL_DATA_LENGTH_UPDATE_RSP |
* | |<-----------------------------|
* | | |
*/
void test_data_length_update_mas_loc_no_eff_change(void)
{
uint8_t err;
struct node_tx *tx;
struct pdu_data_llctrl_length_req local_length_req = { 251, 2120, 211, 1800 };
struct pdu_data_llctrl_length_rsp remote_length_rsp = { 27, 328, 27, 328 };
test_set_role(&conn, BT_HCI_ROLE_CENTRAL);
/* Connect */
ull_cp_state_set(&conn, ULL_CP_CONNECTED);
/* Init DLE data */
ull_conn_default_tx_octets_set(251);
ull_conn_default_tx_time_set(2120);
ull_dle_init(&conn, PHY_1M);
/* Initiate a Data Length Update Procedure */
err = ull_cp_data_length_update(&conn, 211, 1800);
zassert_equal(err, BT_HCI_ERR_SUCCESS, NULL);
event_prepare(&conn);
/* Tx Queue should have one LL Control PDU */
lt_rx(LL_LENGTH_REQ, &conn, &tx, &local_length_req);
lt_rx_q_is_empty(&conn);
/* TX Ack */
event_tx_ack(&conn, tx);
/* Rx */
lt_tx(LL_LENGTH_RSP, &conn, &remote_length_rsp);
event_done(&conn);
/* There should be no host notification */
ut_rx_q_is_empty();
zassert_equal(conn.lll.event_counter, 1, "Wrong event-count %d\n",
conn.lll.event_counter);
}
/*
* Locally triggered Data Length Update procedure -
* - first updating effective DLE and then without update to eff and thus no ntf
*
* +-----+ +-------+ +-----+
* | UT | | LL_A | | LT |
* +-----+ +-------+ +-----+
* | | |
* | Start | |
* | Data Length Update Proc. | |
* |--------------------------->| |
* | | (251,2120,221,1800) |
* | | LL_DATA_LENGTH_UPDATE_REQ |
* | |----------------------------->|
* | | (101,920,251,2120) |
* | | LL_DATA_LENGTH_UPDATE_RSP |
* | |<-----------------------------|
* | (251,2120,101,920) | |
* | Data Length Update Proc. | |
* | Complete | |
* |<---------------------------| |
* | | |
* | Start | |
* | Data Length Update Proc. | |
* |--------------------------->| |
* | | (251,2120,211,1800) |
* | | LL_DATA_LENGTH_UPDATE_REQ |
* | |----------------------------->|
* | | (101, 920,251,2120) |
* | | LL_DATA_LENGTH_UPDATE_RSP |
* | |<-----------------------------|
* | | |
*/
void test_data_length_update_mas_loc_no_eff_change2(void)
{
uint8_t err;
struct node_tx *tx;
struct node_rx_pdu *ntf;
struct pdu_data_llctrl_length_req local_length_req = { 251, 2120, 211, 1800 };
struct pdu_data_llctrl_length_rsp remote_length_rsp = { 101, 920, 251, 2120 };
struct pdu_data_llctrl_length_rsp length_ntf = { 251, 2120, 101, 920 };
struct pdu_data_llctrl_length_req local_length_req2 = { 251, 2120, 211, 1800 };
struct pdu_data_llctrl_length_rsp remote_length_rsp2 = { 101, 920, 251, 2120 };
test_set_role(&conn, BT_HCI_ROLE_CENTRAL);
/* Connect */
ull_cp_state_set(&conn, ULL_CP_CONNECTED);
/* Init DLE data */
ull_conn_default_tx_octets_set(251);
ull_conn_default_tx_time_set(2120);
ull_dle_init(&conn, PHY_1M);
/* Initiate a Data Length Update Procedure */
err = ull_cp_data_length_update(&conn, 211, 1800);
zassert_equal(err, BT_HCI_ERR_SUCCESS, NULL);
event_prepare(&conn);
/* Tx Queue should have one LL Control PDU */
lt_rx(LL_LENGTH_REQ, &conn, &tx, &local_length_req);
lt_rx_q_is_empty(&conn);
/* TX Ack */
event_tx_ack(&conn, tx);
/* Rx */
lt_tx(LL_LENGTH_RSP, &conn, &remote_length_rsp);
event_done(&conn);
/* There should be one host notification */
ut_rx_pdu(LL_LENGTH_RSP, &ntf, &length_ntf);
ut_rx_q_is_empty();
zassert_equal(conn.lll.event_counter, 1, "Wrong event-count %d\n",
conn.lll.event_counter);
/* Now lets generate another DLU, but one that should not result in
* change to effective numbers, thus not generate NTF
*/
err = ull_cp_data_length_update(&conn, 211, 1800);
zassert_equal(err, BT_HCI_ERR_SUCCESS, NULL);
event_prepare(&conn);
/* Tx Queue should have one LL Control PDU */
lt_rx(LL_LENGTH_REQ, &conn, &tx, &local_length_req2);
lt_rx_q_is_empty(&conn);
/* TX Ack */
event_tx_ack(&conn, tx);
/* Rx */
lt_tx(LL_LENGTH_RSP, &conn, &remote_length_rsp2);
event_done(&conn);
/* There should be no host notification */
ut_rx_q_is_empty();
zassert_equal(conn.lll.event_counter, 2, "Wrong event-count %d\n",
conn.lll.event_counter);
}
void test_data_length_update_sla_loc(void)
{
uint64_t err;
struct node_tx *tx;
struct node_rx_pdu *ntf;
struct pdu_data_llctrl_length_req local_length_req = { 251, 2120, 211, 1800 };
struct pdu_data_llctrl_length_rsp remote_length_rsp = { 211, 1800, 251, 2120 };
struct pdu_data_llctrl_length_rsp length_ntf = { 251, 2120, 211, 1800 };
test_set_role(&conn, BT_HCI_ROLE_PERIPHERAL);
/* Connect */
ull_cp_state_set(&conn, ULL_CP_CONNECTED);
/* Init DLE data */
ull_conn_default_tx_octets_set(251);
ull_conn_default_tx_time_set(2120);
ull_dle_init(&conn, PHY_1M);
/* Initiate a Data Length Update Procedure */
err = ull_cp_data_length_update(&conn, 211, 1800);
zassert_equal(err, BT_HCI_ERR_SUCCESS, NULL);
event_prepare(&conn);
/* Tx Queue should have one LL Control PDU */
lt_rx(LL_LENGTH_REQ, &conn, &tx, &local_length_req);
lt_rx_q_is_empty(&conn);
/* TX Ack */
event_tx_ack(&conn, tx);
/* Rx */
lt_tx(LL_LENGTH_RSP, &conn, &remote_length_rsp);
event_done(&conn);
/* There should be one host notification */
ut_rx_pdu(LL_LENGTH_RSP, &ntf, &length_ntf);
ut_rx_q_is_empty();
zassert_equal(conn.lll.event_counter, 1, "Wrong event-count %d\n",
conn.lll.event_counter);
}
/*
* Remotely triggered Data Length Update procedure
*
* +-----+ +-------+ +-----+
* | UT | | LL_A | | LT |
* +-----+ +-------+ +-----+
* | | |
* | | (27, 328, 251, 2120) |
* | | LL_DATA_LENGTH_UPDATE_REQ |
* | |<-----------------------------|
* | | (251, 2120, 211, 1800) |
* | | LL_DATA_LENGTH_UPDATE_RSP |
* | |----------------------------->|
* | (251,2120,27,328) | |
* | Data Length Changed | |
* |<---------------------------| |
* | | |
*/
void test_data_length_update_mas_rem(void)
{
struct node_tx *tx;
struct pdu_data_llctrl_length_req remote_length_req = { 27, 328, 251, 2120 };
struct pdu_data_llctrl_length_rsp local_length_rsp = { 251, 2120, 211, 1800 };
struct pdu_data_llctrl_length_rsp length_ntf = { 251, 2120, 27, 328 };
struct node_rx_pdu *ntf;
test_set_role(&conn, BT_HCI_ROLE_CENTRAL);
ull_cp_state_set(&conn, ULL_CP_CONNECTED);
/* Init DLE data */
ull_conn_default_tx_octets_set(211);
ull_conn_default_tx_time_set(1800);
ull_dle_init(&conn, PHY_1M);
event_prepare(&conn);
/* Tx Queue should have one LL Control PDU */
lt_tx(LL_LENGTH_REQ, &conn, &remote_length_req);
event_done(&conn);
event_prepare(&conn);
/* Tx Queue should have one LL Control PDU */
lt_rx(LL_LENGTH_RSP, &conn, &tx, &local_length_rsp);
lt_rx_q_is_empty(&conn);
/* TX Ack */
event_tx_ack(&conn, tx);
event_done(&conn);
ut_rx_pdu(LL_LENGTH_RSP, &ntf, &length_ntf);
ut_rx_q_is_empty();
}
/*
* Remotely triggered Data Length Update procedure
*
* +-----+ +-------+ +-----+
* | UT | | LL_A | | LT |
* +-----+ +-------+ +-----+
* | | |
* | | (27, 328, 201, 1720) |
* | | LL_DATA_LENGTH_UPDATE_REQ |
* | |<-----------------------------|
* | | |
* | | (251, 2120, 211, 1800) |
* | | LL_DATA_LENGTH_UPDATE_RSP |
* | |----------------------------->|
* | (201,1720,27,328) | |
* | Data Length Changed | |
* |<---------------------------| |
* | | |
*/
void test_data_length_update_sla_rem(void)
{
struct node_tx *tx;
struct pdu_data_llctrl_length_req remote_length_req = { 27, 328, 201, 1720 };
struct pdu_data_llctrl_length_rsp local_length_rsp = { 251, 2120, 211, 1800 };
struct pdu_data_llctrl_length_rsp length_ntf = { 201, 1720, 27, 328 };
struct node_rx_pdu *ntf;
test_set_role(&conn, BT_HCI_ROLE_PERIPHERAL);
ull_cp_state_set(&conn, ULL_CP_CONNECTED);
/* Init DLE data */
ull_conn_default_tx_octets_set(211);
ull_conn_default_tx_time_set(1800);
ull_dle_init(&conn, PHY_1M);
/* Steal all ntf buffers, so as to check that the wait_ntf mechanism works */
while (ll_pdu_rx_alloc_peek(1)) {
ntf = ll_pdu_rx_alloc();
/* Make sure we use a correct type or the release won't work */
ntf->hdr.type = NODE_RX_TYPE_DC_PDU;
}
event_prepare(&conn);
/* Tx Queue should have one LL Control PDU */
lt_tx(LL_LENGTH_REQ, &conn, &remote_length_req);
event_done(&conn);
event_prepare(&conn);
/* Tx Queue should have one LL Control PDU */
lt_rx(LL_LENGTH_RSP, &conn, &tx, &local_length_rsp);
lt_rx_q_is_empty(&conn);
/* TX Ack */
event_tx_ack(&conn, tx);
event_done(&conn);
ut_rx_q_is_empty();
/* Release Ntf, so next cycle will generate NTF and complete procedure */
ull_cp_release_ntf(ntf);
event_prepare(&conn);
event_done(&conn);
ut_rx_pdu(LL_LENGTH_RSP, &ntf, &length_ntf);
ut_rx_q_is_empty();
}
/*
* Remotely triggered Data Length Update procedure with local request piggy back
*
* +-----+ +-------+ +-----+
* | UT | | LL_A | | LT |
* +-----+ +-------+ +-----+
* | | |
* | | (27, 328, 211, 1800) |
* | | LL_DATA_LENGTH_UPDATE_REQ |
* | |<-----------------------------|
* | Start | |
* | Data Length Update Proc. | |
* |--------------------------->| |
* | | |
* | | (251, 2120, 211, 1800) |
* | | LL_DATA_LENGTH_UPDATE_RSP |
* | |----------------------------->|
* | (211,1800,27,328) | |
* | Data Length Changed | |
* |<---------------------------| |
* | | |
*/
void test_data_length_update_sla_rem_and_loc(void)
{
uint64_t err;
struct node_tx *tx;
struct proc_ctx *ctx = NULL;
struct pdu_data_llctrl_length_req remote_length_req = { 27, 328, 211, 1800 };
struct pdu_data_llctrl_length_rsp local_length_rsp = { 251, 2120, 211, 1800 };
struct pdu_data_llctrl_length_rsp length_ntf = { 211, 1800, 27, 328 };
struct node_rx_pdu *ntf;
test_set_role(&conn, BT_HCI_ROLE_PERIPHERAL);
ull_cp_state_set(&conn, ULL_CP_CONNECTED);
/* Init DLE data */
ull_conn_default_tx_octets_set(211);
ull_conn_default_tx_time_set(1800);
ull_dle_init(&conn, PHY_1M);
/* Allocate dummy procedure used to steal all buffers */
ctx = llcp_create_local_procedure(PROC_VERSION_EXCHANGE);
/* Steal all tx buffers */
while (llcp_tx_alloc_peek(&conn, ctx)) {
tx = llcp_tx_alloc(&conn, ctx);
zassert_not_null(tx, NULL);
}
/* Dummy remove, as above loop might queue up ctx */
llcp_tx_alloc_unpeek(ctx);
event_prepare(&conn);
/* Tx Queue should have one LL Control PDU */
lt_tx(LL_LENGTH_REQ, &conn, &remote_length_req);
event_done(&conn);
event_prepare(&conn);
/* Tx Queue should have no LL Control PDU */
lt_rx_q_is_empty(&conn);
/* Initiate a Data Length Update Procedure */
err = ull_cp_data_length_update(&conn, 211, 1800);
zassert_equal(err, BT_HCI_ERR_SUCCESS, NULL);
event_done(&conn);
ull_cp_release_tx(&conn, tx);
event_prepare(&conn);
/* Tx Queue should have one LL Control PDU */
lt_rx(LL_LENGTH_RSP, &conn, &tx, &local_length_rsp);
lt_rx_q_is_empty(&conn);
/* TX Ack */
event_tx_ack(&conn, tx);
event_done(&conn);
ut_rx_pdu(LL_LENGTH_RSP, &ntf, &length_ntf);
ut_rx_q_is_empty();
}
void test_data_length_update_dle_max_time_get(void)
{
uint16_t max_time = 0xffff;
uint16_t max_octets = 211;
#ifdef CONFIG_BT_CTLR_PHY
max_time = 2120;
#endif
conn.llcp.fex.valid = 0;
ull_dle_local_tx_update(&conn, max_octets, max_time);
#ifdef CONFIG_BT_CTLR_PHY
#ifdef CONFIG_BT_CTLR_PHY_CODED
zassert_equal(conn.lll.dle.local.max_rx_time, 2120, "max_rx_time mismatch.\n");
zassert_equal(conn.lll.dle.local.max_tx_time, 2120, "max_tx_time mismatch.\n");
#else
zassert_equal(conn.lll.dle.local.max_rx_time, 2120, "max_rx_time mismatch.\n");
zassert_equal(conn.lll.dle.local.max_tx_time, 2120, "max_tx_time mismatch.\n");
#endif
#else
zassert_equal(conn.lll.dle.local.max_rx_time, 2120, "max_rx_time mismatch.\n");
zassert_equal(conn.lll.dle.local.max_tx_time, 1800, "max_tx_time mismatch.\n");
#endif
/* Emulate complete feat exch without CODED */
conn.llcp.fex.valid = 1;
conn.llcp.fex.features_used = 0;
ull_dle_local_tx_update(&conn, max_octets, max_time);
#ifdef CONFIG_BT_CTLR_PHY
#ifdef CONFIG_BT_CTLR_PHY_CODED
zassert_equal(conn.lll.dle.local.max_rx_time, 2120, "max_rx_time mismatch.\n");
zassert_equal(conn.lll.dle.local.max_tx_time, 2120, "max_tx_time mismatch.\n");
#else
zassert_equal(conn.lll.dle.local.max_rx_time, 2120, "max_rx_time mismatch.\n");
zassert_equal(conn.lll.dle.local.max_tx_time, 2120, "max_tx_time mismatch.\n");
#endif
#else
zassert_equal(conn.lll.dle.local.max_rx_time, 2120, "max_rx_time mismatch.\n");
zassert_equal(conn.lll.dle.local.max_tx_time, 1800, "max_tx_time mismatch.\n");
#endif
/* Check the case of CODED PHY support */
conn.llcp.fex.features_used = LL_FEAT_BIT_PHY_CODED;
ull_dle_local_tx_update(&conn, max_octets, max_time);
#ifdef CONFIG_BT_CTLR_PHY
#ifdef CONFIG_BT_CTLR_PHY_CODED
zassert_equal(conn.lll.dle.local.max_rx_time, 17040, "max_rx_time mismatch.\n");
zassert_equal(conn.lll.dle.local.max_tx_time, 2120, "max_tx_time mismatch.\n");
#else
zassert_equal(conn.lll.dle.local.max_rx_time, 2120, "max_rx_time mismatch.\n");
zassert_equal(conn.lll.dle.local.max_tx_time, 2120, "max_tx_time mismatch.\n");
#endif
#else
zassert_equal(conn.lll.dle.local.max_rx_time, 2120, "max_rx_time mismatch.\n");
zassert_equal(conn.lll.dle.local.max_tx_time, 1800, "max_tx_time mismatch.\n");
#endif
/* Finally check that MAX on max_tx_time works */
max_time = 20000;
ull_dle_local_tx_update(&conn, max_octets, max_time);
#ifdef CONFIG_BT_CTLR_PHY
#ifdef CONFIG_BT_CTLR_PHY_CODED
zassert_equal(conn.lll.dle.local.max_rx_time, 17040, "max_rx_time mismatch.\n");
zassert_equal(conn.lll.dle.local.max_tx_time, 17040, "max_tx_time mismatch.\n");
#else
zassert_equal(conn.lll.dle.local.max_rx_time, 2120, "max_rx_time mismatch.\n");
zassert_equal(conn.lll.dle.local.max_tx_time, 2120, "max_tx_time mismatch.\n");
#endif
#else
zassert_equal(conn.lll.dle.local.max_rx_time, 2120, "max_rx_time mismatch.\n");
zassert_equal(conn.lll.dle.local.max_tx_time, 1800, "max_tx_time mismatch.\n");
#endif
/* Check that MIN works */
max_time = 20;
max_octets = 2;
ull_dle_local_tx_update(&conn, max_octets, max_time);
#ifdef CONFIG_BT_CTLR_PHY
#ifdef CONFIG_BT_CTLR_PHY_CODED
zassert_equal(conn.lll.dle.local.max_rx_time, 17040, "max_rx_time mismatch.\n");
zassert_equal(conn.lll.dle.local.max_tx_time, 328, "max_tx_time mismatch.\n");
#else
zassert_equal(conn.lll.dle.local.max_rx_time, 2120, "max_rx_time mismatch.\n");
zassert_equal(conn.lll.dle.local.max_tx_time, 328, "max_tx_time mismatch.\n");
#endif
#else
zassert_equal(conn.lll.dle.local.max_rx_time, 2120, "max_rx_time mismatch.\n");
zassert_equal(conn.lll.dle.local.max_tx_time, 328, "max_tx_time mismatch.\n");
#endif
}
void test_main(void)
{
ztest_test_suite(
data_length_update_master,
ztest_unit_test_setup_teardown(test_data_length_update_mas_loc, setup,
unit_test_noop),
ztest_unit_test_setup_teardown(test_data_length_update_mas_loc_no_eff_change, setup,
unit_test_noop),
ztest_unit_test_setup_teardown(test_data_length_update_mas_loc_no_eff_change2,
setup, unit_test_noop),
ztest_unit_test_setup_teardown(test_data_length_update_mas_rem, setup,
unit_test_noop));
ztest_test_suite(data_length_update_slave,
ztest_unit_test_setup_teardown(test_data_length_update_sla_loc, setup,
unit_test_noop),
ztest_unit_test_setup_teardown(test_data_length_update_sla_rem, setup,
unit_test_noop),
ztest_unit_test_setup_teardown(test_data_length_update_sla_rem_and_loc,
setup, unit_test_noop)
);
ztest_test_suite(data_length_update_util,
ztest_unit_test_setup_teardown(test_data_length_update_dle_max_time_get,
setup, unit_test_noop));
ztest_run_test_suite(data_length_update_master);
ztest_run_test_suite(data_length_update_slave);
ztest_run_test_suite(data_length_update_util);
}

View file

@ -0,0 +1,12 @@
common:
tags: test_framework bluetooth bt_data_length_update bt_ull_llcp
tests:
bluetooth.controller.ctrl_data_length_update.test:
type: unit
extra_args: CONFIG_BT_CTLR_PHY=y
bluetooth.controller.ctrl_data_length_update.test_codedphy:
type: unit
extra_args: CONFIG_BT_CTLR_PHY=y CONFIG_BT_CTLR_PHY_CODED=y
bluetooth.controller.ctrl_data_length_update.test_nophy:
type: unit
extra_args: KCONFIG_OVERRIDE_FILE="kconfig_override.h"

View file

@ -0,0 +1,16 @@
# SPDX-License-Identifier: Apache-2.0
cmake_minimum_required(VERSION 3.13.1)
if (NOT BOARD STREQUAL unit_testing)
message(FATAL_ERROR "This project can only be used with '-DBOARD=unit_testing'.")
endif()
FILE(GLOB SOURCES
src/*.c
)
project(bluetooth_ull_llcp_encrypt)
find_package(ZephyrUnittest HINTS $ENV{ZEPHYR_BASE})
include(${ZEPHYR_BASE}/tests/bluetooth/controller/common/defaults_cmake.txt)
target_sources(testbinary PRIVATE ${ll_sw_sources} ${mock_sources} ${common_sources})

Some files were not shown because too many files have changed in this diff Show more