Bluetooth: controller: enable dynamic TXP over LEGACY arch (#17731)

This commit targets solving issue #17731 over the Nordic LL LEGACY
arch of the BLE stack in Zephyr. This functionality is exposed
to the user as HCI Zephyr Command extensions

- BT_HCI_OP_VS_WRITE_TX_POWER_LEVEL
- BT_HCI_OP_VS_READ_TX_POWER_LEVEL

which enable Tx power read/write operations within BLE radio events
on a per role/connection basis.

The functionality is enabled upon the Kconfig advanced configuration
triggered by

- BT_CTLR_TX_PWR_DYNAMIC_CONTROL

depending on the enablement of Zephyr HCI vendor-specific command
extensions.

Signed-off-by: Andrei Stoica <stoica.razvan.andrei@gmail.com>
This commit is contained in:
Andrei Stoica 2019-10-22 13:38:57 +02:00 committed by Carles Cufí
parent abd1d047dd
commit 930d178c69
2 changed files with 191 additions and 43 deletions

View file

@ -14,6 +14,7 @@
#include <drivers/entropy.h>
#include <drivers/clock_control.h>
#include <bluetooth/hci.h>
#include <bluetooth/hci_vs.h>
#include <sys/util.h>
#include <sys/byteorder.h>
@ -140,6 +141,10 @@ struct advertiser {
struct radio_adv_data scan_data;
struct connection *conn;
#if defined(CONFIG_BT_CTLR_TX_PWR_DYNAMIC_CONTROL)
s8_t tx_pwr_lvl;
#endif /* CONFIG_BT_CTLR_TX_PWR_DYNAMIC_CONTROL */
};
struct scanner {
@ -174,6 +179,10 @@ struct scanner {
u32_t ticks_conn_slot;
struct connection *conn;
#if defined(CONFIG_BT_CTLR_TX_PWR_DYNAMIC_CONTROL)
s8_t tx_pwr_lvl;
#endif /* CONFIG_BT_CTLR_TX_PWR_DYNAMIC_CONTROL */
u32_t win_offset_us;
};
@ -650,6 +659,11 @@ static void common_init(void)
/* allocate the rx queue */
packet_rx_allocate(0xFF);
#if defined(CONFIG_BT_CTLR_TX_PWR_DYNAMIC_CONTROL)
_radio.advertiser.tx_pwr_lvl = RADIO_TXP_DEFAULT;
_radio.scanner.tx_pwr_lvl = RADIO_TXP_DEFAULT;
#endif /* CONFIG_BT_CTLR_TX_PWR_DYNAMIC_CONTROL */
}
static inline u32_t addr_us_get(u8_t phy)
@ -6603,25 +6617,6 @@ again:
}
#endif /* CONFIG_BT_CONN */
static void adv_scan_conn_configure(void)
{
radio_reset();
radio_tx_power_set(RADIO_TXP_DEFAULT);
radio_isr_set(isr, NULL);
}
static void adv_scan_configure(u8_t phy, u8_t flags)
{
u32_t aa = 0x8e89bed6;
adv_scan_conn_configure();
radio_phy_set(phy, flags);
radio_aa_set((u8_t *)&aa);
radio_pkt_configure(8, PDU_AC_PAYLOAD_SIZE_MAX, (phy << 1));
radio_crc_configure(((0x5bUL) | ((0x06UL) << 8) | ((0x00UL) << 16)),
0x555555);
}
void radio_event_adv_prepare(u32_t ticks_at_expire, u32_t remainder,
u16_t lazy, void *context)
{
@ -6722,6 +6717,7 @@ static void event_adv(u32_t ticks_at_expire, u32_t remainder,
u16_t lazy, void *context)
{
u32_t remainder_us;
u32_t aa = 0x8e89bed6;
ARG_UNUSED(remainder);
ARG_UNUSED(lazy);
@ -6742,13 +6738,29 @@ static void event_adv(u32_t ticks_at_expire, u32_t remainder,
_radio.ticker_id_event = RADIO_TICKER_ID_ADV;
_radio.ticks_anchor = ticks_at_expire;
radio_reset();
#if defined(CONFIG_BT_CTLR_TX_PWR_DYNAMIC_CONTROL)
radio_tx_power_set(_radio.advertiser.tx_pwr_lvl);
#else
radio_tx_power_set(RADIO_TXP_DEFAULT);
#endif /* CONFIG_BT_CTLR_TX_PWR_DYNAMIC_CONTROL */
radio_isr_set(isr, NULL);
#if defined(CONFIG_BT_CTLR_ADV_EXT)
/* TODO: if coded we use S8? */
adv_scan_configure(_radio.advertiser.phy_p, 1);
radio_phy_set(_radio.advertiser.phy_p, 1);
radio_pkt_configure(8, PDU_AC_PAYLOAD_SIZE_MAX,
(_radio.advertiser.phy_p << 1));
#else /* !CONFIG_BT_CTLR_ADV_EXT */
adv_scan_configure(0, 0);
radio_phy_set(0, 0);
radio_pkt_configure(8, PDU_AC_PAYLOAD_SIZE_MAX, 0);
#endif /* !CONFIG_BT_CTLR_ADV_EXT */
radio_aa_set((u8_t *)&aa);
radio_crc_configure(((0x5bUL) | ((0x06UL) << 8) | ((0x00UL) << 16)),
0x555555);
_radio.advertiser.chan_map_current = _radio.advertiser.chan_map;
adv_setup();
@ -7137,6 +7149,7 @@ static void event_scan(u32_t ticks_at_expire, u32_t remainder, u16_t lazy,
{
u32_t remainder_us;
u32_t ret;
u32_t aa = 0x8e89bed6;
ARG_UNUSED(remainder);
ARG_UNUSED(lazy);
@ -7157,12 +7170,29 @@ static void event_scan(u32_t ticks_at_expire, u32_t remainder, u16_t lazy,
_radio.ticks_anchor = ticks_at_expire;
_radio.scanner.state = 0U;
radio_reset();
#if defined(CONFIG_BT_CTLR_TX_PWR_DYNAMIC_CONTROL)
radio_tx_power_set(_radio.scanner.tx_pwr_lvl);
#else
radio_tx_power_set(RADIO_TXP_DEFAULT);
#endif /* CONFIG_BT_CTLR_TX_PWR_DYNAMIC_CONTROL */
radio_isr_set(isr, NULL);
#if defined(CONFIG_BT_CTLR_ADV_EXT)
adv_scan_configure(_radio.scanner.phy, 1); /* if coded then use S8. */
/* if coded then use S8. */
radio_phy_set(_radio.scanner.phy, 1);
radio_pkt_configure(8, PDU_AC_PAYLOAD_SIZE_MAX,
(_radio.scanner.phy << 1));
#else /* !CONFIG_BT_CTLR_ADV_EXT */
adv_scan_configure(0, 0);
radio_phy_set(0, 0);
radio_pkt_configure(8, PDU_AC_PAYLOAD_SIZE_MAX, 0);
#endif /* !CONFIG_BT_CTLR_ADV_EXT */
radio_aa_set((u8_t *)&aa);
radio_crc_configure(((0x5bUL) | ((0x06UL) << 8) | ((0x00UL) << 16)),
0x555555);
chan_set(37 + _radio.scanner.chan++);
if (_radio.scanner.chan == 3) {
_radio.scanner.chan = 0U;
@ -9137,7 +9167,13 @@ event_connection_prepare_skip:
static void connection_configure(struct connection *conn)
{
adv_scan_conn_configure();
radio_reset();
#if defined(CONFIG_BT_CTLR_TX_PWR_DYNAMIC_CONTROL)
radio_tx_power_set(conn->tx_pwr_lvl);
#else
radio_tx_power_set(RADIO_TXP_DEFAULT);
#endif /* CONFIG_BT_CTLR_TX_PWR_DYNAMIC_CONTROL */
radio_isr_set(isr, NULL);
radio_aa_set(conn->access_addr);
radio_crc_configure(((0x5bUL) | ((0x06UL) << 8) | ((0x00UL) << 16)),
(((u32_t)conn->crc_init[2] << 16) |
@ -11526,6 +11562,10 @@ u32_t radio_adv_enable(u16_t interval, u8_t chan_map, u8_t filter_policy,
conn->rssi_sample_count = 0U;
#endif /* CONFIG_BT_CTLR_CONN_RSSI */
#if defined(CONFIG_BT_CTLR_TX_PWR_DYNAMIC_CONTROL)
conn->tx_pwr_lvl = RADIO_TXP_DEFAULT;
#endif /* CONFIG_BT_CTLR_TX_PWR_DYNAMIC_CONTROL */
/* wait for stable 32KHz clock */
k32src_wait();
@ -12073,6 +12113,10 @@ u32_t radio_connect_enable(u8_t adv_addr_type, u8_t *adv_addr, u16_t interval,
conn->rssi_sample_count = 0U;
#endif /* CONFIG_BT_CTLR_CONN_RSSI */
#if defined(CONFIG_BT_CTLR_TX_PWR_DYNAMIC_CONTROL)
conn->tx_pwr_lvl = RADIO_TXP_DEFAULT;
#endif /* CONFIG_BT_CTLR_TX_PWR_DYNAMIC_CONTROL */
/* wait for stable 32KHz clock */
k32src_wait();
@ -12419,25 +12463,6 @@ u8_t ll_terminate_ind_send(u16_t handle, u8_t reason)
return 0;
}
u8_t ll_tx_pwr_lvl_get(u16_t handle, u8_t type, s8_t *tx_pwr_lvl)
{
struct connection *conn;
conn = connection_get(handle);
if (!conn) {
return BT_HCI_ERR_UNKNOWN_CONN_ID;
}
/*TODO: check type here for current or maximum */
/* TODO: Support TX Power Level other than default when dynamic
* updates is implemented.
*/
*tx_pwr_lvl = RADIO_TXP_DEFAULT;
return 0;
}
#if defined(CONFIG_BT_CTLR_CONN_RSSI)
u8_t ll_rssi_get(u16_t handle, u8_t *rssi)
{
@ -12606,6 +12631,125 @@ u8_t ll_phy_req_send(u16_t handle, u8_t tx, u8_t flags, u8_t rx)
#endif /* CONFIG_BT_CTLR_PHY */
#endif /* CONFIG_BT_CONN */
u8_t ll_tx_pwr_lvl_get(u8_t handle_type,
u16_t handle, u8_t type, s8_t *tx_pwr_lvl)
{
switch (handle_type) {
#if defined(CONFIG_BT_BROADCASTER) &&\
defined(CONFIG_BT_CTLR_TX_PWR_DYNAMIC_CONTROL)
case (BT_HCI_VS_LL_HANDLE_TYPE_ADV): {
/* Unique global advertiser as part of ctrl */
*tx_pwr_lvl = _radio.advertiser.tx_pwr_lvl;
break;
}
#endif /* CONFIG_BT_BROADCASTER && CONFIG_BT_CTLR_TX_PWR_DYNAMIC_CONTROL */
#if defined(CONFIG_BT_OBSERVER) &&\
defined(CONFIG_BT_CTLR_TX_PWR_DYNAMIC_CONTROL)
case (BT_HCI_VS_LL_HANDLE_TYPE_SCAN): {
/* Unique global scanner as part of ctrl */
*tx_pwr_lvl = _radio.scanner.tx_pwr_lvl;
break;
}
#endif /* CONFIG_BT_OBSERVER && CONFIG_BT_CTLR_TX_PWR_DYNAMIC_CONTROL */
#if defined(CONFIG_BT_CONN)
case (BT_HCI_VS_LL_HANDLE_TYPE_CONN): {
struct connection *conn;
conn = connection_get(handle);
if (!conn) {
return BT_HCI_ERR_UNKNOWN_CONN_ID;
}
if (type) {
/* Level desired is maximum available */
*tx_pwr_lvl = radio_tx_power_max_get();
} else {
#if defined(CONFIG_BT_CTLR_TX_PWR_DYNAMIC_CONTROL)
/* Current level is requested */
*tx_pwr_lvl = conn->tx_pwr_lvl;
#else
*tx_pwr_lvl = RADIO_TXP_DEFAULT;
#endif /* CONFIG_BT_CTLR_TX_PWR_DYNAMIC_CONTROL */
}
break;
}
#endif /* CONFIG_BT_CONN */
default: {
return BT_HCI_ERR_UNKNOWN_CMD;
}
}
return BT_HCI_ERR_SUCCESS;
}
u8_t ll_tx_pwr_lvl_set(u8_t handle_type,
u16_t handle, s8_t *tx_pwr_lvl)
{
#if defined(CONFIG_BT_CTLR_TX_PWR_DYNAMIC_CONTROL)
if (*tx_pwr_lvl == BT_HCI_VS_LL_TX_POWER_LEVEL_NO_PREF) {
/* If no preference selected, then use default Tx power */
*tx_pwr_lvl = RADIO_TXP_DEFAULT;
}
/**
* Check that desired Tx power matches the achievable transceiver
* Tx power capabilities by flooring - if selected power matches than
* is used, otherwise next smaller power available is used.
*/
*tx_pwr_lvl = lll_radio_tx_pwr_floor(*tx_pwr_lvl);
#endif
switch (handle_type) {
#if defined(CONFIG_BT_CTLR_TX_PWR_DYNAMIC_CONTROL)
#if defined(CONFIG_BT_BROADCASTER)
case (BT_HCI_VS_LL_HANDLE_TYPE_ADV): {
/* Unique global advertiser as part of ctrl */
_radio.advertiser.tx_pwr_lvl = *tx_pwr_lvl;
break;
}
#endif /* CONFIG_BT_BROADCASTER */
#if defined(CONFIG_BT_OBSERVER)
case (BT_HCI_VS_LL_HANDLE_TYPE_SCAN): {
/* Unique global scanner as part of ctrl */
_radio.scanner.tx_pwr_lvl = *tx_pwr_lvl;
break;
}
#endif /* CONFIG_BT_OBSERVER */
#if defined(CONFIG_BT_CONN)
case (BT_HCI_VS_LL_HANDLE_TYPE_CONN): {
struct connection *conn;
conn = connection_get(handle);
if (!conn) {
return BT_HCI_ERR_UNKNOWN_CONN_ID;
}
conn->tx_pwr_lvl = *tx_pwr_lvl;
break;
}
#endif /* CONFIG_BT_CONN */
#endif /* CONFIG_BT_CTLR_TX_PWR_DYNAMIC_CONTROL */
default: {
return BT_HCI_ERR_UNKNOWN_CMD;
}
}
return BT_HCI_ERR_SUCCESS;
}
s8_t lll_radio_tx_pwr_min_get(void)
{
return radio_tx_power_min_get();
}
s8_t lll_radio_tx_pwr_max_get(void)
{
return radio_tx_power_max_get();
}
s8_t lll_radio_tx_pwr_floor(s8_t tx_pwr_lvl)
{
return radio_tx_power_floor(tx_pwr_lvl);
}
static u8_t tx_cmplt_get(u16_t *handle, u8_t *first, u8_t last)
{
u8_t _first;

View file

@ -332,6 +332,10 @@ struct connection {
u8_t rssi_reported;
u8_t rssi_sample_count;
#endif /* CONFIG_BT_CTLR_CONN_RSSI */
#if defined(CONFIG_BT_CTLR_TX_PWR_DYNAMIC_CONTROL)
s8_t tx_pwr_lvl;
#endif /* CONFIG_BT_CTLR_TX_PWR_DYNAMIC_CONTROL */
};
#define CONNECTION_T_SIZE MROUND(sizeof(struct connection))