bluetooth: controller: Add support for all DTM commands

This adds support for the following Direct Test mode
commands:
- HCI LE Receiver Test [v3]
- HCI LE Transmitter Test [v3]
- HCI LE Transmitter Test [v4]

Those commands set add a possibility to test an CTE
reception and transmission. The HCI LE Transmitter
Test [v4] commands allows also setting a transmit power.

Signed-off-by: Kamil Gawor <Kamil.Gawor@nordicsemi.no>
This commit is contained in:
Kamil Gawor 2022-01-18 16:39:39 +01:00 committed by Carles Cufí
commit 5db0302c99
12 changed files with 810 additions and 112 deletions

View file

@ -891,6 +891,17 @@ static void read_supported_commands(struct net_buf *buf, struct net_buf **evt)
rp->commands[35] |= BIT(7);
/* LE Enhanced TX Test. */
rp->commands[36] |= BIT(0);
#if defined(CONFIG_BT_CTLR_DTM_HCI_RX_V3)
rp->commands[39] |= BIT(3);
#endif /* CONFIG_BT_CTLR_DTM_HCI_RX_V3 */
#if defined(CONFIG_BT_CTLR_DTM_HCI_TX_V3)
rp->commands[39] |= BIT(4);
#endif
#if defined(CONFIG_BT_CTLR_DTM_HCI_TX_V4)
rp->commands[45] |= BIT(0);
#endif
#endif /* CONFIG_BT_CTLR_DTM_HCI */
#if defined(CONFIG_BT_CTLR_PRIVACY)
@ -2797,17 +2808,23 @@ static void le_df_set_cl_iq_sampling_enable(struct net_buf *buf, struct net_buf
rp->status = status;
rp->sync_handle = sys_cpu_to_le16(sync_handle);
}
#endif /* CONFIG_BT_CTLR_DF_SCAN_CTE_RX */
#if defined(CONFIG_BT_CTLR_DF_SCAN_CTE_RX) || defined(CONFIG_BT_CTLR_DTM_HCI_DF_IQ_REPORT)
static void le_df_connectionless_iq_report(struct pdu_data *pdu_rx,
struct node_rx_pdu *node_rx,
struct net_buf *buf)
{
struct bt_hci_evt_le_connectionless_iq_report *sep;
struct node_rx_iq_report *iq_report;
struct ll_sync_set *sync;
struct lll_sync *lll;
uint8_t samples_cnt;
int16_t rssi;
uint16_t sync_handle;
uint16_t per_evt_counter;
#if defined(CONFIG_BT_CTLR_DF_SCAN_CTE_RX)
struct ll_sync_set *sync = NULL;
#endif /* CONFIG_BT_CTLR_DF_SCAN_CTE_RX */
iq_report = (struct node_rx_iq_report *)node_rx;
@ -2817,19 +2834,43 @@ static void le_df_connectionless_iq_report(struct pdu_data *pdu_rx,
}
lll = iq_report->hdr.rx_ftr.param;
sync = HDR_LLL2ULL(lll);
/* TX LL thread has higher priority than RX thread. It may happen that
* host successfully disables CTE sampling in the meantime.
* It should be verified here, to avoid reporting IQ samples after
* the functionality was disabled or if sync was lost.
/* If there is not LLL context and CONFIG_BT_CTLR_DTM_HCI_DF_IQ_REPORT is enabled
* the controller is in the Direct Test Mode and may generate
* the Connectionless IQ Report.
*/
if (ull_df_sync_cfg_is_not_enabled(&lll->df_cfg) ||
!sync->timeout_reload) {
/* Drop further processing of the event. */
return;
if (!lll && IS_ENABLED(CONFIG_BT_CTLR_DTM_HCI_DF_IQ_REPORT)) {
/* Set sync_handle to 0x0FFF according to the BT Core 5.3 specification
* Vol 4 7.7.65.21
*/
sync_handle = 0x0FFF;
/* Set periodic event counter to 0 since there is not periodic advertising train. */
per_evt_counter = 0;
}
#if defined(CONFIG_BT_CTLR_DF_SCAN_CTE_RX)
else {
sync = HDR_LLL2ULL(lll);
/* TX LL thread has higher priority than RX thread. It may happen that
* host successfully disables CTE sampling in the meantime.
* It should be verified here, to avoid reporting IQ samples after
* the functionality was disabled or if sync was lost.
*/
if (ull_df_sync_cfg_is_not_enabled(&lll->df_cfg) ||
!sync->timeout_reload) {
/* Drop further processing of the event. */
return;
}
/* Get the sync handle corresponding to the LLL context passed in the
* node rx footer field.
*/
sync_handle = ull_sync_handle_get(sync);
per_evt_counter = lll->event_counter;
}
#endif /* CONFIG_BT_CTLR_DF_SCAN_CTE_RX */
/* If there are no IQ samples due to insufficient resources
* HCI event should inform about it by storing single octet with
* special I_sample and Q_sample data.
@ -2842,16 +2883,14 @@ static void le_df_connectionless_iq_report(struct pdu_data *pdu_rx,
rssi = RSSI_DBM_TO_DECI_DBM(iq_report->hdr.rx_ftr.rssi);
/* Get the sync handle corresponding to the LLL context passed in the
* node rx footer field.
*/
sep->sync_handle = sys_cpu_to_le16(ull_sync_handle_get(sync));
sep->sync_handle = sys_cpu_to_le16(sync_handle);
sep->rssi = sys_cpu_to_le16(rssi);
sep->rssi_ant_id = iq_report->rssi_ant_id;
sep->cte_type = iq_report->cte_info.type;
sep->chan_idx = iq_report->chan_idx;
sep->per_evt_counter = sys_cpu_to_le16(lll->event_counter);
sep->per_evt_counter = sys_cpu_to_le16(per_evt_counter);
if (sep->cte_type == BT_HCI_LE_AOA_CTE) {
sep->slot_durations = iq_report->local_slot_durations;
@ -2876,7 +2915,7 @@ static void le_df_connectionless_iq_report(struct pdu_data *pdu_rx,
sep->sample_count = samples_cnt;
}
}
#endif /* CONFIG_BT_CTLR_DF_SCAN_CTE_RX */
#endif /* defined(CONFIG_BT_CTLR_DF_SCAN_CTE_RX) || defined(CONFIG_BT_CTLR_DTM_HCI_DF_IQ_REPORT) */
#if defined(CONFIG_BT_CTLR_DF_CONN_CTE_TX)
static void le_df_set_conn_cte_tx_params(struct net_buf *buf,
@ -3082,7 +3121,10 @@ static void le_rx_test(struct net_buf *buf, struct net_buf **evt)
struct bt_hci_cp_le_rx_test *cmd = (void *)buf->data;
uint8_t status;
status = ll_test_rx(cmd->rx_ch, 0x01, 0);
status = ll_test_rx(cmd->rx_ch, BT_HCI_LE_RX_PHY_1M, BT_HCI_LE_MOD_INDEX_STANDARD,
BT_HCI_LE_TEST_CTE_DISABLED, BT_HCI_LE_TEST_CTE_TYPE_ANY,
BT_HCI_LE_TEST_SLOT_DURATION_ANY, BT_HCI_LE_TEST_SWITCH_PATTERN_LEN_ANY,
NULL);
*evt = cmd_complete_status(status);
}
@ -3093,7 +3135,9 @@ static void le_tx_test(struct net_buf *buf, struct net_buf **evt)
uint8_t status;
status = ll_test_tx(cmd->tx_ch, cmd->test_data_len, cmd->pkt_payload,
0x01);
BT_HCI_LE_TX_PHY_1M, BT_HCI_LE_TEST_CTE_DISABLED,
BT_HCI_LE_TEST_CTE_TYPE_ANY, BT_HCI_LE_TEST_SWITCH_PATTERN_LEN_ANY,
NULL, BT_HCI_TX_TEST_POWER_MAX_SET);
*evt = cmd_complete_status(status);
}
@ -3102,11 +3146,12 @@ static void le_test_end(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_rp_le_test_end *rp;
uint16_t rx_pkt_count;
uint8_t status;
ll_test_end(&rx_pkt_count);
status = ll_test_end(&rx_pkt_count);
rp = hci_cmd_complete(evt, sizeof(*rp));
rp->status = 0x00;
rp->status = status;
rp->rx_pkt_count = sys_cpu_to_le16(rx_pkt_count);
}
@ -3115,21 +3160,69 @@ static void le_enh_rx_test(struct net_buf *buf, struct net_buf **evt)
struct bt_hci_cp_le_enh_rx_test *cmd = (void *)buf->data;
uint8_t status;
status = ll_test_rx(cmd->rx_ch, cmd->phy, cmd->mod_index);
status = ll_test_rx(cmd->rx_ch, cmd->phy, cmd->mod_index, BT_HCI_LE_TEST_CTE_DISABLED,
BT_HCI_LE_TEST_CTE_TYPE_ANY, BT_HCI_LE_TEST_SLOT_DURATION_ANY,
BT_HCI_LE_TEST_SWITCH_PATTERN_LEN_ANY, NULL);
*evt = cmd_complete_status(status);
}
#if defined(CONFIG_BT_CTLR_DTM_HCI_RX_V3)
static void le_rx_test_v3(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_le_rx_test_v3 *cmd = (void *)buf->data;
uint8_t status;
status = ll_test_rx(cmd->rx_ch, cmd->phy, cmd->mod_index, cmd->expected_cte_len,
cmd->expected_cte_type, cmd->slot_durations, cmd->switch_pattern_len,
cmd->ant_ids);
*evt = cmd_complete_status(status);
}
#endif /* CONFIG_BT_CTLR_DTM_HCI_RX_V3 */
static void le_enh_tx_test(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_le_enh_tx_test *cmd = (void *)buf->data;
uint8_t status;
status = ll_test_tx(cmd->tx_ch, cmd->test_data_len, cmd->pkt_payload,
cmd->phy);
status = ll_test_tx(cmd->tx_ch, cmd->test_data_len, cmd->pkt_payload, cmd->phy,
BT_HCI_LE_TEST_CTE_DISABLED, BT_HCI_LE_TEST_CTE_TYPE_ANY,
BT_HCI_LE_TEST_SWITCH_PATTERN_LEN_ANY, NULL,
BT_HCI_TX_TEST_POWER_MAX_SET);
*evt = cmd_complete_status(status);
}
#if defined(CONFIG_BT_CTLR_DTM_HCI_TX_V3)
static void le_tx_test_v3(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_le_tx_test_v3 *cmd = (void *)buf->data;
uint8_t status;
status = ll_test_tx(cmd->tx_ch, cmd->test_data_len, cmd->pkt_payload, cmd->phy,
cmd->cte_len, cmd->cte_type, cmd->switch_pattern_len, cmd->ant_ids,
BT_HCI_TX_TEST_POWER_MAX_SET);
*evt = cmd_complete_status(status);
}
#endif /* CONFIG_BT_CTLR_DTM_HCI_TX_V3 */
#if defined(CONFIG_BT_CTLR_DTM_HCI_TX_V4)
static void le_tx_test_v4(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_le_tx_test_v4 *cmd = (void *)buf->data;
struct bt_hci_cp_le_tx_test_v4_tx_power *tx_power = (void *)(buf->data +
sizeof(struct bt_hci_cp_le_tx_test_v4) + cmd->switch_pattern_len);
uint8_t status;
status = ll_test_tx(cmd->tx_ch, cmd->test_data_len, cmd->pkt_payload, cmd->phy,
cmd->cte_len, cmd->cte_type, cmd->switch_pattern_len, cmd->ant_ids,
tx_power->tx_power);
*evt = cmd_complete_status(status);
}
#endif /* CONFIG_BT_CTLR_DTM_HCI_TX_V4 */
#endif /* CONFIG_BT_CTLR_DTM_HCI */
#if defined(CONFIG_BT_CTLR_ADV_EXT)
@ -4429,9 +4522,24 @@ static int controller_cmd_handle(uint16_t ocf, struct net_buf *cmd,
case BT_OCF(BT_HCI_OP_LE_ENH_RX_TEST):
le_enh_rx_test(cmd, evt);
break;
#if defined(CONFIG_BT_CTLR_DTM_HCI_RX_V3)
case BT_OCF(BT_HCI_OP_LE_RX_TEST_V3):
le_rx_test_v3(cmd, evt);
break;
#endif /* CONFIG_BT_CTLR_DTM_HCI_RX_V3 */
case BT_OCF(BT_HCI_OP_LE_ENH_TX_TEST):
le_enh_tx_test(cmd, evt);
break;
#if defined(CONFIG_BT_CTLR_DTM_HCI_TX_V3)
case BT_OCF(BT_HCI_OP_LE_TX_TEST_V3):
le_tx_test_v3(cmd, evt);
break;
#endif /* CONFIG_BT_CTLR_DTM_HCI_TX_V3 */
#if defined(CONFIG_BT_CTLR_DTM_HCI_TX_V4)
case BT_OCF(BT_HCI_OP_LE_TX_TEST_V4):
le_tx_test_v4(cmd, evt);
break;
#endif /* CONFIG_BT_CTLR_DTM_HCI_TX_V4 */
#endif /* CONFIG_BT_CTLR_DTM_HCI */
default:
@ -7487,6 +7595,12 @@ static void encode_control(struct node_rx_pdu *node_rx,
return;
#endif /* CONFIG_BT_CTLR_PROFILE_ISR */
#if defined(CONFIG_BT_CTLR_DTM_HCI_DF_IQ_REPORT)
case NODE_RX_TYPE_DTM_IQ_SAMPLE_REPORT:
le_df_connectionless_iq_report(pdu_data, node_rx, buf);
return;
#endif /* CONFIG_BT_CTLR_DTM_HCI_DF_IQ_REPORT */
#if defined(CONFIG_BT_HCI_MESH_EXT)
case NODE_RX_TYPE_MESH_ADV_CPLT:
mesh_adv_cplt(pdu_data, node_rx, buf);
@ -7935,6 +8049,11 @@ uint8_t hci_get_class(struct node_rx_pdu *node_rx)
return HCI_CLASS_ISO_DATA;
#endif /* CONFIG_BT_CTLR_SYNC_ISO || CONFIG_BT_CTLR_CONN_ISO */
#if defined(CONFIG_BT_CTLR_DTM_HCI_DF_IQ_REPORT)
case NODE_RX_TYPE_DTM_IQ_SAMPLE_REPORT:
return HCI_CLASS_EVT_REQUIRED;
#endif /* CONFIG_BT_CTLR_DTM_HCI_DF_IQ_REPORT */
#if CONFIG_BT_CTLR_USER_EVT_RANGE > 0
case NODE_RX_TYPE_USER_START ... NODE_RX_TYPE_USER_END - 1:
return hci_user_ext_get_class(node_rx);