Commit 18835dfa authored by Marcel Holtmann's avatar Marcel Holtmann Committed by Johan Hedberg

Bluetooth: btusb: Use matching names for Broadcom firmware files

The Broadcom firmware files are named with firmware version information
encoded into lmp_subver field. So BCM20702B0_002.001.014.0527.0607.hex
would be represented by 0x410e. To allow for an easier decoding of the
actual firmware names, provide an internal table that does the mapping
and request the firmware file by name.

Bluetooth: hci0: BCM20702B0 (002.001.014) build 0607

Now the complicated encoding of lmp_subver will be decoded and turned
into the name and firmware version information from the firmware files.

The previous attempt of using udev->product failed badly since it never
contains any matching entry to the actual hardware or firmware files
distributed by Broadcom in their Windows drivers. It is even worse since
it can change depending on if the internal bootstrapping happened before
the USB bus enumeration or after. This caused many race conditions.
Signed-off-by: default avatarMarcel Holtmann <marcel@holtmann.org>
Signed-off-by: default avatarJohan Hedberg <johan.hedberg@intel.com>
parent 87e2a020
...@@ -2366,6 +2366,23 @@ static int btusb_set_bdaddr_marvell(struct hci_dev *hdev, ...@@ -2366,6 +2366,23 @@ static int btusb_set_bdaddr_marvell(struct hci_dev *hdev,
return 0; return 0;
} }
static const struct {
u16 subver;
const char *name;
} bcm_subver_table[] = {
{ 0x210b, "BCM43142A0" }, /* 001.001.011 */
{ 0x2112, "BCM4314A0" }, /* 001.001.018 */
{ 0x2118, "BCM20702A0" }, /* 001.001.024 */
{ 0x2126, "BCM4335A0" }, /* 001.001.038 */
{ 0x220e, "BCM20702A1" }, /* 001.002.014 */
{ 0x230f, "BCM4354A2" }, /* 001.003.015 */
{ 0x4106, "BCM4335B0" }, /* 002.001.006 */
{ 0x410e, "BCM20702B0" }, /* 002.001.014 */
{ 0x6109, "BCM4335C0" }, /* 003.001.009 */
{ 0x610c, "BCM4354" }, /* 003.001.012 */
{ }
};
#define BDADDR_BCM20702A0 (&(bdaddr_t) {{0x00, 0xa0, 0x02, 0x70, 0x20, 0x00}}) #define BDADDR_BCM20702A0 (&(bdaddr_t) {{0x00, 0xa0, 0x02, 0x70, 0x20, 0x00}})
static int btusb_setup_bcm_patchram(struct hci_dev *hdev) static int btusb_setup_bcm_patchram(struct hci_dev *hdev)
...@@ -2378,29 +2395,20 @@ static int btusb_setup_bcm_patchram(struct hci_dev *hdev) ...@@ -2378,29 +2395,20 @@ static int btusb_setup_bcm_patchram(struct hci_dev *hdev)
size_t fw_size; size_t fw_size;
const struct hci_command_hdr *cmd; const struct hci_command_hdr *cmd;
const u8 *cmd_param; const u8 *cmd_param;
u16 opcode; u16 opcode, subver, rev;
const char *hw_name = NULL;
struct sk_buff *skb; struct sk_buff *skb;
struct hci_rp_read_local_version *ver; struct hci_rp_read_local_version *ver;
struct hci_rp_read_bd_addr *bda; struct hci_rp_read_bd_addr *bda;
long ret; long ret;
int i;
snprintf(fw_name, sizeof(fw_name), "brcm/%s-%04x-%04x.hcd",
udev->product ? udev->product : "BCM",
le16_to_cpu(udev->descriptor.idVendor),
le16_to_cpu(udev->descriptor.idProduct));
ret = request_firmware(&fw, fw_name, &hdev->dev);
if (ret < 0) {
BT_INFO("%s: BCM: patch %s not found", hdev->name, fw_name);
return 0;
}
/* Reset */ /* Reset */
skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT); skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) { if (IS_ERR(skb)) {
ret = PTR_ERR(skb); ret = PTR_ERR(skb);
BT_ERR("%s: HCI_OP_RESET failed (%ld)", hdev->name, ret); BT_ERR("%s: HCI_OP_RESET failed (%ld)", hdev->name, ret);
goto done; return ret;
} }
kfree_skb(skb); kfree_skb(skb);
...@@ -2411,23 +2419,43 @@ static int btusb_setup_bcm_patchram(struct hci_dev *hdev) ...@@ -2411,23 +2419,43 @@ static int btusb_setup_bcm_patchram(struct hci_dev *hdev)
ret = PTR_ERR(skb); ret = PTR_ERR(skb);
BT_ERR("%s: HCI_OP_READ_LOCAL_VERSION failed (%ld)", BT_ERR("%s: HCI_OP_READ_LOCAL_VERSION failed (%ld)",
hdev->name, ret); hdev->name, ret);
goto done; return ret;
} }
if (skb->len != sizeof(*ver)) { if (skb->len != sizeof(*ver)) {
BT_ERR("%s: HCI_OP_READ_LOCAL_VERSION event length mismatch", BT_ERR("%s: HCI_OP_READ_LOCAL_VERSION event length mismatch",
hdev->name); hdev->name);
kfree_skb(skb); kfree_skb(skb);
ret = -EIO; return -EIO;
goto done;
} }
ver = (struct hci_rp_read_local_version *)skb->data; ver = (struct hci_rp_read_local_version *)skb->data;
BT_INFO("%s: BCM: patching hci_ver=%02x hci_rev=%04x lmp_ver=%02x " rev = le16_to_cpu(ver->hci_rev);
"lmp_subver=%04x", hdev->name, ver->hci_ver, ver->hci_rev, subver = le16_to_cpu(ver->lmp_subver);
ver->lmp_ver, ver->lmp_subver);
kfree_skb(skb); kfree_skb(skb);
for (i = 0; bcm_subver_table[i].name; i++) {
if (subver == bcm_subver_table[i].subver) {
hw_name = bcm_subver_table[i].name;
break;
}
}
BT_INFO("%s: %s (%3.3u.%3.3u.%3.3u) build %4.4u", hdev->name,
hw_name ? : "BCM", (subver & 0x7000) >> 13,
(subver & 0x1f00) >> 8, (subver & 0x00ff), rev & 0x0fff);
snprintf(fw_name, sizeof(fw_name), "brcm/%s-%4.4x-%4.4x.hcd",
hw_name ? : "BCM",
le16_to_cpu(udev->descriptor.idVendor),
le16_to_cpu(udev->descriptor.idProduct));
ret = request_firmware(&fw, fw_name, &hdev->dev);
if (ret < 0) {
BT_INFO("%s: BCM: patch %s not found", hdev->name, fw_name);
return 0;
}
/* Start Download */ /* Start Download */
skb = __hci_cmd_sync(hdev, 0xfc2e, 0, NULL, HCI_INIT_TIMEOUT); skb = __hci_cmd_sync(hdev, 0xfc2e, 0, NULL, HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) { if (IS_ERR(skb)) {
...@@ -2505,11 +2533,14 @@ static int btusb_setup_bcm_patchram(struct hci_dev *hdev) ...@@ -2505,11 +2533,14 @@ static int btusb_setup_bcm_patchram(struct hci_dev *hdev)
} }
ver = (struct hci_rp_read_local_version *)skb->data; ver = (struct hci_rp_read_local_version *)skb->data;
BT_INFO("%s: BCM: firmware hci_ver=%02x hci_rev=%04x lmp_ver=%02x " rev = le16_to_cpu(ver->hci_rev);
"lmp_subver=%04x", hdev->name, ver->hci_ver, ver->hci_rev, subver = le16_to_cpu(ver->lmp_subver);
ver->lmp_ver, ver->lmp_subver);
kfree_skb(skb); kfree_skb(skb);
BT_INFO("%s: %s (%3.3u.%3.3u.%3.3u) build %4.4u", hdev->name,
hw_name ? : "BCM", (subver & 0x7000) >> 13,
(subver & 0x1f00) >> 8, (subver & 0x00ff), rev & 0x0fff);
/* Read BD Address */ /* Read BD Address */
skb = __hci_cmd_sync(hdev, HCI_OP_READ_BD_ADDR, 0, NULL, skb = __hci_cmd_sync(hdev, HCI_OP_READ_BD_ADDR, 0, NULL,
HCI_INIT_TIMEOUT); HCI_INIT_TIMEOUT);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment