From 7fe16b6dfd1f05b75bdb6678a408dfd31533fce6 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Mon, 3 Nov 2025 12:54:22 -0500 Subject: [PATCH] emulator: Add support for LL Extended Feature Set MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds support for emulating LL Extended Feature Set introduced in 6.0 that adds the following: Commands: - HCI_LE_Read_All_Local_Supported_­Features(0x2087)(Feature:47,1) - HCI_LE_Read_All_Remote_Features(0x2088)(Feature:47,2) Events: - HCI_LE_Read_All_Remote_Features_Complete(0x2b)(Mask bit:42) --- emulator/btdev.c | 101 ++++++++++++++++++++++++++++++++++++++++++++-- emulator/btdev.h | 1 + emulator/hciemu.c | 3 ++ emulator/hciemu.h | 1 + emulator/main.c | 2 +- 5 files changed, 103 insertions(+), 5 deletions(-) diff --git a/emulator/btdev.c b/emulator/btdev.c index c53db7040..839b4941c 100644 --- a/emulator/btdev.c +++ b/emulator/btdev.c @@ -178,7 +178,7 @@ struct btdev { uint8_t country_code; uint8_t bdaddr[6]; uint8_t random_addr[6]; - uint8_t le_features[8]; + uint8_t le_features[248]; uint8_t le_states[8]; const struct btdev_cmd *cmds; uint16_t msft_opcode; @@ -7362,6 +7362,75 @@ static const struct btdev_cmd cmd_le[] = { {} }; +static int cmd_le_read_all_local_features(struct btdev *dev, const void *data, + uint8_t len) +{ + struct bt_hci_rsp_le_read_all_local_features rsp; + + memset(&rsp, 0, sizeof(rsp)); + rsp.status = BT_HCI_ERR_SUCCESS; + memcpy(rsp.features, dev->le_features, 248); + + cmd_complete(dev, BT_HCI_CMD_LE_READ_ALL_LOCAL_FEATURES, &rsp, + sizeof(rsp)); + + return 0; +} + +static int cmd_le_read_all_remote_features(struct btdev *dev, const void *data, + uint8_t len) +{ + const struct bt_hci_cmd_le_read_all_remote_features *cmd = data; + struct bt_hci_evt_le_read_all_remote_features_complete ev; + struct btdev_conn *conn; + uint8_t status = BT_HCI_ERR_SUCCESS; + + conn = queue_find(dev->conns, match_handle, + UINT_TO_PTR(le16_to_cpu(cmd->handle))); + if (!conn) + status = BT_HCI_ERR_UNKNOWN_CONN_ID; + + cmd_status(dev, status, BT_HCI_CMD_LE_READ_ALL_REMOTE_FEATURES); + + if (status) + return 0; + + memset(&ev, 0, sizeof(ev)); + ev.status = BT_HCI_ERR_SUCCESS; + ev.handle = cpu_to_le16(conn->handle); + ev.max_pages = 1; + ev.valid_pages = 1; + memcpy(ev.features, conn->link->dev->le_features, 248); + + le_meta_event(dev, BT_HCI_EVT_LE_READ_ALL_REMOTE_FEATURES_COMPLETE, &ev, + sizeof(ev)); + + return 0; +} + +#define CMD_LE_60 \ + CMD(BT_HCI_CMD_LE_READ_ALL_LOCAL_FEATURES, \ + cmd_le_read_all_local_features, NULL), \ + CMD(BT_HCI_CMD_LE_READ_ALL_REMOTE_FEATURES, \ + cmd_le_read_all_remote_features, NULL) + +static const struct btdev_cmd cmd_le_6_0[] = { + CMD_COMMON_ALL, + CMD_COMMON_BREDR_LE, + CMD_LE, + CMD_LE_50, + CMD_LE_52, + CMD_LE_60, + {} +}; + +static void set_le_60_commands(struct btdev *btdev) +{ + btdev->commands[47] |= BIT(2); /* LE Read All Local Features */ + btdev->commands[47] |= BIT(3); /* LE Read All Remote Features */ + btdev->cmds = cmd_le_6_0; +} + static void set_le_commands(struct btdev *btdev) { set_common_commands_all(btdev); @@ -7427,6 +7496,12 @@ static void set_le_commands(struct btdev *btdev) set_le_52_commands(btdev); btdev->cmds = cmd_le_5_2; } + + /* Extra LE commands for >= 6.0 adapters */ + if (btdev->type >= BTDEV_TYPE_BREDRLE52) { + set_le_60_commands(btdev); + btdev->cmds = cmd_le_6_0; + } } static int cmd_set_event_mask_2(struct btdev *dev, const void *data, @@ -7607,6 +7682,7 @@ static void set_bredrle_features(struct btdev *btdev) } if (btdev->type >= BTDEV_TYPE_BREDRLE52) { + btdev->version = 0x0b; btdev->le_features[1] |= 0x20; /* LE PER ADV */ btdev->le_features[3] |= BIT(0); /* LE PAST Sender */ btdev->le_features[3] |= BIT(1); /* LE PAST Receiver */ @@ -7617,6 +7693,11 @@ static void set_bredrle_features(struct btdev *btdev) btdev->le_features[4] |= 0x01; /* LE ISO channels */ } + if (btdev->type >= BTDEV_TYPE_BREDRLE60) { + btdev->version = 0x0e; + btdev->le_features[7] |= BIT(7); /* LL Extended Features */ + } + btdev->feat_page_2[0] |= 0x01; /* CPB - Central Operation */ btdev->feat_page_2[0] |= 0x02; /* CPB - Peripheral Operation */ btdev->feat_page_2[0] |= 0x04; /* Synchronization Train */ @@ -7717,14 +7798,23 @@ struct btdev *btdev_create(enum btdev_type type, uint16_t id) memset(btdev, 0, sizeof(*btdev)); - if (type == BTDEV_TYPE_BREDRLE || type == BTDEV_TYPE_LE || - type == BTDEV_TYPE_BREDRLE50 || - type == BTDEV_TYPE_BREDRLE52) { + switch (type) { + case BTDEV_TYPE_BREDRLE: + case BTDEV_TYPE_LE: + case BTDEV_TYPE_BREDRLE50: + case BTDEV_TYPE_BREDRLE52: + case BTDEV_TYPE_BREDRLE60: btdev->crypto = bt_crypto_new(); if (!btdev->crypto) { free(btdev); return NULL; } + break; + case BTDEV_TYPE_BREDR: + case BTDEV_TYPE_BREDR20: + case BTDEV_TYPE_AMP: + default: + break; } btdev->type = type; @@ -7736,6 +7826,7 @@ struct btdev *btdev_create(enum btdev_type type, uint16_t id) case BTDEV_TYPE_BREDRLE: case BTDEV_TYPE_BREDRLE50: case BTDEV_TYPE_BREDRLE52: + case BTDEV_TYPE_BREDRLE60: btdev->version = 0x09; set_bredrle_features(btdev); set_bredrle_commands(btdev); @@ -8448,6 +8539,7 @@ int btdev_set_msft_opcode(struct btdev *btdev, uint16_t opcode) case BTDEV_TYPE_BREDRLE: case BTDEV_TYPE_BREDRLE50: case BTDEV_TYPE_BREDRLE52: + case BTDEV_TYPE_BREDRLE60: btdev->msft_opcode = opcode; btdev->msft_cmds = cmd_msft; return 0; @@ -8505,6 +8597,7 @@ int btdev_set_emu_opcode(struct btdev *btdev, uint16_t opcode) case BTDEV_TYPE_BREDRLE: case BTDEV_TYPE_BREDRLE50: case BTDEV_TYPE_BREDRLE52: + case BTDEV_TYPE_BREDRLE60: btdev->emu_opcode = opcode; btdev->emu_cmds = cmd_emu; return 0; diff --git a/emulator/btdev.h b/emulator/btdev.h index c7b3b468a..d473bd4b3 100644 --- a/emulator/btdev.h +++ b/emulator/btdev.h @@ -52,6 +52,7 @@ enum btdev_type { BTDEV_TYPE_BREDR20, BTDEV_TYPE_BREDRLE50, BTDEV_TYPE_BREDRLE52, + BTDEV_TYPE_BREDRLE60, }; enum btdev_hook_type { diff --git a/emulator/hciemu.c b/emulator/hciemu.c index 3521e9994..8c73a07ee 100644 --- a/emulator/hciemu.c +++ b/emulator/hciemu.c @@ -382,6 +382,9 @@ struct hciemu *hciemu_new_num(enum hciemu_type type, uint8_t num) case HCIEMU_TYPE_BREDRLE52: hciemu->btdev_type = BTDEV_TYPE_BREDRLE52; break; + case HCIEMU_TYPE_BREDRLE60: + hciemu->btdev_type = BTDEV_TYPE_BREDRLE60; + break; default: return NULL; } diff --git a/emulator/hciemu.h b/emulator/hciemu.h index 9fbe34316..1164be651 100644 --- a/emulator/hciemu.h +++ b/emulator/hciemu.h @@ -21,6 +21,7 @@ enum hciemu_type { HCIEMU_TYPE_LEGACY, HCIEMU_TYPE_BREDRLE50, HCIEMU_TYPE_BREDRLE52, + HCIEMU_TYPE_BREDRLE60, }; enum hciemu_hook_type { diff --git a/emulator/main.c b/emulator/main.c index bdc2f0deb..456fcd98e 100644 --- a/emulator/main.c +++ b/emulator/main.c @@ -92,7 +92,7 @@ int main(int argc, char *argv[]) bool serial_enabled = false; int letest_count = 0; int vhci_count = 0; - enum btdev_type type = BTDEV_TYPE_BREDRLE52; + enum btdev_type type = BTDEV_TYPE_BREDRLE60; int i; mainloop_init(); -- 2.47.3