1567 lines · 39.6 KB · C
RawDownload
// 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.