Skip to content

Commit

Permalink
Bluetooth: btrtl: Add RTL8822BE Bluetooth device
Browse files Browse the repository at this point in the history
The RTL8822BE is a new Realtek wifi and BT device. Support for the BT
part is hereby added.

As this device is similar to most of the other Realtek BT devices, the
changes are minimal. The main difference is that the 8822BE needs a
configuration file for enabling and disabling features. Thus code is
added to select and load this configuration file. Although not needed
at the moment, hooks are added for the other devices that might need
such configuration files.

One additional change is to the routine that tests that the project
ID contained in the firmware matches the hardware. As the project IDs
are not sequential, continuing to use the position in the array as the
expected value of the ID would require adding extra unused entries in
the table, and any subsequant rearrangment of the array would break the
code. To fix these problems, the array elements now contain both the
hardware ID and the expected value for the project ID.

Signed-off-by: 陆朱伟 <alex_lu@realsil.com.cn>
Signed-off-by: Larry Finger <Larry.Finger@lwfinger.net>
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
  • Loading branch information
Larry Finger authored and Marcel Holtmann committed Sep 19, 2016
1 parent 83ebb9e commit 1110a2d
Showing 1 changed file with 95 additions and 12 deletions.
107 changes: 95 additions & 12 deletions drivers/bluetooth/btrtl.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
#define RTL_ROM_LMP_8723B 0x8723
#define RTL_ROM_LMP_8821A 0x8821
#define RTL_ROM_LMP_8761A 0x8761
#define RTL_ROM_LMP_8822B 0x8822

static int rtl_read_rom_version(struct hci_dev *hdev, u8 *version)
{
Expand Down Expand Up @@ -78,11 +79,15 @@ static int rtl8723b_parse_firmware(struct hci_dev *hdev, u16 lmp_subver,
const unsigned char *patch_length_base, *patch_offset_base;
u32 patch_offset = 0;
u16 patch_length, num_patches;
const u16 project_id_to_lmp_subver[] = {
RTL_ROM_LMP_8723A,
RTL_ROM_LMP_8723B,
RTL_ROM_LMP_8821A,
RTL_ROM_LMP_8761A
static const struct {
__u16 lmp_subver;
__u8 id;
} project_id_to_lmp_subver[] = {
{ RTL_ROM_LMP_8723A, 0 },
{ RTL_ROM_LMP_8723B, 1 },
{ RTL_ROM_LMP_8821A, 2 },
{ RTL_ROM_LMP_8761A, 3 },
{ RTL_ROM_LMP_8822B, 8 },
};

ret = rtl_read_rom_version(hdev, &rom_version);
Expand Down Expand Up @@ -134,14 +139,20 @@ static int rtl8723b_parse_firmware(struct hci_dev *hdev, u16 lmp_subver,
return -EINVAL;
}

if (project_id >= ARRAY_SIZE(project_id_to_lmp_subver)) {
/* Find project_id in table */
for (i = 0; i < ARRAY_SIZE(project_id_to_lmp_subver); i++) {
if (project_id == project_id_to_lmp_subver[i].id)
break;
}

if (i >= ARRAY_SIZE(project_id_to_lmp_subver)) {
BT_ERR("%s: unknown project id %d", hdev->name, project_id);
return -EINVAL;
}

if (lmp_subver != project_id_to_lmp_subver[project_id]) {
if (lmp_subver != project_id_to_lmp_subver[i].lmp_subver) {
BT_ERR("%s: firmware is for %x but this is a %x", hdev->name,
project_id_to_lmp_subver[project_id], lmp_subver);
project_id_to_lmp_subver[i].lmp_subver, lmp_subver);
return -EINVAL;
}

Expand Down Expand Up @@ -257,6 +268,26 @@ static int rtl_download_firmware(struct hci_dev *hdev,
return ret;
}

static int rtl_load_config(struct hci_dev *hdev, const char *name, u8 **buff)
{
const struct firmware *fw;
int ret;

BT_INFO("%s: rtl: loading %s", hdev->name, name);
ret = request_firmware(&fw, name, &hdev->dev);
if (ret < 0) {
BT_ERR("%s: Failed to load %s", hdev->name, name);
return ret;
}

ret = fw->size;
*buff = kmemdup(fw->data, ret, GFP_KERNEL);

release_firmware(fw);

return ret;
}

static int btrtl_setup_rtl8723a(struct hci_dev *hdev)
{
const struct firmware *fw;
Expand Down Expand Up @@ -296,25 +327,74 @@ static int btrtl_setup_rtl8723b(struct hci_dev *hdev, u16 lmp_subver,
unsigned char *fw_data = NULL;
const struct firmware *fw;
int ret;
int cfg_sz;
u8 *cfg_buff = NULL;
u8 *tbuff;
char *cfg_name = NULL;

switch (lmp_subver) {
case RTL_ROM_LMP_8723B:
cfg_name = "rtl_bt/rtl8723b_config.bin";
break;
case RTL_ROM_LMP_8821A:
cfg_name = "rtl_bt/rtl8821a_config.bin";
break;
case RTL_ROM_LMP_8761A:
cfg_name = "rtl_bt/rtl8761a_config.bin";
break;
case RTL_ROM_LMP_8822B:
cfg_name = "rtl_bt/rtl8822b_config.bin";
break;
default:
BT_ERR("%s: rtl: no config according to lmp_subver %04x",
hdev->name, lmp_subver);
break;
}

if (cfg_name) {
cfg_sz = rtl_load_config(hdev, cfg_name, &cfg_buff);
if (cfg_sz < 0)
cfg_sz = 0;
} else
cfg_sz = 0;

BT_INFO("%s: rtl: loading %s", hdev->name, fw_name);
ret = request_firmware(&fw, fw_name, &hdev->dev);
if (ret < 0) {
BT_ERR("%s: Failed to load %s", hdev->name, fw_name);
return ret;
goto err_req_fw;
}

ret = rtl8723b_parse_firmware(hdev, lmp_subver, fw, &fw_data);
if (ret < 0)
goto out;

if (cfg_sz) {
tbuff = kzalloc(ret + cfg_sz, GFP_KERNEL);
if (!tbuff) {
ret = -ENOMEM;
goto out;
}

memcpy(tbuff, fw_data, ret);
kfree(fw_data);

memcpy(tbuff + ret, cfg_buff, cfg_sz);
ret += cfg_sz;

fw_data = tbuff;
}

BT_INFO("cfg_sz %d, total size %d", cfg_sz, ret);

ret = rtl_download_firmware(hdev, fw_data, ret);
kfree(fw_data);
if (ret < 0)
goto out;

out:
release_firmware(fw);
kfree(fw_data);
err_req_fw:
if (cfg_sz)
kfree(cfg_buff);
return ret;
}

Expand Down Expand Up @@ -377,6 +457,9 @@ int btrtl_setup_realtek(struct hci_dev *hdev)
case RTL_ROM_LMP_8761A:
return btrtl_setup_rtl8723b(hdev, lmp_subver,
"rtl_bt/rtl8761a_fw.bin");
case RTL_ROM_LMP_8822B:
return btrtl_setup_rtl8723b(hdev, lmp_subver,
"rtl_bt/rtl8822b_fw.bin");
default:
BT_INFO("rtl: assuming no firmware upload needed.");
return 0;
Expand Down

0 comments on commit 1110a2d

Please sign in to comment.