From c0337262a1282ed2edac31b577f5025f14541f15 Mon Sep 17 00:00:00 2001 From: Timothy Keys Date: Mon, 13 Oct 2025 16:01:31 +0100 Subject: [PATCH 1/3] bluetooth: host: Add Shorter Connection Intervals support This commit adds support for the Shorter Connection Intervals feature to the Bluetooth host. Signed-off-by: Timothy Keys --- include/zephyr/bluetooth/conn.h | 269 +++++++++++++++++++++++++- include/zephyr/bluetooth/hci_types.h | 80 ++++++++ subsys/bluetooth/Kconfig | 11 +- subsys/bluetooth/controller/Kconfig | 14 ++ subsys/bluetooth/host/conn.c | 199 ++++++++++++++++++- subsys/bluetooth/host/conn_internal.h | 3 + subsys/bluetooth/host/hci_core.c | 85 +++++++- 7 files changed, 657 insertions(+), 4 deletions(-) diff --git a/include/zephyr/bluetooth/conn.h b/include/zephyr/bluetooth/conn.h index f07e35e315595..4bcd17c13d7cf 100644 --- a/include/zephyr/bluetooth/conn.h +++ b/include/zephyr/bluetooth/conn.h @@ -252,6 +252,169 @@ struct bt_conn_le_subrate_changed { uint16_t supervision_timeout; }; +/** @brief Maximum Connection Interval Groups possible + * + * The practical maximum is 41 groups based on HCI event size constraints: + * (HCI event max size - cmd_complete overhead - response fields) / sizeof(group) + * (255 - 4 - 3) / 6 = 41 groups max + */ +#define BT_CONN_LE_MAX_CONN_INTERVAL_GROUPS 41 + +/** @brief Minimum supported connection interval group + * + * Each group represents an arithmetic sequence of supported connection intervals: + * min, min + stride, min + 2 * stride, ..., min + n * stride (where min + n * stride <= max) + * + * Example: min = 10 (1250 us), max = 60 (7500 us), stride = 5 (625 us) + * -> Supported: 1250 us, 1875 us, 2500 us, ..., 6875 us, 7500 us + */ +struct bt_conn_le_min_conn_interval_group { + /** @brief Lower bound of group interval range + * + * Unit: 125 microseconds + * + * Range: @ref BT_HCI_LE_SCI_INTERVAL_MIN_125US - @ref BT_HCI_LE_SCI_INTERVAL_MAX_125US + */ + uint16_t min_125us; + /** @brief Upper bound of group interval range + * + * Unit: 125 microseconds + * + * Range: @ref BT_HCI_LE_SCI_INTERVAL_MIN_125US - @ref BT_HCI_LE_SCI_INTERVAL_MAX_125US + */ + uint16_t max_125us; + /** @brief Increment between consecutive supported intervals + * + * Unit: 125 microseconds + * + * Range: @ref BT_HCI_LE_SCI_STRIDE_MIN_125US - @ref BT_HCI_LE_SCI_INTERVAL_MAX_125US + */ + uint16_t stride_125us; +}; + +/** Minimum supported connection interval information */ +struct bt_conn_le_min_conn_interval_info { + /** @brief Minimum supported connection interval + * + * Unit: microseconds + * + * Range: @ref BT_HCI_LE_MIN_SUPP_CONN_INT_MIN_US - @ref BT_HCI_LE_MIN_SUPP_CONN_INT_MAX_US + */ + uint16_t min_supported_conn_interval_us; + /** @brief Number of interval groups. + * + * Range: 0 - @ref BT_CONN_LE_MAX_CONN_INTERVAL_GROUPS + * + * If zero, the controller only supports Rounded ConnInterval Values (RCV). + */ + uint8_t num_groups; + /** @brief Array of supported connection interval groups. + * + * Multiple groups allow representing non-contiguous or differently-strided ranges. + */ + struct bt_conn_le_min_conn_interval_group groups[BT_CONN_LE_MAX_CONN_INTERVAL_GROUPS]; +}; + +/** Connection rate parameters for LE connections */ +struct bt_conn_le_conn_rate_param { + /** @brief Minimum connection interval + * + * Unit: 125 microseconds + * + * Range: @ref BT_HCI_LE_SCI_INTERVAL_MIN_125US - @ref BT_HCI_LE_SCI_INTERVAL_MAX_125US + */ + uint16_t interval_min_125us; + /** @brief Maximum connection interval + * + * Unit: 125 microseconds + * + * Range: @ref BT_HCI_LE_SCI_INTERVAL_MIN_125US - @ref BT_HCI_LE_SCI_INTERVAL_MAX_125US + */ + uint16_t interval_max_125us; + /** @brief Minimum subrate factor + * + * Range: @ref BT_HCI_LE_SUBRATE_FACTOR_MIN - @ref BT_HCI_LE_SUBRATE_FACTOR_MAX + */ + uint16_t subrate_min; + /** @brief Maximum subrate factor + * + * Range: @ref BT_HCI_LE_SUBRATE_FACTOR_MIN - @ref BT_HCI_LE_SUBRATE_FACTOR_MAX + */ + uint16_t subrate_max; + /** @brief Maximum Peripheral latency + * + * Unit: subrated connection intervals @ref bt_conn_le_conn_rate_changed.subrate_factor + * + * Range: @ref BT_HCI_LE_PERIPHERAL_LATENCY_MIN - @ref BT_HCI_LE_PERIPHERAL_LATENCY_MAX + */ + uint16_t max_latency; + /** @brief Minimum number of underlying connection events to remain active + * after a packet containing a Link Layer PDU with a non-zero Length + * field is sent or received. + * + * Range: @ref BT_HCI_LE_CONTINUATION_NUM_MIN - @ref BT_HCI_LE_CONTINUATION_NUM_MAX + */ + uint16_t continuation_number; + /** @brief Connection Supervision timeout + * + * Unit: 10 milliseconds + * + * Range: @ref BT_HCI_LE_SUPERVISON_TIMEOUT_MIN - @ref BT_HCI_LE_SUPERVISON_TIMEOUT_MAX + */ + uint16_t supervision_timeout_10ms; + /** @brief Minimum length of connection event + * + * Unit: 125 microseconds + * + * Range: @ref BT_HCI_LE_SCI_CE_LEN_MIN_125US - @ref BT_HCI_LE_SCI_CE_LEN_MAX_125US + */ + uint16_t min_ce_len_125us; + /** @brief Maximum length of connection event + * + * Unit: 125 microseconds + * + * Range: @ref BT_HCI_LE_SCI_CE_LEN_MIN_125US - @ref BT_HCI_LE_SCI_CE_LEN_MAX_125US + */ + uint16_t max_ce_len_125us; +}; + +/** Updated connection rate parameters */ +struct bt_conn_le_conn_rate_changed { + /** Connection interval + * + * Unit: microseconds + * + * Range: @ref BT_HCI_LE_SCI_INTERVAL_MIN_US - @ref BT_HCI_LE_SCI_INTERVAL_MAX_US + */ + uint32_t interval_us; + /** Connection subrate factor + * + * Range: @ref BT_HCI_LE_SUBRATE_FACTOR_MIN - @ref BT_HCI_LE_SUBRATE_FACTOR_MAX + */ + uint16_t subrate_factor; + /** Peripheral latency + * + * Unit: subrated connection intervals @ref bt_conn_le_conn_rate_changed.subrate_factor + * + * Range: @ref BT_HCI_LE_PERIPHERAL_LATENCY_MIN - @ref BT_HCI_LE_PERIPHERAL_LATENCY_MAX + */ + uint16_t peripheral_latency; + /** Number of underlying connection events to remain active after + * a packet containing a Link Layer PDU with a non-zero Length + * field is sent or received. + * + * Range: @ref BT_HCI_LE_CONTINUATION_NUM_MIN - @ref BT_HCI_LE_CONTINUATION_NUM_MAX + */ + uint16_t continuation_number; + /** Connection Supervision timeout + * + * Unit: 10 milliseconds + * + * Range: @ref BT_HCI_LE_SUPERVISON_TIMEOUT_MIN - @ref BT_HCI_LE_SUPERVISON_TIMEOUT_MAX + */ + uint16_t supervision_timeout_10ms; +}; + /** Read all remote features complete callback params */ struct bt_conn_le_read_all_remote_feat_complete { /** @brief HCI Status from LE Read All Remote Features Complete event. @@ -890,7 +1053,12 @@ struct bt_conn_le_info { const bt_addr_le_t *local; /** Remote device address used during connection setup. */ const bt_addr_le_t *remote; - uint16_t interval; /**< Connection interval */ + /** Connection interval in microseconds */ + uint32_t interval_us; +#if !defined(CONFIG_BT_SHORTER_CONNECTION_INTERVALS) || defined(__DOXYGEN__) + /** Connection interval in units of 1.25 ms */ + uint16_t interval; +#endif /* !CONFIG_BT_SHORTER_CONNECTION_INTERVALS */ uint16_t latency; /**< Connection peripheral latency */ uint16_t timeout; /**< Connection supervision timeout */ @@ -924,6 +1092,12 @@ struct bt_conn_le_info { */ #define BT_CONN_INTERVAL_TO_US(interval) ((interval) * 1250U) +/** @brief Convert shorter connection interval to microseconds + * + * Multiply by 125 to get microseconds. + */ +#define BT_CONN_SCI_INTERVAL_TO_US(interval) ((interval) * BT_HCI_LE_SCI_INTERVAL_UNIT_US) + /** BR/EDR Connection Info Structure */ struct bt_conn_br_info { const bt_addr_t *dst; /**< Destination (Remote) BR/EDR address */ @@ -1329,6 +1503,74 @@ int bt_conn_le_subrate_set_defaults(const struct bt_conn_le_subrate_param *param int bt_conn_le_subrate_request(struct bt_conn *conn, const struct bt_conn_le_subrate_param *params); +/** @brief Read Minimum Supported Connection Interval Groups. + * + * Read the minimum supported connection interval and supported interval + * groups from the local controller. This information describes what the + * local controller supports. + * + * @sa bt_conn_le_read_min_conn_interval if only + * @ref bt_conn_le_min_conn_interval_info.min_supported_conn_interval_us + * is needed. + * + * @kconfig_dep{CONFIG_BT_SHORTER_CONNECTION_INTERVALS} + * + * @param info Pointer to structure to receive the minimum connection interval + * information. + * + * @return Zero on success or (negative) error code on failure. + */ +int bt_conn_le_read_min_conn_interval_groups(struct bt_conn_le_min_conn_interval_info *info); + +/** @brief Read Minimum Supported Connection Interval. + * + * Read the minimum supported connection interval from the local controller. + * + * @sa bt_conn_le_read_min_conn_interval_groups if groups are needed. + * + * @kconfig_dep{CONFIG_BT_SHORTER_CONNECTION_INTERVALS} + * + * @param min_interval_us Pointer to variable to receive the minimum connection interval + * in microseconds. + * + * @return Zero on success or (negative) error code on failure. + */ +int bt_conn_le_read_min_conn_interval(uint16_t *min_interval_us); + +/** @brief Set Default Connection Rate Parameters. + * + * Set default connection rate parameters to be used for future connections. + * This command does not affect any existing connection. + * Parameters set for specific connection will always have precedence. + * + * @kconfig_dep{CONFIG_BT_SHORTER_CONNECTION_INTERVALS} + * + * @param params Connection rate parameters. + * + * @return Zero on success or (negative) error code on failure. + */ +int bt_conn_le_conn_rate_set_defaults(const struct bt_conn_le_conn_rate_param *params); + +/** @brief Request New Connection Rate Parameters. + * + * Request a change to the connection parameters of a connection. This includes + * Subrate parameters. This allows changing the connection interval to below the + * Baseline ConnInterval Values (BCV) and with finer granularity, if supported. + * + * Valid intervals of the local and peer controller should be known. + * See @ref bt_conn_le_read_min_conn_interval_groups + * + * @kconfig_dep{CONFIG_BT_SHORTER_CONNECTION_INTERVALS} + * + * @param conn @ref BT_CONN_TYPE_LE connection object. + * @param params Connection rate parameters. + * + * @return Zero on success or (negative) error code on failure. + * @return -EINVAL @p conn is not a valid @ref BT_CONN_TYPE_LE connection. + */ +int bt_conn_le_conn_rate_request(struct bt_conn *conn, + const struct bt_conn_le_conn_rate_param *params); + /** @brief Read remote feature pages. * * Request remote feature pages, from 0 up to pages_requested or the number @@ -2064,6 +2306,31 @@ struct bt_conn_cb { const struct bt_conn_le_subrate_changed *params); #endif /* CONFIG_BT_SUBRATING */ +#if defined(CONFIG_BT_SHORTER_CONNECTION_INTERVALS) || defined(__DOXYGEN__) + /** @brief LE Connection Rate Changed event. + * + * This callback notifies the application that the connection rate + * parameters (including both connection interval and subrating) + * of the connection may have changed. + * + * @param conn Connection object. + * @param status HCI Status from LE Connection Rate Change event. + * Possible Status codes: + * - Success (0x00) + * - Unknown Connection Identifier (0x02) + * - Command Disallowed (0x0C) + * - Unsupported Feature or Parameter Value (0x11) + * - Invalid HCI Command Parameters (0x12) + * - Unsupported Remote Feature (0x1A) + * - Unsupported LL Parameter Value (0x20) + * @param params New connection rate parameters. + * The connection rate parameters will be NULL + * if @p status is not @ref BT_HCI_ERR_SUCCESS. + */ + void (*conn_rate_changed)(struct bt_conn *conn, uint8_t status, + const struct bt_conn_le_conn_rate_changed *params); +#endif /* CONFIG_BT_SHORTER_CONNECTION_INTERVALS */ + #if defined(CONFIG_BT_LE_EXTENDED_FEAT_SET) /** @brief Read all remote features complete event. * diff --git a/include/zephyr/bluetooth/hci_types.h b/include/zephyr/bluetooth/hci_types.h index 9cbc6778f09ec..e22fe84705402 100644 --- a/include/zephyr/bluetooth/hci_types.h +++ b/include/zephyr/bluetooth/hci_types.h @@ -210,6 +210,8 @@ struct bt_hci_cmd_hdr { #define BT_LE_FEAT_BIT_CHANNEL_SOUNDING_TONE_QUAL_IND 48 #define BT_LE_FEAT_BIT_EXTENDED_FEAT_SET 63 #define BT_LE_FEAT_BIT_FRAME_SPACE_UPDATE 65 +#define BT_LE_FEAT_BIT_SHORTER_CONN_INTERVALS 72 +#define BT_LE_FEAT_BIT_SHORTER_CONN_INTERVALS_HOST_SUPP 73 #define BT_LE_FEAT_TEST(feat, n) (feat[(n) >> 3] & \ BIT((n) & 7)) @@ -290,6 +292,10 @@ struct bt_hci_cmd_hdr { BT_LE_FEAT_BIT_EXTENDED_FEAT_SET) #define BT_FEAT_LE_FRAME_SPACE_UPDATE_SET(feat) BT_LE_FEAT_TEST(feat, \ BT_LE_FEAT_BIT_FRAME_SPACE_UPDATE) +#define BT_FEAT_LE_SHORTER_CONN_INTERVALS(feat) BT_LE_FEAT_TEST(feat, \ + BT_LE_FEAT_BIT_SHORTER_CONN_INTERVALS) +#define BT_FEAT_LE_SHORTER_CONN_INTERVALS_HOST_SUPP(feat) BT_LE_FEAT_TEST(feat, \ + BT_LE_FEAT_BIT_SHORTER_CONN_INTERVALS_HOST_SUPP) #define BT_FEAT_LE_CIS(feat) (BT_FEAT_LE_CIS_CENTRAL(feat) | \ BT_FEAT_LE_CIS_PERIPHERAL(feat)) @@ -2925,6 +2931,66 @@ struct bt_hci_cp_le_frame_space_update { #define BT_HCI_OP_LE_FRAME_SPACE_UPDATE BT_OP(BT_OGF_LE, 0x009D) /* 0x209D */ +/** All limits according to BT Core spec 6.2 [Vol 4, Part E, 7.8.154]. */ +#define BT_HCI_LE_SCI_INTERVAL_MIN_125US (0x0003U) +#define BT_HCI_LE_SCI_INTERVAL_MAX_125US (0x7D00U) +#define BT_HCI_LE_SCI_INTERVAL_MIN_US (375U) +#define BT_HCI_LE_SCI_INTERVAL_MAX_US (4000000U) +#define BT_HCI_LE_SCI_INTERVAL_UNIT_US (125U) + +#define BT_HCI_LE_SCI_STRIDE_MIN_125US (0x0001U) + +#define BT_HCI_LE_MIN_SUPP_CONN_INT_MIN_US (375U) +#define BT_HCI_LE_MIN_SUPP_CONN_INT_MAX_US (7500U) + +#define BT_HCI_LE_SCI_CE_LEN_MIN_125US (0x0001U) +#define BT_HCI_LE_SCI_CE_LEN_MAX_125US (0x3E7FU) + +struct bt_hci_le_read_min_supported_conn_interval_group { + uint16_t group_min; + uint16_t group_max; + uint16_t group_stride; +} __packed; + +struct bt_hci_op_le_read_min_supported_conn_interval { + uint8_t status; + uint8_t min_supported_conn_interval; + uint8_t num_groups; + struct bt_hci_le_read_min_supported_conn_interval_group groups[]; +} __packed; + +#define BT_HCI_OP_LE_READ_MIN_SUPPORTED_CONN_INTERVAL \ + BT_OP(BT_OGF_LE, 0x00A3) /* 0x20A3 */ + +struct bt_hci_op_le_set_default_rate_parameters { + uint16_t conn_interval_min; + uint16_t conn_interval_max; + uint16_t subrate_min; + uint16_t subrate_max; + uint16_t max_latency; + uint16_t continuation_number; + uint16_t supervision_timeout; + uint16_t min_ce_len; + uint16_t max_ce_len; +} __packed; + +#define BT_HCI_OP_LE_SET_DEFAULT_RATE_PARAMETERS BT_OP(BT_OGF_LE, 0x00A2) /* 0x20A2 */ + +struct bt_hci_op_le_connection_rate_request { + uint16_t handle; + uint16_t conn_interval_min; + uint16_t conn_interval_max; + uint16_t subrate_min; + uint16_t subrate_max; + uint16_t max_latency; + uint16_t continuation_number; + uint16_t supervision_timeout; + uint16_t min_ce_len; + uint16_t max_ce_len; +} __packed; + +#define BT_HCI_OP_LE_CONNECTION_RATE_REQUEST BT_OP(BT_OGF_LE, 0x00A1) /* 0x20A1 */ + /* Event definitions */ #define BT_HCI_EVT_UNKNOWN 0x00 @@ -3303,6 +3369,7 @@ struct bt_hci_evt_le_advertising_report { /** All limits according to BT Core Spec v5.4 [Vol 4, Part E]. */ #define BT_HCI_LE_INTERVAL_MIN 0x0006 #define BT_HCI_LE_INTERVAL_MAX 0x0c80 +#define BT_HCI_LE_PERIPHERAL_LATENCY_MIN (0x0000U) #define BT_HCI_LE_PERIPHERAL_LATENCY_MAX 0x01f3 #define BT_HCI_LE_SUPERVISON_TIMEOUT_MIN 0x000a #define BT_HCI_LE_SUPERVISON_TIMEOUT_MAX 0x0c80 @@ -3694,6 +3761,7 @@ struct bt_hci_evt_le_biginfo_adv_report { /** All limits according to BT Core Spec v5.4 [Vol 4, Part E]. */ #define BT_HCI_LE_SUBRATE_FACTOR_MIN 0x0001 #define BT_HCI_LE_SUBRATE_FACTOR_MAX 0x01f4 +#define BT_HCI_LE_CONTINUATION_NUM_MIN (0x0000U) #define BT_HCI_LE_CONTINUATION_NUM_MAX 0x01f3 #define BT_HCI_EVT_LE_SUBRATE_CHANGE 0x23 @@ -4138,6 +4206,17 @@ struct bt_hci_evt_le_frame_space_update_complete { uint16_t spacing_types; } __packed; +#define BT_HCI_EVT_LE_CONN_RATE_CHANGE 0x37 +struct bt_hci_evt_le_conn_rate_change { + uint8_t status; + uint16_t handle; + uint16_t conn_interval; + uint16_t subrate_factor; + uint16_t peripheral_latency; + uint16_t continuation_number; + uint16_t supervision_timeout; +} __packed; + /* Event mask bits */ #define BT_EVT_BIT(n) (1ULL << (n)) @@ -4240,6 +4319,7 @@ struct bt_hci_evt_le_frame_space_update_complete { #define BT_EVT_MASK_LE_CS_TEST_END_COMPLETE BT_EVT_BIT(50) #define BT_EVT_MASK_LE_FRAME_SPACE_UPDATE_COMPLETE BT_EVT_BIT(52) +#define BT_EVT_MASK_LE_CONN_RATE_CHANGE BT_EVT_BIT(54) /** HCI Error Codes, BT Core Spec v5.4 [Vol 1, Part F]. */ #define BT_HCI_ERR_SUCCESS 0x00 diff --git a/subsys/bluetooth/Kconfig b/subsys/bluetooth/Kconfig index 7e8fa5dca6ecb..ec5d4fafcc889 100644 --- a/subsys/bluetooth/Kconfig +++ b/subsys/bluetooth/Kconfig @@ -117,7 +117,7 @@ config BT_CONN_TX config BT_LE_LOCAL_MINIMUM_REQUIRED_FEATURE_PAGE int - default 1 if BT_FRAME_SPACE_UPDATE + default 1 if BT_FRAME_SPACE_UPDATE || BT_SHORTER_CONNECTION_INTERVALS default 0 depends on BT_LE_EXTENDED_FEAT_SET help @@ -260,6 +260,15 @@ config BT_CHANNEL_SOUNDING_REASSEMBLY_BUFFER_CNT results. Each running CS procedure is allocated one buffer and the number of concurrent CS procedures is limited by this value. +config BT_SHORTER_CONNECTION_INTERVALS + bool "Shorter Connection Intervals" + depends on !HAS_BT_CTLR || BT_CTLR_SHORTER_CONNECTION_INTERVALS_SUPPORT + depends on BT_LE_EXTENDED_FEAT_SET + depends on BT_SUBRATING + help + Enable support for the Bluetooth 6.2 Shorter Connection Intervals + feature. + endif # BT_CONN config BT_ISO diff --git a/subsys/bluetooth/controller/Kconfig b/subsys/bluetooth/controller/Kconfig index 7fd3c46fe35af..07cdcd2941fde 100644 --- a/subsys/bluetooth/controller/Kconfig +++ b/subsys/bluetooth/controller/Kconfig @@ -133,6 +133,9 @@ config BT_CTLR_CHANNEL_SOUNDING_SUPPORT config BT_CTLR_EXTENDED_FEAT_SET_SUPPORT bool +config BT_CTLR_SHORTER_CONNECTION_INTERVALS_SUPPORT + bool + # Virtual option that all local LL implementations should select config HAS_BT_CTLR bool @@ -1184,6 +1187,17 @@ config BT_CTLR_EXTENDED_FEAT_SET Enable support for Bluetooth 6.0 LL Extended Feature Set in the Controller. +config BT_CTLR_SHORTER_CONNECTION_INTERVALS + bool "Shorter Connection Intervals" + depends on BT_CTLR_SHORTER_CONNECTION_INTERVALS_SUPPORT + depends on BT_CTLR_EXTENDED_FEAT_SET + depends on BT_CTLR_SUBRATING + select BT_CTLR_SET_HOST_FEATURE + default y if BT_SHORTER_CONNECTION_INTERVALS + help + Enable support for Bluetooth 6.2 Shorter Connection Intervals + in the controller. + rsource "Kconfig.df" rsource "Kconfig.ll_sw_split" rsource "Kconfig.dtm" diff --git a/subsys/bluetooth/host/conn.c b/subsys/bluetooth/host/conn.c index ce1a94c58f961..1ab0ef0d210d2 100644 --- a/subsys/bluetooth/host/conn.c +++ b/subsys/bluetooth/host/conn.c @@ -2886,7 +2886,10 @@ int bt_conn_get_info(const struct bt_conn *conn, struct bt_conn_info *info) info->le.local = &conn->le.resp_addr; info->le.remote = &conn->le.init_addr; } - info->le.interval = conn->le.interval; + info->le.interval_us = conn->le.interval_us; +#if !defined(CONFIG_BT_SHORTER_CONNECTION_INTERVALS) + info->le.interval = conn->le.interval_us / BT_HCI_LE_INTERVAL_UNIT_US; +#endif info->le.latency = conn->le.latency; info->le.timeout = conn->le.timeout; #if defined(CONFIG_BT_USER_PHY_UPDATE) @@ -3244,6 +3247,24 @@ void bt_conn_notify_subrate_change(struct bt_conn *conn, } } +#if defined(CONFIG_BT_SHORTER_CONNECTION_INTERVALS) +void bt_conn_notify_conn_rate_change(struct bt_conn *conn, uint8_t status, + const struct bt_conn_le_conn_rate_changed *params) +{ + BT_CONN_CB_DYNAMIC_FOREACH(callback) { + if (callback->conn_rate_changed != NULL) { + callback->conn_rate_changed(conn, status, params); + } + } + + STRUCT_SECTION_FOREACH(bt_conn_cb, cb) { + if (cb->conn_rate_changed != NULL) { + cb->conn_rate_changed(conn, status, params); + } + } +} +#endif /* CONFIG_BT_SHORTER_CONNECTION_INTERVALS */ + static bool le_subrate_common_params_valid(const struct bt_conn_le_subrate_param *param) { /* All limits according to BT Core spec 5.4 [Vol 4, Part E, 7.8.123] */ @@ -3332,6 +3353,182 @@ int bt_conn_le_subrate_request(struct bt_conn *conn, } #endif /* CONFIG_BT_SUBRATING */ +#if defined(CONFIG_BT_SHORTER_CONNECTION_INTERVALS) +int bt_conn_le_read_min_conn_interval_groups(struct bt_conn_le_min_conn_interval_info *info) +{ + struct net_buf *rsp; + struct bt_hci_op_le_read_min_supported_conn_interval *rp; + int err; + + if (info == NULL) { + return -EINVAL; + } + + err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_READ_MIN_SUPPORTED_CONN_INTERVAL, NULL, + &rsp); + if (err != 0) { + return err; + } + + rp = (struct bt_hci_op_le_read_min_supported_conn_interval *)rsp->data; + + if (rp->num_groups > BT_CONN_LE_MAX_CONN_INTERVAL_GROUPS) { + LOG_ERR("Too many groups: %d (max %d)", + rp->num_groups, BT_CONN_LE_MAX_CONN_INTERVAL_GROUPS); + net_buf_unref(rsp); + return -ENOMEM; + } + + info->min_supported_conn_interval_us = + BT_CONN_SCI_INTERVAL_TO_US(rp->min_supported_conn_interval); + info->num_groups = rp->num_groups; + + /* Copy groups up to the number allocated by the application */ + for (uint8_t i = 0; i < rp->num_groups; i++) { + info->groups[i].min_125us = sys_le16_to_cpu(rp->groups[i].group_min); + info->groups[i].max_125us = sys_le16_to_cpu(rp->groups[i].group_max); + info->groups[i].stride_125us = sys_le16_to_cpu(rp->groups[i].group_stride); + } + + net_buf_unref(rsp); + return 0; +} + +int bt_conn_le_read_min_conn_interval(uint16_t *min_interval_us) +{ + int err; + struct net_buf *rsp; + struct bt_hci_op_le_read_min_supported_conn_interval *rp; + + if (min_interval_us == NULL) { + return -EINVAL; + } + + err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_READ_MIN_SUPPORTED_CONN_INTERVAL, NULL, + &rsp); + if (err != 0) { + return err; + } + + rp = (struct bt_hci_op_le_read_min_supported_conn_interval *)rsp->data; + *min_interval_us = BT_CONN_SCI_INTERVAL_TO_US(rp->min_supported_conn_interval); + + net_buf_unref(rsp); + return 0; +} + +static bool le_conn_rate_common_params_valid(const struct bt_conn_le_conn_rate_param *param) +{ + /* All limits according to BT Core spec 6.2 [Vol 4, Part E, 7.8.154] */ + + if (!IN_RANGE(param->interval_min_125us, BT_HCI_LE_SCI_INTERVAL_MIN_125US, + BT_HCI_LE_SCI_INTERVAL_MAX_125US) || + !IN_RANGE(param->interval_max_125us, BT_HCI_LE_SCI_INTERVAL_MIN_125US, + BT_HCI_LE_SCI_INTERVAL_MAX_125US) || + param->interval_min_125us > param->interval_max_125us) { + return false; + } + + if (!IN_RANGE(param->subrate_min, BT_HCI_LE_SUBRATE_FACTOR_MIN, + BT_HCI_LE_SUBRATE_FACTOR_MAX) || + !IN_RANGE(param->subrate_max, BT_HCI_LE_SUBRATE_FACTOR_MIN, + BT_HCI_LE_SUBRATE_FACTOR_MAX) || + param->subrate_min > param->subrate_max) { + return false; + } + + if (!IN_RANGE(param->max_latency, 0, BT_HCI_LE_PERIPHERAL_LATENCY_MAX) || + param->subrate_max * (param->max_latency + 1) > 500) { + return false; + } + + if (!IN_RANGE(param->continuation_number, 0, BT_HCI_LE_CONTINUATION_NUM_MAX) || + param->continuation_number >= param->subrate_max) { + return false; + } + + if (!IN_RANGE(param->supervision_timeout_10ms, BT_HCI_LE_SUPERVISON_TIMEOUT_MIN, + BT_HCI_LE_SUPERVISON_TIMEOUT_MAX)) { + return false; + } + + if (!IN_RANGE(param->min_ce_len_125us, BT_HCI_LE_SCI_CE_LEN_MIN_125US, + BT_HCI_LE_SCI_CE_LEN_MAX_125US) || + !IN_RANGE(param->max_ce_len_125us, BT_HCI_LE_SCI_CE_LEN_MIN_125US, + BT_HCI_LE_SCI_CE_LEN_MAX_125US) || + param->max_ce_len_125us < param->min_ce_len_125us) { + return false; + } + + return true; +} + +#if defined(CONFIG_BT_CENTRAL) +int bt_conn_le_conn_rate_set_defaults(const struct bt_conn_le_conn_rate_param *params) +{ + struct bt_hci_op_le_set_default_rate_parameters *cp; + struct net_buf *buf; + + if (params == NULL || !le_conn_rate_common_params_valid(params)) { + return -EINVAL; + } + + buf = bt_hci_cmd_alloc(K_FOREVER); + if (buf == NULL) { + return -ENOBUFS; + } + + cp = net_buf_add(buf, sizeof(*cp)); + cp->conn_interval_min = sys_cpu_to_le16(params->interval_min_125us); + cp->conn_interval_max = sys_cpu_to_le16(params->interval_max_125us); + cp->subrate_min = sys_cpu_to_le16(params->subrate_min); + cp->subrate_max = sys_cpu_to_le16(params->subrate_max); + cp->max_latency = sys_cpu_to_le16(params->max_latency); + cp->continuation_number = sys_cpu_to_le16(params->continuation_number); + cp->supervision_timeout = sys_cpu_to_le16(params->supervision_timeout_10ms); + cp->min_ce_len = sys_cpu_to_le16(params->min_ce_len_125us); + cp->max_ce_len = sys_cpu_to_le16(params->max_ce_len_125us); + + return bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_DEFAULT_RATE_PARAMETERS, buf, NULL); +} +#endif /* CONFIG_BT_CENTRAL */ + +int bt_conn_le_conn_rate_request(struct bt_conn *conn, + const struct bt_conn_le_conn_rate_param *params) +{ + struct bt_hci_op_le_connection_rate_request *cp; + struct net_buf *buf; + + if (!bt_conn_is_le(conn)) { + LOG_DBG("Invalid connection type: %u for %p", conn->type, conn); + return -EINVAL; + } + + if (params == NULL || !le_conn_rate_common_params_valid(params)) { + return -EINVAL; + } + + buf = bt_hci_cmd_alloc(K_FOREVER); + if (buf == NULL) { + return -ENOBUFS; + } + + cp = net_buf_add(buf, sizeof(*cp)); + cp->handle = sys_cpu_to_le16(conn->handle); + cp->conn_interval_min = sys_cpu_to_le16(params->interval_min_125us); + cp->conn_interval_max = sys_cpu_to_le16(params->interval_max_125us); + cp->subrate_min = sys_cpu_to_le16(params->subrate_min); + cp->subrate_max = sys_cpu_to_le16(params->subrate_max); + cp->max_latency = sys_cpu_to_le16(params->max_latency); + cp->continuation_number = sys_cpu_to_le16(params->continuation_number); + cp->supervision_timeout = sys_cpu_to_le16(params->supervision_timeout_10ms); + cp->min_ce_len = sys_cpu_to_le16(params->min_ce_len_125us); + cp->max_ce_len = sys_cpu_to_le16(params->max_ce_len_125us); + + return bt_hci_cmd_send_sync(BT_HCI_OP_LE_CONNECTION_RATE_REQUEST, buf, NULL); +} +#endif /* CONFIG_BT_SHORTER_CONNECTION_INTERVALS */ + #if defined(CONFIG_BT_LE_EXTENDED_FEAT_SET) void bt_conn_notify_read_all_remote_feat_complete(struct bt_conn *conn, struct bt_conn_le_read_all_remote_feat_complete *params) diff --git a/subsys/bluetooth/host/conn_internal.h b/subsys/bluetooth/host/conn_internal.h index 06151e21402cb..a9a6f95018002 100644 --- a/subsys/bluetooth/host/conn_internal.h +++ b/subsys/bluetooth/host/conn_internal.h @@ -538,6 +538,9 @@ void bt_conn_notify_path_loss_threshold_report(struct bt_conn *conn, void bt_conn_notify_subrate_change(struct bt_conn *conn, struct bt_conn_le_subrate_changed params); +void bt_conn_notify_conn_rate_change(struct bt_conn *conn, uint8_t status, + const struct bt_conn_le_conn_rate_changed *params); + void bt_conn_notify_read_all_remote_feat_complete(struct bt_conn *conn, struct bt_conn_le_read_all_remote_feat_complete *params); diff --git a/subsys/bluetooth/host/hci_core.c b/subsys/bluetooth/host/hci_core.c index 24c7a4c67acc2..6e5d5eff92308 100644 --- a/subsys/bluetooth/host/hci_core.c +++ b/subsys/bluetooth/host/hci_core.c @@ -2808,6 +2808,72 @@ void bt_hci_le_subrate_change_event(struct net_buf *buf) } #endif /* CONFIG_BT_SUBRATING */ +#if defined(CONFIG_BT_SHORTER_CONNECTION_INTERVALS) +void bt_hci_le_conn_rate_change_event(struct net_buf *buf) +{ + struct bt_hci_evt_le_conn_rate_change *evt; + struct bt_conn_le_conn_rate_changed params; + 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 == NULL) { + LOG_ERR("Unknown conn handle 0x%04X for connection rate event", + sys_le16_to_cpu(evt->handle)); + return; + } + + if (evt->status == BT_HCI_ERR_SUCCESS) { + conn->le.interval_us = + BT_CONN_SCI_INTERVAL_TO_US(sys_le16_to_cpu(evt->conn_interval)); + conn->le.subrate.factor = sys_le16_to_cpu(evt->subrate_factor); + conn->le.subrate.continuation_number = sys_le16_to_cpu(evt->continuation_number); + conn->le.latency = sys_le16_to_cpu(evt->peripheral_latency); + conn->le.timeout = sys_le16_to_cpu(evt->supervision_timeout); + + if (!IS_ENABLED(CONFIG_BT_CONN_PARAM_ANY)) { + if (!IN_RANGE(conn->le.interval_us / BT_HCI_LE_SCI_INTERVAL_UNIT_US, + BT_HCI_LE_SCI_INTERVAL_MIN_125US, + BT_HCI_LE_SCI_INTERVAL_MAX_125US)) { + LOG_WRN("interval_us exceeds the valid range %u us", + conn->le.interval_us); + } + if (!IN_RANGE(conn->le.subrate.factor, BT_HCI_LE_SUBRATE_FACTOR_MIN, + BT_HCI_LE_SUBRATE_FACTOR_MAX)) { + LOG_WRN("subrate_factor exceeds the valid range %d", + conn->le.subrate.factor); + } + if (conn->le.latency > BT_HCI_LE_PERIPHERAL_LATENCY_MAX) { + LOG_WRN("peripheral_latency exceeds the valid range 0x%04x", + conn->le.latency); + } + if (conn->le.subrate.continuation_number > BT_HCI_LE_CONTINUATION_NUM_MAX) { + LOG_WRN("continuation_number exceeds the valid range %d", + conn->le.subrate.continuation_number); + } + if (!IN_RANGE(conn->le.timeout, BT_HCI_LE_SUPERVISON_TIMEOUT_MIN, + BT_HCI_LE_SUPERVISON_TIMEOUT_MAX)) { + LOG_WRN("supervision_timeout exceeds the valid range 0x%04x", + conn->le.timeout); + } + } + + params.interval_us = conn->le.interval_us; + params.subrate_factor = conn->le.subrate.factor; + params.continuation_number = conn->le.subrate.continuation_number; + params.peripheral_latency = conn->le.latency; + params.supervision_timeout_10ms = conn->le.timeout; + + bt_conn_notify_conn_rate_change(conn, evt->status, ¶ms); + } else { + bt_conn_notify_conn_rate_change(conn, evt->status, NULL); + } + + bt_conn_unref(conn); +} +#endif /* CONFIG_BT_SHORTER_CONNECTION_INTERVALS */ + 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, @@ -2958,7 +3024,11 @@ static const struct event_handler meta_events[] = { #if defined(CONFIG_BT_SUBRATING) EVENT_HANDLER(BT_HCI_EVT_LE_SUBRATE_CHANGE, bt_hci_le_subrate_change_event, sizeof(struct bt_hci_evt_le_subrate_change)), -#endif /* CONFIG_BT_PATH_LOSS_MONITORING */ +#endif /* CONFIG_BT_SUBRATING */ +#if defined(CONFIG_BT_SHORTER_CONNECTION_INTERVALS) + EVENT_HANDLER(BT_HCI_EVT_LE_CONN_RATE_CHANGE, bt_hci_le_conn_rate_change_event, + sizeof(struct bt_hci_evt_le_conn_rate_change)), +#endif /* CONFIG_BT_SHORTER_CONNECTION_INTERVALS */ #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)), @@ -3571,6 +3641,11 @@ static int le_set_event_mask(void) mask |= BT_EVT_MASK_LE_SUBRATE_CHANGE; } + if (IS_ENABLED(CONFIG_BT_SHORTER_CONNECTION_INTERVALS) && + BT_FEAT_LE_SHORTER_CONN_INTERVALS(bt_dev.le.features)) { + mask |= BT_EVT_MASK_LE_CONN_RATE_CHANGE; + } + if (IS_ENABLED(CONFIG_BT_LE_EXTENDED_FEAT_SET) && BT_FEAT_LE_EXTENDED_FEAT_SET(bt_dev.le.features)) { mask |= BT_EVT_MASK_LE_READ_ALL_REMOTE_FEAT_COMPLETE; @@ -3885,6 +3960,14 @@ static int le_init(void) } } + if (IS_ENABLED(CONFIG_BT_SHORTER_CONNECTION_INTERVALS) && + BT_FEAT_LE_SHORTER_CONN_INTERVALS(bt_dev.le.features)) { + err = le_set_host_feature(BT_LE_FEAT_BIT_SHORTER_CONN_INTERVALS_HOST_SUPP, 1); + if (err != 0) { + return err; + } + } + return le_set_event_mask(); } From ac1fc40ae002bfafd9d310bf26b7a4b319270984 Mon Sep 17 00:00:00 2001 From: Timothy Keys Date: Thu, 16 Oct 2025 16:49:42 +0100 Subject: [PATCH 2/3] bluetooth: host: Change uses of interval to interval_us Since Shorter Connection Intervals changes the unit that connection intervals can be represented in. It is necessary to change how they are stored and represented. This commit deprecates interval in favour of interval_us. Remove use of interval in internal bt_conn struct since it is no longer needed. Signed-off-by: Timothy Keys --- doc/releases/migration-guide-4.4.rst | 3 +++ doc/releases/release-notes-4.4.rst | 6 ++++++ include/zephyr/bluetooth/conn.h | 16 +++++++++++++--- include/zephyr/bluetooth/hci_types.h | 2 ++ subsys/bluetooth/audio/ascs.c | 10 +++++----- subsys/bluetooth/audio/bap_broadcast_assistant.c | 4 ++-- subsys/bluetooth/audio/bap_scan_delegator.c | 2 +- subsys/bluetooth/audio/bap_unicast_client.c | 4 ++-- subsys/bluetooth/audio/tbs.c | 2 +- subsys/bluetooth/host/att.c | 2 +- subsys/bluetooth/host/conn.c | 16 ++++++++-------- subsys/bluetooth/host/conn_internal.h | 2 +- subsys/bluetooth/host/hci_core.c | 13 +++++++------ subsys/bluetooth/host/shell/bt.c | 15 ++++++++++----- tests/bluetooth/audio/ascs/src/main.c | 2 +- tests/bluetooth/audio/ascs/src/test_common.c | 2 +- tests/bluetooth/tester/src/btp_gap.c | 2 +- 17 files changed, 65 insertions(+), 38 deletions(-) diff --git a/doc/releases/migration-guide-4.4.rst b/doc/releases/migration-guide-4.4.rst index cb99605c9efc5..0f15ff38da9bb 100644 --- a/doc/releases/migration-guide-4.4.rst +++ b/doc/releases/migration-guide-4.4.rst @@ -48,6 +48,9 @@ Bluetooth Host * :kconfig:option:`CONFIG_BT_SIGNING` has been deprecated. * :c:macro:`BT_GATT_CHRC_AUTH` has been deprecated. +* :c:member:`bt_conn_le_info.interval` has been deprecated. Use + :c:member:`bt_conn_le_info.interval_us` instead. Note that the units have changed: ``interval`` + was in units of 1.25 milliseconds, while ``interval_us`` is in microseconds. Networking ********** diff --git a/doc/releases/release-notes-4.4.rst b/doc/releases/release-notes-4.4.rst index 9622d9c5bd388..79a2bbf802e2b 100644 --- a/doc/releases/release-notes-4.4.rst +++ b/doc/releases/release-notes-4.4.rst @@ -62,6 +62,12 @@ Deprecated APIs and options * The callback :c:member:`output_number` in :c:struct:`bt_mesh_prov` structure was deprecated. Applications should use :c:member:`output_numeric` callback instead. + * Host + + * :c:member:`bt_conn_le_info.interval` has been deprecated. Use + :c:member:`bt_conn_le_info.interval_us` instead. Note that the units have changed: + ``interval`` was in units of 1.25 milliseconds, while ``interval_us`` is in microseconds. + New APIs and options ==================== diff --git a/include/zephyr/bluetooth/conn.h b/include/zephyr/bluetooth/conn.h index 4bcd17c13d7cf..14bf2d657f28a 100644 --- a/include/zephyr/bluetooth/conn.h +++ b/include/zephyr/bluetooth/conn.h @@ -1056,9 +1056,19 @@ struct bt_conn_le_info { /** Connection interval in microseconds */ uint32_t interval_us; #if !defined(CONFIG_BT_SHORTER_CONNECTION_INTERVALS) || defined(__DOXYGEN__) - /** Connection interval in units of 1.25 ms */ - uint16_t interval; + union { + /** @brief Connection interval in units of 1.25 ms + * + * @deprecated Use @ref bt_conn_le_info.interval_us instead + */ + __deprecated uint16_t interval; + /** @cond INTERNAL_HIDDEN */ + /** Workaround for setting deprecated @ref bt_conn_le_info.interval */ + uint16_t _interval; + /** @endcond */ + }; #endif /* !CONFIG_BT_SHORTER_CONNECTION_INTERVALS */ + uint16_t latency; /**< Connection peripheral latency */ uint16_t timeout; /**< Connection supervision timeout */ @@ -1096,7 +1106,7 @@ struct bt_conn_le_info { * * Multiply by 125 to get microseconds. */ -#define BT_CONN_SCI_INTERVAL_TO_US(interval) ((interval) * BT_HCI_LE_SCI_INTERVAL_UNIT_US) +#define BT_CONN_SCI_INTERVAL_TO_US(_interval) ((_interval) * BT_HCI_LE_SCI_INTERVAL_UNIT_US) /** BR/EDR Connection Info Structure */ struct bt_conn_br_info { diff --git a/include/zephyr/bluetooth/hci_types.h b/include/zephyr/bluetooth/hci_types.h index e22fe84705402..71fb29c828aa6 100644 --- a/include/zephyr/bluetooth/hci_types.h +++ b/include/zephyr/bluetooth/hci_types.h @@ -3374,6 +3374,8 @@ struct bt_hci_evt_le_advertising_report { #define BT_HCI_LE_SUPERVISON_TIMEOUT_MIN 0x000a #define BT_HCI_LE_SUPERVISON_TIMEOUT_MAX 0x0c80 +#define BT_HCI_LE_INTERVAL_UNIT_US (1250U) + #define BT_HCI_EVT_LE_CONN_UPDATE_COMPLETE 0x03 struct bt_hci_evt_le_conn_update_complete { uint8_t status; diff --git a/subsys/bluetooth/audio/ascs.c b/subsys/bluetooth/audio/ascs.c index 4636ca605a378..9072c220e5f98 100644 --- a/subsys/bluetooth/audio/ascs.c +++ b/subsys/bluetooth/audio/ascs.c @@ -525,7 +525,7 @@ static void state_transition_work_handler(struct k_work *work) err = ase_state_notify(ase); if (err == -ENOMEM) { struct bt_conn_info info; - uint32_t retry_delay_ms; + uint32_t retry_delay_us; /* Revert back to old state */ ase->ep.state = old_state; @@ -533,14 +533,14 @@ static void state_transition_work_handler(struct k_work *work) err = bt_conn_get_info(ase->conn, &info); __ASSERT_NO_MSG(err == 0); - retry_delay_ms = BT_CONN_INTERVAL_TO_MS(info.le.interval); + retry_delay_us = info.le.interval_us; /* Reschedule the state transition */ - err = k_work_reschedule(d_work, K_MSEC(retry_delay_ms)); + err = k_work_reschedule(d_work, K_USEC(retry_delay_us)); if (err >= 0) { LOG_DBG("Out of buffers for ase state notification. " - "Will retry in %dms", - retry_delay_ms); + "Will retry in %dus", + retry_delay_us); return; } } diff --git a/subsys/bluetooth/audio/bap_broadcast_assistant.c b/subsys/bluetooth/audio/bap_broadcast_assistant.c index fca4bf39d7013..acfef673bf259 100644 --- a/subsys/bluetooth/audio/bap_broadcast_assistant.c +++ b/subsys/bluetooth/audio/bap_broadcast_assistant.c @@ -453,12 +453,12 @@ static void long_bap_read(struct bt_conn *conn, uint16_t handle) if (err != 0) { LOG_DBG("Failed to get conn info, use default interval"); - conn_info.le.interval = BT_GAP_INIT_CONN_INT_MIN; + conn_info.le.interval_us = BT_CONN_INTERVAL_TO_US(BT_GAP_INIT_CONN_INT_MIN); } /* Wait a connection interval to retry */ err = k_work_reschedule(&inst->bap_read_work, - K_USEC(BT_CONN_INTERVAL_TO_US(conn_info.le.interval))); + K_USEC(conn_info.le.interval_us)); if (err < 0) { LOG_DBG("Failed to reschedule read work: %d", err); bap_long_read_reset(inst); diff --git a/subsys/bluetooth/audio/bap_scan_delegator.c b/subsys/bluetooth/audio/bap_scan_delegator.c index b70d9c4d34c7f..860ea8fa56a69 100644 --- a/subsys/bluetooth/audio/bap_scan_delegator.c +++ b/subsys/bluetooth/audio/bap_scan_delegator.c @@ -239,7 +239,7 @@ static void receive_state_notify_cb(struct bt_conn *conn, void *data) LOG_DBG("Could not notify receive state: %d", err); err = k_work_reschedule(&internal_state->notify_work, - K_USEC(BT_CONN_INTERVAL_TO_US(conn_info.le.interval))); + K_USEC(conn_info.le.interval_us)); __ASSERT(err >= 0, "Failed to reschedule work: %d", err); } } diff --git a/subsys/bluetooth/audio/bap_unicast_client.c b/subsys/bluetooth/audio/bap_unicast_client.c index 05aa9ca08f218..99ba63c2f42ad 100644 --- a/subsys/bluetooth/audio/bap_unicast_client.c +++ b/subsys/bluetooth/audio/bap_unicast_client.c @@ -1767,12 +1767,12 @@ static void long_ase_read(struct bt_bap_unicast_client_ep *client_ep) if (err != 0) { LOG_DBG("Failed to get conn info, use default interval"); - conn_info.le.interval = BT_GAP_INIT_CONN_INT_MIN; + conn_info.le.interval_us = BT_CONN_INTERVAL_TO_US(BT_GAP_INIT_CONN_INT_MIN); } /* Wait a connection interval to retry */ err = k_work_reschedule(&client_ep->ase_read_work, - K_USEC(BT_CONN_INTERVAL_TO_US(conn_info.le.interval))); + K_USEC(conn_info.le.interval_us)); if (err < 0) { LOG_DBG("Failed to reschedule ASE long read work: %d", err); } diff --git a/subsys/bluetooth/audio/tbs.c b/subsys/bluetooth/audio/tbs.c index 7526c5327e0f2..d7026c63dbe39 100644 --- a/subsys/bluetooth/audio/tbs.c +++ b/subsys/bluetooth/audio/tbs.c @@ -1005,7 +1005,7 @@ static void notify_handler_cb(struct bt_conn *conn, void *data) LOG_DBG("Notify failed (%d), retrying next connection interval", err); reschedule: err = k_work_reschedule(&inst->notify_work, - K_USEC(BT_CONN_INTERVAL_TO_US(info.le.interval))); + K_USEC(info.le.interval_us)); __ASSERT(err >= 0, "Failed to reschedule work: %d", err); } diff --git a/subsys/bluetooth/host/att.c b/subsys/bluetooth/host/att.c index 66833d1cfcc53..6b2586bdda4eb 100644 --- a/subsys/bluetooth/host/att.c +++ b/subsys/bluetooth/host/att.c @@ -3523,7 +3523,7 @@ static k_timeout_t credit_based_connection_delay(struct bt_conn *conn) * result in an overflow */ const uint32_t calculated_delay_us = - 2 * (conn->le.latency + 1) * BT_CONN_INTERVAL_TO_US(conn->le.interval); + 2 * (conn->le.latency + 1) * conn->le.interval_us; const uint32_t calculated_delay_ms = calculated_delay_us / USEC_PER_MSEC; return K_MSEC(MAX(100, calculated_delay_ms + rand_delay)); diff --git a/subsys/bluetooth/host/conn.c b/subsys/bluetooth/host/conn.c index 1ab0ef0d210d2..3bb442e3f1859 100644 --- a/subsys/bluetooth/host/conn.c +++ b/subsys/bluetooth/host/conn.c @@ -2003,30 +2003,30 @@ void bt_conn_notify_remote_info(struct bt_conn *conn) void bt_conn_notify_le_param_updated(struct bt_conn *conn) { + uint16_t interval_1250us = conn->le.interval_us / BT_HCI_LE_INTERVAL_UNIT_US; + /* If new connection parameters meet requirement of pending * parameters don't send peripheral conn param request anymore on timeout */ if (atomic_test_bit(conn->flags, BT_CONN_PERIPHERAL_PARAM_SET) && - conn->le.interval >= conn->le.interval_min && - conn->le.interval <= conn->le.interval_max && + interval_1250us >= conn->le.interval_min && + interval_1250us <= conn->le.interval_max && conn->le.latency == conn->le.pending_latency && conn->le.timeout == conn->le.pending_timeout) { atomic_clear_bit(conn->flags, BT_CONN_PERIPHERAL_PARAM_SET); } - BT_CONN_CB_DYNAMIC_FOREACH(callback) { if (callback->le_param_updated) { - callback->le_param_updated(conn, conn->le.interval, + callback->le_param_updated(conn, interval_1250us, conn->le.latency, conn->le.timeout); } } STRUCT_SECTION_FOREACH(bt_conn_cb, cb) { if (cb->le_param_updated) { - cb->le_param_updated(conn, conn->le.interval, - conn->le.latency, - conn->le.timeout); + cb->le_param_updated(conn, interval_1250us, + conn->le.latency, conn->le.timeout); } } } @@ -2888,7 +2888,7 @@ int bt_conn_get_info(const struct bt_conn *conn, struct bt_conn_info *info) } info->le.interval_us = conn->le.interval_us; #if !defined(CONFIG_BT_SHORTER_CONNECTION_INTERVALS) - info->le.interval = conn->le.interval_us / BT_HCI_LE_INTERVAL_UNIT_US; + info->le._interval = conn->le.interval_us / BT_HCI_LE_INTERVAL_UNIT_US; #endif info->le.latency = conn->le.latency; info->le.timeout = conn->le.timeout; diff --git a/subsys/bluetooth/host/conn_internal.h b/subsys/bluetooth/host/conn_internal.h index a9a6f95018002..dd2857079dee0 100644 --- a/subsys/bluetooth/host/conn_internal.h +++ b/subsys/bluetooth/host/conn_internal.h @@ -108,7 +108,7 @@ struct bt_conn_le { bt_addr_le_t init_addr; bt_addr_le_t resp_addr; - uint16_t interval; + uint32_t interval_us; uint16_t interval_min; uint16_t interval_max; diff --git a/subsys/bluetooth/host/hci_core.c b/subsys/bluetooth/host/hci_core.c index 6e5d5eff92308..f4bc0613ea825 100644 --- a/subsys/bluetooth/host/hci_core.c +++ b/subsys/bluetooth/host/hci_core.c @@ -1409,7 +1409,7 @@ static void update_conn(struct bt_conn *conn, const bt_addr_le_t *id_addr, { conn->handle = sys_le16_to_cpu(evt->handle); bt_addr_le_copy(&conn->le.dst, id_addr); - conn->le.interval = sys_le16_to_cpu(evt->interval); + conn->le.interval_us = sys_le16_to_cpu(evt->interval) * BT_HCI_LE_INTERVAL_UNIT_US; conn->le.latency = sys_le16_to_cpu(evt->latency); conn->le.timeout = sys_le16_to_cpu(evt->supv_timeout); conn->role = evt->role; @@ -2061,15 +2061,16 @@ static void le_conn_update_complete(struct net_buf *buf) bt_l2cap_update_conn_param(conn, ¶m); } else { if (!evt->status) { - conn->le.interval = sys_le16_to_cpu(evt->interval); + conn->le.interval_us = + sys_le16_to_cpu(evt->interval) * BT_HCI_LE_INTERVAL_UNIT_US; conn->le.latency = sys_le16_to_cpu(evt->latency); conn->le.timeout = sys_le16_to_cpu(evt->supv_timeout); if (!IS_ENABLED(CONFIG_BT_CONN_PARAM_ANY)) { - if (!IN_RANGE(conn->le.interval, BT_HCI_LE_INTERVAL_MIN, - BT_HCI_LE_INTERVAL_MAX)) { - LOG_WRN("interval exceeds the valid range 0x%04x", - conn->le.interval); + if (!IN_RANGE(conn->le.interval_us / BT_HCI_LE_INTERVAL_UNIT_US, + BT_HCI_LE_INTERVAL_MIN, BT_HCI_LE_INTERVAL_MAX)) { + LOG_WRN("interval exceeds the valid range %u us", + conn->le.interval_us); } if (conn->le.latency > BT_HCI_LE_PERIPHERAL_LATENCY_MAX) { LOG_WRN("latency exceeds the valid range 0x%04x", diff --git a/subsys/bluetooth/host/shell/bt.c b/subsys/bluetooth/host/shell/bt.c index 816b6549853b0..4395a3c62c267 100644 --- a/subsys/bluetooth/host/shell/bt.c +++ b/subsys/bluetooth/host/shell/bt.c @@ -3715,8 +3715,13 @@ static int cmd_info(const struct shell *sh, size_t argc, char *argv[]) print_le_addr("Local on-air", info.le.local); shell_print(sh, "Interval: 0x%04x (%u us)", - info.le.interval, - BT_CONN_INTERVAL_TO_US(info.le.interval)); +#if defined(CONFIG_BT_SHORTER_CONNECTION_INTERVALS) + info.le.interval_us / BT_HCI_LE_SCI_INTERVAL_UNIT_US, +#else + info.le.interval_us / BT_HCI_LE_INTERVAL_UNIT_US, +#endif /* CONFIG_BT_SHORTER_CONNECTION_INTERVALS */ + info.le.interval_us); + shell_print(sh, "Latency: 0x%04x", info.le.latency); shell_print(sh, "Supervision timeout: 0x%04x (%d ms)", @@ -4138,9 +4143,9 @@ static void connection_info(struct bt_conn *conn, void *user_data) #endif case BT_CONN_TYPE_LE: bt_addr_le_to_str(info.le.dst, addr, sizeof(addr)); - bt_shell_print("%s#%u [LE][%s] %s: Interval %u latency %u timeout %u", selected, - info.id, role_str, addr, info.le.interval, info.le.latency, - info.le.timeout); + bt_shell_print("%s#%u [LE][%s] %s: Interval %u us, latency %u, timeout %u ms", + selected, info.id, role_str, addr, info.le.interval_us, + info.le.latency, info.le.timeout * 10); break; #if defined(CONFIG_BT_ISO) case BT_CONN_TYPE_ISO: diff --git a/tests/bluetooth/audio/ascs/src/main.c b/tests/bluetooth/audio/ascs/src/main.c index a326c7b1495fb..4aca7cee1c110 100644 --- a/tests/bluetooth/audio/ascs/src/main.c +++ b/tests/bluetooth/audio/ascs/src/main.c @@ -667,7 +667,7 @@ ZTEST_F(ascs_test_suite, test_ase_state_notification_retry) zassert_equal(err, 0); /* Wait for ASE state notification retry */ - k_sleep(K_MSEC(BT_CONN_INTERVAL_TO_MS(info.le.interval))); + k_sleep(K_USEC(info.le.interval_us)); expect_bt_bap_stream_ops_configured_called_once(stream, EMPTY); } diff --git a/tests/bluetooth/audio/ascs/src/test_common.c b/tests/bluetooth/audio/ascs/src/test_common.c index b09677592f46f..f97840fe40acb 100644 --- a/tests/bluetooth/audio/ascs/src/test_common.c +++ b/tests/bluetooth/audio/ascs/src/test_common.c @@ -77,7 +77,7 @@ void test_conn_init(struct bt_conn *conn) conn->info.security.level = BT_SECURITY_L2; conn->info.security.enc_key_size = BT_ENC_KEY_SIZE_MAX; conn->info.security.flags = BT_SECURITY_FLAG_OOB | BT_SECURITY_FLAG_SC; - conn->info.le.interval = BT_GAP_INIT_CONN_INT_MIN; + conn->info.le.interval_us = BT_GAP_INIT_CONN_INT_MIN * BT_HCI_LE_INTERVAL_UNIT_US; } const struct bt_gatt_attr *test_ase_control_point_get(void) diff --git a/tests/bluetooth/tester/src/btp_gap.c b/tests/bluetooth/tester/src/btp_gap.c index bc8de31e1451c..4d0867b5a33f8 100644 --- a/tests/bluetooth/tester/src/btp_gap.c +++ b/tests/bluetooth/tester/src/btp_gap.c @@ -135,7 +135,7 @@ static void le_connected(struct bt_conn *conn, uint8_t err) if (bt_conn_is_type(conn, BT_CONN_TYPE_LE)) { bt_addr_le_copy(&ev.address, info.le.dst); - ev.interval = sys_cpu_to_le16(info.le.interval); + ev.interval = sys_cpu_to_le16(info.le.interval_us / BT_HCI_LE_INTERVAL_UNIT_US); ev.latency = sys_cpu_to_le16(info.le.latency); ev.timeout = sys_cpu_to_le16(info.le.timeout); } else if (IS_ENABLED(CONFIG_BT_CLASSIC) && bt_conn_is_type(conn, BT_CONN_TYPE_BR)) { From 89fb42a79f5b09bde82cb267f98c19707a27c82d Mon Sep 17 00:00:00 2001 From: Timothy Keys Date: Wed, 15 Oct 2025 16:03:50 +0100 Subject: [PATCH 3/3] bluetooth: host: shell: Add SCI shell commands This adds support for Shorter Connection Interval commands in the bt shell. Signed-off-by: Timothy Keys --- subsys/bluetooth/host/shell/bt.c | 182 ++++++++++++++++++++++++++-- tests/bluetooth/shell/testcase.yaml | 7 ++ 2 files changed, 178 insertions(+), 11 deletions(-) diff --git a/subsys/bluetooth/host/shell/bt.c b/subsys/bluetooth/host/shell/bt.c index 4395a3c62c267..21302647bdf78 100644 --- a/subsys/bluetooth/host/shell/bt.c +++ b/subsys/bluetooth/host/shell/bt.c @@ -1009,7 +1009,30 @@ void subrate_changed(struct bt_conn *conn, bt_shell_print("Subrate change failed (HCI status 0x%02x)", params->status); } } -#endif + +#if defined(CONFIG_BT_SHORTER_CONNECTION_INTERVALS) +static void conn_rate_changed(struct bt_conn *conn, uint8_t status, + const struct bt_conn_le_conn_rate_changed *params) +{ + if (status == BT_HCI_ERR_SUCCESS) { + bt_shell_print("Connection rate parameters changed: " + "Connection Interval: %u us " + "Subrate Factor: %d " + "Peripheral latency: 0x%04x " + "Continuation Number: %d " + "Supervision timeout: 0x%04x (%d ms)", + params->interval_us, + params->subrate_factor, + params->peripheral_latency, + params->continuation_number, + params->supervision_timeout_10ms, + params->supervision_timeout_10ms * 10); + } else { + bt_shell_print("Connection rate change failed (HCI status 0x%02x)", status); + } +} +#endif /* CONFIG_BT_SHORTER_CONNECTION_INTERVALS */ +#endif /* CONFIG_BT_SUBRATING */ #if defined(CONFIG_BT_LE_EXTENDED_FEAT_SET) void read_all_remote_feat_complete(struct bt_conn *conn, @@ -1255,6 +1278,9 @@ BT_CONN_CB_DEFINE(conn_callbacks) = { #if defined(CONFIG_BT_SUBRATING) .subrate_changed = subrate_changed, #endif +#if defined(CONFIG_BT_SHORTER_CONNECTION_INTERVALS) + .conn_rate_changed = conn_rate_changed, +#endif #if defined(CONFIG_BT_LE_EXTENDED_FEAT_SET) .read_all_remote_feat_complete = read_all_remote_feat_complete, #endif @@ -3361,7 +3387,130 @@ static int cmd_subrate_request(const struct shell *sh, size_t argc, char *argv[] return 0; } -#endif + +#if defined(CONFIG_BT_SHORTER_CONNECTION_INTERVALS) +static int cmd_read_min_conn_interval_groups(const struct shell *sh, size_t argc, char *argv[]) +{ + int err; + struct bt_conn_le_min_conn_interval_info info; + + err = bt_conn_le_read_min_conn_interval_groups(&info); + if (err != 0) { + shell_error(sh, "bt_conn_le_read_min_conn_interval_groups returned error %d", err); + return -ENOEXEC; + } + + shell_print(sh, "Minimum supported connection interval: %u us", + info.min_supported_conn_interval_us); + shell_print(sh, "Number of groups: %u", info.num_groups); + + for (uint8_t i = 0; i < info.num_groups; i++) { + shell_print( + sh, + " Group %u: min=0x%04x (%u us), max=0x%04x (%u us), stride=0x%04x (%u us)", + i, + info.groups[i].min_125us, + BT_CONN_SCI_INTERVAL_TO_US(info.groups[i].min_125us), + info.groups[i].max_125us, + BT_CONN_SCI_INTERVAL_TO_US(info.groups[i].max_125us), + info.groups[i].stride_125us, + BT_CONN_SCI_INTERVAL_TO_US(info.groups[i].stride_125us)); + } + + return 0; +} + +static int cmd_read_min_conn_interval(const struct shell *sh, size_t argc, char *argv[]) +{ + int err; + uint16_t min_interval_us; + + err = bt_conn_le_read_min_conn_interval(&min_interval_us); + if (err != 0) { + shell_error(sh, "bt_conn_le_read_min_conn_interval returned error %d", err); + return -ENOEXEC; + } + + shell_print(sh, "Minimum supported connection interval: %u us", min_interval_us); + return 0; +} + +static int cmd_conn_rate_set_defaults(const struct shell *sh, size_t argc, char *argv[]) +{ + int err = 0; + + for (size_t argn = 1; argn < argc; argn++) { + (void)shell_strtoul(argv[argn], 10, &err); + + if (err != 0) { + shell_help(sh); + shell_error(sh, "Could not parse input number %zu", argn); + return SHELL_CMD_HELP_PRINTED; + } + } + + const struct bt_conn_le_conn_rate_param params = { + .interval_min_125us = shell_strtoul(argv[1], 10, &err), + .interval_max_125us = shell_strtoul(argv[2], 10, &err), + .subrate_min = shell_strtoul(argv[3], 10, &err), + .subrate_max = shell_strtoul(argv[4], 10, &err), + .max_latency = shell_strtoul(argv[5], 10, &err), + .continuation_number = shell_strtoul(argv[6], 10, &err), + .supervision_timeout_10ms = shell_strtoul(argv[7], 10, &err), + .min_ce_len_125us = shell_strtoul(argv[8], 10, &err), + .max_ce_len_125us = shell_strtoul(argv[9], 10, &err), + }; + + err = bt_conn_le_conn_rate_set_defaults(¶ms); + if (err != 0) { + shell_error(sh, "bt_conn_le_conn_rate_set_defaults returned error %d", err); + return -ENOEXEC; + } + + return 0; +} + +static int cmd_conn_rate_request(const struct shell *sh, size_t argc, char *argv[]) +{ + int err = 0; + + if (default_conn == NULL) { + shell_error(sh, "Conn handle error, at least one connection is required."); + return -ENOEXEC; + } + + for (size_t argn = 1; argn < argc; argn++) { + (void)shell_strtoul(argv[argn], 10, &err); + + if (err != 0) { + shell_help(sh); + shell_error(sh, "Could not parse input number %zu", argn); + return SHELL_CMD_HELP_PRINTED; + } + } + + const struct bt_conn_le_conn_rate_param params = { + .interval_min_125us = shell_strtoul(argv[1], 10, &err), + .interval_max_125us = shell_strtoul(argv[2], 10, &err), + .subrate_min = shell_strtoul(argv[3], 10, &err), + .subrate_max = shell_strtoul(argv[4], 10, &err), + .max_latency = shell_strtoul(argv[5], 10, &err), + .continuation_number = shell_strtoul(argv[6], 10, &err), + .supervision_timeout_10ms = shell_strtoul(argv[7], 10, &err), + .min_ce_len_125us = shell_strtoul(argv[8], 10, &err), + .max_ce_len_125us = shell_strtoul(argv[9], 10, &err), + }; + + err = bt_conn_le_conn_rate_request(default_conn, ¶ms); + if (err != 0) { + shell_error(sh, "bt_conn_le_conn_rate_request returned error %d", err); + return -ENOEXEC; + } + + return 0; +} +#endif /* CONFIG_BT_SHORTER_CONNECTION_INTERVALS */ +#endif /* CONFIG_BT_SUBRATING */ #if defined(CONFIG_BT_LE_EXTENDED_FEAT_SET) static int cmd_read_all_remote_features(const struct shell *sh, size_t argc, char *argv[]) @@ -3714,14 +3863,7 @@ static int cmd_info(const struct shell *sh, size_t argc, char *argv[]) print_le_addr("Remote on-air", info.le.remote); print_le_addr("Local on-air", info.le.local); - shell_print(sh, "Interval: 0x%04x (%u us)", -#if defined(CONFIG_BT_SHORTER_CONNECTION_INTERVALS) - info.le.interval_us / BT_HCI_LE_SCI_INTERVAL_UNIT_US, -#else - info.le.interval_us / BT_HCI_LE_INTERVAL_UNIT_US, -#endif /* CONFIG_BT_SHORTER_CONNECTION_INTERVALS */ - info.le.interval_us); - + shell_print(sh, "Interval: %u us", info.le.interval_us); shell_print(sh, "Latency: 0x%04x", info.le.latency); shell_print(sh, "Supervision timeout: 0x%04x (%d ms)", @@ -5216,7 +5358,25 @@ SHELL_STATIC_SUBCMD_SET_CREATE(bt_cmds, " " " ", cmd_subrate_request, 6, 0), -#endif +#if defined(CONFIG_BT_SHORTER_CONNECTION_INTERVALS) + SHELL_CMD_ARG(read-min-conn-interval-groups, NULL, + "Read minimum supported connection interval and groups", + cmd_read_min_conn_interval_groups, 1, 0), + SHELL_CMD_ARG(read-min-conn-interval, NULL, + "Read minimum supported connection interval only", + cmd_read_min_conn_interval, 1, 0), + SHELL_CMD_ARG(conn-rate-set-defaults, NULL, + " " + " " + " ", + cmd_conn_rate_set_defaults, 10, 0), + SHELL_CMD_ARG(conn-rate-request, NULL, + " " + " " + " ", + cmd_conn_rate_request, 10, 0), +#endif /* CONFIG_BT_SHORTER_CONNECTION_INTERVALS */ +#endif /* CONFIG_BT_SUBRATING */ #if defined(CONFIG_BT_LE_EXTENDED_FEAT_SET) SHELL_CMD_ARG(read-all-remote-features, NULL, "", cmd_read_all_remote_features, 2, 0), diff --git a/tests/bluetooth/shell/testcase.yaml b/tests/bluetooth/shell/testcase.yaml index 783d3a670e1ee..5e4984e2d509b 100644 --- a/tests/bluetooth/shell/testcase.yaml +++ b/tests/bluetooth/shell/testcase.yaml @@ -46,6 +46,13 @@ tests: - CONFIG_BT_LE_EXTENDED_FEAT_SET=y - CONFIG_BT_LL_SW_SPLIT=n build_only: true + bluetooth.shell.shorter_connection_intervals: + extra_configs: + - CONFIG_BT_SHORTER_CONNECTION_INTERVALS=y + - CONFIG_BT_LE_EXTENDED_FEAT_SET=y + - CONFIG_BT_SUBRATING=y + - CONFIG_BT_LL_SW_SPLIT=n + build_only: true bluetooth.shell.channel_sounding: extra_configs: - CONFIG_BT_CHANNEL_SOUNDING=y