From 70fd5eeed2aad774a8240875843776e1ca30c483 Mon Sep 17 00:00:00 2001 From: Sean Madigan Date: Wed, 9 Jul 2025 12:32:54 +0100 Subject: [PATCH 1/2] bluetooth: host: hci_core: allow both orders of version and feature events Previously, the version complete event had to come after the remote feature complete event for the notify_remote_info function to be called. This becomes less practical when adding the read all remote features complete event, as this can take some time. Meaning the order of events is not predictable. Signed-off-by: Sean Madigan --- subsys/bluetooth/host/hci_core.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/subsys/bluetooth/host/hci_core.c b/subsys/bluetooth/host/hci_core.c index 11288fa9972b..c3b0bba3f805 100644 --- a/subsys/bluetooth/host/hci_core.c +++ b/subsys/bluetooth/host/hci_core.c @@ -1792,7 +1792,8 @@ static void le_remote_feat_complete(struct net_buf *buf) atomic_set_bit(conn->flags, BT_CONN_LE_FEATURES_EXCHANGED); if (IS_ENABLED(CONFIG_BT_REMOTE_INFO) && - !IS_ENABLED(CONFIG_BT_REMOTE_VERSION)) { + (!IS_ENABLED(CONFIG_BT_REMOTE_VERSION) || + atomic_test_bit(conn->flags, BT_CONN_AUTO_VERSION_INFO))) { notify_remote_info(conn); } @@ -2372,7 +2373,8 @@ static void bt_hci_evt_read_remote_version_complete(struct net_buf *buf) atomic_set_bit(conn->flags, BT_CONN_AUTO_VERSION_INFO); - if (IS_ENABLED(CONFIG_BT_REMOTE_INFO)) { + if (IS_ENABLED(CONFIG_BT_REMOTE_INFO) && + atomic_test_bit(conn->flags, BT_CONN_LE_FEATURES_EXCHANGED)) { /* Remote features is already present */ notify_remote_info(conn); } From d48554830235adf22ca1517607f339aac414e38e Mon Sep 17 00:00:00 2001 From: Sean Madigan Date: Tue, 8 Jul 2025 17:11:51 +0100 Subject: [PATCH 2/2] bluetooth: host: Add support for extended feature set feature This commit adds support for the extended feature set feature. This includes: - hci boilerplate - kconfigs, including one for a max local feature page - reading remote features is done by a command and callback - this is not linked into the auto feature request on connection as this procedure can take quite a few connection events, and we do not want to delay the user - added the commands to the bt shell Signed-off-by: Sean Madigan --- include/zephyr/bluetooth/bluetooth.h | 12 +++- include/zephyr/bluetooth/conn.h | 58 ++++++++++++++++++ include/zephyr/bluetooth/hci_types.h | 36 ++++++++++- subsys/bluetooth/Kconfig | 15 +++++ subsys/bluetooth/common/Kconfig | 2 +- subsys/bluetooth/controller/Kconfig | 11 ++++ subsys/bluetooth/host/conn.c | 47 +++++++++++++++ subsys/bluetooth/host/conn_internal.h | 3 + subsys/bluetooth/host/hci_core.c | 86 +++++++++++++++++++++++++-- subsys/bluetooth/host/shell/bt.c | 66 ++++++++++++++++++++ tests/bluetooth/shell/testcase.yaml | 6 ++ 11 files changed, 331 insertions(+), 11 deletions(-) diff --git a/include/zephyr/bluetooth/bluetooth.h b/include/zephyr/bluetooth/bluetooth.h index ccd10411e22b..65d1f4a1f35a 100644 --- a/include/zephyr/bluetooth/bluetooth.h +++ b/include/zephyr/bluetooth/bluetooth.h @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -71,11 +72,16 @@ extern "C" { #define BT_ID_DEFAULT 0 /** - * @brief Number of octets for local supported + * @brief Number of octets for local supported features * - * The value of 8 correspond to page 0 in the LE Controller supported features + * The value of 8 correspond to page 0 in the LE Controller supported features. + * 24 bytes are required for all subsequent supported feature pages. */ -#define BT_LE_LOCAL_SUPPORTED_FEATURES_SIZE 8 +#define BT_LE_LOCAL_SUPPORTED_FEATURES_SIZE \ + (BT_HCI_LE_BYTES_PAGE_0_FEATURE_PAGE + \ + COND_CODE_1(CONFIG_BT_LE_MAX_LOCAL_SUPPORTED_FEATURE_PAGE, \ + CONFIG_BT_LE_MAX_LOCAL_SUPPORTED_FEATURE_PAGE * BT_HCI_LE_BYTES_PER_FEATURE_PAGE, \ + (0U))) /** Opaque type representing an advertiser. */ struct bt_le_ext_adv; diff --git a/include/zephyr/bluetooth/conn.h b/include/zephyr/bluetooth/conn.h index 8bc060bb05b5..bf8a31603b85 100644 --- a/include/zephyr/bluetooth/conn.h +++ b/include/zephyr/bluetooth/conn.h @@ -252,6 +252,27 @@ struct bt_conn_le_subrate_changed { uint16_t supervision_timeout; }; +/** 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. + * + * The remaining parameters will be unchanged if status is not @ref BT_HCI_ERR_SUCCESS. + */ + uint8_t status; + /** Number of pages supported by remote device. */ + uint8_t max_remote_page; + /** Number of pages fetched from remote device. */ + uint8_t max_valid_page; + /** @brief Pointer to array of size 248, with feature bits of remote supported features. + * + * Page 0 being 8 bytes, with the following 10 pages of 24 bytes. + * Refer to BT_LE_FEAT_BIT_* for values. + * Refer to the BT_FEAT_LE_* macros for value comparison. + * See Bluetooth Core Specification, Vol 6, Part B, Section 4.6. + */ + const uint8_t *features; +}; + /** Connection Type */ enum __packed bt_conn_type { /** LE Connection Type */ @@ -1186,6 +1207,24 @@ 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 remote feature pages. + * + * Request remote feature pages, from 0 up to pages_requested or the number + * of pages supported by the peer. There is a maximum of 10 pages. + * This function will trigger the read_all_remote_feat_complete callback + * when the procedure is completed. + * + * @kconfig_dep{CONFIG_BT_LE_EXTENDED_FEAT_SET} + * + * @param conn @ref BT_CONN_TYPE_LE connection object. + * @param pages_requested Number of feature pages to be requested from peer. + * There is a maximum of 10 pages. + * + * @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_read_all_remote_features(struct bt_conn *conn, uint8_t pages_requested); + /** @brief Update the connection parameters. * * If the local device is in the peripheral role then updating the connection @@ -1877,6 +1916,25 @@ struct bt_conn_cb { const struct bt_conn_le_subrate_changed *params); #endif /* CONFIG_BT_SUBRATING */ +#if defined(CONFIG_BT_LE_EXTENDED_FEAT_SET) + /** @brief Read all remote features complete event. + * + * This callback notifies the application that a 'read all remote + * features' procedure of the connection is completed. The other params + * will not be populated if status is not @ref BT_HCI_ERR_SUCCESS. + * + * This callback can be triggered by calling @ref + * bt_conn_le_read_all_remote_features or by the procedure running + * autonomously in the controller. + * + * @param conn Connection object. + * @param params Remote features. + */ + void (*read_all_remote_feat_complete)( + struct bt_conn *conn, + const struct bt_conn_le_read_all_remote_feat_complete *params); +#endif /* CONFIG_BT_LE_EXTENDED_FEAT_SET */ + #if defined(CONFIG_BT_CHANNEL_SOUNDING) /** @brief LE CS Read Remote Supported Capabilities Complete event. * diff --git a/include/zephyr/bluetooth/hci_types.h b/include/zephyr/bluetooth/hci_types.h index 0b2c1b1a3976..a20852712394 100644 --- a/include/zephyr/bluetooth/hci_types.h +++ b/include/zephyr/bluetooth/hci_types.h @@ -207,7 +207,7 @@ struct bt_hci_cmd_hdr { #define BT_LE_FEAT_BIT_CHANNEL_SOUNDING 46 #define BT_LE_FEAT_BIT_CHANNEL_SOUNDING_HOST 47 #define BT_LE_FEAT_BIT_CHANNEL_SOUNDING_TONE_QUAL_IND 48 -#define BT_LE_FEAT_BIT_LL_EXTENDED_FEAT_SET 63 +#define BT_LE_FEAT_BIT_EXTENDED_FEAT_SET 63 #define BT_LE_FEAT_TEST(feat, n) (feat[(n) >> 3] & \ BIT((n) & 7)) @@ -284,6 +284,8 @@ struct bt_hci_cmd_hdr { BT_LE_FEAT_BIT_CHANNEL_SOUNDING) #define BT_FEAT_LE_CHANNEL_SOUNDING_HOST(feat) BT_LE_FEAT_TEST(feat, \ BT_LE_FEAT_BIT_CHANNEL_SOUNDING_HOST) +#define BT_FEAT_LE_EXTENDED_FEAT_SET(feat) BT_LE_FEAT_TEST(feat, \ + BT_LE_FEAT_BIT_EXTENDED_FEAT_SET) #define BT_FEAT_LE_CIS(feat) (BT_FEAT_LE_CIS_CENTRAL(feat) | \ BT_FEAT_LE_CIS_PERIPHERAL(feat)) @@ -2438,6 +2440,22 @@ struct bt_hci_cp_le_tx_test_v4 { uint8_t ant_ids[0]; } __packed; +#define BT_HCI_OP_LE_READ_ALL_LOCAL_SUPPORTED_FEATURES BT_OP(BT_OGF_LE, 0x0087) /* 0x2087 */ +struct bt_hci_rp_le_read_all_local_supported_features { + uint8_t status; + uint8_t max_page; + uint8_t features[248]; +} __packed; + +#define BT_READ_ALL_LOCAL_FEATURES_SUPPORTED(supported_commands) \ + BT_CMD_TEST(supported_commands, 47, 2) + +#define BT_HCI_OP_LE_READ_ALL_REMOTE_FEATURES BT_OP(BT_OGF_LE, 0x0088) /* 0x2088 */ +struct bt_hci_cp_le_read_all_remote_features { + uint16_t handle; + uint8_t pages_requested; +} __packed; + #define BT_HCI_TX_TEST_POWER_MIN -0x7F #define BT_HCI_TX_TEST_POWER_MAX 0x14 @@ -3552,6 +3570,20 @@ struct bt_hci_evt_le_cis_established_v2 { uint8_t framing; } __packed; +#define BT_HCI_EVT_LE_READ_ALL_REMOTE_FEAT_COMPLETE 0x2b + +#define BT_HCI_LE_FEATURE_PAGE_MAX 10 +#define BT_HCI_LE_BYTES_PER_FEATURE_PAGE 24 +#define BT_HCI_LE_BYTES_PAGE_0_FEATURE_PAGE 8 + +struct bt_hci_evt_le_read_all_remote_feat_complete { + uint8_t status; + uint16_t handle; + uint8_t max_remote_page; + uint8_t max_valid_page; + uint8_t features[248]; +} __packed; + #define BT_HCI_LE_CS_INITIATOR_ROLE_MASK BIT(0) #define BT_HCI_LE_CS_REFLECTOR_ROLE_MASK BIT(1) @@ -4024,6 +4056,8 @@ struct bt_hci_evt_le_cs_procedure_enable_complete { #define BT_EVT_MASK_LE_ENH_CONN_COMPLETE_V2 BT_EVT_BIT(40) #define BT_EVT_MASK_LE_CIS_ESTABLISHED_V2 BT_EVT_BIT(41) +#define BT_EVT_MASK_LE_READ_ALL_REMOTE_FEAT_COMPLETE BT_EVT_BIT(42) + #define BT_EVT_MASK_LE_CS_READ_REMOTE_SUPPORTED_CAPABILITIES_COMPLETE BT_EVT_BIT(43) #define BT_EVT_MASK_LE_CS_READ_REMOTE_FAE_TABLE_COMPLETE BT_EVT_BIT(44) #define BT_EVT_MASK_LE_CS_SECURITY_ENABLE_COMPLETE BT_EVT_BIT(45) diff --git a/subsys/bluetooth/Kconfig b/subsys/bluetooth/Kconfig index 68d08819e1c3..c98c7a5ff538 100644 --- a/subsys/bluetooth/Kconfig +++ b/subsys/bluetooth/Kconfig @@ -115,6 +115,21 @@ config BT_CONN_TX help Hidden configuration that is true if ACL or broadcast ISO is enabled +config BT_LE_MAX_LOCAL_SUPPORTED_FEATURE_PAGE + int "Maximum supported feature page" + default 0 + range 0 10 + depends on BT_LE_EXTENDED_FEAT_SET + help + Maximum supported feature page that can be stored locally and fetched + from the remote connection with the LL Extended Feature Set feature. + +config BT_LE_EXTENDED_FEAT_SET + bool "LL Extended Feature Set" + depends on !HAS_BT_CTLR || BT_CTLR_EXTENDED_FEAT_SET_SUPPORT + help + Enable support for the LL Extended Feature Set feature. + if BT_CONN config BT_HCI_ACL_FLOW_CONTROL bool "Controller to Host ACL flow control support" diff --git a/subsys/bluetooth/common/Kconfig b/subsys/bluetooth/common/Kconfig index 2b7b698d8f8e..60328a1dd35a 100644 --- a/subsys/bluetooth/common/Kconfig +++ b/subsys/bluetooth/common/Kconfig @@ -119,7 +119,7 @@ config BT_BUF_ACL_RX_COUNT config BT_BUF_EVT_RX_SIZE int "Maximum supported HCI Event buffer length" - default $(UINT8_MAX) if (BT_EXT_ADV && BT_OBSERVER) || BT_PER_ADV_SYNC || BT_DF_CONNECTION_CTE_RX || BT_CLASSIC || BT_CHANNEL_SOUNDING + default $(UINT8_MAX) if (BT_EXT_ADV && BT_OBSERVER) || BT_PER_ADV_SYNC || BT_DF_CONNECTION_CTE_RX || BT_CLASSIC || BT_CHANNEL_SOUNDING || BT_LE_EXTENDED_FEAT_SET # LE Read Supported Commands command complete event. default 68 range 68 $(UINT8_MAX) diff --git a/subsys/bluetooth/controller/Kconfig b/subsys/bluetooth/controller/Kconfig index 02042b35074a..1144d742bee6 100644 --- a/subsys/bluetooth/controller/Kconfig +++ b/subsys/bluetooth/controller/Kconfig @@ -128,6 +128,9 @@ config BT_CTLR_SUBRATING_SUPPORT config BT_CTLR_CHANNEL_SOUNDING_SUPPORT bool +config BT_CTLR_EXTENDED_FEAT_SET_SUPPORT + bool + # Virtual option that all local LL implementations should select config HAS_BT_CTLR bool @@ -1174,6 +1177,14 @@ config BT_CTLR_CHANNEL_SOUNDING Enable support for Bluetooth 6.0 Channel Sounding in the Controller. +config BT_CTLR_EXTENDED_FEAT_SET + bool "LL Extended Feature Set support" + depends on BT_CTLR_EXTENDED_FEAT_SET_SUPPORT + default y if BT_LE_EXTENDED_FEAT_SET + help + Enable support for Bluetooth 6.0 LL Extended Feature Set + 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 9e9f18c040a0..5479721541af 100644 --- a/subsys/bluetooth/host/conn.c +++ b/subsys/bluetooth/host/conn.c @@ -3289,6 +3289,53 @@ int bt_conn_le_subrate_request(struct bt_conn *conn, } #endif /* CONFIG_BT_SUBRATING */ +#if defined(CONFIG_BT_LE_EXTENDED_FEAT_SET) +void notify_read_all_remote_feat_complete(struct bt_conn *conn, + struct bt_conn_le_read_all_remote_feat_complete *params) +{ + struct bt_conn_cb *callback; + + SYS_SLIST_FOR_EACH_CONTAINER(&conn_cbs, callback, _node) { + if (callback->read_all_remote_feat_complete != NULL) { + callback->read_all_remote_feat_complete(conn, params); + } + } + + STRUCT_SECTION_FOREACH(bt_conn_cb, cb) + { + if (cb->read_all_remote_feat_complete != NULL) { + cb->read_all_remote_feat_complete(conn, params); + } + } +} + +int bt_conn_le_read_all_remote_features(struct bt_conn *conn, uint8_t pages_requested) +{ + struct bt_hci_cp_le_read_all_remote_features *cp; + struct net_buf *buf; + + if (!bt_conn_is_type(conn, BT_CONN_TYPE_LE)) { + LOG_DBG("Invalid connection type: %u for %p", conn->type, conn); + return -EINVAL; + } + + if (pages_requested > BT_HCI_LE_FEATURE_PAGE_MAX) { + 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->pages_requested = pages_requested; + + return bt_hci_cmd_send_sync(BT_HCI_OP_LE_READ_ALL_REMOTE_FEATURES, buf, NULL); +} +#endif /* CONFIG_BT_LE_EXTENDED_FEAT_SET */ + #if defined(CONFIG_BT_CHANNEL_SOUNDING) void notify_remote_cs_capabilities(struct bt_conn *conn, uint8_t status, struct bt_conn_le_cs_capabilities *params) diff --git a/subsys/bluetooth/host/conn_internal.h b/subsys/bluetooth/host/conn_internal.h index 909c0ebd22ef..7d3cc1303942 100644 --- a/subsys/bluetooth/host/conn_internal.h +++ b/subsys/bluetooth/host/conn_internal.h @@ -512,6 +512,9 @@ void notify_path_loss_threshold_report(struct bt_conn *conn, void notify_subrate_change(struct bt_conn *conn, struct bt_conn_le_subrate_changed params); +void notify_read_all_remote_feat_complete(struct bt_conn *conn, + struct bt_conn_le_read_all_remote_feat_complete *params); + void notify_remote_cs_capabilities(struct bt_conn *conn, uint8_t status, struct bt_conn_le_cs_capabilities *params); diff --git a/subsys/bluetooth/host/hci_core.c b/subsys/bluetooth/host/hci_core.c index c3b0bba3f805..e6738c8a69be 100644 --- a/subsys/bluetooth/host/hci_core.c +++ b/subsys/bluetooth/host/hci_core.c @@ -1800,6 +1800,37 @@ static void le_remote_feat_complete(struct net_buf *buf) bt_conn_unref(conn); } +#if defined(CONFIG_BT_LE_EXTENDED_FEAT_SET) +static void le_read_all_remote_feat_complete(struct net_buf *buf) +{ + struct bt_hci_evt_le_read_all_remote_feat_complete *evt = (void *)buf->data; + struct bt_conn *conn; + struct bt_conn_le_read_all_remote_feat_complete params; + uint16_t handle = sys_le16_to_cpu(evt->handle); + + LOG_DBG("Read all remote feature complete: 0x%02x %s handle %u", evt->status, + bt_hci_err_to_str(evt->status), handle); + + conn = bt_conn_lookup_handle(handle, BT_CONN_TYPE_LE); + if (conn == NULL) { + LOG_ERR("Unknown conn handle 0x%04X", handle); + return; + } + + params.status = evt->status; + + if (params.status == BT_HCI_ERR_SUCCESS) { + params.max_remote_page = evt->max_remote_page; + params.max_valid_page = evt->max_valid_page; + params.features = evt->features; + } + + notify_read_all_remote_feat_complete(conn, ¶ms); + + bt_conn_unref(conn); +} +#endif /* CONFIG_BT_LE_EXTENDED_FEAT_SET */ + #if defined(CONFIG_BT_DATA_LEN_UPDATE) static void le_data_len_change(struct net_buf *buf) { @@ -2922,6 +2953,11 @@ static const struct event_handler meta_events[] = { EVENT_HANDLER(BT_HCI_EVT_LE_ENH_CONN_COMPLETE_V2, le_enh_conn_complete_v2, sizeof(struct bt_hci_evt_le_enh_conn_complete_v2)), #endif /* CONFIG_BT_PER_ADV_RSP || CONFIG_BT_PER_ADV_SYNC_RSP */ +#if defined(CONFIG_BT_LE_EXTENDED_FEAT_SET) + EVENT_HANDLER(BT_HCI_EVT_LE_READ_ALL_REMOTE_FEAT_COMPLETE, + le_read_all_remote_feat_complete, + sizeof(struct bt_hci_evt_le_read_all_remote_feat_complete)), +#endif /* CONFIG_BT_LE_EXTENDED_FEAT_SET */ #endif /* CONFIG_BT_CONN */ #if defined(CONFIG_BT_CHANNEL_SOUNDING) EVENT_HANDLER(BT_HCI_EVT_LE_CS_READ_REMOTE_SUPPORTED_CAPABILITIES_COMPLETE, @@ -3158,9 +3194,47 @@ static void read_le_features_complete(struct net_buf *buf) LOG_DBG("status 0x%02x %s", rp->status, bt_hci_err_to_str(rp->status)); + memcpy(bt_dev.le.features, rp->features, sizeof(rp->features)); +} + +static void read_le_all_supported_features_complete(struct net_buf *buf) +{ + struct bt_hci_rp_le_read_all_local_supported_features *rp = (void *)buf->data; + + LOG_DBG("status 0x%02x %s", rp->status, bt_hci_err_to_str(rp->status)); + memcpy(bt_dev.le.features, rp->features, sizeof(bt_dev.le.features)); } +static int read_le_local_supported_features(void) +{ + struct net_buf *rsp; + int err; + + /* Read Low Energy Supported Features */ + if (IS_ENABLED(CONFIG_BT_LE_EXTENDED_FEAT_SET) && + BT_READ_ALL_LOCAL_FEATURES_SUPPORTED(bt_dev.supported_commands)) { + err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_READ_ALL_LOCAL_SUPPORTED_FEATURES, NULL, + &rsp); + if (err != 0) { + return err; + } + + read_le_all_supported_features_complete(rsp); + } else { + err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_READ_LOCAL_FEATURES, NULL, + &rsp); + if (err != 0) { + return err; + } + + read_le_features_complete(rsp); + } + + net_buf_unref(rsp); + return 0; +} + #if defined(CONFIG_BT_CONN) #if !defined(CONFIG_BT_CLASSIC) static void read_buffer_size_complete(struct net_buf *buf) @@ -3445,6 +3519,11 @@ static int le_set_event_mask(void) BT_FEAT_LE_CONN_SUBRATING(bt_dev.le.features)) { mask |= BT_EVT_MASK_LE_SUBRATE_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; + } } if (IS_ENABLED(CONFIG_BT_SMP) && @@ -3583,16 +3662,11 @@ static int le_init(void) return -ENODEV; } - /* Read Low Energy Supported Features */ - err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_READ_LOCAL_FEATURES, NULL, - &rsp); + err = read_le_local_supported_features(); if (err) { return err; } - read_le_features_complete(rsp); - net_buf_unref(rsp); - if (IS_ENABLED(CONFIG_BT_ISO) && BT_FEAT_LE_ISO(bt_dev.le.features)) { err = le_init_iso(); diff --git a/subsys/bluetooth/host/shell/bt.c b/subsys/bluetooth/host/shell/bt.c index c133608bd9d2..ab2282554dfb 100644 --- a/subsys/bluetooth/host/shell/bt.c +++ b/subsys/bluetooth/host/shell/bt.c @@ -1007,6 +1007,37 @@ void subrate_changed(struct bt_conn *conn, } #endif +#if defined(CONFIG_BT_LE_EXTENDED_FEAT_SET) +void read_all_remote_feat_complete(struct bt_conn *conn, + const struct bt_conn_le_read_all_remote_feat_complete *params) +{ + if (params->status == BT_HCI_ERR_SUCCESS) { + uint8_t number_of_bytes = BT_HCI_LE_BYTES_PAGE_0_FEATURE_PAGE; + + if (params->max_valid_page > 0) { + number_of_bytes += + (params->max_valid_page * BT_HCI_LE_BYTES_PER_FEATURE_PAGE); + } + + bt_shell_fprintf_print( + "Read all remote features complete, Max Remote Page %d, LE Features: 0x", + params->max_remote_page); + + for (int i = number_of_bytes - 1; i >= 0; i--) { + uint8_t features = params->features[i]; + char features_str[(2 * sizeof(uint8_t)) + 1]; + + bin2hex(&features, sizeof(features), features_str, sizeof(features_str)); + bt_shell_fprintf_print("%s", features_str); + } + bt_shell_fprintf_print("\n"); + } else { + bt_shell_print("Read all remote features failed (HCI status 0x%02x)", + params->status); + } +} +#endif + #if defined(CONFIG_BT_CHANNEL_SOUNDING) void print_remote_cs_capabilities(struct bt_conn *conn, uint8_t status, @@ -1186,6 +1217,9 @@ static struct bt_conn_cb conn_callbacks = { #if defined(CONFIG_BT_SUBRATING) .subrate_changed = subrate_changed, #endif +#if defined(CONFIG_BT_LE_EXTENDED_FEAT_SET) + .read_all_remote_feat_complete = read_all_remote_feat_complete, +#endif #if defined(CONFIG_BT_CHANNEL_SOUNDING) .le_cs_read_remote_capabilities_complete = print_remote_cs_capabilities, .le_cs_read_remote_fae_table_complete = print_remote_cs_fae_table, @@ -3284,6 +3318,34 @@ static int cmd_subrate_request(const struct shell *sh, size_t argc, char *argv[] } #endif +#if defined(CONFIG_BT_LE_EXTENDED_FEAT_SET) +static int cmd_read_all_remote_features(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; + } + + uint8_t pages_requested = shell_strtoul(argv[1], 10, &err); + + if (err != 0) { + shell_help(sh); + shell_error(sh, "Could not parse input for pages_requested"); + return SHELL_CMD_HELP_PRINTED; + } + + err = bt_conn_le_read_all_remote_features(default_conn, pages_requested); + if (err != 0) { + shell_error(sh, "bt_conn_le_read_all_remote_features returned error %d", err); + return -ENOEXEC; + } + + return 0; +} +#endif + #if defined(CONFIG_BT_CONN) #if defined(CONFIG_BT_CENTRAL) static int bt_do_connect_le(int *ercd, size_t argc, char *argv[]) @@ -5035,6 +5097,10 @@ SHELL_STATIC_SUBCMD_SET_CREATE(bt_cmds, " ", cmd_subrate_request, 6, 0), #endif +#if defined(CONFIG_BT_LE_EXTENDED_FEAT_SET) + SHELL_CMD_ARG(read-all-remote-features, NULL, "", + cmd_read_all_remote_features, 2, 0), +#endif #if defined(CONFIG_BT_BROADCASTER) SHELL_CMD_ARG(advertise, NULL, " [mode: discov, non_discov] " diff --git a/tests/bluetooth/shell/testcase.yaml b/tests/bluetooth/shell/testcase.yaml index 812aee24eeb3..673f13e481ca 100644 --- a/tests/bluetooth/shell/testcase.yaml +++ b/tests/bluetooth/shell/testcase.yaml @@ -34,6 +34,12 @@ tests: - CONFIG_BT_SUBRATING=y - CONFIG_BT_LL_SW_SPLIT=n build_only: true + bluetooth.shell.extended_feature_set: + extra_configs: + - CONFIG_BT_LE_MAX_LOCAL_SUPPORTED_FEATURE_PAGE=10 + - CONFIG_BT_LE_EXTENDED_FEAT_SET=y + - CONFIG_BT_LL_SW_SPLIT=n + build_only: true bluetooth.shell.channel_sounding: extra_configs: - CONFIG_BT_CHANNEL_SOUNDING=y