// SPDX-License-Identifier: GPL-2.0-or-later/* * Bluetooth support for Realtek devices * * Copyright (C) 2015 Endless Mobile, Inc. */ #include <linux/module.h>#include <linux/firmware.h>#include <linux/unaligned.h>#include <linux/usb.h> #include <net/bluetooth/bluetooth.h>#include <net/bluetooth/hci_core.h> #include "btrtl.h" #define VERSION "0.1" #define RTL_CHIP_8723CS_CG 3#define RTL_CHIP_8723CS_VF 4#define RTL_CHIP_8723CS_XX 5#define RTL_EPATCH_SIGNATURE "Realtech"#define RTL_EPATCH_SIGNATURE_V2 "RTBTCore"#define RTL_ROM_LMP_8703B 0x8703#define RTL_ROM_LMP_8723A 0x1200#define RTL_ROM_LMP_8723B 0x8723#define RTL_ROM_LMP_8821A 0x8821#define RTL_ROM_LMP_8761A 0x8761#define RTL_ROM_LMP_8822B 0x8822#define RTL_ROM_LMP_8852A 0x8852#define RTL_ROM_LMP_8851B 0x8851#define RTL_ROM_LMP_8922A 0x8922#define RTL_CONFIG_MAGIC 0x8723ab55 #define RTL_VSC_OP_COREDUMP 0xfcff #define IC_MATCH_FL_LMPSUBV (1 << 0)#define IC_MATCH_FL_HCIREV (1 << 1)#define IC_MATCH_FL_HCIVER (1 << 2)#define IC_MATCH_FL_HCIBUS (1 << 3)#define IC_MATCH_FL_CHIP_TYPE (1 << 4)#define IC_INFO(lmps, hcir, hciv, bus) \ .match_flags = IC_MATCH_FL_LMPSUBV | IC_MATCH_FL_HCIREV | \ IC_MATCH_FL_HCIVER | IC_MATCH_FL_HCIBUS, \ .lmp_subver = (lmps), \ .hci_rev = (hcir), \ .hci_ver = (hciv), \ .hci_bus = (bus) #define RTL_CHIP_SUBVER (&(struct rtl_vendor_cmd) {{0x10, 0x38, 0x04, 0x28, 0x80}})#define RTL_CHIP_REV (&(struct rtl_vendor_cmd) {{0x10, 0x3A, 0x04, 0x28, 0x80}})#define RTL_SEC_PROJ (&(struct rtl_vendor_cmd) {{0x10, 0xA4, 0xAD, 0x00, 0xb0}}) #define RTL_PATCH_SNIPPETS 0x01#define RTL_PATCH_DUMMY_HEADER 0x02#define RTL_PATCH_SECURITY_HEADER 0x03 enum btrtl_chip_id { CHIP_ID_8723A, CHIP_ID_8723B, CHIP_ID_8821A, CHIP_ID_8761A, CHIP_ID_8822B = 8, CHIP_ID_8723D, CHIP_ID_8821C, CHIP_ID_8822C = 13, CHIP_ID_8761B, CHIP_ID_8852A = 18, CHIP_ID_8852B = 20, CHIP_ID_8852C = 25, CHIP_ID_8851B = 36, CHIP_ID_8922A = 44, CHIP_ID_8852BT = 47, CHIP_ID_8761C = 51,}; struct id_table { __u16 match_flags; __u16 lmp_subver; __u16 hci_rev; __u8 hci_ver; __u8 hci_bus; __u8 chip_type; bool config_needed; bool has_rom_version; bool has_msft_ext; char *fw_name; char *cfg_name; char *hw_info;}; struct btrtl_device_info { const struct id_table *ic_info; u8 rom_version; u8 *fw_data; int fw_len; u8 *cfg_data; int cfg_len; bool drop_fw; int project_id; u8 key_id; struct list_head patch_subsecs;}; static const struct id_table ic_id_table[] = { /* 8723A */ { IC_INFO(RTL_ROM_LMP_8723A, 0xb, 0x6, HCI_USB), .config_needed = false, .has_rom_version = false, .fw_name = "rtl_bt/rtl8723a_fw", .cfg_name = NULL, .hw_info = "rtl8723au" }, /* 8723BS */ { IC_INFO(RTL_ROM_LMP_8723B, 0xb, 0x6, HCI_UART), .config_needed = true, .has_rom_version = true, .fw_name = "rtl_bt/rtl8723bs_fw", .cfg_name = "rtl_bt/rtl8723bs_config", .hw_info = "rtl8723bs" }, /* 8723B */ { IC_INFO(RTL_ROM_LMP_8723B, 0xb, 0x6, HCI_USB), .config_needed = false, .has_rom_version = true, .fw_name = "rtl_bt/rtl8723b_fw", .cfg_name = "rtl_bt/rtl8723b_config", .hw_info = "rtl8723bu" }, /* 8723CS-CG */ { .match_flags = IC_MATCH_FL_LMPSUBV | IC_MATCH_FL_CHIP_TYPE | IC_MATCH_FL_HCIBUS, .lmp_subver = RTL_ROM_LMP_8703B, .chip_type = RTL_CHIP_8723CS_CG, .hci_bus = HCI_UART, .config_needed = true, .has_rom_version = true, .fw_name = "rtl_bt/rtl8723cs_cg_fw", .cfg_name = "rtl_bt/rtl8723cs_cg_config", .hw_info = "rtl8723cs-cg" }, /* 8723CS-VF */ { .match_flags = IC_MATCH_FL_LMPSUBV | IC_MATCH_FL_CHIP_TYPE | IC_MATCH_FL_HCIBUS, .lmp_subver = RTL_ROM_LMP_8703B, .chip_type = RTL_CHIP_8723CS_VF, .hci_bus = HCI_UART, .config_needed = true, .has_rom_version = true, .fw_name = "rtl_bt/rtl8723cs_vf_fw", .cfg_name = "rtl_bt/rtl8723cs_vf_config", .hw_info = "rtl8723cs-vf" }, /* 8723CS-XX */ { .match_flags = IC_MATCH_FL_LMPSUBV | IC_MATCH_FL_CHIP_TYPE | IC_MATCH_FL_HCIBUS, .lmp_subver = RTL_ROM_LMP_8703B, .chip_type = RTL_CHIP_8723CS_XX, .hci_bus = HCI_UART, .config_needed = true, .has_rom_version = true, .fw_name = "rtl_bt/rtl8723cs_xx_fw", .cfg_name = "rtl_bt/rtl8723cs_xx_config", .hw_info = "rtl8723cs" }, /* 8723D */ { IC_INFO(RTL_ROM_LMP_8723B, 0xd, 0x8, HCI_USB), .config_needed = true, .has_rom_version = true, .fw_name = "rtl_bt/rtl8723d_fw", .cfg_name = "rtl_bt/rtl8723d_config", .hw_info = "rtl8723du" }, /* 8723DS */ { IC_INFO(RTL_ROM_LMP_8723B, 0xd, 0x8, HCI_UART), .config_needed = true, .has_rom_version = true, .fw_name = "rtl_bt/rtl8723ds_fw", .cfg_name = "rtl_bt/rtl8723ds_config", .hw_info = "rtl8723ds" }, /* 8821A */ { IC_INFO(RTL_ROM_LMP_8821A, 0xa, 0x6, HCI_USB), .config_needed = false, .has_rom_version = true, .fw_name = "rtl_bt/rtl8821a_fw", .cfg_name = "rtl_bt/rtl8821a_config", .hw_info = "rtl8821au" }, /* 8821C */ { IC_INFO(RTL_ROM_LMP_8821A, 0xc, 0x8, HCI_USB), .config_needed = false, .has_rom_version = true, .has_msft_ext = true, .fw_name = "rtl_bt/rtl8821c_fw", .cfg_name = "rtl_bt/rtl8821c_config", .hw_info = "rtl8821cu" }, /* 8821CS */ { IC_INFO(RTL_ROM_LMP_8821A, 0xc, 0x8, HCI_UART), .config_needed = true, .has_rom_version = true, .has_msft_ext = true, .fw_name = "rtl_bt/rtl8821cs_fw", .cfg_name = "rtl_bt/rtl8821cs_config", .hw_info = "rtl8821cs" }, /* 8761A */ { IC_INFO(RTL_ROM_LMP_8761A, 0xa, 0x6, HCI_USB), .config_needed = false, .has_rom_version = true, .fw_name = "rtl_bt/rtl8761a_fw", .cfg_name = "rtl_bt/rtl8761a_config", .hw_info = "rtl8761au" }, /* 8761B */ { IC_INFO(RTL_ROM_LMP_8761A, 0xb, 0xa, HCI_UART), .config_needed = false, .has_rom_version = true, .has_msft_ext = true, .fw_name = "rtl_bt/rtl8761b_fw", .cfg_name = "rtl_bt/rtl8761b_config", .hw_info = "rtl8761btv" }, /* 8761BU */ { IC_INFO(RTL_ROM_LMP_8761A, 0xb, 0xa, HCI_USB), .config_needed = false, .has_rom_version = true, .fw_name = "rtl_bt/rtl8761bu_fw", .cfg_name = "rtl_bt/rtl8761bu_config", .hw_info = "rtl8761bu" }, /* 8761CU */ { IC_INFO(RTL_ROM_LMP_8761A, 0x0e, 0, HCI_USB), .config_needed = false, .has_rom_version = true, .fw_name = "rtl_bt/rtl8761cu_fw", .cfg_name = "rtl_bt/rtl8761cu_config", .hw_info = "rtl8761cu" }, /* 8822C with UART interface */ { IC_INFO(RTL_ROM_LMP_8822B, 0xc, 0x8, HCI_UART), .config_needed = true, .has_rom_version = true, .has_msft_ext = true, .fw_name = "rtl_bt/rtl8822cs_fw", .cfg_name = "rtl_bt/rtl8822cs_config", .hw_info = "rtl8822cs" }, /* 8822C with UART interface */ { IC_INFO(RTL_ROM_LMP_8822B, 0xc, 0xa, HCI_UART), .config_needed = true, .has_rom_version = true, .has_msft_ext = true, .fw_name = "rtl_bt/rtl8822cs_fw", .cfg_name = "rtl_bt/rtl8822cs_config", .hw_info = "rtl8822cs" }, /* 8822C with USB interface */ { IC_INFO(RTL_ROM_LMP_8822B, 0xc, 0xa, HCI_USB), .config_needed = false, .has_rom_version = true, .has_msft_ext = true, .fw_name = "rtl_bt/rtl8822cu_fw", .cfg_name = "rtl_bt/rtl8822cu_config", .hw_info = "rtl8822cu" }, /* 8822B */ { IC_INFO(RTL_ROM_LMP_8822B, 0xb, 0x7, HCI_USB), .config_needed = true, .has_rom_version = true, .has_msft_ext = true, .fw_name = "rtl_bt/rtl8822b_fw", .cfg_name = "rtl_bt/rtl8822b_config", .hw_info = "rtl8822bu" }, /* 8852A */ { IC_INFO(RTL_ROM_LMP_8852A, 0xa, 0xb, HCI_USB), .config_needed = false, .has_rom_version = true, .has_msft_ext = true, .fw_name = "rtl_bt/rtl8852au_fw", .cfg_name = "rtl_bt/rtl8852au_config", .hw_info = "rtl8852au" }, /* 8852B with UART interface */ { IC_INFO(RTL_ROM_LMP_8852A, 0xb, 0xb, HCI_UART), .config_needed = true, .has_rom_version = true, .has_msft_ext = true, .fw_name = "rtl_bt/rtl8852bs_fw", .cfg_name = "rtl_bt/rtl8852bs_config", .hw_info = "rtl8852bs" }, /* 8852B */ { IC_INFO(RTL_ROM_LMP_8852A, 0xb, 0xb, HCI_USB), .config_needed = false, .has_rom_version = true, .has_msft_ext = true, .fw_name = "rtl_bt/rtl8852bu_fw", .cfg_name = "rtl_bt/rtl8852bu_config", .hw_info = "rtl8852bu" }, /* 8852C */ { IC_INFO(RTL_ROM_LMP_8852A, 0xc, 0xc, HCI_USB), .config_needed = false, .has_rom_version = true, .has_msft_ext = true, .fw_name = "rtl_bt/rtl8852cu_fw", .cfg_name = "rtl_bt/rtl8852cu_config", .hw_info = "rtl8852cu" }, /* 8851B */ { IC_INFO(RTL_ROM_LMP_8851B, 0xb, 0xc, HCI_USB), .config_needed = false, .has_rom_version = true, .has_msft_ext = false, .fw_name = "rtl_bt/rtl8851bu_fw", .cfg_name = "rtl_bt/rtl8851bu_config", .hw_info = "rtl8851bu" }, /* 8922A */ { IC_INFO(RTL_ROM_LMP_8922A, 0xa, 0xc, HCI_USB), .config_needed = false, .has_rom_version = true, .has_msft_ext = true, .fw_name = "rtl_bt/rtl8922au_fw", .cfg_name = "rtl_bt/rtl8922au_config", .hw_info = "rtl8922au" }, /* 8852BT/8852BE-VT */ { IC_INFO(RTL_ROM_LMP_8852A, 0x87, 0xc, HCI_USB), .config_needed = false, .has_rom_version = true, .has_msft_ext = true, .fw_name = "rtl_bt/rtl8852btu_fw", .cfg_name = "rtl_bt/rtl8852btu_config", .hw_info = "rtl8852btu" }, }; static const struct id_table *btrtl_match_ic(u16 lmp_subver, u16 hci_rev, u8 hci_ver, u8 hci_bus, u8 chip_type){ int i; for (i = 0; i < ARRAY_SIZE(ic_id_table); i++) { if ((ic_id_table[i].match_flags & IC_MATCH_FL_LMPSUBV) && (ic_id_table[i].lmp_subver != lmp_subver)) continue; if ((ic_id_table[i].match_flags & IC_MATCH_FL_HCIREV) && (ic_id_table[i].hci_rev != hci_rev)) continue; if ((ic_id_table[i].match_flags & IC_MATCH_FL_HCIVER) && (ic_id_table[i].hci_ver != hci_ver) && (ic_id_table[i].hci_ver != 0)) continue; if ((ic_id_table[i].match_flags & IC_MATCH_FL_HCIBUS) && (ic_id_table[i].hci_bus != hci_bus)) continue; if ((ic_id_table[i].match_flags & IC_MATCH_FL_CHIP_TYPE) && (ic_id_table[i].chip_type != chip_type)) continue; break; } if (i >= ARRAY_SIZE(ic_id_table)) return NULL; return &ic_id_table[i];} static struct sk_buff *btrtl_read_local_version(struct hci_dev *hdev){ struct sk_buff *skb; skb = __hci_cmd_sync(hdev, HCI_OP_READ_LOCAL_VERSION, 0, NULL, HCI_INIT_TIMEOUT); if (IS_ERR(skb)) { rtl_dev_err(hdev, "HCI_OP_READ_LOCAL_VERSION failed (%ld)", PTR_ERR(skb)); return skb; } if (skb->len != sizeof(struct hci_rp_read_local_version)) { rtl_dev_err(hdev, "HCI_OP_READ_LOCAL_VERSION event length mismatch"); kfree_skb(skb); return ERR_PTR(-EIO); } return skb;} static int rtl_read_rom_version(struct hci_dev *hdev, u8 *version){ struct rtl_rom_version_evt *rom_version; struct sk_buff *skb; /* Read RTL ROM version command */ skb = __hci_cmd_sync(hdev, 0xfc6d, 0, NULL, HCI_INIT_TIMEOUT); if (IS_ERR(skb)) { rtl_dev_err(hdev, "Read ROM version failed (%ld)", PTR_ERR(skb)); return PTR_ERR(skb); } if (skb->len != sizeof(*rom_version)) { rtl_dev_err(hdev, "version event length mismatch"); kfree_skb(skb); return -EIO; } rom_version = (struct rtl_rom_version_evt *)skb->data; rtl_dev_info(hdev, "rom_version status=%x version=%x", rom_version->status, rom_version->version); *version = rom_version->version; kfree_skb(skb); return 0;} static int btrtl_vendor_read_reg16(struct hci_dev *hdev, struct rtl_vendor_cmd *cmd, u8 *rp){ struct sk_buff *skb; int err = 0; skb = __hci_cmd_sync(hdev, 0xfc61, sizeof(*cmd), cmd, HCI_INIT_TIMEOUT); if (IS_ERR(skb)) { err = PTR_ERR(skb); rtl_dev_err(hdev, "RTL: Read reg16 failed (%d)", err); return err; } if (skb->len != 3 || skb->data[0]) { bt_dev_err(hdev, "RTL: Read reg16 length mismatch"); kfree_skb(skb); return -EIO; } if (rp) memcpy(rp, skb->data + 1, 2); kfree_skb(skb); return 0;} static void *rtl_iov_pull_data(struct rtl_iovec *iov, u32 len){ void *data = iov->data; if (iov->len < len) return NULL; iov->data += len; iov->len -= len; return data;} static void btrtl_insert_ordered_subsec(struct rtl_subsection *node, struct btrtl_device_info *btrtl_dev){ struct list_head *pos; struct list_head *next; struct rtl_subsection *subsec; list_for_each_safe(pos, next, &btrtl_dev->patch_subsecs) { subsec = list_entry(pos, struct rtl_subsection, list); if (subsec->prio >= node->prio) break; } __list_add(&node->list, pos->prev, pos);} static int btrtl_parse_section(struct hci_dev *hdev, struct btrtl_device_info *btrtl_dev, u32 opcode, u8 *data, u32 len){ struct rtl_section_hdr *hdr; struct rtl_subsection *subsec; struct rtl_common_subsec *common_subsec; struct rtl_sec_hdr *sec_hdr; int i; u8 *ptr; u16 num_subsecs; u32 subsec_len; int rc = 0; struct rtl_iovec iov = { .data = data, .len = len, }; hdr = rtl_iov_pull_data(&iov, sizeof(*hdr)); if (!hdr) return -EINVAL;Lines 1–500 of 1567. Load more
// SPDX-License-Identifier: GPL-2.0-or-later /* * Bluetooth support for Realtek devices * * Copyright (C) 2015 Endless Mobile, Inc. */ #include <linux/module.h> #include <linux/firmware.h> #include <linux/unaligned.h> #include <linux/usb.h> #include <net/bluetooth/bluetooth.h> #include <net/bluetooth/hci_core.h> #include "btrtl.h" #define VERSION "0.1" #define RTL_CHIP_8723CS_CG 3 #define RTL_CHIP_8723CS_VF 4 #define RTL_CHIP_8723CS_XX 5 #define RTL_EPATCH_SIGNATURE "Realtech" #define RTL_EPATCH_SIGNATURE_V2 "RTBTCore" #define RTL_ROM_LMP_8703B 0x8703 #define RTL_ROM_LMP_8723A 0x1200 #define RTL_ROM_LMP_8723B 0x8723 #define RTL_ROM_LMP_8821A 0x8821 #define RTL_ROM_LMP_8761A 0x8761 #define RTL_ROM_LMP_8822B 0x8822 #define RTL_ROM_LMP_8852A 0x8852 #define RTL_ROM_LMP_8851B 0x8851 #define RTL_ROM_LMP_8922A 0x8922 #define RTL_CONFIG_MAGIC 0x8723ab55 #define RTL_VSC_OP_COREDUMP 0xfcff #define IC_MATCH_FL_LMPSUBV (1 << 0) #define IC_MATCH_FL_HCIREV (1 << 1) #define IC_MATCH_FL_HCIVER (1 << 2) #define IC_MATCH_FL_HCIBUS (1 << 3) #define IC_MATCH_FL_CHIP_TYPE (1 << 4) #define IC_INFO(lmps, hcir, hciv, bus) \ .match_flags = IC_MATCH_FL_LMPSUBV | IC_MATCH_FL_HCIREV | \ IC_MATCH_FL_HCIVER | IC_MATCH_FL_HCIBUS, \ .lmp_subver = (lmps), \ .hci_rev = (hcir), \ .hci_ver = (hciv), \ .hci_bus = (bus) #define RTL_CHIP_SUBVER (&(struct rtl_vendor_cmd) {{0x10, 0x38, 0x04, 0x28, 0x80}}) #define RTL_CHIP_REV (&(struct rtl_vendor_cmd) {{0x10, 0x3A, 0x04, 0x28, 0x80}}) #define RTL_SEC_PROJ (&(struct rtl_vendor_cmd) {{0x10, 0xA4, 0xAD, 0x00, 0xb0}}) #define RTL_PATCH_SNIPPETS 0x01 #define RTL_PATCH_DUMMY_HEADER 0x02 #define RTL_PATCH_SECURITY_HEADER 0x03 enum btrtl_chip_id { CHIP_ID_8723A, CHIP_ID_8723B, CHIP_ID_8821A, CHIP_ID_8761A, CHIP_ID_8822B = 8, CHIP_ID_8723D, CHIP_ID_8821C, CHIP_ID_8822C = 13, CHIP_ID_8761B, CHIP_ID_8852A = 18, CHIP_ID_8852B = 20, CHIP_ID_8852C = 25, CHIP_ID_8851B = 36, CHIP_ID_8922A = 44, CHIP_ID_8852BT = 47, CHIP_ID_8761C = 51, }; struct id_table { __u16 match_flags; __u16 lmp_subver; __u16 hci_rev; __u8 hci_ver; __u8 hci_bus; __u8 chip_type; bool config_needed; bool has_rom_version; bool has_msft_ext; char *fw_name; char *cfg_name; char *hw_info; }; struct btrtl_device_info { const struct id_table *ic_info; u8 rom_version; u8 *fw_data; int fw_len; u8 *cfg_data; int cfg_len; bool drop_fw; int project_id; u8 key_id; struct list_head patch_subsecs; }; static const struct id_table ic_id_table[] = { /* 8723A */ { IC_INFO(RTL_ROM_LMP_8723A, 0xb, 0x6, HCI_USB), .config_needed = false, .has_rom_version = false, .fw_name = "rtl_bt/rtl8723a_fw", .cfg_name = NULL, .hw_info = "rtl8723au" }, /* 8723BS */ { IC_INFO(RTL_ROM_LMP_8723B, 0xb, 0x6, HCI_UART), .config_needed = true, .has_rom_version = true, .fw_name = "rtl_bt/rtl8723bs_fw", .cfg_name = "rtl_bt/rtl8723bs_config", .hw_info = "rtl8723bs" }, /* 8723B */ { IC_INFO(RTL_ROM_LMP_8723B, 0xb, 0x6, HCI_USB), .config_needed = false, .has_rom_version = true, .fw_name = "rtl_bt/rtl8723b_fw", .cfg_name = "rtl_bt/rtl8723b_config", .hw_info = "rtl8723bu" }, /* 8723CS-CG */ { .match_flags = IC_MATCH_FL_LMPSUBV | IC_MATCH_FL_CHIP_TYPE | IC_MATCH_FL_HCIBUS, .lmp_subver = RTL_ROM_LMP_8703B, .chip_type = RTL_CHIP_8723CS_CG, .hci_bus = HCI_UART, .config_needed = true, .has_rom_version = true, .fw_name = "rtl_bt/rtl8723cs_cg_fw", .cfg_name = "rtl_bt/rtl8723cs_cg_config", .hw_info = "rtl8723cs-cg" }, /* 8723CS-VF */ { .match_flags = IC_MATCH_FL_LMPSUBV | IC_MATCH_FL_CHIP_TYPE | IC_MATCH_FL_HCIBUS, .lmp_subver = RTL_ROM_LMP_8703B, .chip_type = RTL_CHIP_8723CS_VF, .hci_bus = HCI_UART, .config_needed = true, .has_rom_version = true, .fw_name = "rtl_bt/rtl8723cs_vf_fw", .cfg_name = "rtl_bt/rtl8723cs_vf_config", .hw_info = "rtl8723cs-vf" }, /* 8723CS-XX */ { .match_flags = IC_MATCH_FL_LMPSUBV | IC_MATCH_FL_CHIP_TYPE | IC_MATCH_FL_HCIBUS, .lmp_subver = RTL_ROM_LMP_8703B, .chip_type = RTL_CHIP_8723CS_XX, .hci_bus = HCI_UART, .config_needed = true, .has_rom_version = true, .fw_name = "rtl_bt/rtl8723cs_xx_fw", .cfg_name = "rtl_bt/rtl8723cs_xx_config", .hw_info = "rtl8723cs" }, /* 8723D */ { IC_INFO(RTL_ROM_LMP_8723B, 0xd, 0x8, HCI_USB), .config_needed = true, .has_rom_version = true, .fw_name = "rtl_bt/rtl8723d_fw", .cfg_name = "rtl_bt/rtl8723d_config", .hw_info = "rtl8723du" }, /* 8723DS */ { IC_INFO(RTL_ROM_LMP_8723B, 0xd, 0x8, HCI_UART), .config_needed = true, .has_rom_version = true, .fw_name = "rtl_bt/rtl8723ds_fw", .cfg_name = "rtl_bt/rtl8723ds_config", .hw_info = "rtl8723ds" }, /* 8821A */ { IC_INFO(RTL_ROM_LMP_8821A, 0xa, 0x6, HCI_USB), .config_needed = false, .has_rom_version = true, .fw_name = "rtl_bt/rtl8821a_fw", .cfg_name = "rtl_bt/rtl8821a_config", .hw_info = "rtl8821au" }, /* 8821C */ { IC_INFO(RTL_ROM_LMP_8821A, 0xc, 0x8, HCI_USB), .config_needed = false, .has_rom_version = true, .has_msft_ext = true, .fw_name = "rtl_bt/rtl8821c_fw", .cfg_name = "rtl_bt/rtl8821c_config", .hw_info = "rtl8821cu" }, /* 8821CS */ { IC_INFO(RTL_ROM_LMP_8821A, 0xc, 0x8, HCI_UART), .config_needed = true, .has_rom_version = true, .has_msft_ext = true, .fw_name = "rtl_bt/rtl8821cs_fw", .cfg_name = "rtl_bt/rtl8821cs_config", .hw_info = "rtl8821cs" }, /* 8761A */ { IC_INFO(RTL_ROM_LMP_8761A, 0xa, 0x6, HCI_USB), .config_needed = false, .has_rom_version = true, .fw_name = "rtl_bt/rtl8761a_fw", .cfg_name = "rtl_bt/rtl8761a_config", .hw_info = "rtl8761au" }, /* 8761B */ { IC_INFO(RTL_ROM_LMP_8761A, 0xb, 0xa, HCI_UART), .config_needed = false, .has_rom_version = true, .has_msft_ext = true, .fw_name = "rtl_bt/rtl8761b_fw", .cfg_name = "rtl_bt/rtl8761b_config", .hw_info = "rtl8761btv" }, /* 8761BU */ { IC_INFO(RTL_ROM_LMP_8761A, 0xb, 0xa, HCI_USB), .config_needed = false, .has_rom_version = true, .fw_name = "rtl_bt/rtl8761bu_fw", .cfg_name = "rtl_bt/rtl8761bu_config", .hw_info = "rtl8761bu" }, /* 8761CU */ { IC_INFO(RTL_ROM_LMP_8761A, 0x0e, 0, HCI_USB), .config_needed = false, .has_rom_version = true, .fw_name = "rtl_bt/rtl8761cu_fw", .cfg_name = "rtl_bt/rtl8761cu_config", .hw_info = "rtl8761cu" }, /* 8822C with UART interface */ { IC_INFO(RTL_ROM_LMP_8822B, 0xc, 0x8, HCI_UART), .config_needed = true, .has_rom_version = true, .has_msft_ext = true, .fw_name = "rtl_bt/rtl8822cs_fw", .cfg_name = "rtl_bt/rtl8822cs_config", .hw_info = "rtl8822cs" }, /* 8822C with UART interface */ { IC_INFO(RTL_ROM_LMP_8822B, 0xc, 0xa, HCI_UART), .config_needed = true, .has_rom_version = true, .has_msft_ext = true, .fw_name = "rtl_bt/rtl8822cs_fw", .cfg_name = "rtl_bt/rtl8822cs_config", .hw_info = "rtl8822cs" }, /* 8822C with USB interface */ { IC_INFO(RTL_ROM_LMP_8822B, 0xc, 0xa, HCI_USB), .config_needed = false, .has_rom_version = true, .has_msft_ext = true, .fw_name = "rtl_bt/rtl8822cu_fw", .cfg_name = "rtl_bt/rtl8822cu_config", .hw_info = "rtl8822cu" }, /* 8822B */ { IC_INFO(RTL_ROM_LMP_8822B, 0xb, 0x7, HCI_USB), .config_needed = true, .has_rom_version = true, .has_msft_ext = true, .fw_name = "rtl_bt/rtl8822b_fw", .cfg_name = "rtl_bt/rtl8822b_config", .hw_info = "rtl8822bu" }, /* 8852A */ { IC_INFO(RTL_ROM_LMP_8852A, 0xa, 0xb, HCI_USB), .config_needed = false, .has_rom_version = true, .has_msft_ext = true, .fw_name = "rtl_bt/rtl8852au_fw", .cfg_name = "rtl_bt/rtl8852au_config", .hw_info = "rtl8852au" }, /* 8852B with UART interface */ { IC_INFO(RTL_ROM_LMP_8852A, 0xb, 0xb, HCI_UART), .config_needed = true, .has_rom_version = true, .has_msft_ext = true, .fw_name = "rtl_bt/rtl8852bs_fw", .cfg_name = "rtl_bt/rtl8852bs_config", .hw_info = "rtl8852bs" }, /* 8852B */ { IC_INFO(RTL_ROM_LMP_8852A, 0xb, 0xb, HCI_USB), .config_needed = false, .has_rom_version = true, .has_msft_ext = true, .fw_name = "rtl_bt/rtl8852bu_fw", .cfg_name = "rtl_bt/rtl8852bu_config", .hw_info = "rtl8852bu" }, /* 8852C */ { IC_INFO(RTL_ROM_LMP_8852A, 0xc, 0xc, HCI_USB), .config_needed = false, .has_rom_version = true, .has_msft_ext = true, .fw_name = "rtl_bt/rtl8852cu_fw", .cfg_name = "rtl_bt/rtl8852cu_config", .hw_info = "rtl8852cu" }, /* 8851B */ { IC_INFO(RTL_ROM_LMP_8851B, 0xb, 0xc, HCI_USB), .config_needed = false, .has_rom_version = true, .has_msft_ext = false, .fw_name = "rtl_bt/rtl8851bu_fw", .cfg_name = "rtl_bt/rtl8851bu_config", .hw_info = "rtl8851bu" }, /* 8922A */ { IC_INFO(RTL_ROM_LMP_8922A, 0xa, 0xc, HCI_USB), .config_needed = false, .has_rom_version = true, .has_msft_ext = true, .fw_name = "rtl_bt/rtl8922au_fw", .cfg_name = "rtl_bt/rtl8922au_config", .hw_info = "rtl8922au" }, /* 8852BT/8852BE-VT */ { IC_INFO(RTL_ROM_LMP_8852A, 0x87, 0xc, HCI_USB), .config_needed = false, .has_rom_version = true, .has_msft_ext = true, .fw_name = "rtl_bt/rtl8852btu_fw", .cfg_name = "rtl_bt/rtl8852btu_config", .hw_info = "rtl8852btu" }, }; static const struct id_table *btrtl_match_ic(u16 lmp_subver, u16 hci_rev, u8 hci_ver, u8 hci_bus, u8 chip_type) { int i; for (i = 0; i < ARRAY_SIZE(ic_id_table); i++) { if ((ic_id_table[i].match_flags & IC_MATCH_FL_LMPSUBV) && (ic_id_table[i].lmp_subver != lmp_subver)) continue; if ((ic_id_table[i].match_flags & IC_MATCH_FL_HCIREV) && (ic_id_table[i].hci_rev != hci_rev)) continue; if ((ic_id_table[i].match_flags & IC_MATCH_FL_HCIVER) && (ic_id_table[i].hci_ver != hci_ver) && (ic_id_table[i].hci_ver != 0)) continue; if ((ic_id_table[i].match_flags & IC_MATCH_FL_HCIBUS) && (ic_id_table[i].hci_bus != hci_bus)) continue; if ((ic_id_table[i].match_flags & IC_MATCH_FL_CHIP_TYPE) && (ic_id_table[i].chip_type != chip_type)) continue; break; } if (i >= ARRAY_SIZE(ic_id_table)) return NULL; return &ic_id_table[i]; } static struct sk_buff *btrtl_read_local_version(struct hci_dev *hdev) { struct sk_buff *skb; skb = __hci_cmd_sync(hdev, HCI_OP_READ_LOCAL_VERSION, 0, NULL, HCI_INIT_TIMEOUT); if (IS_ERR(skb)) { rtl_dev_err(hdev, "HCI_OP_READ_LOCAL_VERSION failed (%ld)", PTR_ERR(skb)); return skb; } if (skb->len != sizeof(struct hci_rp_read_local_version)) { rtl_dev_err(hdev, "HCI_OP_READ_LOCAL_VERSION event length mismatch"); kfree_skb(skb); return ERR_PTR(-EIO); } return skb; } static int rtl_read_rom_version(struct hci_dev *hdev, u8 *version) { struct rtl_rom_version_evt *rom_version; struct sk_buff *skb; /* Read RTL ROM version command */ skb = __hci_cmd_sync(hdev, 0xfc6d, 0, NULL, HCI_INIT_TIMEOUT); if (IS_ERR(skb)) { rtl_dev_err(hdev, "Read ROM version failed (%ld)", PTR_ERR(skb)); return PTR_ERR(skb); } if (skb->len != sizeof(*rom_version)) { rtl_dev_err(hdev, "version event length mismatch"); kfree_skb(skb); return -EIO; } rom_version = (struct rtl_rom_version_evt *)skb->data; rtl_dev_info(hdev, "rom_version status=%x version=%x", rom_version->status, rom_version->version); *version = rom_version->version; kfree_skb(skb); return 0; } static int btrtl_vendor_read_reg16(struct hci_dev *hdev, struct rtl_vendor_cmd *cmd, u8 *rp) { struct sk_buff *skb; int err = 0; skb = __hci_cmd_sync(hdev, 0xfc61, sizeof(*cmd), cmd, HCI_INIT_TIMEOUT); if (IS_ERR(skb)) { err = PTR_ERR(skb); rtl_dev_err(hdev, "RTL: Read reg16 failed (%d)", err); return err; } if (skb->len != 3 || skb->data[0]) { bt_dev_err(hdev, "RTL: Read reg16 length mismatch"); kfree_skb(skb); return -EIO; } if (rp) memcpy(rp, skb->data + 1, 2); kfree_skb(skb); return 0; } static void *rtl_iov_pull_data(struct rtl_iovec *iov, u32 len) { void *data = iov->data; if (iov->len < len) return NULL; iov->data += len; iov->len -= len; return data; } static void btrtl_insert_ordered_subsec(struct rtl_subsection *node, struct btrtl_device_info *btrtl_dev) { struct list_head *pos; struct list_head *next; struct rtl_subsection *subsec; list_for_each_safe(pos, next, &btrtl_dev->patch_subsecs) { subsec = list_entry(pos, struct rtl_subsection, list); if (subsec->prio >= node->prio) break; } __list_add(&node->list, pos->prev, pos); } static int btrtl_parse_section(struct hci_dev *hdev, struct btrtl_device_info *btrtl_dev, u32 opcode, u8 *data, u32 len) { struct rtl_section_hdr *hdr; struct rtl_subsection *subsec; struct rtl_common_subsec *common_subsec; struct rtl_sec_hdr *sec_hdr; int i; u8 *ptr; u16 num_subsecs; u32 subsec_len; int rc = 0; struct rtl_iovec iov = { .data = data, .len = len, }; hdr = rtl_iov_pull_data(&iov, sizeof(*hdr)); if (!hdr) return -EINVAL;