// 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, 0x0D, 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,
};
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" },
/* 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 = "rtl8
|