Skip to content

Bluetooth: Controller: Adds path loss monitoring functionality #92450

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 17 additions & 6 deletions include/zephyr/bluetooth/hci_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -706,6 +706,7 @@ struct bt_hci_cp_le_set_tx_power_report_enable {
uint8_t remote_enable;
} __packed;

#define BT_HCI_OP_LE_SET_PATH_LOSS_REPORTING_PARAMETERS BT_OP(BT_OGF_LE, 0x0078) /* 0x2078 */
struct bt_hci_cp_le_set_path_loss_reporting_parameters {
uint16_t handle;
uint8_t high_threshold;
Expand All @@ -715,17 +716,28 @@ struct bt_hci_cp_le_set_path_loss_reporting_parameters {
uint16_t min_time_spent;
} __packed;

struct bt_hci_cp_le_set_path_loss_reporting_enable {
struct bt_hci_rp_le_set_path_loss_reporting_parameters {
uint16_t handle;
uint8_t enable;
uint8_t status;
} __packed;

#define BT_HCI_OP_LE_SET_PATH_LOSS_REPORTING_PARAMETERS BT_OP(BT_OGF_LE, 0x0078) /* 0x2078 */

#define BT_HCI_LE_PATH_LOSS_REPORTING_DISABLE 0x00
#define BT_HCI_LE_PATH_LOSS_REPORTING_ENABLE 0x01
#define BT_HCI_OP_LE_SET_PATH_LOSS_REPORTING_ENABLE BT_OP(BT_OGF_LE, 0x0079) /* 0x2079 */

struct bt_hci_cp_le_set_path_loss_reporting_enable {
uint16_t handle;
uint8_t enable;
} __packed;

struct bt_hci_rp_le_set_path_loss_reporting_enable {
uint16_t handle;
uint8_t status;
} __packed;

#define BT_HCI_OP_LE_SET_DEFAULT_SUBRATE BT_OP(BT_OGF_LE, 0x007D) /* 0x207D */

struct bt_hci_cp_le_set_default_subrate {
uint16_t subrate_min;
uint16_t subrate_max;
Expand All @@ -734,6 +746,8 @@ struct bt_hci_cp_le_set_default_subrate {
uint16_t supervision_timeout;
} __packed;

#define BT_HCI_OP_LE_SUBRATE_REQUEST BT_OP(BT_OGF_LE, 0x007E) /* 0x207E */

struct bt_hci_cp_le_subrate_request {
uint16_t handle;
uint16_t subrate_min;
Expand All @@ -743,9 +757,6 @@ struct bt_hci_cp_le_subrate_request {
uint16_t supervision_timeout;
} __packed;

#define BT_HCI_OP_LE_SET_DEFAULT_SUBRATE BT_OP(BT_OGF_LE, 0x007D) /* 0x207D */
#define BT_HCI_OP_LE_SUBRATE_REQUEST BT_OP(BT_OGF_LE, 0x007E) /* 0x207E */

#define BT_HCI_CTL_TO_HOST_FLOW_DISABLE 0x00
#define BT_HCI_CTL_TO_HOST_FLOW_ENABLE 0x01
#define BT_HCI_OP_SET_CTL_TO_HOST_FLOW BT_OP(BT_OGF_BASEBAND, 0x0031) /* 0x0c31 */
Expand Down
2 changes: 0 additions & 2 deletions subsys/bluetooth/controller/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,6 @@ config BT_CTLR_LE_POWER_CONTROL_SUPPORT
bool

config BT_CTLR_LE_PATH_LOSS_MONITORING_SUPPORT
depends on BT_CTLR_LE_POWER_CONTROL_SUPPORT
bool

config BT_CTLR_SUBRATING_SUPPORT
Expand Down Expand Up @@ -661,7 +660,6 @@ config BT_CTLR_LE_PATH_LOSS_MONITORING
bool "LE Path Loss Monitoring Feature"
depends on BT_CTLR_LE_PATH_LOSS_MONITORING_SUPPORT
default y if BT_PATH_LOSS_MONITORING
select BT_CTLR_LE_POWER_CONTROL
help
Enable support for LE Path Loss Monitoring feature that is defined in the
Bluetooth Core specification, Version 5.4 | Vol 6, Part B, Section 4.6.32.
Expand Down
92 changes: 92 additions & 0 deletions subsys/bluetooth/controller/hci/hci.c
Original file line number Diff line number Diff line change
Expand Up @@ -2793,6 +2793,55 @@ static void le_set_phy(struct net_buf *buf, struct net_buf **evt)
*evt = cmd_status(status);
}
#endif /* CONFIG_BT_CTLR_PHY */


#if defined(CONFIG_BT_CTLR_LE_PATH_LOSS_MONITORING)

#define PATH_LOSS_FACTOR_INVALID 0xFF
static void le_path_loss_set_parameters(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_le_set_path_loss_reporting_parameters *cmd = (void *)buf->data;
struct bt_hci_rp_le_set_path_loss_reporting_parameters *rp;

uint16_t handle = sys_le16_to_cpu(cmd->handle);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not particularly related to this PR, as it's basically done nowhere in the controller, but cmd->handle is only 12 bits. If we receive a a handle of 0xFFFF we should return BT_HCI_ERR_INVALID_PARAM, right?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very good point. Maybe it's worth making a Github issue and fixing this file up? 👍

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, could be a nice first-time issue as it's just fixing a missing check several places :) Chances are that it's being handled somewhere else that I'm not aware of @cvinayak ?

uint16_t min_time_spent = sys_le16_to_cpu(cmd->min_time_spent);

rp = hci_cmd_complete(evt, sizeof(*rp));
rp->handle = handle;

if (cmd->high_threshold + cmd->high_hysteresis > PATH_LOSS_FACTOR_INVALID ||
cmd->low_threshold < cmd->low_hysteresis ||
cmd->low_threshold > cmd->high_threshold ||
cmd->low_threshold + cmd->low_hysteresis > cmd->high_threshold - cmd->high_hysteresis) {
rp->status = BT_HCI_ERR_INVALID_PARAM;
return;
}

rp->status = ll_conn_set_path_loss_parameters(handle, cmd->high_threshold,
cmd->high_hysteresis, cmd->low_threshold,
cmd->low_hysteresis, min_time_spent);
}

#define PATH_LOSS_ENABLED 0x01
#define PATH_LOSS_DISABLED 0x00
static void le_path_loss_enable(struct net_buf *buf, struct net_buf **evt)
{
struct bt_hci_cp_le_set_path_loss_reporting_enable *cmd = (void *)buf->data;
struct bt_hci_rp_le_set_path_loss_reporting_enable *rp;

uint16_t handle = sys_le16_to_cpu(cmd->handle);

rp = hci_cmd_complete(evt, sizeof(*rp));
rp->handle = handle;

if (cmd->enable != PATH_LOSS_ENABLED &&
cmd->enable != PATH_LOSS_DISABLED) {
rp->status = BT_HCI_ERR_INVALID_PARAM;
}

rp->status = ll_conn_set_path_loss_reporting(handle, cmd->enable);
}
#endif /* CONFIG_BT_CTLR_LE_PATH_LOSS_MONITORING */
#endif /* CONFIG_BT_CONN */

#if defined(CONFIG_BT_CTLR_PRIVACY)
Expand Down Expand Up @@ -4767,6 +4816,17 @@ static int controller_cmd_handle(uint16_t ocf, struct net_buf *cmd,
le_set_phy(cmd, evt);
break;
#endif /* CONFIG_BT_CTLR_PHY */

#if defined(CONFIG_BT_CTLR_LE_PATH_LOSS_MONITORING)
case BT_OCF(BT_HCI_OP_LE_SET_PATH_LOSS_REPORTING_PARAMETERS):
le_path_loss_set_parameters(cmd, evt);
break;

case BT_OCF(BT_HCI_OP_LE_SET_PATH_LOSS_REPORTING_ENABLE):
le_path_loss_enable(cmd, evt);
break;

#endif /* CONFIG_BT_CTLR_LE_PATH_LOSS_MONITORING */
#endif /* CONFIG_BT_CONN */

#if defined(CONFIG_BT_CTLR_ADV_EXT)
Expand Down Expand Up @@ -8645,6 +8705,28 @@ static void le_req_peer_sca_complete(struct pdu_data *pdu, uint16_t handle,
sep->sca = scau->sca;
}
#endif /* CONFIG_BT_CTLR_SCA_UPDATE */
#if defined(CONFIG_BT_CTLR_LE_PATH_LOSS_MONITORING)
static void le_path_loss_threshold(struct pdu_data *pdu,
uint16_t handle,
struct net_buf *buf)
{
struct bt_hci_evt_le_path_loss_threshold *sep;
struct node_rx_path_loss *pl_pdu;

pl_pdu = (void *)pdu;

if (!(event_mask & BT_EVT_MASK_LE_META_EVENT) ||
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (!(event_mask & BT_EVT_MASK_LE_META_EVENT) ||
if ((event_mask & BT_EVT_MASK_LE_META_EVENT) == 0 ||

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer to leave it as is, since it's similar to how it's done in the other functions above.

Examples:

if (!(event_mask & BT_EVT_MASK_LE_META_EVENT) ||

if (!(event_mask & BT_EVT_MASK_LE_META_EVENT) ||

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll defer that to the maintainer of the controller. The reason why I mentioned here is that it's in violation of rule 85 of the Zephyr coding guidelines that does not allow using integers as controlling expression https://docs.zephyrproject.org/latest/contribute/coding_guidelines/index.html : https://gitlab.com/MISRA/MISRA-C/MISRA-C-2012/Example-Suite/-/blob/master/R_14_04.c

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cvinayak Perhaps we could make a Github issue and fix up this file to be compliant after this PR? Then we can get all the changes done at once.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixing it for a single file won't do much :D It's pretty much in every file and everywhere, since it has been the coding standard for many years before we adaptoed the aforementioned rule. I'm personally slowly going through some of the coding style violations in the LE Audio code, but there are thousands of cases for each rule :)

!(le_event_mask & BT_EVT_MASK_LE_PATH_LOSS_THRESHOLD)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
!(le_event_mask & BT_EVT_MASK_LE_PATH_LOSS_THRESHOLD)) {
(le_event_mask & BT_EVT_MASK_LE_PATH_LOSS_THRESHOLD) == 0) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same suggestion as above, since this would stick out from the other, similar functions.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return;
}

sep = meta_evt(buf, BT_HCI_EVT_LE_PATH_LOSS_THRESHOLD, sizeof(*sep));

sep->handle = sys_cpu_to_le16(handle);
sep->current_path_loss = pl_pdu->current_path_loss;
sep->zone_entered = pl_pdu->zone_entered;
}
#endif /* CONFIG_BT_CTLR_LE_PATH_LOSS_MONITORING */
#endif /* CONFIG_BT_CONN */

#if defined(CONFIG_BT_HCI_MESH_EXT)
Expand Down Expand Up @@ -8847,6 +8929,12 @@ static void encode_control(struct node_rx_pdu *node_rx,
#endif /* CONFIG_BT_CTLR_DF_VS_CONN_IQ_REPORT_16_BITS_IQ_SAMPLES */
return;
#endif /* CONFIG_BT_CTLR_DF_CONN_CTE_RX */

#if defined(CONFIG_BT_CTLR_LE_PATH_LOSS_MONITORING)
case NODE_RX_TYPE_PATH_LOSS:
le_path_loss_threshold(pdu_data, handle, buf);
return;
#endif /* CONFIG_BT_CTLR_LE_PATH_LOSS_MONITORING */
#endif /* CONFIG_BT_CONN */

#if defined(CONFIG_BT_CTLR_ADV_INDICATION)
Expand Down Expand Up @@ -9332,6 +9420,10 @@ uint8_t hci_get_class(struct node_rx_pdu *node_rx)
#endif /* CONFIG_BT_CTLR_PHY */

return HCI_CLASS_EVT_CONNECTION;
#if defined(CONFIG_BT_CTLR_LE_PATH_LOSS_MONITORING)
case NODE_RX_TYPE_PATH_LOSS:
return HCI_CLASS_EVT_REQUIRED;
#endif /* CONFIG_BT_CTLR_LE_PATH_LOSS_MONITORING */
#endif /* CONFIG_BT_CONN */

#if defined(CONFIG_BT_CTLR_SYNC_ISO) || defined(CONFIG_BT_CTLR_CONN_ISO)
Expand Down
9 changes: 9 additions & 0 deletions subsys/bluetooth/controller/include/ll.h
Original file line number Diff line number Diff line change
Expand Up @@ -343,3 +343,12 @@ void ll_coex_ticker_id_get(uint8_t * const instance_index,
uint8_t * const ticker_id);
void ll_radio_state_abort(void);
uint32_t ll_radio_state_is_idle(void);

uint8_t ll_conn_set_path_loss_parameters(uint16_t handle,
uint8_t high_threshold,
uint8_t high_hysteresis,
uint8_t low_threshold,
uint8_t low_hysteresis,
uint16_t min_time_spent);

uint8_t ll_conn_set_path_loss_reporting(uint16_t handle, uint8_t enable);
1 change: 1 addition & 0 deletions subsys/bluetooth/controller/ll_sw/lll.h
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,7 @@ enum node_rx_type {
NODE_RX_TYPE_IQ_SAMPLE_REPORT_ULL_RELEASE,
NODE_RX_TYPE_IQ_SAMPLE_REPORT_LLL_RELEASE,
NODE_RX_TYPE_SYNC_TRANSFER_RECEIVED,
NODE_RX_TYPE_PATH_LOSS,
/* Signals retention (ie non-release) of rx node */
NODE_RX_TYPE_RETAIN,

Expand Down
21 changes: 21 additions & 0 deletions subsys/bluetooth/controller/ll_sw/lll_conn.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,22 @@
#define LLL_CONN_MIC_PASS 1
#define LLL_CONN_MIC_FAIL 2


struct path_loss_params {
uint16_t min_time_spent;
uint8_t enabled;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some values like enabled and conn_handle can use fewer bits than the full size of their types. Consider if we can optimize the size here a bit

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In practice, I think they can be optimized (like enabled), though it seems the Bluetooth spec only specifies between 1 and 2 octets without specifying meaningful bits, so I'd prefer to keep it as is.

uint8_t high_threshold;
uint8_t high_hysteresis;
uint8_t low_threshold;
uint8_t low_hysteresis;
} __packed;

struct path_loss_state {
uint16_t conn_handle;
uint16_t min_time_counter;
uint8_t new_zone;
} __packed;

struct lll_tx {
uint16_t handle;
void *node;
Expand Down Expand Up @@ -157,6 +173,11 @@ struct lll_conn {
uint8_t rssi_sample_count;
#endif /* CONFIG_BT_CTLR_CONN_RSSI_EVENT */
#endif /* CONFIG_BT_CTLR_CONN_RSSI */
#if defined(CONFIG_BT_CTLR_LE_PATH_LOSS_MONITORING)
struct path_loss_params pl_params;
struct path_loss_state pl_state;
uint8_t pl_current_zone;
#endif /* CONFIG_BT_CTLR_LE_PATH_LOSS_MONITORING */

#if defined(CONFIG_BT_CTLR_CONN_META)
struct lll_conn_meta conn_meta;
Expand Down
3 changes: 3 additions & 0 deletions subsys/bluetooth/controller/ll_sw/ull.c
Original file line number Diff line number Diff line change
Expand Up @@ -1392,6 +1392,7 @@ void ll_rx_dequeue(void)
#if defined(CONFIG_BT_CTLR_DTM_HCI_DF_IQ_REPORT)
case NODE_RX_TYPE_DTM_IQ_SAMPLE_REPORT:
#endif /* CONFIG_BT_CTLR_DTM_HCI_DF_IQ_REPORT */
case NODE_RX_TYPE_PATH_LOSS:

/* Ensure that at least one 'case' statement is present for this
* code block.
Expand Down Expand Up @@ -1582,6 +1583,7 @@ void ll_rx_mem_release(void **node_rx)
#if defined(CONFIG_BT_CTLR_ISO)
case NODE_RX_TYPE_ISO_PDU:
#endif
case NODE_RX_TYPE_PATH_LOSS:

/* Ensure that at least one 'case' statement is present for this
* code block.
Expand Down Expand Up @@ -2965,6 +2967,7 @@ static inline void rx_demux_rx(memq_link_t *link, struct node_rx_hdr *rx)
#if defined(CONFIG_BT_CTLR_SCAN_INDICATION)
case NODE_RX_TYPE_SCAN_INDICATION:
#endif /* CONFIG_BT_CTLR_SCAN_INDICATION */
case NODE_RX_TYPE_PATH_LOSS:

case NODE_RX_TYPE_RELEASE:
{
Expand Down
50 changes: 50 additions & 0 deletions subsys/bluetooth/controller/ll_sw/ull_conn.c
Original file line number Diff line number Diff line change
Expand Up @@ -2910,6 +2910,56 @@ uint8_t ull_conn_lll_phy_active(struct ll_conn *conn, uint8_t phys)
return 1;
}

#if defined(CONFIG_BT_CTLR_LE_PATH_LOSS_MONITORING)
uint8_t ll_conn_set_path_loss_parameters(uint16_t handle,
uint8_t high_threshold,
uint8_t high_hysteresis,
uint8_t low_threshold,
uint8_t low_hysteresis,
uint16_t min_time_spent)
{
struct ll_conn *conn;

conn = ll_connected_get(handle);

if (!conn) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (!conn) {
if (conn == NULL) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer to leave this as is, since this file has many cases of being done this way.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return BT_HCI_ERR_UNKNOWN_CONN_ID;
}

conn->lll.pl_params.high_threshold = high_threshold;
conn->lll.pl_params.high_hysteresis = high_hysteresis;
conn->lll.pl_params.low_threshold = low_threshold;
conn->lll.pl_params.low_hysteresis = low_hysteresis;
conn->lll.pl_params.min_time_spent = min_time_spent;

/* Reset the counter and zone after any update from the host */
conn->lll.pl_state.min_time_counter = 0;
conn->lll.pl_current_zone = BT_HCI_LE_ZONE_ENTERED_LOW;

return BT_HCI_ERR_SUCCESS;
}

uint8_t ll_conn_set_path_loss_reporting(uint16_t handle, uint8_t enable)
{
struct ll_conn *conn;

conn = ll_connected_get(handle);

if (!conn) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (!conn) {
if (conn == NULL) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer to leave this as is, since this file has many cases of being done this way.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return BT_HCI_ERR_UNKNOWN_CONN_ID;
}

conn->lll.pl_params.enabled = enable;

/* Reset the counter and zone after any update from the host */
conn->lll.pl_state.min_time_counter = 0;
conn->lll.pl_current_zone = BT_HCI_LE_ZONE_ENTERED_LOW;

return BT_HCI_ERR_SUCCESS;

}
#endif /* CONFIG_BT_CTLR_LE_PATH_LOSS_MONITORING */

uint8_t ull_is_lll_tx_queue_empty(struct ll_conn *conn)
{
return (memq_peek(conn->lll.memq_tx.head, conn->lll.memq_tx.tail, NULL) == NULL);
Expand Down
10 changes: 10 additions & 0 deletions subsys/bluetooth/controller/ll_sw/ull_conn_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -138,3 +138,13 @@ void ull_conn_resume_rx_data(struct ll_conn *conn);
* @brief Check if the lower link layer transmit queue is empty
*/
uint8_t ull_is_lll_tx_queue_empty(struct ll_conn *conn);

/**
* @brief Set path loss parameters
*/
void ull_path_loss_set_parameters(void);

/**
* @brief Enable path loss reporting
*/
void ull_enable_path_loss_reporting(void);
5 changes: 5 additions & 0 deletions subsys/bluetooth/controller/ll_sw/ull_conn_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -301,3 +301,8 @@ struct node_rx_sca {
uint8_t status;
uint8_t sca;
};

struct node_rx_path_loss {
uint8_t current_path_loss;
uint8_t zone_entered;
};