1c942fddfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 2db33c77dSCarlo Caione /* 3db33c77dSCarlo Caione * Bluetooth support for Realtek devices 4db33c77dSCarlo Caione * 5db33c77dSCarlo Caione * Copyright (C) 2015 Endless Mobile, Inc. 6db33c77dSCarlo Caione */ 7db33c77dSCarlo Caione 8db33c77dSCarlo Caione #include <linux/module.h> 9db33c77dSCarlo Caione #include <linux/firmware.h> 10db33c77dSCarlo Caione #include <asm/unaligned.h> 11db33c77dSCarlo Caione #include <linux/usb.h> 12db33c77dSCarlo Caione 13db33c77dSCarlo Caione #include <net/bluetooth/bluetooth.h> 14db33c77dSCarlo Caione #include <net/bluetooth/hci_core.h> 15db33c77dSCarlo Caione 16db33c77dSCarlo Caione #include "btrtl.h" 17db33c77dSCarlo Caione 18db33c77dSCarlo Caione #define VERSION "0.1" 19db33c77dSCarlo Caione 20c0123cb6SVasily Khoruzhick #define RTL_CHIP_8723CS_CG 3 21c0123cb6SVasily Khoruzhick #define RTL_CHIP_8723CS_VF 4 22c0123cb6SVasily Khoruzhick #define RTL_CHIP_8723CS_XX 5 23db33c77dSCarlo Caione #define RTL_EPATCH_SIGNATURE "Realtech" 24*9a24ce5eSMax Chou #define RTL_EPATCH_SIGNATURE_V2 "RTBTCore" 25c0123cb6SVasily Khoruzhick #define RTL_ROM_LMP_8703B 0x8703 26db33c77dSCarlo Caione #define RTL_ROM_LMP_8723A 0x1200 27db33c77dSCarlo Caione #define RTL_ROM_LMP_8723B 0x8723 28db33c77dSCarlo Caione #define RTL_ROM_LMP_8821A 0x8821 29db33c77dSCarlo Caione #define RTL_ROM_LMP_8761A 0x8761 301110a2dbSLarry Finger #define RTL_ROM_LMP_8822B 0x8822 310d484db6SMax Chou #define RTL_ROM_LMP_8852A 0x8852 32b85b0ee1SMartin Blumenstingl #define RTL_CONFIG_MAGIC 0x8723ab55 33db33c77dSCarlo Caione 34907f8499SAlex Lu #define IC_MATCH_FL_LMPSUBV (1 << 0) 35907f8499SAlex Lu #define IC_MATCH_FL_HCIREV (1 << 1) 36c50903e3SMartin Blumenstingl #define IC_MATCH_FL_HCIVER (1 << 2) 37c50903e3SMartin Blumenstingl #define IC_MATCH_FL_HCIBUS (1 << 3) 38c0123cb6SVasily Khoruzhick #define IC_MATCH_FL_CHIP_TYPE (1 << 4) 396f9ff246SMax Chou #define IC_INFO(lmps, hcir, hciv, bus) \ 406f9ff246SMax Chou .match_flags = IC_MATCH_FL_LMPSUBV | IC_MATCH_FL_HCIREV | \ 416f9ff246SMax Chou IC_MATCH_FL_HCIVER | IC_MATCH_FL_HCIBUS, \ 42907f8499SAlex Lu .lmp_subver = (lmps), \ 436f9ff246SMax Chou .hci_rev = (hcir), \ 446f9ff246SMax Chou .hci_ver = (hciv), \ 456f9ff246SMax Chou .hci_bus = (bus) 46907f8499SAlex Lu 47*9a24ce5eSMax Chou #define RTL_CHIP_SUBVER (&(struct rtl_vendor_cmd) {{0x10, 0x38, 0x04, 0x28, 0x80}}) 48*9a24ce5eSMax Chou #define RTL_CHIP_REV (&(struct rtl_vendor_cmd) {{0x10, 0x3A, 0x04, 0x28, 0x80}}) 49*9a24ce5eSMax Chou #define RTL_SEC_PROJ (&(struct rtl_vendor_cmd) {{0x10, 0xA4, 0x0D, 0x00, 0xb0}}) 50*9a24ce5eSMax Chou 51*9a24ce5eSMax Chou #define RTL_PATCH_SNIPPETS 0x01 52*9a24ce5eSMax Chou #define RTL_PATCH_DUMMY_HEADER 0x02 53*9a24ce5eSMax Chou #define RTL_PATCH_SECURITY_HEADER 0x03 54*9a24ce5eSMax Chou 559ab9235fSMax Chou enum btrtl_chip_id { 569ab9235fSMax Chou CHIP_ID_8723A, 579ab9235fSMax Chou CHIP_ID_8723B, 589ab9235fSMax Chou CHIP_ID_8821A, 599ab9235fSMax Chou CHIP_ID_8761A, 609ab9235fSMax Chou CHIP_ID_8822B = 8, 619ab9235fSMax Chou CHIP_ID_8723D, 629ab9235fSMax Chou CHIP_ID_8821C, 639ab9235fSMax Chou CHIP_ID_8822C = 13, 649ab9235fSMax Chou CHIP_ID_8761B, 659ab9235fSMax Chou CHIP_ID_8852A = 18, 6618e8055cSMax Chou CHIP_ID_8852B = 20, 678b1d66b5SMax Chou CHIP_ID_8852C = 25, 689ab9235fSMax Chou }; 699ab9235fSMax Chou 70907f8499SAlex Lu struct id_table { 71907f8499SAlex Lu __u16 match_flags; 72907f8499SAlex Lu __u16 lmp_subver; 73907f8499SAlex Lu __u16 hci_rev; 74c50903e3SMartin Blumenstingl __u8 hci_ver; 75c50903e3SMartin Blumenstingl __u8 hci_bus; 76c0123cb6SVasily Khoruzhick __u8 chip_type; 77907f8499SAlex Lu bool config_needed; 7826503ad2SMartin Blumenstingl bool has_rom_version; 79f4bcba0eSMarcel Holtmann bool has_msft_ext; 80907f8499SAlex Lu char *fw_name; 81907f8499SAlex Lu char *cfg_name; 82907f8499SAlex Lu }; 83907f8499SAlex Lu 8426503ad2SMartin Blumenstingl struct btrtl_device_info { 8526503ad2SMartin Blumenstingl const struct id_table *ic_info; 8626503ad2SMartin Blumenstingl u8 rom_version; 8726503ad2SMartin Blumenstingl u8 *fw_data; 8826503ad2SMartin Blumenstingl int fw_len; 8926503ad2SMartin Blumenstingl u8 *cfg_data; 9026503ad2SMartin Blumenstingl int cfg_len; 911996d9caSKai-Heng Feng bool drop_fw; 929ab9235fSMax Chou int project_id; 93*9a24ce5eSMax Chou u8 key_id; 94*9a24ce5eSMax Chou struct list_head patch_subsecs; 9526503ad2SMartin Blumenstingl }; 9626503ad2SMartin Blumenstingl 97907f8499SAlex Lu static const struct id_table ic_id_table[] = { 986f9ff246SMax Chou /* 8723A */ 996f9ff246SMax Chou { IC_INFO(RTL_ROM_LMP_8723A, 0xb, 0x6, HCI_USB), 10026503ad2SMartin Blumenstingl .config_needed = false, 10126503ad2SMartin Blumenstingl .has_rom_version = false, 10226503ad2SMartin Blumenstingl .fw_name = "rtl_bt/rtl8723a_fw.bin", 10326503ad2SMartin Blumenstingl .cfg_name = NULL }, 10426503ad2SMartin Blumenstingl 105c50903e3SMartin Blumenstingl /* 8723BS */ 1066f9ff246SMax Chou { IC_INFO(RTL_ROM_LMP_8723B, 0xb, 0x6, HCI_UART), 107c50903e3SMartin Blumenstingl .config_needed = true, 108c50903e3SMartin Blumenstingl .has_rom_version = true, 109c50903e3SMartin Blumenstingl .fw_name = "rtl_bt/rtl8723bs_fw.bin", 1101cc194caSHans de Goede .cfg_name = "rtl_bt/rtl8723bs_config" }, 111c50903e3SMartin Blumenstingl 112907f8499SAlex Lu /* 8723B */ 1136f9ff246SMax Chou { IC_INFO(RTL_ROM_LMP_8723B, 0xb, 0x6, HCI_USB), 114907f8499SAlex Lu .config_needed = false, 11526503ad2SMartin Blumenstingl .has_rom_version = true, 116907f8499SAlex Lu .fw_name = "rtl_bt/rtl8723b_fw.bin", 1171cc194caSHans de Goede .cfg_name = "rtl_bt/rtl8723b_config" }, 118907f8499SAlex Lu 119c0123cb6SVasily Khoruzhick /* 8723CS-CG */ 120c0123cb6SVasily Khoruzhick { .match_flags = IC_MATCH_FL_LMPSUBV | IC_MATCH_FL_CHIP_TYPE | 121c0123cb6SVasily Khoruzhick IC_MATCH_FL_HCIBUS, 122c0123cb6SVasily Khoruzhick .lmp_subver = RTL_ROM_LMP_8703B, 123c0123cb6SVasily Khoruzhick .chip_type = RTL_CHIP_8723CS_CG, 124c0123cb6SVasily Khoruzhick .hci_bus = HCI_UART, 125c0123cb6SVasily Khoruzhick .config_needed = true, 126c0123cb6SVasily Khoruzhick .has_rom_version = true, 127c0123cb6SVasily Khoruzhick .fw_name = "rtl_bt/rtl8723cs_cg_fw.bin", 128c0123cb6SVasily Khoruzhick .cfg_name = "rtl_bt/rtl8723cs_cg_config" }, 129c0123cb6SVasily Khoruzhick 130c0123cb6SVasily Khoruzhick /* 8723CS-VF */ 131c0123cb6SVasily Khoruzhick { .match_flags = IC_MATCH_FL_LMPSUBV | IC_MATCH_FL_CHIP_TYPE | 132c0123cb6SVasily Khoruzhick IC_MATCH_FL_HCIBUS, 133c0123cb6SVasily Khoruzhick .lmp_subver = RTL_ROM_LMP_8703B, 134c0123cb6SVasily Khoruzhick .chip_type = RTL_CHIP_8723CS_VF, 135c0123cb6SVasily Khoruzhick .hci_bus = HCI_UART, 136c0123cb6SVasily Khoruzhick .config_needed = true, 137c0123cb6SVasily Khoruzhick .has_rom_version = true, 138c0123cb6SVasily Khoruzhick .fw_name = "rtl_bt/rtl8723cs_vf_fw.bin", 139c0123cb6SVasily Khoruzhick .cfg_name = "rtl_bt/rtl8723cs_vf_config" }, 140c0123cb6SVasily Khoruzhick 141c0123cb6SVasily Khoruzhick /* 8723CS-XX */ 142c0123cb6SVasily Khoruzhick { .match_flags = IC_MATCH_FL_LMPSUBV | IC_MATCH_FL_CHIP_TYPE | 143c0123cb6SVasily Khoruzhick IC_MATCH_FL_HCIBUS, 144c0123cb6SVasily Khoruzhick .lmp_subver = RTL_ROM_LMP_8703B, 145c0123cb6SVasily Khoruzhick .chip_type = RTL_CHIP_8723CS_XX, 146c0123cb6SVasily Khoruzhick .hci_bus = HCI_UART, 147c0123cb6SVasily Khoruzhick .config_needed = true, 148c0123cb6SVasily Khoruzhick .has_rom_version = true, 149c0123cb6SVasily Khoruzhick .fw_name = "rtl_bt/rtl8723cs_xx_fw.bin", 150c0123cb6SVasily Khoruzhick .cfg_name = "rtl_bt/rtl8723cs_xx_config" }, 151c0123cb6SVasily Khoruzhick 152907f8499SAlex Lu /* 8723D */ 1536f9ff246SMax Chou { IC_INFO(RTL_ROM_LMP_8723B, 0xd, 0x8, HCI_USB), 154907f8499SAlex Lu .config_needed = true, 15526503ad2SMartin Blumenstingl .has_rom_version = true, 156907f8499SAlex Lu .fw_name = "rtl_bt/rtl8723d_fw.bin", 1571cc194caSHans de Goede .cfg_name = "rtl_bt/rtl8723d_config" }, 158907f8499SAlex Lu 159c50903e3SMartin Blumenstingl /* 8723DS */ 1606f9ff246SMax Chou { IC_INFO(RTL_ROM_LMP_8723B, 0xd, 0x8, HCI_UART), 161c50903e3SMartin Blumenstingl .config_needed = true, 162c50903e3SMartin Blumenstingl .has_rom_version = true, 163c50903e3SMartin Blumenstingl .fw_name = "rtl_bt/rtl8723ds_fw.bin", 1641cc194caSHans de Goede .cfg_name = "rtl_bt/rtl8723ds_config" }, 165c50903e3SMartin Blumenstingl 166907f8499SAlex Lu /* 8821A */ 1676f9ff246SMax Chou { IC_INFO(RTL_ROM_LMP_8821A, 0xa, 0x6, HCI_USB), 168907f8499SAlex Lu .config_needed = false, 16926503ad2SMartin Blumenstingl .has_rom_version = true, 170907f8499SAlex Lu .fw_name = "rtl_bt/rtl8821a_fw.bin", 1711cc194caSHans de Goede .cfg_name = "rtl_bt/rtl8821a_config" }, 172907f8499SAlex Lu 173907f8499SAlex Lu /* 8821C */ 1746f9ff246SMax Chou { IC_INFO(RTL_ROM_LMP_8821A, 0xc, 0x8, HCI_USB), 175907f8499SAlex Lu .config_needed = false, 17626503ad2SMartin Blumenstingl .has_rom_version = true, 1774d51fb04SMarcel Holtmann .has_msft_ext = true, 178907f8499SAlex Lu .fw_name = "rtl_bt/rtl8821c_fw.bin", 1791cc194caSHans de Goede .cfg_name = "rtl_bt/rtl8821c_config" }, 180907f8499SAlex Lu 18110c9e010SChris Morgan /* 8821CS */ 18210c9e010SChris Morgan { IC_INFO(RTL_ROM_LMP_8821A, 0xc, 0x8, HCI_UART), 18310c9e010SChris Morgan .config_needed = true, 18410c9e010SChris Morgan .has_rom_version = true, 18510c9e010SChris Morgan .has_msft_ext = true, 18610c9e010SChris Morgan .fw_name = "rtl_bt/rtl8821cs_fw.bin", 18710c9e010SChris Morgan .cfg_name = "rtl_bt/rtl8821cs_config" }, 18810c9e010SChris Morgan 189907f8499SAlex Lu /* 8761A */ 1906f9ff246SMax Chou { IC_INFO(RTL_ROM_LMP_8761A, 0xa, 0x6, HCI_USB), 191907f8499SAlex Lu .config_needed = false, 19226503ad2SMartin Blumenstingl .has_rom_version = true, 193907f8499SAlex Lu .fw_name = "rtl_bt/rtl8761a_fw.bin", 1941cc194caSHans de Goede .cfg_name = "rtl_bt/rtl8761a_config" }, 195907f8499SAlex Lu 19604896832SZiqian SUN (Zamir) /* 8761B */ 1979fd2e294SJoakim Tjernlund { IC_INFO(RTL_ROM_LMP_8761A, 0xb, 0xa, HCI_UART), 19804896832SZiqian SUN (Zamir) .config_needed = false, 19904896832SZiqian SUN (Zamir) .has_rom_version = true, 200f4bcba0eSMarcel Holtmann .has_msft_ext = true, 20104896832SZiqian SUN (Zamir) .fw_name = "rtl_bt/rtl8761b_fw.bin", 20204896832SZiqian SUN (Zamir) .cfg_name = "rtl_bt/rtl8761b_config" }, 20304896832SZiqian SUN (Zamir) 2049fd2e294SJoakim Tjernlund /* 8761BU */ 2059fd2e294SJoakim Tjernlund { IC_INFO(RTL_ROM_LMP_8761A, 0xb, 0xa, HCI_USB), 2069fd2e294SJoakim Tjernlund .config_needed = false, 2079fd2e294SJoakim Tjernlund .has_rom_version = true, 2089fd2e294SJoakim Tjernlund .fw_name = "rtl_bt/rtl8761bu_fw.bin", 2099fd2e294SJoakim Tjernlund .cfg_name = "rtl_bt/rtl8761bu_config" }, 2109fd2e294SJoakim Tjernlund 211848fc616SMax Chou /* 8822C with UART interface */ 212b050c5bbSVyacheslav Bocharov { IC_INFO(RTL_ROM_LMP_8822B, 0xc, 0x8, HCI_UART), 213b050c5bbSVyacheslav Bocharov .config_needed = true, 214b050c5bbSVyacheslav Bocharov .has_rom_version = true, 215b050c5bbSVyacheslav Bocharov .has_msft_ext = true, 216b050c5bbSVyacheslav Bocharov .fw_name = "rtl_bt/rtl8822cs_fw.bin", 217b050c5bbSVyacheslav Bocharov .cfg_name = "rtl_bt/rtl8822cs_config" }, 218b050c5bbSVyacheslav Bocharov 219b050c5bbSVyacheslav Bocharov /* 8822C with UART interface */ 2206f9ff246SMax Chou { IC_INFO(RTL_ROM_LMP_8822B, 0xc, 0xa, HCI_UART), 221848fc616SMax Chou .config_needed = true, 222848fc616SMax Chou .has_rom_version = true, 223f4bcba0eSMarcel Holtmann .has_msft_ext = true, 224848fc616SMax Chou .fw_name = "rtl_bt/rtl8822cs_fw.bin", 225848fc616SMax Chou .cfg_name = "rtl_bt/rtl8822cs_config" }, 226848fc616SMax Chou 2278ecfdc95SAlex Lu /* 8822C with USB interface */ 2286f9ff246SMax Chou { IC_INFO(RTL_ROM_LMP_8822B, 0xc, 0xa, HCI_USB), 2298ecfdc95SAlex Lu .config_needed = false, 2308ecfdc95SAlex Lu .has_rom_version = true, 231f4bcba0eSMarcel Holtmann .has_msft_ext = true, 2328ecfdc95SAlex Lu .fw_name = "rtl_bt/rtl8822cu_fw.bin", 2338ecfdc95SAlex Lu .cfg_name = "rtl_bt/rtl8822cu_config" }, 2348ecfdc95SAlex Lu 235907f8499SAlex Lu /* 8822B */ 2366f9ff246SMax Chou { IC_INFO(RTL_ROM_LMP_8822B, 0xb, 0x7, HCI_USB), 237907f8499SAlex Lu .config_needed = true, 23826503ad2SMartin Blumenstingl .has_rom_version = true, 239f4bcba0eSMarcel Holtmann .has_msft_ext = true, 240907f8499SAlex Lu .fw_name = "rtl_bt/rtl8822b_fw.bin", 2411cc194caSHans de Goede .cfg_name = "rtl_bt/rtl8822b_config" }, 2420d484db6SMax Chou 2430d484db6SMax Chou /* 8852A */ 2440d484db6SMax Chou { IC_INFO(RTL_ROM_LMP_8852A, 0xa, 0xb, HCI_USB), 2450d484db6SMax Chou .config_needed = false, 2460d484db6SMax Chou .has_rom_version = true, 247f4bcba0eSMarcel Holtmann .has_msft_ext = true, 2480d484db6SMax Chou .fw_name = "rtl_bt/rtl8852au_fw.bin", 2490d484db6SMax Chou .cfg_name = "rtl_bt/rtl8852au_config" }, 25018e8055cSMax Chou 251fe4b71b5SVictor Hassan /* 8852B with UART interface */ 252fe4b71b5SVictor Hassan { IC_INFO(RTL_ROM_LMP_8852A, 0xb, 0xb, HCI_UART), 253fe4b71b5SVictor Hassan .config_needed = true, 254fe4b71b5SVictor Hassan .has_rom_version = true, 255fe4b71b5SVictor Hassan .has_msft_ext = true, 256fe4b71b5SVictor Hassan .fw_name = "rtl_bt/rtl8852bs_fw.bin", 257fe4b71b5SVictor Hassan .cfg_name = "rtl_bt/rtl8852bs_config" }, 258fe4b71b5SVictor Hassan 25918e8055cSMax Chou /* 8852B */ 26018e8055cSMax Chou { IC_INFO(RTL_ROM_LMP_8852A, 0xb, 0xb, HCI_USB), 26118e8055cSMax Chou .config_needed = false, 26218e8055cSMax Chou .has_rom_version = true, 26318e8055cSMax Chou .has_msft_ext = true, 26418e8055cSMax Chou .fw_name = "rtl_bt/rtl8852bu_fw.bin", 26518e8055cSMax Chou .cfg_name = "rtl_bt/rtl8852bu_config" }, 2668b1d66b5SMax Chou 2678b1d66b5SMax Chou /* 8852C */ 2688b1d66b5SMax Chou { IC_INFO(RTL_ROM_LMP_8852A, 0xc, 0xc, HCI_USB), 2698b1d66b5SMax Chou .config_needed = false, 2708b1d66b5SMax Chou .has_rom_version = true, 2718b1d66b5SMax Chou .has_msft_ext = true, 2728b1d66b5SMax Chou .fw_name = "rtl_bt/rtl8852cu_fw.bin", 2738b1d66b5SMax Chou .cfg_name = "rtl_bt/rtl8852cu_config" }, 274907f8499SAlex Lu }; 275907f8499SAlex Lu 276c50903e3SMartin Blumenstingl static const struct id_table *btrtl_match_ic(u16 lmp_subver, u16 hci_rev, 277c0123cb6SVasily Khoruzhick u8 hci_ver, u8 hci_bus, 278c0123cb6SVasily Khoruzhick u8 chip_type) 27926503ad2SMartin Blumenstingl { 28026503ad2SMartin Blumenstingl int i; 28126503ad2SMartin Blumenstingl 28226503ad2SMartin Blumenstingl for (i = 0; i < ARRAY_SIZE(ic_id_table); i++) { 28326503ad2SMartin Blumenstingl if ((ic_id_table[i].match_flags & IC_MATCH_FL_LMPSUBV) && 28426503ad2SMartin Blumenstingl (ic_id_table[i].lmp_subver != lmp_subver)) 28526503ad2SMartin Blumenstingl continue; 28626503ad2SMartin Blumenstingl if ((ic_id_table[i].match_flags & IC_MATCH_FL_HCIREV) && 28726503ad2SMartin Blumenstingl (ic_id_table[i].hci_rev != hci_rev)) 28826503ad2SMartin Blumenstingl continue; 289c50903e3SMartin Blumenstingl if ((ic_id_table[i].match_flags & IC_MATCH_FL_HCIVER) && 290c50903e3SMartin Blumenstingl (ic_id_table[i].hci_ver != hci_ver)) 291c50903e3SMartin Blumenstingl continue; 292c50903e3SMartin Blumenstingl if ((ic_id_table[i].match_flags & IC_MATCH_FL_HCIBUS) && 293c50903e3SMartin Blumenstingl (ic_id_table[i].hci_bus != hci_bus)) 294c50903e3SMartin Blumenstingl continue; 295c0123cb6SVasily Khoruzhick if ((ic_id_table[i].match_flags & IC_MATCH_FL_CHIP_TYPE) && 296c0123cb6SVasily Khoruzhick (ic_id_table[i].chip_type != chip_type)) 297c0123cb6SVasily Khoruzhick continue; 29826503ad2SMartin Blumenstingl 29926503ad2SMartin Blumenstingl break; 30026503ad2SMartin Blumenstingl } 30126503ad2SMartin Blumenstingl if (i >= ARRAY_SIZE(ic_id_table)) 30226503ad2SMartin Blumenstingl return NULL; 30326503ad2SMartin Blumenstingl 30426503ad2SMartin Blumenstingl return &ic_id_table[i]; 30526503ad2SMartin Blumenstingl } 30626503ad2SMartin Blumenstingl 307240b64a8SAlex Lu static struct sk_buff *btrtl_read_local_version(struct hci_dev *hdev) 308240b64a8SAlex Lu { 309240b64a8SAlex Lu struct sk_buff *skb; 310240b64a8SAlex Lu 311240b64a8SAlex Lu skb = __hci_cmd_sync(hdev, HCI_OP_READ_LOCAL_VERSION, 0, NULL, 312240b64a8SAlex Lu HCI_INIT_TIMEOUT); 313240b64a8SAlex Lu if (IS_ERR(skb)) { 314240b64a8SAlex Lu rtl_dev_err(hdev, "HCI_OP_READ_LOCAL_VERSION failed (%ld)", 315240b64a8SAlex Lu PTR_ERR(skb)); 316240b64a8SAlex Lu return skb; 317240b64a8SAlex Lu } 318240b64a8SAlex Lu 319240b64a8SAlex Lu if (skb->len != sizeof(struct hci_rp_read_local_version)) { 320240b64a8SAlex Lu rtl_dev_err(hdev, "HCI_OP_READ_LOCAL_VERSION event length mismatch"); 321240b64a8SAlex Lu kfree_skb(skb); 322240b64a8SAlex Lu return ERR_PTR(-EIO); 323240b64a8SAlex Lu } 324240b64a8SAlex Lu 325240b64a8SAlex Lu return skb; 326240b64a8SAlex Lu } 327240b64a8SAlex Lu 328db33c77dSCarlo Caione static int rtl_read_rom_version(struct hci_dev *hdev, u8 *version) 329db33c77dSCarlo Caione { 330db33c77dSCarlo Caione struct rtl_rom_version_evt *rom_version; 331db33c77dSCarlo Caione struct sk_buff *skb; 332db33c77dSCarlo Caione 333db33c77dSCarlo Caione /* Read RTL ROM version command */ 334db33c77dSCarlo Caione skb = __hci_cmd_sync(hdev, 0xfc6d, 0, NULL, HCI_INIT_TIMEOUT); 335db33c77dSCarlo Caione if (IS_ERR(skb)) { 336f1300c03SAlex Lu rtl_dev_err(hdev, "Read ROM version failed (%ld)", 337a5c76e67SHans de Goede PTR_ERR(skb)); 338db33c77dSCarlo Caione return PTR_ERR(skb); 339db33c77dSCarlo Caione } 340db33c77dSCarlo Caione 341db33c77dSCarlo Caione if (skb->len != sizeof(*rom_version)) { 342f1300c03SAlex Lu rtl_dev_err(hdev, "version event length mismatch"); 343db33c77dSCarlo Caione kfree_skb(skb); 344db33c77dSCarlo Caione return -EIO; 345db33c77dSCarlo Caione } 346db33c77dSCarlo Caione 347db33c77dSCarlo Caione rom_version = (struct rtl_rom_version_evt *)skb->data; 348f1300c03SAlex Lu rtl_dev_info(hdev, "rom_version status=%x version=%x", 3492064ee33SMarcel Holtmann rom_version->status, rom_version->version); 350db33c77dSCarlo Caione 351db33c77dSCarlo Caione *version = rom_version->version; 352db33c77dSCarlo Caione 353db33c77dSCarlo Caione kfree_skb(skb); 354db33c77dSCarlo Caione return 0; 355db33c77dSCarlo Caione } 356db33c77dSCarlo Caione 357*9a24ce5eSMax Chou static int btrtl_vendor_read_reg16(struct hci_dev *hdev, 358*9a24ce5eSMax Chou struct rtl_vendor_cmd *cmd, u8 *rp) 359*9a24ce5eSMax Chou { 360*9a24ce5eSMax Chou struct sk_buff *skb; 361*9a24ce5eSMax Chou int err = 0; 362*9a24ce5eSMax Chou 363*9a24ce5eSMax Chou skb = __hci_cmd_sync(hdev, 0xfc61, sizeof(*cmd), cmd, 364*9a24ce5eSMax Chou HCI_INIT_TIMEOUT); 365*9a24ce5eSMax Chou if (IS_ERR(skb)) { 366*9a24ce5eSMax Chou err = PTR_ERR(skb); 367*9a24ce5eSMax Chou rtl_dev_err(hdev, "RTL: Read reg16 failed (%d)", err); 368*9a24ce5eSMax Chou return err; 369*9a24ce5eSMax Chou } 370*9a24ce5eSMax Chou 371*9a24ce5eSMax Chou if (skb->len != 3 || skb->data[0]) { 372*9a24ce5eSMax Chou bt_dev_err(hdev, "RTL: Read reg16 length mismatch"); 373*9a24ce5eSMax Chou kfree_skb(skb); 374*9a24ce5eSMax Chou return -EIO; 375*9a24ce5eSMax Chou } 376*9a24ce5eSMax Chou 377*9a24ce5eSMax Chou if (rp) 378*9a24ce5eSMax Chou memcpy(rp, skb->data + 1, 2); 379*9a24ce5eSMax Chou 380*9a24ce5eSMax Chou kfree_skb(skb); 381*9a24ce5eSMax Chou 382*9a24ce5eSMax Chou return 0; 383*9a24ce5eSMax Chou } 384*9a24ce5eSMax Chou 385*9a24ce5eSMax Chou static void *rtl_iov_pull_data(struct rtl_iovec *iov, u32 len) 386*9a24ce5eSMax Chou { 387*9a24ce5eSMax Chou void *data = iov->data; 388*9a24ce5eSMax Chou 389*9a24ce5eSMax Chou if (iov->len < len) 390*9a24ce5eSMax Chou return NULL; 391*9a24ce5eSMax Chou 392*9a24ce5eSMax Chou iov->data += len; 393*9a24ce5eSMax Chou iov->len -= len; 394*9a24ce5eSMax Chou 395*9a24ce5eSMax Chou return data; 396*9a24ce5eSMax Chou } 397*9a24ce5eSMax Chou 398*9a24ce5eSMax Chou static void btrtl_insert_ordered_subsec(struct rtl_subsection *node, 399*9a24ce5eSMax Chou struct btrtl_device_info *btrtl_dev) 400*9a24ce5eSMax Chou { 401*9a24ce5eSMax Chou struct list_head *pos; 402*9a24ce5eSMax Chou struct list_head *next; 403*9a24ce5eSMax Chou struct rtl_subsection *subsec; 404*9a24ce5eSMax Chou 405*9a24ce5eSMax Chou list_for_each_safe(pos, next, &btrtl_dev->patch_subsecs) { 406*9a24ce5eSMax Chou subsec = list_entry(pos, struct rtl_subsection, list); 407*9a24ce5eSMax Chou if (subsec->prio >= node->prio) 408*9a24ce5eSMax Chou break; 409*9a24ce5eSMax Chou } 410*9a24ce5eSMax Chou __list_add(&node->list, pos->prev, pos); 411*9a24ce5eSMax Chou } 412*9a24ce5eSMax Chou 413*9a24ce5eSMax Chou static int btrtl_parse_section(struct hci_dev *hdev, 414*9a24ce5eSMax Chou struct btrtl_device_info *btrtl_dev, u32 opcode, 415*9a24ce5eSMax Chou u8 *data, u32 len) 416*9a24ce5eSMax Chou { 417*9a24ce5eSMax Chou struct rtl_section_hdr *hdr; 418*9a24ce5eSMax Chou struct rtl_subsection *subsec; 419*9a24ce5eSMax Chou struct rtl_common_subsec *common_subsec; 420*9a24ce5eSMax Chou struct rtl_sec_hdr *sec_hdr; 421*9a24ce5eSMax Chou int i; 422*9a24ce5eSMax Chou u8 *ptr; 423*9a24ce5eSMax Chou u16 num_subsecs; 424*9a24ce5eSMax Chou u32 subsec_len; 425*9a24ce5eSMax Chou int rc = 0; 426*9a24ce5eSMax Chou struct rtl_iovec iov = { 427*9a24ce5eSMax Chou .data = data, 428*9a24ce5eSMax Chou .len = len, 429*9a24ce5eSMax Chou }; 430*9a24ce5eSMax Chou 431*9a24ce5eSMax Chou hdr = rtl_iov_pull_data(&iov, sizeof(*hdr)); 432*9a24ce5eSMax Chou if (!hdr) 433*9a24ce5eSMax Chou return -EINVAL; 434*9a24ce5eSMax Chou num_subsecs = le16_to_cpu(hdr->num); 435*9a24ce5eSMax Chou 436*9a24ce5eSMax Chou for (i = 0; i < num_subsecs; i++) { 437*9a24ce5eSMax Chou common_subsec = rtl_iov_pull_data(&iov, sizeof(*common_subsec)); 438*9a24ce5eSMax Chou if (!common_subsec) 439*9a24ce5eSMax Chou break; 440*9a24ce5eSMax Chou subsec_len = le32_to_cpu(common_subsec->len); 441*9a24ce5eSMax Chou 442*9a24ce5eSMax Chou rtl_dev_dbg(hdev, "subsec, eco 0x%02x, len %08x", 443*9a24ce5eSMax Chou common_subsec->eco, subsec_len); 444*9a24ce5eSMax Chou 445*9a24ce5eSMax Chou ptr = rtl_iov_pull_data(&iov, subsec_len); 446*9a24ce5eSMax Chou if (!ptr) 447*9a24ce5eSMax Chou break; 448*9a24ce5eSMax Chou 449*9a24ce5eSMax Chou if (common_subsec->eco != btrtl_dev->rom_version + 1) 450*9a24ce5eSMax Chou continue; 451*9a24ce5eSMax Chou 452*9a24ce5eSMax Chou switch (opcode) { 453*9a24ce5eSMax Chou case RTL_PATCH_SECURITY_HEADER: 454*9a24ce5eSMax Chou sec_hdr = (void *)common_subsec; 455*9a24ce5eSMax Chou if (sec_hdr->key_id != btrtl_dev->key_id) 456*9a24ce5eSMax Chou continue; 457*9a24ce5eSMax Chou break; 458*9a24ce5eSMax Chou } 459*9a24ce5eSMax Chou 460*9a24ce5eSMax Chou subsec = kzalloc(sizeof(*subsec), GFP_KERNEL); 461*9a24ce5eSMax Chou if (!subsec) 462*9a24ce5eSMax Chou return -ENOMEM; 463*9a24ce5eSMax Chou subsec->opcode = opcode; 464*9a24ce5eSMax Chou subsec->prio = common_subsec->prio; 465*9a24ce5eSMax Chou subsec->len = subsec_len; 466*9a24ce5eSMax Chou subsec->data = ptr; 467*9a24ce5eSMax Chou btrtl_insert_ordered_subsec(subsec, btrtl_dev); 468*9a24ce5eSMax Chou rc += subsec_len; 469*9a24ce5eSMax Chou } 470*9a24ce5eSMax Chou 471*9a24ce5eSMax Chou return rc; 472*9a24ce5eSMax Chou } 473*9a24ce5eSMax Chou 474*9a24ce5eSMax Chou static int rtlbt_parse_firmware_v2(struct hci_dev *hdev, 475*9a24ce5eSMax Chou struct btrtl_device_info *btrtl_dev, 476*9a24ce5eSMax Chou unsigned char **_buf) 477*9a24ce5eSMax Chou { 478*9a24ce5eSMax Chou struct rtl_epatch_header_v2 *hdr; 479*9a24ce5eSMax Chou int rc; 480*9a24ce5eSMax Chou u8 reg_val[2]; 481*9a24ce5eSMax Chou u8 key_id; 482*9a24ce5eSMax Chou u32 num_sections; 483*9a24ce5eSMax Chou struct rtl_section *section; 484*9a24ce5eSMax Chou struct rtl_subsection *entry, *tmp; 485*9a24ce5eSMax Chou u32 section_len; 486*9a24ce5eSMax Chou u32 opcode; 487*9a24ce5eSMax Chou int len = 0; 488*9a24ce5eSMax Chou int i; 489*9a24ce5eSMax Chou u8 *ptr; 490*9a24ce5eSMax Chou struct rtl_iovec iov = { 491*9a24ce5eSMax Chou .data = btrtl_dev->fw_data, 492*9a24ce5eSMax Chou .len = btrtl_dev->fw_len - 7, /* Cut the tail */ 493*9a24ce5eSMax Chou }; 494*9a24ce5eSMax Chou 495*9a24ce5eSMax Chou rc = btrtl_vendor_read_reg16(hdev, RTL_SEC_PROJ, reg_val); 496*9a24ce5eSMax Chou if (rc < 0) 497*9a24ce5eSMax Chou return -EIO; 498*9a24ce5eSMax Chou key_id = reg_val[0]; 499*9a24ce5eSMax Chou 500*9a24ce5eSMax Chou rtl_dev_dbg(hdev, "%s: key id %u", __func__, key_id); 501*9a24ce5eSMax Chou 502*9a24ce5eSMax Chou btrtl_dev->key_id = key_id; 503*9a24ce5eSMax Chou 504*9a24ce5eSMax Chou hdr = rtl_iov_pull_data(&iov, sizeof(*hdr)); 505*9a24ce5eSMax Chou if (!hdr) 506*9a24ce5eSMax Chou return -EINVAL; 507*9a24ce5eSMax Chou num_sections = le32_to_cpu(hdr->num_sections); 508*9a24ce5eSMax Chou 509*9a24ce5eSMax Chou rtl_dev_dbg(hdev, "FW version %08x-%08x", *((u32 *)hdr->fw_version), 510*9a24ce5eSMax Chou *((u32 *)(hdr->fw_version + 4))); 511*9a24ce5eSMax Chou 512*9a24ce5eSMax Chou for (i = 0; i < num_sections; i++) { 513*9a24ce5eSMax Chou section = rtl_iov_pull_data(&iov, sizeof(*section)); 514*9a24ce5eSMax Chou if (!section) 515*9a24ce5eSMax Chou break; 516*9a24ce5eSMax Chou section_len = le32_to_cpu(section->len); 517*9a24ce5eSMax Chou opcode = le32_to_cpu(section->opcode); 518*9a24ce5eSMax Chou 519*9a24ce5eSMax Chou rtl_dev_dbg(hdev, "opcode 0x%04x", section->opcode); 520*9a24ce5eSMax Chou 521*9a24ce5eSMax Chou ptr = rtl_iov_pull_data(&iov, section_len); 522*9a24ce5eSMax Chou if (!ptr) 523*9a24ce5eSMax Chou break; 524*9a24ce5eSMax Chou 525*9a24ce5eSMax Chou switch (opcode) { 526*9a24ce5eSMax Chou case RTL_PATCH_SNIPPETS: 527*9a24ce5eSMax Chou rc = btrtl_parse_section(hdev, btrtl_dev, opcode, 528*9a24ce5eSMax Chou ptr, section_len); 529*9a24ce5eSMax Chou break; 530*9a24ce5eSMax Chou case RTL_PATCH_SECURITY_HEADER: 531*9a24ce5eSMax Chou /* If key_id from chip is zero, ignore all security 532*9a24ce5eSMax Chou * headers. 533*9a24ce5eSMax Chou */ 534*9a24ce5eSMax Chou if (!key_id) 535*9a24ce5eSMax Chou break; 536*9a24ce5eSMax Chou rc = btrtl_parse_section(hdev, btrtl_dev, opcode, 537*9a24ce5eSMax Chou ptr, section_len); 538*9a24ce5eSMax Chou break; 539*9a24ce5eSMax Chou case RTL_PATCH_DUMMY_HEADER: 540*9a24ce5eSMax Chou rc = btrtl_parse_section(hdev, btrtl_dev, opcode, 541*9a24ce5eSMax Chou ptr, section_len); 542*9a24ce5eSMax Chou break; 543*9a24ce5eSMax Chou default: 544*9a24ce5eSMax Chou rc = 0; 545*9a24ce5eSMax Chou break; 546*9a24ce5eSMax Chou } 547*9a24ce5eSMax Chou if (rc < 0) { 548*9a24ce5eSMax Chou rtl_dev_err(hdev, "RTL: Parse section (%u) err %d", 549*9a24ce5eSMax Chou opcode, rc); 550*9a24ce5eSMax Chou return rc; 551*9a24ce5eSMax Chou } 552*9a24ce5eSMax Chou len += rc; 553*9a24ce5eSMax Chou } 554*9a24ce5eSMax Chou 555*9a24ce5eSMax Chou if (!len) 556*9a24ce5eSMax Chou return -ENODATA; 557*9a24ce5eSMax Chou 558*9a24ce5eSMax Chou /* Allocate mem and copy all found subsecs. */ 559*9a24ce5eSMax Chou ptr = kvmalloc(len, GFP_KERNEL); 560*9a24ce5eSMax Chou if (!ptr) 561*9a24ce5eSMax Chou return -ENOMEM; 562*9a24ce5eSMax Chou 563*9a24ce5eSMax Chou len = 0; 564*9a24ce5eSMax Chou list_for_each_entry_safe(entry, tmp, &btrtl_dev->patch_subsecs, list) { 565*9a24ce5eSMax Chou rtl_dev_dbg(hdev, "RTL: opcode %08x, addr %p, len 0x%x", 566*9a24ce5eSMax Chou entry->opcode, entry->data, entry->len); 567*9a24ce5eSMax Chou memcpy(ptr + len, entry->data, entry->len); 568*9a24ce5eSMax Chou len += entry->len; 569*9a24ce5eSMax Chou } 570*9a24ce5eSMax Chou 571*9a24ce5eSMax Chou if (!len) 572*9a24ce5eSMax Chou return -EPERM; 573*9a24ce5eSMax Chou 574*9a24ce5eSMax Chou *_buf = ptr; 575*9a24ce5eSMax Chou return len; 576*9a24ce5eSMax Chou } 577*9a24ce5eSMax Chou 57826503ad2SMartin Blumenstingl static int rtlbt_parse_firmware(struct hci_dev *hdev, 57926503ad2SMartin Blumenstingl struct btrtl_device_info *btrtl_dev, 580db33c77dSCarlo Caione unsigned char **_buf) 581db33c77dSCarlo Caione { 582e5070e07SColin Ian King static const u8 extension_sig[] = { 0x51, 0x04, 0xfd, 0x77 }; 583db33c77dSCarlo Caione struct rtl_epatch_header *epatch_info; 584db33c77dSCarlo Caione unsigned char *buf; 58526503ad2SMartin Blumenstingl int i, len; 586db33c77dSCarlo Caione size_t min_size; 58726503ad2SMartin Blumenstingl u8 opcode, length, data; 588db33c77dSCarlo Caione int project_id = -1; 589db33c77dSCarlo Caione const unsigned char *fwptr, *chip_id_base; 590db33c77dSCarlo Caione const unsigned char *patch_length_base, *patch_offset_base; 591db33c77dSCarlo Caione u32 patch_offset = 0; 592db33c77dSCarlo Caione u16 patch_length, num_patches; 5931110a2dbSLarry Finger static const struct { 5941110a2dbSLarry Finger __u16 lmp_subver; 5951110a2dbSLarry Finger __u8 id; 5961110a2dbSLarry Finger } project_id_to_lmp_subver[] = { 5971110a2dbSLarry Finger { RTL_ROM_LMP_8723A, 0 }, 5981110a2dbSLarry Finger { RTL_ROM_LMP_8723B, 1 }, 5991110a2dbSLarry Finger { RTL_ROM_LMP_8821A, 2 }, 6001110a2dbSLarry Finger { RTL_ROM_LMP_8761A, 3 }, 601c0123cb6SVasily Khoruzhick { RTL_ROM_LMP_8703B, 7 }, 6021110a2dbSLarry Finger { RTL_ROM_LMP_8822B, 8 }, 603907f8499SAlex Lu { RTL_ROM_LMP_8723B, 9 }, /* 8723D */ 604907f8499SAlex Lu { RTL_ROM_LMP_8821A, 10 }, /* 8821C */ 6058ecfdc95SAlex Lu { RTL_ROM_LMP_8822B, 13 }, /* 8822C */ 60604896832SZiqian SUN (Zamir) { RTL_ROM_LMP_8761A, 14 }, /* 8761B */ 6070d484db6SMax Chou { RTL_ROM_LMP_8852A, 18 }, /* 8852A */ 60818e8055cSMax Chou { RTL_ROM_LMP_8852A, 20 }, /* 8852B */ 6098b1d66b5SMax Chou { RTL_ROM_LMP_8852A, 25 }, /* 8852C */ 610db33c77dSCarlo Caione }; 611db33c77dSCarlo Caione 612*9a24ce5eSMax Chou if (btrtl_dev->fw_len <= 8) 613*9a24ce5eSMax Chou return -EINVAL; 614*9a24ce5eSMax Chou 615*9a24ce5eSMax Chou if (!memcmp(btrtl_dev->fw_data, RTL_EPATCH_SIGNATURE, 8)) 616*9a24ce5eSMax Chou min_size = sizeof(struct rtl_epatch_header) + 617*9a24ce5eSMax Chou sizeof(extension_sig) + 3; 618*9a24ce5eSMax Chou else if (!memcmp(btrtl_dev->fw_data, RTL_EPATCH_SIGNATURE_V2, 8)) 619*9a24ce5eSMax Chou min_size = sizeof(struct rtl_epatch_header_v2) + 620*9a24ce5eSMax Chou sizeof(extension_sig) + 3; 621*9a24ce5eSMax Chou else 622*9a24ce5eSMax Chou return -EINVAL; 623*9a24ce5eSMax Chou 62426503ad2SMartin Blumenstingl if (btrtl_dev->fw_len < min_size) 625db33c77dSCarlo Caione return -EINVAL; 626db33c77dSCarlo Caione 62726503ad2SMartin Blumenstingl fwptr = btrtl_dev->fw_data + btrtl_dev->fw_len - sizeof(extension_sig); 628db33c77dSCarlo Caione if (memcmp(fwptr, extension_sig, sizeof(extension_sig)) != 0) { 629f1300c03SAlex Lu rtl_dev_err(hdev, "extension section signature mismatch"); 630db33c77dSCarlo Caione return -EINVAL; 631db33c77dSCarlo Caione } 632db33c77dSCarlo Caione 633db33c77dSCarlo Caione /* Loop from the end of the firmware parsing instructions, until 634db33c77dSCarlo Caione * we find an instruction that identifies the "project ID" for the 635db33c77dSCarlo Caione * hardware supported by this firwmare file. 63638f230f1Sshaomin Deng * Once we have that, we double-check that project_id is suitable 637db33c77dSCarlo Caione * for the hardware we are working with. 638db33c77dSCarlo Caione */ 63926503ad2SMartin Blumenstingl while (fwptr >= btrtl_dev->fw_data + (sizeof(*epatch_info) + 3)) { 640db33c77dSCarlo Caione opcode = *--fwptr; 641db33c77dSCarlo Caione length = *--fwptr; 642db33c77dSCarlo Caione data = *--fwptr; 643db33c77dSCarlo Caione 644db33c77dSCarlo Caione BT_DBG("check op=%x len=%x data=%x", opcode, length, data); 645db33c77dSCarlo Caione 646db33c77dSCarlo Caione if (opcode == 0xff) /* EOF */ 647db33c77dSCarlo Caione break; 648db33c77dSCarlo Caione 649db33c77dSCarlo Caione if (length == 0) { 650f1300c03SAlex Lu rtl_dev_err(hdev, "found instruction with length 0"); 651db33c77dSCarlo Caione return -EINVAL; 652db33c77dSCarlo Caione } 653db33c77dSCarlo Caione 654db33c77dSCarlo Caione if (opcode == 0 && length == 1) { 655db33c77dSCarlo Caione project_id = data; 656db33c77dSCarlo Caione break; 657db33c77dSCarlo Caione } 658db33c77dSCarlo Caione 659db33c77dSCarlo Caione fwptr -= length; 660db33c77dSCarlo Caione } 661db33c77dSCarlo Caione 662db33c77dSCarlo Caione if (project_id < 0) { 663f1300c03SAlex Lu rtl_dev_err(hdev, "failed to find version instruction"); 664db33c77dSCarlo Caione return -EINVAL; 665db33c77dSCarlo Caione } 666db33c77dSCarlo Caione 6671110a2dbSLarry Finger /* Find project_id in table */ 6681110a2dbSLarry Finger for (i = 0; i < ARRAY_SIZE(project_id_to_lmp_subver); i++) { 6699ab9235fSMax Chou if (project_id == project_id_to_lmp_subver[i].id) { 6709ab9235fSMax Chou btrtl_dev->project_id = project_id; 6711110a2dbSLarry Finger break; 6721110a2dbSLarry Finger } 6739ab9235fSMax Chou } 6741110a2dbSLarry Finger 6751110a2dbSLarry Finger if (i >= ARRAY_SIZE(project_id_to_lmp_subver)) { 676f1300c03SAlex Lu rtl_dev_err(hdev, "unknown project id %d", project_id); 677db33c77dSCarlo Caione return -EINVAL; 678db33c77dSCarlo Caione } 679db33c77dSCarlo Caione 68026503ad2SMartin Blumenstingl if (btrtl_dev->ic_info->lmp_subver != 68126503ad2SMartin Blumenstingl project_id_to_lmp_subver[i].lmp_subver) { 682f1300c03SAlex Lu rtl_dev_err(hdev, "firmware is for %x but this is a %x", 68326503ad2SMartin Blumenstingl project_id_to_lmp_subver[i].lmp_subver, 68426503ad2SMartin Blumenstingl btrtl_dev->ic_info->lmp_subver); 685db33c77dSCarlo Caione return -EINVAL; 686db33c77dSCarlo Caione } 687db33c77dSCarlo Caione 688*9a24ce5eSMax Chou if (memcmp(btrtl_dev->fw_data, RTL_EPATCH_SIGNATURE, 8) != 0) { 689*9a24ce5eSMax Chou if (!memcmp(btrtl_dev->fw_data, RTL_EPATCH_SIGNATURE_V2, 8)) 690*9a24ce5eSMax Chou return rtlbt_parse_firmware_v2(hdev, btrtl_dev, _buf); 691f1300c03SAlex Lu rtl_dev_err(hdev, "bad EPATCH signature"); 692db33c77dSCarlo Caione return -EINVAL; 693db33c77dSCarlo Caione } 694db33c77dSCarlo Caione 695*9a24ce5eSMax Chou epatch_info = (struct rtl_epatch_header *)btrtl_dev->fw_data; 696db33c77dSCarlo Caione num_patches = le16_to_cpu(epatch_info->num_patches); 697db33c77dSCarlo Caione BT_DBG("fw_version=%x, num_patches=%d", 698db33c77dSCarlo Caione le32_to_cpu(epatch_info->fw_version), num_patches); 699db33c77dSCarlo Caione 700db33c77dSCarlo Caione /* After the rtl_epatch_header there is a funky patch metadata section. 701db33c77dSCarlo Caione * Assuming 2 patches, the layout is: 702db33c77dSCarlo Caione * ChipID1 ChipID2 PatchLength1 PatchLength2 PatchOffset1 PatchOffset2 703db33c77dSCarlo Caione * 704db33c77dSCarlo Caione * Find the right patch for this chip. 705db33c77dSCarlo Caione */ 706db33c77dSCarlo Caione min_size += 8 * num_patches; 70726503ad2SMartin Blumenstingl if (btrtl_dev->fw_len < min_size) 708db33c77dSCarlo Caione return -EINVAL; 709db33c77dSCarlo Caione 71026503ad2SMartin Blumenstingl chip_id_base = btrtl_dev->fw_data + sizeof(struct rtl_epatch_header); 711db33c77dSCarlo Caione patch_length_base = chip_id_base + (sizeof(u16) * num_patches); 712db33c77dSCarlo Caione patch_offset_base = patch_length_base + (sizeof(u16) * num_patches); 713db33c77dSCarlo Caione for (i = 0; i < num_patches; i++) { 714db33c77dSCarlo Caione u16 chip_id = get_unaligned_le16(chip_id_base + 715db33c77dSCarlo Caione (i * sizeof(u16))); 71626503ad2SMartin Blumenstingl if (chip_id == btrtl_dev->rom_version + 1) { 717db33c77dSCarlo Caione patch_length = get_unaligned_le16(patch_length_base + 718db33c77dSCarlo Caione (i * sizeof(u16))); 719db33c77dSCarlo Caione patch_offset = get_unaligned_le32(patch_offset_base + 720db33c77dSCarlo Caione (i * sizeof(u32))); 721db33c77dSCarlo Caione break; 722db33c77dSCarlo Caione } 723db33c77dSCarlo Caione } 724db33c77dSCarlo Caione 725db33c77dSCarlo Caione if (!patch_offset) { 726a5c76e67SHans de Goede rtl_dev_err(hdev, "didn't find patch for chip id %d", 727a5c76e67SHans de Goede btrtl_dev->rom_version); 728db33c77dSCarlo Caione return -EINVAL; 729db33c77dSCarlo Caione } 730db33c77dSCarlo Caione 731db33c77dSCarlo Caione BT_DBG("length=%x offset=%x index %d", patch_length, patch_offset, i); 732db33c77dSCarlo Caione min_size = patch_offset + patch_length; 73326503ad2SMartin Blumenstingl if (btrtl_dev->fw_len < min_size) 734db33c77dSCarlo Caione return -EINVAL; 735db33c77dSCarlo Caione 736db33c77dSCarlo Caione /* Copy the firmware into a new buffer and write the version at 737db33c77dSCarlo Caione * the end. 738db33c77dSCarlo Caione */ 739db33c77dSCarlo Caione len = patch_length; 740268d3636SMaxim Mikityanskiy buf = kvmalloc(patch_length, GFP_KERNEL); 741db33c77dSCarlo Caione if (!buf) 742db33c77dSCarlo Caione return -ENOMEM; 743db33c77dSCarlo Caione 744268d3636SMaxim Mikityanskiy memcpy(buf, btrtl_dev->fw_data + patch_offset, patch_length - 4); 745db33c77dSCarlo Caione memcpy(buf + patch_length - 4, &epatch_info->fw_version, 4); 746db33c77dSCarlo Caione 747db33c77dSCarlo Caione *_buf = buf; 748db33c77dSCarlo Caione return len; 749db33c77dSCarlo Caione } 750db33c77dSCarlo Caione 751db33c77dSCarlo Caione static int rtl_download_firmware(struct hci_dev *hdev, 752db33c77dSCarlo Caione const unsigned char *data, int fw_len) 753db33c77dSCarlo Caione { 754db33c77dSCarlo Caione struct rtl_download_cmd *dl_cmd; 755db33c77dSCarlo Caione int frag_num = fw_len / RTL_FRAG_LEN + 1; 756db33c77dSCarlo Caione int frag_len = RTL_FRAG_LEN; 757db33c77dSCarlo Caione int ret = 0; 758db33c77dSCarlo Caione int i; 759*9a24ce5eSMax Chou int j = 0; 760240b64a8SAlex Lu struct sk_buff *skb; 761240b64a8SAlex Lu struct hci_rp_read_local_version *rp; 762db33c77dSCarlo Caione 763db33c77dSCarlo Caione dl_cmd = kmalloc(sizeof(struct rtl_download_cmd), GFP_KERNEL); 764db33c77dSCarlo Caione if (!dl_cmd) 765db33c77dSCarlo Caione return -ENOMEM; 766db33c77dSCarlo Caione 767db33c77dSCarlo Caione for (i = 0; i < frag_num; i++) { 768db33c77dSCarlo Caione struct sk_buff *skb; 769db33c77dSCarlo Caione 770*9a24ce5eSMax Chou dl_cmd->index = j++; 771*9a24ce5eSMax Chou if (dl_cmd->index == 0x7f) 772*9a24ce5eSMax Chou j = 1; 773cf0d9a70SMax Chou 774db33c77dSCarlo Caione if (i == (frag_num - 1)) { 775db33c77dSCarlo Caione dl_cmd->index |= 0x80; /* data end */ 776db33c77dSCarlo Caione frag_len = fw_len % RTL_FRAG_LEN; 777db33c77dSCarlo Caione } 778*9a24ce5eSMax Chou rtl_dev_dbg(hdev, "download fw (%d/%d). index = %d", i, 779*9a24ce5eSMax Chou frag_num, dl_cmd->index); 780db33c77dSCarlo Caione memcpy(dl_cmd->data, data, frag_len); 781db33c77dSCarlo Caione 782db33c77dSCarlo Caione /* Send download command */ 783db33c77dSCarlo Caione skb = __hci_cmd_sync(hdev, 0xfc20, frag_len + 1, dl_cmd, 784db33c77dSCarlo Caione HCI_INIT_TIMEOUT); 785db33c77dSCarlo Caione if (IS_ERR(skb)) { 786f1300c03SAlex Lu rtl_dev_err(hdev, "download fw command failed (%ld)", 787a5c76e67SHans de Goede PTR_ERR(skb)); 788d171dfb6SMax Chou ret = PTR_ERR(skb); 789db33c77dSCarlo Caione goto out; 790db33c77dSCarlo Caione } 791db33c77dSCarlo Caione 792db33c77dSCarlo Caione if (skb->len != sizeof(struct rtl_download_response)) { 793f1300c03SAlex Lu rtl_dev_err(hdev, "download fw event length mismatch"); 794db33c77dSCarlo Caione kfree_skb(skb); 795db33c77dSCarlo Caione ret = -EIO; 796db33c77dSCarlo Caione goto out; 797db33c77dSCarlo Caione } 798db33c77dSCarlo Caione 799db33c77dSCarlo Caione kfree_skb(skb); 800db33c77dSCarlo Caione data += RTL_FRAG_LEN; 801db33c77dSCarlo Caione } 802db33c77dSCarlo Caione 803240b64a8SAlex Lu skb = btrtl_read_local_version(hdev); 804240b64a8SAlex Lu if (IS_ERR(skb)) { 805240b64a8SAlex Lu ret = PTR_ERR(skb); 806240b64a8SAlex Lu rtl_dev_err(hdev, "read local version failed"); 807240b64a8SAlex Lu goto out; 808240b64a8SAlex Lu } 809240b64a8SAlex Lu 810240b64a8SAlex Lu rp = (struct hci_rp_read_local_version *)skb->data; 811240b64a8SAlex Lu rtl_dev_info(hdev, "fw version 0x%04x%04x", 812240b64a8SAlex Lu __le16_to_cpu(rp->hci_rev), __le16_to_cpu(rp->lmp_subver)); 813240b64a8SAlex Lu kfree_skb(skb); 814240b64a8SAlex Lu 815db33c77dSCarlo Caione out: 816db33c77dSCarlo Caione kfree(dl_cmd); 817db33c77dSCarlo Caione return ret; 818db33c77dSCarlo Caione } 819db33c77dSCarlo Caione 82026503ad2SMartin Blumenstingl static int rtl_load_file(struct hci_dev *hdev, const char *name, u8 **buff) 8211110a2dbSLarry Finger { 8221110a2dbSLarry Finger const struct firmware *fw; 8231110a2dbSLarry Finger int ret; 8241110a2dbSLarry Finger 825f1300c03SAlex Lu rtl_dev_info(hdev, "loading %s", name); 8261110a2dbSLarry Finger ret = request_firmware(&fw, name, &hdev->dev); 827abed84a0SLarry Finger if (ret < 0) 8281110a2dbSLarry Finger return ret; 8291110a2dbSLarry Finger ret = fw->size; 830268d3636SMaxim Mikityanskiy *buff = kvmalloc(fw->size, GFP_KERNEL); 831268d3636SMaxim Mikityanskiy if (*buff) 832268d3636SMaxim Mikityanskiy memcpy(*buff, fw->data, ret); 833268d3636SMaxim Mikityanskiy else 834c3327bdeSDan Carpenter ret = -ENOMEM; 8351110a2dbSLarry Finger 8361110a2dbSLarry Finger release_firmware(fw); 8371110a2dbSLarry Finger 8381110a2dbSLarry Finger return ret; 8391110a2dbSLarry Finger } 8401110a2dbSLarry Finger 84126503ad2SMartin Blumenstingl static int btrtl_setup_rtl8723a(struct hci_dev *hdev, 84226503ad2SMartin Blumenstingl struct btrtl_device_info *btrtl_dev) 843db33c77dSCarlo Caione { 84426503ad2SMartin Blumenstingl if (btrtl_dev->fw_len < 8) 84526503ad2SMartin Blumenstingl return -EINVAL; 846db33c77dSCarlo Caione 847db33c77dSCarlo Caione /* Check that the firmware doesn't have the epatch signature 848db33c77dSCarlo Caione * (which is only for RTL8723B and newer). 849db33c77dSCarlo Caione */ 85026503ad2SMartin Blumenstingl if (!memcmp(btrtl_dev->fw_data, RTL_EPATCH_SIGNATURE, 8)) { 851f1300c03SAlex Lu rtl_dev_err(hdev, "unexpected EPATCH signature!"); 852907f8499SAlex Lu return -EINVAL; 853907f8499SAlex Lu } 854907f8499SAlex Lu 85526503ad2SMartin Blumenstingl return rtl_download_firmware(hdev, btrtl_dev->fw_data, 85626503ad2SMartin Blumenstingl btrtl_dev->fw_len); 857db33c77dSCarlo Caione } 858db33c77dSCarlo Caione 85926503ad2SMartin Blumenstingl static int btrtl_setup_rtl8723b(struct hci_dev *hdev, 86026503ad2SMartin Blumenstingl struct btrtl_device_info *btrtl_dev) 86126503ad2SMartin Blumenstingl { 86226503ad2SMartin Blumenstingl unsigned char *fw_data = NULL; 86326503ad2SMartin Blumenstingl int ret; 86426503ad2SMartin Blumenstingl u8 *tbuff; 86526503ad2SMartin Blumenstingl 86626503ad2SMartin Blumenstingl ret = rtlbt_parse_firmware(hdev, btrtl_dev, &fw_data); 867db33c77dSCarlo Caione if (ret < 0) 868db33c77dSCarlo Caione goto out; 869db33c77dSCarlo Caione 87026503ad2SMartin Blumenstingl if (btrtl_dev->cfg_len > 0) { 871268d3636SMaxim Mikityanskiy tbuff = kvzalloc(ret + btrtl_dev->cfg_len, GFP_KERNEL); 8721110a2dbSLarry Finger if (!tbuff) { 8731110a2dbSLarry Finger ret = -ENOMEM; 874db33c77dSCarlo Caione goto out; 8751110a2dbSLarry Finger } 8761110a2dbSLarry Finger 8771110a2dbSLarry Finger memcpy(tbuff, fw_data, ret); 878268d3636SMaxim Mikityanskiy kvfree(fw_data); 8791110a2dbSLarry Finger 88026503ad2SMartin Blumenstingl memcpy(tbuff + ret, btrtl_dev->cfg_data, btrtl_dev->cfg_len); 88126503ad2SMartin Blumenstingl ret += btrtl_dev->cfg_len; 8821110a2dbSLarry Finger 8831110a2dbSLarry Finger fw_data = tbuff; 8841110a2dbSLarry Finger } 8851110a2dbSLarry Finger 886f1300c03SAlex Lu rtl_dev_info(hdev, "cfg_sz %d, total sz %d", btrtl_dev->cfg_len, ret); 8871110a2dbSLarry Finger 8881110a2dbSLarry Finger ret = rtl_download_firmware(hdev, fw_data, ret); 889db33c77dSCarlo Caione 890db33c77dSCarlo Caione out: 891268d3636SMaxim Mikityanskiy kvfree(fw_data); 892db33c77dSCarlo Caione return ret; 893db33c77dSCarlo Caione } 894db33c77dSCarlo Caione 895c0123cb6SVasily Khoruzhick static bool rtl_has_chip_type(u16 lmp_subver) 896c0123cb6SVasily Khoruzhick { 897c0123cb6SVasily Khoruzhick switch (lmp_subver) { 898c0123cb6SVasily Khoruzhick case RTL_ROM_LMP_8703B: 899c0123cb6SVasily Khoruzhick return true; 900c0123cb6SVasily Khoruzhick default: 901c0123cb6SVasily Khoruzhick break; 902c0123cb6SVasily Khoruzhick } 903c0123cb6SVasily Khoruzhick 904c0123cb6SVasily Khoruzhick return false; 905c0123cb6SVasily Khoruzhick } 906c0123cb6SVasily Khoruzhick 907c0123cb6SVasily Khoruzhick static int rtl_read_chip_type(struct hci_dev *hdev, u8 *type) 908c0123cb6SVasily Khoruzhick { 909c0123cb6SVasily Khoruzhick struct rtl_chip_type_evt *chip_type; 910c0123cb6SVasily Khoruzhick struct sk_buff *skb; 911c0123cb6SVasily Khoruzhick const unsigned char cmd_buf[] = {0x00, 0x94, 0xa0, 0x00, 0xb0}; 912c0123cb6SVasily Khoruzhick 913c0123cb6SVasily Khoruzhick /* Read RTL chip type command */ 914c0123cb6SVasily Khoruzhick skb = __hci_cmd_sync(hdev, 0xfc61, 5, cmd_buf, HCI_INIT_TIMEOUT); 915c0123cb6SVasily Khoruzhick if (IS_ERR(skb)) { 916c0123cb6SVasily Khoruzhick rtl_dev_err(hdev, "Read chip type failed (%ld)", 917c0123cb6SVasily Khoruzhick PTR_ERR(skb)); 918c0123cb6SVasily Khoruzhick return PTR_ERR(skb); 919c0123cb6SVasily Khoruzhick } 920c0123cb6SVasily Khoruzhick 921c0123cb6SVasily Khoruzhick chip_type = skb_pull_data(skb, sizeof(*chip_type)); 922c0123cb6SVasily Khoruzhick if (!chip_type) { 923c0123cb6SVasily Khoruzhick rtl_dev_err(hdev, "RTL chip type event length mismatch"); 924c0123cb6SVasily Khoruzhick kfree_skb(skb); 925c0123cb6SVasily Khoruzhick return -EIO; 926c0123cb6SVasily Khoruzhick } 927c0123cb6SVasily Khoruzhick 928c0123cb6SVasily Khoruzhick rtl_dev_info(hdev, "chip_type status=%x type=%x", 929c0123cb6SVasily Khoruzhick chip_type->status, chip_type->type); 930c0123cb6SVasily Khoruzhick 931c0123cb6SVasily Khoruzhick *type = chip_type->type & 0x0f; 932c0123cb6SVasily Khoruzhick 933c0123cb6SVasily Khoruzhick kfree_skb(skb); 934c0123cb6SVasily Khoruzhick return 0; 935c0123cb6SVasily Khoruzhick } 936c0123cb6SVasily Khoruzhick 93726503ad2SMartin Blumenstingl void btrtl_free(struct btrtl_device_info *btrtl_dev) 938db33c77dSCarlo Caione { 939*9a24ce5eSMax Chou struct rtl_subsection *entry, *tmp; 940*9a24ce5eSMax Chou 941268d3636SMaxim Mikityanskiy kvfree(btrtl_dev->fw_data); 942268d3636SMaxim Mikityanskiy kvfree(btrtl_dev->cfg_data); 943*9a24ce5eSMax Chou 944*9a24ce5eSMax Chou list_for_each_entry_safe(entry, tmp, &btrtl_dev->patch_subsecs, list) { 945*9a24ce5eSMax Chou list_del(&entry->list); 946*9a24ce5eSMax Chou kfree(entry); 947*9a24ce5eSMax Chou } 948*9a24ce5eSMax Chou 94926503ad2SMartin Blumenstingl kfree(btrtl_dev); 95026503ad2SMartin Blumenstingl } 95126503ad2SMartin Blumenstingl EXPORT_SYMBOL_GPL(btrtl_free); 95226503ad2SMartin Blumenstingl 9531cc194caSHans de Goede struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev, 9541cc194caSHans de Goede const char *postfix) 95526503ad2SMartin Blumenstingl { 95626503ad2SMartin Blumenstingl struct btrtl_device_info *btrtl_dev; 957db33c77dSCarlo Caione struct sk_buff *skb; 958db33c77dSCarlo Caione struct hci_rp_read_local_version *resp; 9591cc194caSHans de Goede char cfg_name[40]; 960907f8499SAlex Lu u16 hci_rev, lmp_subver; 961*9a24ce5eSMax Chou u8 hci_ver, lmp_ver, chip_type = 0; 96226503ad2SMartin Blumenstingl int ret; 9631996d9caSKai-Heng Feng u16 opcode; 9641996d9caSKai-Heng Feng u8 cmd[2]; 965*9a24ce5eSMax Chou u8 reg_val[2]; 96626503ad2SMartin Blumenstingl 96726503ad2SMartin Blumenstingl btrtl_dev = kzalloc(sizeof(*btrtl_dev), GFP_KERNEL); 96826503ad2SMartin Blumenstingl if (!btrtl_dev) { 96926503ad2SMartin Blumenstingl ret = -ENOMEM; 97026503ad2SMartin Blumenstingl goto err_alloc; 97126503ad2SMartin Blumenstingl } 972db33c77dSCarlo Caione 973*9a24ce5eSMax Chou INIT_LIST_HEAD(&btrtl_dev->patch_subsecs); 974*9a24ce5eSMax Chou 975*9a24ce5eSMax Chou check_version: 976*9a24ce5eSMax Chou ret = btrtl_vendor_read_reg16(hdev, RTL_CHIP_SUBVER, reg_val); 977*9a24ce5eSMax Chou if (ret < 0) 978*9a24ce5eSMax Chou goto err_free; 979*9a24ce5eSMax Chou lmp_subver = get_unaligned_le16(reg_val); 980*9a24ce5eSMax Chou 981*9a24ce5eSMax Chou if (lmp_subver == RTL_ROM_LMP_8822B) { 982*9a24ce5eSMax Chou ret = btrtl_vendor_read_reg16(hdev, RTL_CHIP_REV, reg_val); 983*9a24ce5eSMax Chou if (ret < 0) 984*9a24ce5eSMax Chou goto err_free; 985*9a24ce5eSMax Chou hci_rev = get_unaligned_le16(reg_val); 986*9a24ce5eSMax Chou 987*9a24ce5eSMax Chou /* 8822E */ 988*9a24ce5eSMax Chou if (hci_rev == 0x000e) { 989*9a24ce5eSMax Chou hci_ver = 0x0c; 990*9a24ce5eSMax Chou lmp_ver = 0x0c; 991*9a24ce5eSMax Chou btrtl_dev->ic_info = btrtl_match_ic(lmp_subver, hci_rev, 992*9a24ce5eSMax Chou hci_ver, hdev->bus, 993*9a24ce5eSMax Chou chip_type); 994*9a24ce5eSMax Chou goto next; 995*9a24ce5eSMax Chou } 996*9a24ce5eSMax Chou } 997*9a24ce5eSMax Chou 998db33c77dSCarlo Caione skb = btrtl_read_local_version(hdev); 99926503ad2SMartin Blumenstingl if (IS_ERR(skb)) { 100026503ad2SMartin Blumenstingl ret = PTR_ERR(skb); 100126503ad2SMartin Blumenstingl goto err_free; 100226503ad2SMartin Blumenstingl } 1003db33c77dSCarlo Caione 1004db33c77dSCarlo Caione resp = (struct hci_rp_read_local_version *)skb->data; 1005db33c77dSCarlo Caione 1006c50903e3SMartin Blumenstingl hci_ver = resp->hci_ver; 1007907f8499SAlex Lu hci_rev = le16_to_cpu(resp->hci_rev); 1008*9a24ce5eSMax Chou lmp_ver = resp->lmp_ver; 1009db33c77dSCarlo Caione lmp_subver = le16_to_cpu(resp->lmp_subver); 10101996d9caSKai-Heng Feng 1011*9a24ce5eSMax Chou kfree_skb(skb); 1012*9a24ce5eSMax Chou 1013c0123cb6SVasily Khoruzhick if (rtl_has_chip_type(lmp_subver)) { 1014c0123cb6SVasily Khoruzhick ret = rtl_read_chip_type(hdev, &chip_type); 1015c0123cb6SVasily Khoruzhick if (ret) 1016c0123cb6SVasily Khoruzhick goto err_free; 1017c0123cb6SVasily Khoruzhick } 1018c0123cb6SVasily Khoruzhick 1019cd36742aSHilda Wu btrtl_dev->ic_info = btrtl_match_ic(lmp_subver, hci_rev, hci_ver, 1020c0123cb6SVasily Khoruzhick hdev->bus, chip_type); 1021cd36742aSHilda Wu 1022*9a24ce5eSMax Chou next: 1023*9a24ce5eSMax Chou rtl_dev_info(hdev, "examining hci_ver=%02x hci_rev=%04x lmp_ver=%02x lmp_subver=%04x", 1024*9a24ce5eSMax Chou hci_ver, hci_rev, 1025*9a24ce5eSMax Chou lmp_ver, lmp_subver); 1026*9a24ce5eSMax Chou 1027*9a24ce5eSMax Chou if (!btrtl_dev->ic_info && !btrtl_dev->drop_fw) 10281996d9caSKai-Heng Feng btrtl_dev->drop_fw = true; 1029*9a24ce5eSMax Chou else 1030*9a24ce5eSMax Chou btrtl_dev->drop_fw = false; 10311996d9caSKai-Heng Feng 10321996d9caSKai-Heng Feng if (btrtl_dev->drop_fw) { 10331996d9caSKai-Heng Feng opcode = hci_opcode_pack(0x3f, 0x66); 10341996d9caSKai-Heng Feng cmd[0] = opcode & 0xff; 10351996d9caSKai-Heng Feng cmd[1] = opcode >> 8; 10361996d9caSKai-Heng Feng 10371996d9caSKai-Heng Feng skb = bt_skb_alloc(sizeof(cmd), GFP_KERNEL); 1038f5e8e215SColin Ian King if (!skb) 1039*9a24ce5eSMax Chou goto err_free; 10401996d9caSKai-Heng Feng 10411996d9caSKai-Heng Feng skb_put_data(skb, cmd, sizeof(cmd)); 10421996d9caSKai-Heng Feng hci_skb_pkt_type(skb) = HCI_COMMAND_PKT; 10431996d9caSKai-Heng Feng 1044*9a24ce5eSMax Chou ret = hdev->send(hdev, skb); 1045*9a24ce5eSMax Chou if (ret < 0) { 1046*9a24ce5eSMax Chou bt_dev_err(hdev, "sending frame failed (%d)", ret); 1047*9a24ce5eSMax Chou kfree_skb(skb); 1048*9a24ce5eSMax Chou goto err_free; 1049*9a24ce5eSMax Chou } 10501996d9caSKai-Heng Feng 10511996d9caSKai-Heng Feng /* Ensure the above vendor command is sent to controller and 10521996d9caSKai-Heng Feng * process has done. 10531996d9caSKai-Heng Feng */ 10541996d9caSKai-Heng Feng msleep(200); 10551996d9caSKai-Heng Feng 1056*9a24ce5eSMax Chou goto check_version; 10571996d9caSKai-Heng Feng } 10581996d9caSKai-Heng Feng 105926503ad2SMartin Blumenstingl if (!btrtl_dev->ic_info) { 1060d182215dSAlex Lu rtl_dev_info(hdev, "unknown IC info, lmp subver %04x, hci rev %04x, hci ver %04x", 1061c50903e3SMartin Blumenstingl lmp_subver, hci_rev, hci_ver); 106200df214bSKai-Heng Feng return btrtl_dev; 106326503ad2SMartin Blumenstingl } 106426503ad2SMartin Blumenstingl 106526503ad2SMartin Blumenstingl if (btrtl_dev->ic_info->has_rom_version) { 106626503ad2SMartin Blumenstingl ret = rtl_read_rom_version(hdev, &btrtl_dev->rom_version); 106726503ad2SMartin Blumenstingl if (ret) 106826503ad2SMartin Blumenstingl goto err_free; 106926503ad2SMartin Blumenstingl } 107026503ad2SMartin Blumenstingl 107126503ad2SMartin Blumenstingl btrtl_dev->fw_len = rtl_load_file(hdev, btrtl_dev->ic_info->fw_name, 107226503ad2SMartin Blumenstingl &btrtl_dev->fw_data); 107326503ad2SMartin Blumenstingl if (btrtl_dev->fw_len < 0) { 1074f1300c03SAlex Lu rtl_dev_err(hdev, "firmware file %s not found", 107526503ad2SMartin Blumenstingl btrtl_dev->ic_info->fw_name); 107626503ad2SMartin Blumenstingl ret = btrtl_dev->fw_len; 107726503ad2SMartin Blumenstingl goto err_free; 107826503ad2SMartin Blumenstingl } 107926503ad2SMartin Blumenstingl 108026503ad2SMartin Blumenstingl if (btrtl_dev->ic_info->cfg_name) { 10811cc194caSHans de Goede if (postfix) { 10821cc194caSHans de Goede snprintf(cfg_name, sizeof(cfg_name), "%s-%s.bin", 10831cc194caSHans de Goede btrtl_dev->ic_info->cfg_name, postfix); 10841cc194caSHans de Goede } else { 10851cc194caSHans de Goede snprintf(cfg_name, sizeof(cfg_name), "%s.bin", 10861cc194caSHans de Goede btrtl_dev->ic_info->cfg_name); 10871cc194caSHans de Goede } 10881cc194caSHans de Goede btrtl_dev->cfg_len = rtl_load_file(hdev, cfg_name, 108926503ad2SMartin Blumenstingl &btrtl_dev->cfg_data); 109026503ad2SMartin Blumenstingl if (btrtl_dev->ic_info->config_needed && 109126503ad2SMartin Blumenstingl btrtl_dev->cfg_len <= 0) { 1092f1300c03SAlex Lu rtl_dev_err(hdev, "mandatory config file %s not found", 109326503ad2SMartin Blumenstingl btrtl_dev->ic_info->cfg_name); 109426503ad2SMartin Blumenstingl ret = btrtl_dev->cfg_len; 109526503ad2SMartin Blumenstingl goto err_free; 109626503ad2SMartin Blumenstingl } 109726503ad2SMartin Blumenstingl } 109826503ad2SMartin Blumenstingl 10997f6a750aSArchie Pusaka /* The following chips supports the Microsoft vendor extension, 11007f6a750aSArchie Pusaka * therefore set the corresponding VsMsftOpCode. 1101673fae14SMiao-chen Chou */ 1102f4bcba0eSMarcel Holtmann if (btrtl_dev->ic_info->has_msft_ext) 1103673fae14SMiao-chen Chou hci_set_msft_opcode(hdev, 0xFCF0); 1104673fae14SMiao-chen Chou 110526503ad2SMartin Blumenstingl return btrtl_dev; 110626503ad2SMartin Blumenstingl 110726503ad2SMartin Blumenstingl err_free: 110826503ad2SMartin Blumenstingl btrtl_free(btrtl_dev); 110926503ad2SMartin Blumenstingl err_alloc: 111026503ad2SMartin Blumenstingl return ERR_PTR(ret); 111126503ad2SMartin Blumenstingl } 111226503ad2SMartin Blumenstingl EXPORT_SYMBOL_GPL(btrtl_initialize); 111326503ad2SMartin Blumenstingl 111426503ad2SMartin Blumenstingl int btrtl_download_firmware(struct hci_dev *hdev, 111526503ad2SMartin Blumenstingl struct btrtl_device_info *btrtl_dev) 111626503ad2SMartin Blumenstingl { 1117db33c77dSCarlo Caione /* Match a set of subver values that correspond to stock firmware, 1118db33c77dSCarlo Caione * which is not compatible with standard btusb. 1119db33c77dSCarlo Caione * If matched, upload an alternative firmware that does conform to 1120db33c77dSCarlo Caione * standard btusb. Once that firmware is uploaded, the subver changes 1121db33c77dSCarlo Caione * to a different value. 1122db33c77dSCarlo Caione */ 112300df214bSKai-Heng Feng if (!btrtl_dev->ic_info) { 1124f1300c03SAlex Lu rtl_dev_info(hdev, "assuming no firmware upload needed"); 112500df214bSKai-Heng Feng return 0; 112600df214bSKai-Heng Feng } 112700df214bSKai-Heng Feng 112826503ad2SMartin Blumenstingl switch (btrtl_dev->ic_info->lmp_subver) { 1129db33c77dSCarlo Caione case RTL_ROM_LMP_8723A: 113026503ad2SMartin Blumenstingl return btrtl_setup_rtl8723a(hdev, btrtl_dev); 1131db33c77dSCarlo Caione case RTL_ROM_LMP_8723B: 1132db33c77dSCarlo Caione case RTL_ROM_LMP_8821A: 1133db33c77dSCarlo Caione case RTL_ROM_LMP_8761A: 11341110a2dbSLarry Finger case RTL_ROM_LMP_8822B: 11350d484db6SMax Chou case RTL_ROM_LMP_8852A: 1136c0123cb6SVasily Khoruzhick case RTL_ROM_LMP_8703B: 113726503ad2SMartin Blumenstingl return btrtl_setup_rtl8723b(hdev, btrtl_dev); 1138db33c77dSCarlo Caione default: 1139f1300c03SAlex Lu rtl_dev_info(hdev, "assuming no firmware upload needed"); 1140db33c77dSCarlo Caione return 0; 1141db33c77dSCarlo Caione } 1142db33c77dSCarlo Caione } 114326503ad2SMartin Blumenstingl EXPORT_SYMBOL_GPL(btrtl_download_firmware); 114426503ad2SMartin Blumenstingl 11453011faa2SArchie Pusaka void btrtl_set_quirks(struct hci_dev *hdev, struct btrtl_device_info *btrtl_dev) 114626503ad2SMartin Blumenstingl { 114765251e2eSAlex Lu /* Enable controller to do both LE scan and BR/EDR inquiry 114865251e2eSAlex Lu * simultaneously. 114965251e2eSAlex Lu */ 115065251e2eSAlex Lu set_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks); 115165251e2eSAlex Lu 115205672a2cSAbhishek Pandit-Subedi /* Enable central-peripheral role (able to create new connections with 115305672a2cSAbhishek Pandit-Subedi * an existing connection in slave role). 115405672a2cSAbhishek Pandit-Subedi */ 11559ab9235fSMax Chou /* Enable WBS supported for the specific Realtek devices. */ 11569ab9235fSMax Chou switch (btrtl_dev->project_id) { 11579ab9235fSMax Chou case CHIP_ID_8822C: 11589ab9235fSMax Chou case CHIP_ID_8852A: 115918e8055cSMax Chou case CHIP_ID_8852B: 11608b1d66b5SMax Chou case CHIP_ID_8852C: 116105672a2cSAbhishek Pandit-Subedi set_bit(HCI_QUIRK_VALID_LE_STATES, &hdev->quirks); 11629ab9235fSMax Chou set_bit(HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED, &hdev->quirks); 1163a479e713SHilda Wu 1164a479e713SHilda Wu /* RTL8852C needs to transmit mSBC data continuously without 1165a479e713SHilda Wu * the zero length of USB packets for the ALT 6 supported chips 1166a479e713SHilda Wu */ 1167a479e713SHilda Wu if (btrtl_dev->project_id == CHIP_ID_8852C) 1168a479e713SHilda Wu btrealtek_set_flag(hdev, REALTEK_ALT6_CONTINUOUS_TX_CHIP); 1169a479e713SHilda Wu 1170099c6d31SJoseph Hwang hci_set_aosp_capable(hdev); 117105672a2cSAbhishek Pandit-Subedi break; 117205672a2cSAbhishek Pandit-Subedi default: 117305672a2cSAbhishek Pandit-Subedi rtl_dev_dbg(hdev, "Central-peripheral role not enabled."); 11749ab9235fSMax Chou rtl_dev_dbg(hdev, "WBS supported not enabled."); 117505672a2cSAbhishek Pandit-Subedi break; 117605672a2cSAbhishek Pandit-Subedi } 1177c0123cb6SVasily Khoruzhick 1178253cf30eSMax Chou if (!btrtl_dev->ic_info) 1179253cf30eSMax Chou return; 1180253cf30eSMax Chou 1181c0123cb6SVasily Khoruzhick switch (btrtl_dev->ic_info->lmp_subver) { 1182c0123cb6SVasily Khoruzhick case RTL_ROM_LMP_8703B: 1183c0123cb6SVasily Khoruzhick /* 8723CS reports two pages for local ext features, 1184c0123cb6SVasily Khoruzhick * but it doesn't support any features from page 2 - 1185c0123cb6SVasily Khoruzhick * it either responds with garbage or with error status 1186c0123cb6SVasily Khoruzhick */ 1187c0123cb6SVasily Khoruzhick set_bit(HCI_QUIRK_BROKEN_LOCAL_EXT_FEATURES_PAGE_2, 1188c0123cb6SVasily Khoruzhick &hdev->quirks); 1189c0123cb6SVasily Khoruzhick break; 1190c0123cb6SVasily Khoruzhick default: 1191c0123cb6SVasily Khoruzhick break; 1192c0123cb6SVasily Khoruzhick } 11933011faa2SArchie Pusaka } 11943011faa2SArchie Pusaka EXPORT_SYMBOL_GPL(btrtl_set_quirks); 11953011faa2SArchie Pusaka 11963011faa2SArchie Pusaka int btrtl_setup_realtek(struct hci_dev *hdev) 11973011faa2SArchie Pusaka { 11983011faa2SArchie Pusaka struct btrtl_device_info *btrtl_dev; 11993011faa2SArchie Pusaka int ret; 12003011faa2SArchie Pusaka 12013011faa2SArchie Pusaka btrtl_dev = btrtl_initialize(hdev, NULL); 12023011faa2SArchie Pusaka if (IS_ERR(btrtl_dev)) 12033011faa2SArchie Pusaka return PTR_ERR(btrtl_dev); 12043011faa2SArchie Pusaka 12053011faa2SArchie Pusaka ret = btrtl_download_firmware(hdev, btrtl_dev); 12063011faa2SArchie Pusaka 12073011faa2SArchie Pusaka btrtl_set_quirks(hdev, btrtl_dev); 120805672a2cSAbhishek Pandit-Subedi 120905672a2cSAbhishek Pandit-Subedi btrtl_free(btrtl_dev); 121026503ad2SMartin Blumenstingl return ret; 121126503ad2SMartin Blumenstingl } 1212db33c77dSCarlo Caione EXPORT_SYMBOL_GPL(btrtl_setup_realtek); 1213db33c77dSCarlo Caione 12147af3f558SJian-Hong Pan int btrtl_shutdown_realtek(struct hci_dev *hdev) 12157af3f558SJian-Hong Pan { 12167af3f558SJian-Hong Pan struct sk_buff *skb; 12177af3f558SJian-Hong Pan int ret; 12187af3f558SJian-Hong Pan 12197af3f558SJian-Hong Pan /* According to the vendor driver, BT must be reset on close to avoid 12207af3f558SJian-Hong Pan * firmware crash. 12217af3f558SJian-Hong Pan */ 12227af3f558SJian-Hong Pan skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT); 12237af3f558SJian-Hong Pan if (IS_ERR(skb)) { 12247af3f558SJian-Hong Pan ret = PTR_ERR(skb); 12257af3f558SJian-Hong Pan bt_dev_err(hdev, "HCI reset during shutdown failed"); 12267af3f558SJian-Hong Pan return ret; 12277af3f558SJian-Hong Pan } 12287af3f558SJian-Hong Pan kfree_skb(skb); 12297af3f558SJian-Hong Pan 12307af3f558SJian-Hong Pan return 0; 12317af3f558SJian-Hong Pan } 12327af3f558SJian-Hong Pan EXPORT_SYMBOL_GPL(btrtl_shutdown_realtek); 12337af3f558SJian-Hong Pan 1234b85b0ee1SMartin Blumenstingl static unsigned int btrtl_convert_baudrate(u32 device_baudrate) 1235b85b0ee1SMartin Blumenstingl { 1236b85b0ee1SMartin Blumenstingl switch (device_baudrate) { 1237b85b0ee1SMartin Blumenstingl case 0x0252a00a: 1238b85b0ee1SMartin Blumenstingl return 230400; 1239b85b0ee1SMartin Blumenstingl 1240b85b0ee1SMartin Blumenstingl case 0x05f75004: 1241b85b0ee1SMartin Blumenstingl return 921600; 1242b85b0ee1SMartin Blumenstingl 1243b85b0ee1SMartin Blumenstingl case 0x00005004: 1244b85b0ee1SMartin Blumenstingl return 1000000; 1245b85b0ee1SMartin Blumenstingl 1246b85b0ee1SMartin Blumenstingl case 0x04928002: 1247b85b0ee1SMartin Blumenstingl case 0x01128002: 1248b85b0ee1SMartin Blumenstingl return 1500000; 1249b85b0ee1SMartin Blumenstingl 1250b85b0ee1SMartin Blumenstingl case 0x00005002: 1251b85b0ee1SMartin Blumenstingl return 2000000; 1252b85b0ee1SMartin Blumenstingl 1253b85b0ee1SMartin Blumenstingl case 0x0000b001: 1254b85b0ee1SMartin Blumenstingl return 2500000; 1255b85b0ee1SMartin Blumenstingl 1256b85b0ee1SMartin Blumenstingl case 0x04928001: 1257b85b0ee1SMartin Blumenstingl return 3000000; 1258b85b0ee1SMartin Blumenstingl 1259b85b0ee1SMartin Blumenstingl case 0x052a6001: 1260b85b0ee1SMartin Blumenstingl return 3500000; 1261b85b0ee1SMartin Blumenstingl 1262b85b0ee1SMartin Blumenstingl case 0x00005001: 1263b85b0ee1SMartin Blumenstingl return 4000000; 1264b85b0ee1SMartin Blumenstingl 1265b85b0ee1SMartin Blumenstingl case 0x0252c014: 1266b85b0ee1SMartin Blumenstingl default: 1267b85b0ee1SMartin Blumenstingl return 115200; 1268b85b0ee1SMartin Blumenstingl } 1269b85b0ee1SMartin Blumenstingl } 1270b85b0ee1SMartin Blumenstingl 1271b85b0ee1SMartin Blumenstingl int btrtl_get_uart_settings(struct hci_dev *hdev, 1272b85b0ee1SMartin Blumenstingl struct btrtl_device_info *btrtl_dev, 1273b85b0ee1SMartin Blumenstingl unsigned int *controller_baudrate, 1274b85b0ee1SMartin Blumenstingl u32 *device_baudrate, bool *flow_control) 1275b85b0ee1SMartin Blumenstingl { 1276b85b0ee1SMartin Blumenstingl struct rtl_vendor_config *config; 1277b85b0ee1SMartin Blumenstingl struct rtl_vendor_config_entry *entry; 1278b85b0ee1SMartin Blumenstingl int i, total_data_len; 1279b85b0ee1SMartin Blumenstingl bool found = false; 1280b85b0ee1SMartin Blumenstingl 1281b85b0ee1SMartin Blumenstingl total_data_len = btrtl_dev->cfg_len - sizeof(*config); 1282b85b0ee1SMartin Blumenstingl if (total_data_len <= 0) { 1283f1300c03SAlex Lu rtl_dev_warn(hdev, "no config loaded"); 1284b85b0ee1SMartin Blumenstingl return -EINVAL; 1285b85b0ee1SMartin Blumenstingl } 1286b85b0ee1SMartin Blumenstingl 1287b85b0ee1SMartin Blumenstingl config = (struct rtl_vendor_config *)btrtl_dev->cfg_data; 1288b85b0ee1SMartin Blumenstingl if (le32_to_cpu(config->signature) != RTL_CONFIG_MAGIC) { 1289f1300c03SAlex Lu rtl_dev_err(hdev, "invalid config magic"); 1290b85b0ee1SMartin Blumenstingl return -EINVAL; 1291b85b0ee1SMartin Blumenstingl } 1292b85b0ee1SMartin Blumenstingl 1293b85b0ee1SMartin Blumenstingl if (total_data_len < le16_to_cpu(config->total_len)) { 1294f1300c03SAlex Lu rtl_dev_err(hdev, "config is too short"); 1295b85b0ee1SMartin Blumenstingl return -EINVAL; 1296b85b0ee1SMartin Blumenstingl } 1297b85b0ee1SMartin Blumenstingl 1298b85b0ee1SMartin Blumenstingl for (i = 0; i < total_data_len; ) { 1299b85b0ee1SMartin Blumenstingl entry = ((void *)config->entry) + i; 1300b85b0ee1SMartin Blumenstingl 1301b85b0ee1SMartin Blumenstingl switch (le16_to_cpu(entry->offset)) { 1302b85b0ee1SMartin Blumenstingl case 0xc: 1303b85b0ee1SMartin Blumenstingl if (entry->len < sizeof(*device_baudrate)) { 1304f1300c03SAlex Lu rtl_dev_err(hdev, "invalid UART config entry"); 1305b85b0ee1SMartin Blumenstingl return -EINVAL; 1306b85b0ee1SMartin Blumenstingl } 1307b85b0ee1SMartin Blumenstingl 1308b85b0ee1SMartin Blumenstingl *device_baudrate = get_unaligned_le32(entry->data); 1309b85b0ee1SMartin Blumenstingl *controller_baudrate = btrtl_convert_baudrate( 1310b85b0ee1SMartin Blumenstingl *device_baudrate); 1311b85b0ee1SMartin Blumenstingl 1312b85b0ee1SMartin Blumenstingl if (entry->len >= 13) 1313b85b0ee1SMartin Blumenstingl *flow_control = !!(entry->data[12] & BIT(2)); 1314b85b0ee1SMartin Blumenstingl else 1315b85b0ee1SMartin Blumenstingl *flow_control = false; 1316b85b0ee1SMartin Blumenstingl 1317b85b0ee1SMartin Blumenstingl found = true; 1318b85b0ee1SMartin Blumenstingl break; 1319b85b0ee1SMartin Blumenstingl 1320b85b0ee1SMartin Blumenstingl default: 1321f1300c03SAlex Lu rtl_dev_dbg(hdev, "skipping config entry 0x%x (len %u)", 1322b85b0ee1SMartin Blumenstingl le16_to_cpu(entry->offset), entry->len); 1323b85b0ee1SMartin Blumenstingl break; 1324515d6798SYueHaibing } 1325b85b0ee1SMartin Blumenstingl 1326b85b0ee1SMartin Blumenstingl i += sizeof(*entry) + entry->len; 1327b85b0ee1SMartin Blumenstingl } 1328b85b0ee1SMartin Blumenstingl 1329b85b0ee1SMartin Blumenstingl if (!found) { 1330f1300c03SAlex Lu rtl_dev_err(hdev, "no UART config entry found"); 1331b85b0ee1SMartin Blumenstingl return -ENOENT; 1332b85b0ee1SMartin Blumenstingl } 1333b85b0ee1SMartin Blumenstingl 1334f1300c03SAlex Lu rtl_dev_dbg(hdev, "device baudrate = 0x%08x", *device_baudrate); 1335f1300c03SAlex Lu rtl_dev_dbg(hdev, "controller baudrate = %u", *controller_baudrate); 1336f1300c03SAlex Lu rtl_dev_dbg(hdev, "flow control %d", *flow_control); 1337b85b0ee1SMartin Blumenstingl 1338b85b0ee1SMartin Blumenstingl return 0; 1339b85b0ee1SMartin Blumenstingl } 1340b85b0ee1SMartin Blumenstingl EXPORT_SYMBOL_GPL(btrtl_get_uart_settings); 1341b85b0ee1SMartin Blumenstingl 1342db33c77dSCarlo Caione MODULE_AUTHOR("Daniel Drake <drake@endlessm.com>"); 1343db33c77dSCarlo Caione MODULE_DESCRIPTION("Bluetooth support for Realtek devices ver " VERSION); 1344db33c77dSCarlo Caione MODULE_VERSION(VERSION); 1345db33c77dSCarlo Caione MODULE_LICENSE("GPL"); 1346f96dbd32SMartin Blumenstingl MODULE_FIRMWARE("rtl_bt/rtl8723a_fw.bin"); 1347f96dbd32SMartin Blumenstingl MODULE_FIRMWARE("rtl_bt/rtl8723b_fw.bin"); 1348f96dbd32SMartin Blumenstingl MODULE_FIRMWARE("rtl_bt/rtl8723b_config.bin"); 1349c50903e3SMartin Blumenstingl MODULE_FIRMWARE("rtl_bt/rtl8723bs_fw.bin"); 1350c50903e3SMartin Blumenstingl MODULE_FIRMWARE("rtl_bt/rtl8723bs_config.bin"); 1351c0123cb6SVasily Khoruzhick MODULE_FIRMWARE("rtl_bt/rtl8723cs_cg_fw.bin"); 1352c0123cb6SVasily Khoruzhick MODULE_FIRMWARE("rtl_bt/rtl8723cs_cg_config.bin"); 1353c0123cb6SVasily Khoruzhick MODULE_FIRMWARE("rtl_bt/rtl8723cs_vf_fw.bin"); 1354c0123cb6SVasily Khoruzhick MODULE_FIRMWARE("rtl_bt/rtl8723cs_vf_config.bin"); 1355c0123cb6SVasily Khoruzhick MODULE_FIRMWARE("rtl_bt/rtl8723cs_xx_fw.bin"); 1356c0123cb6SVasily Khoruzhick MODULE_FIRMWARE("rtl_bt/rtl8723cs_xx_config.bin"); 1357c50903e3SMartin Blumenstingl MODULE_FIRMWARE("rtl_bt/rtl8723ds_fw.bin"); 1358c50903e3SMartin Blumenstingl MODULE_FIRMWARE("rtl_bt/rtl8723ds_config.bin"); 1359f96dbd32SMartin Blumenstingl MODULE_FIRMWARE("rtl_bt/rtl8761a_fw.bin"); 1360f96dbd32SMartin Blumenstingl MODULE_FIRMWARE("rtl_bt/rtl8761a_config.bin"); 1361f96dbd32SMartin Blumenstingl MODULE_FIRMWARE("rtl_bt/rtl8821a_fw.bin"); 1362f96dbd32SMartin Blumenstingl MODULE_FIRMWARE("rtl_bt/rtl8821a_config.bin"); 1363f96dbd32SMartin Blumenstingl MODULE_FIRMWARE("rtl_bt/rtl8822b_fw.bin"); 1364f96dbd32SMartin Blumenstingl MODULE_FIRMWARE("rtl_bt/rtl8822b_config.bin"); 13650d484db6SMax Chou MODULE_FIRMWARE("rtl_bt/rtl8852au_fw.bin"); 13660d484db6SMax Chou MODULE_FIRMWARE("rtl_bt/rtl8852au_config.bin"); 1367fe4b71b5SVictor Hassan MODULE_FIRMWARE("rtl_bt/rtl8852bs_fw.bin"); 1368fe4b71b5SVictor Hassan MODULE_FIRMWARE("rtl_bt/rtl8852bs_config.bin"); 136918e8055cSMax Chou MODULE_FIRMWARE("rtl_bt/rtl8852bu_fw.bin"); 137018e8055cSMax Chou MODULE_FIRMWARE("rtl_bt/rtl8852bu_config.bin"); 13718b1d66b5SMax Chou MODULE_FIRMWARE("rtl_bt/rtl8852cu_fw.bin"); 13728b1d66b5SMax Chou MODULE_FIRMWARE("rtl_bt/rtl8852cu_config.bin"); 1373