diff --git a/include/zephyr/bluetooth/conn.h b/include/zephyr/bluetooth/conn.h index 09b8321d685..bdc644d82e9 100644 --- a/include/zephyr/bluetooth/conn.h +++ b/include/zephyr/bluetooth/conn.h @@ -479,6 +479,42 @@ struct bt_conn_le_tx_power { int8_t max_level; }; + +/** LE Transmit Power Reporting Structure */ +struct bt_conn_le_tx_power_report { + + /** Reason for Transmit power reporting, + * as documented in Core Spec. Version 5.4 Vol. 4, Part E, 7.7.65.33. + */ + uint8_t reason; + + /** Phy of Transmit power reporting. */ + enum bt_conn_le_tx_power_phy phy; + + /** Transmit power level + * - 0xXX - Transmit power level + * + Range: -127 to 20 + * + Units: dBm + * + * - 0x7E - Remote device is not managing power levels on this PHY. + * - 0x7F - Transmit power level is not available + */ + int8_t tx_power_level; + + /** Bit 0: Transmit power level is at minimum level. + * Bit 1: Transmit power level is at maximum level. + */ + uint8_t tx_power_level_flag; + + /** Change in transmit power level + * - 0xXX - Change in transmit power level (positive indicates increased + * power, negative indicates decreased power, zero indicates unchanged) + * Units: dB + * - 0x7F - Change is not available or is out of range. + */ + int8_t delta; +}; + /** @brief Passkey Keypress Notification type * * The numeric values are the same as in the Core specification for Pairing @@ -530,6 +566,41 @@ int bt_conn_get_remote_info(struct bt_conn *conn, int bt_conn_le_get_tx_power_level(struct bt_conn *conn, struct bt_conn_le_tx_power *tx_power_level); +/** @brief Get local enhanced connection transmit power level. + * + * @param conn Connection object. + * @param tx_power Transmit power level descriptor. + * + * @return Zero on success or (negative) error code on failure. + * @retval -ENOBUFS HCI command buffer is not available. + */ +int bt_conn_le_enhanced_get_tx_power_level(struct bt_conn *conn, + struct bt_conn_le_tx_power *tx_power); + +/** @brief Get remote (peer) transmit power level. + * + * @param conn Connection object. + * @param phy PHY information. + * + * @return Zero on success or (negative) error code on failure. + * @retval -ENOBUFS HCI command buffer is not available. + */ +int bt_conn_le_get_remote_tx_power_level(struct bt_conn *conn, + enum bt_conn_le_tx_power_phy phy); + +/** @brief Enable transmit power reporting. + * + * @param conn Connection object. + * @param local_enable Enable/disable reporting for local. + * @param remote_enable Enable/disable reporting for remote. + * + * @return Zero on success or (negative) error code on failure. + * @retval -ENOBUFS HCI command buffer is not available. + */ +int bt_conn_le_set_tx_power_report_enable(struct bt_conn *conn, + bool local_enable, + bool remote_enable); + /** @brief Update the connection parameters. * * If the local device is in the peripheral role then updating the connection @@ -1052,6 +1123,22 @@ struct bt_conn_cb { const struct bt_df_conn_iq_samples_report *iq_report); #endif /* CONFIG_BT_DF_CONNECTION_CTE_RX */ +#if defined(CONFIG_BT_TRANSMIT_POWER_CONTROL) + /** @brief LE Read Remote Transmit Power Level procedure has completed or LE + * Transmit Power Reporting event. + * + * This callback notifies the application that either the remote transmit power level + * has been read from the peer or transmit power level has changed for the local or + * remote controller when transmit power reporting is enabled for the respective side + * using @ref bt_conn_le_set_tx_power_report_enable. + * + * @param conn Connection object. + * @param report Transmit power report. + */ + void (*tx_power_report)(struct bt_conn *conn, + const struct bt_conn_le_tx_power_report *report); +#endif /* CONFIG_BT_TRANSMIT_POWER_CONTROL */ + struct bt_conn_cb *_next; }; diff --git a/include/zephyr/bluetooth/hci_types.h b/include/zephyr/bluetooth/hci_types.h index 4d637bbe4e2..f1cb92f372c 100644 --- a/include/zephyr/bluetooth/hci_types.h +++ b/include/zephyr/bluetooth/hci_types.h @@ -571,6 +571,35 @@ struct bt_hci_rp_read_tx_power_level { int8_t tx_power_level; } __packed; +#define BT_HCI_LE_TX_POWER_PHY_1M 0x01 +#define BT_HCI_LE_TX_POWER_PHY_2M 0x02 +#define BT_HCI_LE_TX_POWER_PHY_CODED_S8 0x03 +#define BT_HCI_LE_TX_POWER_PHY_CODED_S2 0x04 +#define BT_HCI_OP_LE_ENH_READ_TX_POWER_LEVEL BT_OP(BT_OGF_LE, 0x0076) +struct bt_hci_cp_le_read_tx_power_level { + uint16_t handle; + uint8_t phy; +} __packed; + +struct bt_hci_rp_le_read_tx_power_level { + uint8_t status; + uint16_t handle; + uint8_t phy; + int8_t current_tx_power_level; + int8_t max_tx_power_level; +} __packed; + +#define BT_HCI_OP_LE_READ_REMOTE_TX_POWER_LEVEL BT_OP(BT_OGF_LE, 0x0077) + +#define BT_HCI_LE_TX_POWER_REPORT_DISABLE 0x00 +#define BT_HCI_LE_TX_POWER_REPORT_ENABLE 0x01 +#define BT_HCI_OP_LE_SET_TX_POWER_REPORT_ENABLE BT_OP(BT_OGF_LE, 0x007A) +struct bt_hci_cp_le_set_tx_power_report_enable { + uint16_t handle; + uint8_t local_enable; + uint8_t remote_enable; +} __packed; + #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) @@ -2905,6 +2934,26 @@ struct bt_hci_evt_le_req_peer_sca_complete { uint8_t sca; } __packed; +/** Reason for Transmit power reporting. + */ +/* Local Transmit power changed. */ +#define BT_HCI_LE_TX_POWER_REPORT_REASON_LOCAL_CHANGED 0x00 +/* Remote Transmit power changed. */ +#define BT_HCI_LE_TX_POWER_REPORT_REASON_REMOTE_CHANGED 0x01 +/* HCI_LE_Read_Remote_Transmit_Power_Level command completed. */ +#define BT_HCI_LE_TX_POWER_REPORT_REASON_READ_REMOTE_COMPLETED 0x02 + +#define BT_HCI_EVT_LE_TRANSMIT_POWER_REPORT 0x21 +struct bt_hci_evt_le_transmit_power_report { + uint8_t status; + uint16_t handle; + uint8_t reason; + uint8_t phy; + int8_t tx_power_level; + uint8_t tx_power_level_flag; + int8_t delta; +} __packed; + #define BT_HCI_EVT_LE_BIGINFO_ADV_REPORT 0x22 struct bt_hci_evt_le_biginfo_adv_report { uint16_t sync_handle; diff --git a/subsys/bluetooth/Kconfig b/subsys/bluetooth/Kconfig index e02901cd906..912846e4cda 100644 --- a/subsys/bluetooth/Kconfig +++ b/subsys/bluetooth/Kconfig @@ -175,6 +175,14 @@ config BT_SCA_UPDATE depends on !BT_CTLR || BT_CTLR_SCA_UPDATE_SUPPORT help Enable support for Bluetooth 5.1 Sleep Clock Accuracy Update Procedure + +config BT_TRANSMIT_POWER_CONTROL + bool "LE Power Control" + depends on !BT_CTLR || BT_CTLR_LE_POWER_CONTROL_SUPPORT + help + Enable support for LE Power Control Request feature that is defined in the + Bluetooth Core specification, Version 5.4 | Vol 6, Part B, Section 4.6.31. + endif # BT_CONN rsource "Kconfig.iso" diff --git a/subsys/bluetooth/host/conn.c b/subsys/bluetooth/host/conn.c index 1fd2f5fbc49..06a3e7553a2 100644 --- a/subsys/bluetooth/host/conn.c +++ b/subsys/bluetooth/host/conn.c @@ -2619,16 +2619,116 @@ static int bt_conn_get_tx_power_level(struct bt_conn *conn, uint8_t type, return 0; } +#if defined(CONFIG_BT_TRANSMIT_POWER_CONTROL) +void notify_tx_power_report(struct bt_conn *conn, + struct bt_conn_le_tx_power_report report) +{ + for (struct bt_conn_cb *cb = callback_list; cb; cb = cb->_next) { + if (cb->tx_power_report) { + cb->tx_power_report(conn, &report); + } + } + + STRUCT_SECTION_FOREACH(bt_conn_cb, cb) + { + if (cb->tx_power_report) { + cb->tx_power_report(conn, &report); + } + } +} + +int bt_conn_le_enhanced_get_tx_power_level(struct bt_conn *conn, + struct bt_conn_le_tx_power *tx_power) +{ + int err; + struct bt_hci_rp_le_read_tx_power_level *rp; + struct net_buf *rsp; + struct bt_hci_cp_le_read_tx_power_level *cp; + struct net_buf *buf; + + if (!tx_power->phy) { + return -EINVAL; + } + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_ENH_READ_TX_POWER_LEVEL, sizeof(*cp)); + if (!buf) { + return -ENOBUFS; + } + + cp = net_buf_add(buf, sizeof(*cp)); + cp->handle = sys_cpu_to_le16(conn->handle); + cp->phy = tx_power->phy; + + err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_ENH_READ_TX_POWER_LEVEL, buf, &rsp); + if (err) { + return err; + } + + rp = (void *) rsp->data; + tx_power->phy = rp->phy; + tx_power->current_level = rp->current_tx_power_level; + tx_power->max_level = rp->max_tx_power_level; + net_buf_unref(rsp); + + return 0; +} + +int bt_conn_le_get_remote_tx_power_level(struct bt_conn *conn, + enum bt_conn_le_tx_power_phy phy) +{ + struct bt_hci_cp_le_read_tx_power_level *cp; + struct net_buf *buf; + + if (!phy) { + return -EINVAL; + } + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_READ_REMOTE_TX_POWER_LEVEL, sizeof(*cp)); + if (!buf) { + return -ENOBUFS; + } + + cp = net_buf_add(buf, sizeof(*cp)); + cp->handle = sys_cpu_to_le16(conn->handle); + cp->phy = phy; + + return bt_hci_cmd_send_sync(BT_HCI_OP_LE_READ_REMOTE_TX_POWER_LEVEL, buf, NULL); +} + +int bt_conn_le_set_tx_power_report_enable(struct bt_conn *conn, + bool local_enable, + bool remote_enable) +{ + struct bt_hci_cp_le_set_tx_power_report_enable *cp; + struct net_buf *buf; + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_TX_POWER_REPORT_ENABLE, sizeof(*cp)); + if (!buf) { + return -ENOBUFS; + } + + cp = net_buf_add(buf, sizeof(*cp)); + cp->handle = sys_cpu_to_le16(conn->handle); + cp->local_enable = local_enable ? BT_HCI_LE_TX_POWER_REPORT_ENABLE : + BT_HCI_LE_TX_POWER_REPORT_DISABLE; + cp->remote_enable = remote_enable ? BT_HCI_LE_TX_POWER_REPORT_ENABLE : + BT_HCI_LE_TX_POWER_REPORT_DISABLE; + + return bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_TX_POWER_REPORT_ENABLE, buf, NULL); +} +#endif /* CONFIG_BT_TRANSMIT_POWER_CONTROL */ + int bt_conn_le_get_tx_power_level(struct bt_conn *conn, struct bt_conn_le_tx_power *tx_power_level) { int err; if (tx_power_level->phy != 0) { - /* Extend the implementation when LE Enhanced Read Transmit - * Power Level HCI command is available for use. - */ - return -ENOTSUP; + if (IS_ENABLED(CONFIG_BT_TRANSMIT_POWER_CONTROL)) { + return bt_conn_le_enhanced_get_tx_power_level(conn, tx_power_level); + } else { + return -ENOTSUP; + } } err = bt_conn_get_tx_power_level(conn, BT_TX_POWER_LEVEL_CURRENT, diff --git a/subsys/bluetooth/host/conn_internal.h b/subsys/bluetooth/host/conn_internal.h index 7741e259a61..a13adff704a 100644 --- a/subsys/bluetooth/host/conn_internal.h +++ b/subsys/bluetooth/host/conn_internal.h @@ -364,6 +364,9 @@ void notify_le_phy_updated(struct bt_conn *conn); bool le_param_req(struct bt_conn *conn, struct bt_le_conn_param *param); +void notify_tx_power_report(struct bt_conn *conn, + struct bt_conn_le_tx_power_report report); + #if defined(CONFIG_BT_SMP) /* If role specific LTK is present */ bool bt_conn_ltk_present(const struct bt_conn *conn); diff --git a/subsys/bluetooth/host/hci_core.c b/subsys/bluetooth/host/hci_core.c index de4fb637b49..b37cd45e1ae 100644 --- a/subsys/bluetooth/host/hci_core.c +++ b/subsys/bluetooth/host/hci_core.c @@ -2399,6 +2399,33 @@ int bt_hci_register_vnd_evt_cb(bt_hci_vnd_evt_cb_t cb) } #endif /* CONFIG_BT_HCI_VS_EVT_USER */ +#if defined(CONFIG_BT_TRANSMIT_POWER_CONTROL) +void bt_hci_le_transmit_power_report(struct net_buf *buf) +{ + struct bt_hci_evt_le_transmit_power_report *evt; + struct bt_conn_le_tx_power_report report; + struct bt_conn *conn; + + evt = net_buf_pull_mem(buf, sizeof(*evt)); + conn = bt_conn_lookup_handle(sys_le16_to_cpu(evt->handle), BT_CONN_TYPE_LE); + if (!conn) { + LOG_ERR("Unknown conn handle 0x%04X for transmit power report", + sys_le16_to_cpu(evt->handle)); + return; + } + + report.reason = evt->reason; + report.phy = evt->phy; + report.tx_power_level = evt->tx_power_level; + report.tx_power_level_flag = evt->tx_power_level_flag; + report.delta = evt->delta; + + notify_tx_power_report(conn, report); + + bt_conn_unref(conn); +} +#endif /* CONFIG_BT_TRANSMIT_POWER_CONTROL */ + static const struct event_handler vs_events[] = { #if defined(CONFIG_BT_DF_VS_CL_IQ_REPORT_16_BITS_IQ_SAMPLES) EVENT_HANDLER(BT_HCI_EVT_VS_LE_CONNECTIONLESS_IQ_REPORT, @@ -2544,6 +2571,10 @@ static const struct event_handler meta_events[] = { EVENT_HANDLER(BT_HCI_EVT_LE_CTE_REQUEST_FAILED, bt_hci_le_df_cte_req_failed, sizeof(struct bt_hci_evt_le_cte_req_failed)), #endif /* CONFIG_BT_DF_CONNECTION_CTE_REQ */ +#if defined(CONFIG_BT_TRANSMIT_POWER_CONTROL) + EVENT_HANDLER(BT_HCI_EVT_LE_TRANSMIT_POWER_REPORT, bt_hci_le_transmit_power_report, + sizeof(struct bt_hci_evt_le_transmit_power_report)), +#endif /* CONFIG_BT_TRANSMIT_POWER_CONTROL */ #if defined(CONFIG_BT_PER_ADV_SYNC_RSP) EVENT_HANDLER(BT_HCI_EVT_LE_PER_ADVERTISING_REPORT_V2, bt_hci_le_per_adv_report_v2, sizeof(struct bt_hci_evt_le_per_advertising_report_v2)), @@ -3089,6 +3120,9 @@ static int le_set_event_mask(void) BT_FEAT_LE_PHY_CODED(bt_dev.le.features))) { mask |= BT_EVT_MASK_LE_PHY_UPDATE_COMPLETE; } + if (IS_ENABLED(CONFIG_BT_TRANSMIT_POWER_CONTROL)) { + mask |= BT_EVT_MASK_LE_TRANSMIT_POWER_REPORTING; + } } if (IS_ENABLED(CONFIG_BT_SMP) &&