2014-05-08 22:50:01

by Petri Gynther

[permalink] [raw]
Subject: [PATCH v2] Bluetooth: btusb: Add Broadcom patch RAM support

After hardware reset, some BCM Bluetooth adapters obtain their initial firmware
from OTPROM chip. Once this initial firmware is running, the firmware can be
further upgraded over HCI interface with .hcd files provided by Broadcom. This
is also known as "patch RAM" support. This change implements that.

If the .hcd file is not found in /lib/firmware, BCM Bluetooth adapter continues
to operate with the initial firmware. Sample kernel log:
hotplug: sys=firmware act=add fw=brcm/BCM20702A0-0a5c-22be.hcd dev=...
Bluetooth: hci0: BCM: patch brcm/BCM20702A0-0a5c-22be.hcd not found

If the .hcd file is found, btusb driver pushes it to the BCM Bluetooth adapter and
it starts using the new firmware. Sample kernel log:
hotplug: sys=firmware act=add fw=brcm/BCM20702A0-0a5c-22be.hcd dev=...
Bluetooth: hci0: BCM: patching hci_ver=06 hci_rev=1000 lmp_ver=06 lmp_subver=220e
Bluetooth: hci0: BCM: firmware hci_ver=06 hci_rev=1389 lmp_ver=06 lmp_subver=220e

Above, we can see that hci_rev goes from 1000 to 1389 as a result of the upgrade.

Signed-off-by: Petri Gynther <[email protected]>
---
drivers/bluetooth/btusb.c | 155 +++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 154 insertions(+), 1 deletion(-)

diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
index f338b0c..8f579cd 100644
--- a/drivers/bluetooth/btusb.c
+++ b/drivers/bluetooth/btusb.c
@@ -49,6 +49,7 @@ static struct usb_driver btusb_driver;
#define BTUSB_WRONG_SCO_MTU 0x40
#define BTUSB_ATH3012 0x80
#define BTUSB_INTEL 0x100
+#define BTUSB_BCM_PATCHRAM 0x200

static const struct usb_device_id btusb_table[] = {
/* Generic Bluetooth USB device */
@@ -111,7 +112,8 @@ static const struct usb_device_id btusb_table[] = {
{ USB_VENDOR_AND_INTERFACE_INFO(0x0489, 0xff, 0x01, 0x01) },

/* Broadcom devices with vendor specific id */
- { USB_VENDOR_AND_INTERFACE_INFO(0x0a5c, 0xff, 0x01, 0x01) },
+ { USB_VENDOR_AND_INTERFACE_INFO(0x0a5c, 0xff, 0x01, 0x01),
+ .driver_info = BTUSB_BCM_PATCHRAM },

/* Belkin F8065bf - Broadcom based */
{ USB_VENDOR_AND_INTERFACE_INFO(0x050d, 0xff, 0x01, 0x01) },
@@ -1380,6 +1382,154 @@ exit_mfg_deactivate:
return 0;
}

+static int btusb_setup_bcm_patchram(struct hci_dev *hdev)
+{
+ struct btusb_data *data = hci_get_drvdata(hdev);
+ struct usb_device *udev = data->udev;
+ char fw_name[64];
+ const struct firmware *fw;
+ const u8 *fw_ptr;
+ size_t fw_size;
+ const struct hci_command_hdr *cmd;
+ const u8 *cmd_param;
+ u16 opcode;
+ struct sk_buff *skb;
+ struct hci_rp_read_local_version *ver;
+ long ret;
+
+ 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 */
+ skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT);
+ if (IS_ERR(skb)) {
+ ret = PTR_ERR(skb);
+ BT_ERR("%s: HCI_OP_RESET failed (%ld)", hdev->name, ret);
+ goto done;
+ }
+ kfree_skb(skb);
+
+ /* Read Local Version Info */
+ skb = __hci_cmd_sync(hdev, HCI_OP_READ_LOCAL_VERSION, 0, NULL,
+ HCI_INIT_TIMEOUT);
+ if (IS_ERR(skb)) {
+ ret = PTR_ERR(skb);
+ BT_ERR("%s: HCI_OP_READ_LOCAL_VERSION failed (%ld)",
+ hdev->name, ret);
+ goto done;
+ }
+
+ if (skb->len != sizeof(*ver)) {
+ BT_ERR("%s: HCI_OP_READ_LOCAL_VERSION event length mismatch",
+ hdev->name);
+ kfree_skb(skb);
+ ret = -EIO;
+ goto done;
+ }
+
+ ver = (struct hci_rp_read_local_version *) skb->data;
+ BT_INFO("%s: BCM: patching hci_ver=%02x hci_rev=%04x lmp_ver=%02x "
+ "lmp_subver=%04x", hdev->name, ver->hci_ver, ver->hci_rev,
+ ver->lmp_ver, ver->lmp_subver);
+ kfree_skb(skb);
+
+ /* Start Download */
+ skb = __hci_cmd_sync(hdev, 0xfc2e, 0, NULL, HCI_INIT_TIMEOUT);
+ if (IS_ERR(skb)) {
+ ret = PTR_ERR(skb);
+ BT_ERR("%s: BCM: Download Minidrv command failed (%ld)",
+ hdev->name, ret);
+ goto reset_fw;
+ }
+ kfree_skb(skb);
+
+ /* 50 msec delay after Download Minidrv completes */
+ msleep(50);
+
+ fw_ptr = fw->data;
+ fw_size = fw->size;
+
+ while (fw_size >= sizeof(*cmd)) {
+ cmd = (struct hci_command_hdr *) fw_ptr;
+ fw_ptr += sizeof(*cmd);
+ fw_size -= sizeof(*cmd);
+
+ if (fw_size < cmd->plen) {
+ BT_ERR("%s: BCM: patch %s is corrupted",
+ hdev->name, fw_name);
+ ret = -EINVAL;
+ goto reset_fw;
+ }
+
+ cmd_param = fw_ptr;
+ fw_ptr += cmd->plen;
+ fw_size -= cmd->plen;
+
+ opcode = le16_to_cpu(cmd->opcode);
+
+ skb = __hci_cmd_sync(hdev, opcode, cmd->plen, cmd_param,
+ HCI_INIT_TIMEOUT);
+ if (IS_ERR(skb)) {
+ ret = PTR_ERR(skb);
+ BT_ERR("%s: BCM: patch command %04x failed (%ld)",
+ hdev->name, opcode, ret);
+ goto reset_fw;
+ }
+ kfree_skb(skb);
+ }
+
+ /* 250 msec delay after Launch Ram completes */
+ msleep(250);
+
+reset_fw:
+ /* Reset */
+ skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT);
+ if (IS_ERR(skb)) {
+ ret = PTR_ERR(skb);
+ BT_ERR("%s: HCI_OP_RESET failed (%ld)", hdev->name, ret);
+ goto done;
+ }
+ kfree_skb(skb);
+
+ /* Read Local Version Info */
+ skb = __hci_cmd_sync(hdev, HCI_OP_READ_LOCAL_VERSION, 0, NULL,
+ HCI_INIT_TIMEOUT);
+ if (IS_ERR(skb)) {
+ ret = PTR_ERR(skb);
+ BT_ERR("%s: HCI_OP_READ_LOCAL_VERSION failed (%ld)",
+ hdev->name, ret);
+ goto done;
+ }
+
+ if (skb->len != sizeof(*ver)) {
+ BT_ERR("%s: HCI_OP_READ_LOCAL_VERSION event length mismatch",
+ hdev->name);
+ kfree_skb(skb);
+ ret = -EIO;
+ goto done;
+ }
+
+ ver = (struct hci_rp_read_local_version *) skb->data;
+ BT_INFO("%s: BCM: firmware hci_ver=%02x hci_rev=%04x lmp_ver=%02x "
+ "lmp_subver=%04x", hdev->name, ver->hci_ver, ver->hci_rev,
+ ver->lmp_ver, ver->lmp_subver);
+ kfree_skb(skb);
+
+done:
+ release_firmware(fw);
+
+ return ret;
+}
+
static int btusb_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
@@ -1485,6 +1635,9 @@ static int btusb_probe(struct usb_interface *intf,
if (id->driver_info & BTUSB_BCM92035)
hdev->setup = btusb_setup_bcm92035;

+ if (id->driver_info & BTUSB_BCM_PATCHRAM)
+ hdev->setup = btusb_setup_bcm_patchram;
+
if (id->driver_info & BTUSB_INTEL) {
usb_enable_autosuspend(data->udev);
hdev->setup = btusb_setup_intel;
--
1.9.1.423.g4596e3a



2014-05-09 02:35:45

by Petri Gynther

[permalink] [raw]
Subject: Re: [PATCH v2] Bluetooth: btusb: Add Broadcom patch RAM support

Hi Marcel,

On Thu, May 8, 2014 at 4:17 PM, Marcel Holtmann <[email protected]> wrote=
:
> Hi Petri,
>
>> After hardware reset, some BCM Bluetooth adapters obtain their initial f=
irmware
>> from OTPROM chip. Once this initial firmware is running, the firmware ca=
n be
>> further upgraded over HCI interface with .hcd files provided by Broadcom=
. This
>> is also known as "patch RAM" support. This change implements that.
>>
>> If the .hcd file is not found in /lib/firmware, BCM Bluetooth adapter co=
ntinues
>> to operate with the initial firmware. Sample kernel log:
>> hotplug: sys=3Dfirmware act=3Dadd fw=3Dbrcm/BCM20702A0-0a5c-22be.hcd de=
v=3D...
>> Bluetooth: hci0: BCM: patch brcm/BCM20702A0-0a5c-22be.hcd not found
>>
>> If the .hcd file is found, btusb driver pushes it to the BCM Bluetooth a=
dapter and
>> it starts using the new firmware. Sample kernel log:
>> hotplug: sys=3Dfirmware act=3Dadd fw=3Dbrcm/BCM20702A0-0a5c-22be.hcd de=
v=3D...
>> Bluetooth: hci0: BCM: patching hci_ver=3D06 hci_rev=3D1000 lmp_ver=3D06=
lmp_subver=3D220e
>> Bluetooth: hci0: BCM: firmware hci_ver=3D06 hci_rev=3D1389 lmp_ver=3D06=
lmp_subver=3D220e
>>
>> Above, we can see that hci_rev goes from 1000 to 1389 as a result of the=
upgrade.
>
> so I tested this with an off the shelf USB dongle now and a firmware conv=
erted with the hex2hcd utility (which we most likely should just include in=
bluez.git).
>
> [193812.000971] Bluetooth: hci0: BCM: patching hci_ver=3D06 hci_rev=3D100=
0 lmp_ver=3D06 lmp_subver=3D220e
> [193812.738603] Bluetooth: hci0: BCM: firmware hci_ver=3D06 hci_rev=3D153=
a lmp_ver=3D06 lmp_subver=3D220e
>
> Of course I have no idea what kind of bugs this new firmware now fixes. I=
am also almost certain that just based on the lmp_subver and hci_rev we co=
uld pick a firmware to load. And it would be nice if Broadcom starts puttin=
g firmware files in linux-firmware.git like any other company.
>
> Anyhow, one minor thing that bugs me is this:
>
> [193452.790194] Bluetooth: hci0: BCM: patch brcm/BCM20702A0-0a5c-21e8.hcd=
not found
>
> While I want the firmware loading available for all Broadcom controllers,=
I do think we might have to mark some of them as firmware absolutely requi=
red since otherwise they will not work. And in that case we should print th=
e error. Otherwise we might just silently not print anything. Thoughts?
>

I understand. So, we really need two cases,
BTUSB_BCM_PATCHRAM_REQUIRED and BTUSB_BCM_PATCHRAM_OPTIONAL.

For BTUSB_BCM_PATCHRAM_REQUIRED, I would go with:
BT_ERR("%s: BCM: required patch %s not found, adapter setup failed",
hdev->name, fw_name);
return -ENOENT;

For BTUSB_BCM_PATCHRAM_OPTIONAL, I would still display informational messag=
e:
BT_INFO("%s: BCM: optional patch %s not found, operating with
default firmware", hdev->name, fw_name);
return 0;

Can we easily access id->driver_info flags in btusb_setup_bcm_patchram()?

Thanks for applying this patch.

-- Petri

>> Signed-off-by: Petri Gynther <[email protected]>
>> ---
>> drivers/bluetooth/btusb.c | 155 ++++++++++++++++++++++++++++++++++++++++=
+++++-
>> 1 file changed, 154 insertions(+), 1 deletion(-)
>
> Patch has been applied to bluetooth-next tree.
>
> Regards
>
> Marcel
>

2014-05-08 23:17:24

by Marcel Holtmann

[permalink] [raw]
Subject: Re: [PATCH v2] Bluetooth: btusb: Add Broadcom patch RAM support

Hi Petri,

> After hardware reset, some BCM Bluetooth adapters obtain their initial firmware
> from OTPROM chip. Once this initial firmware is running, the firmware can be
> further upgraded over HCI interface with .hcd files provided by Broadcom. This
> is also known as "patch RAM" support. This change implements that.
>
> If the .hcd file is not found in /lib/firmware, BCM Bluetooth adapter continues
> to operate with the initial firmware. Sample kernel log:
> hotplug: sys=firmware act=add fw=brcm/BCM20702A0-0a5c-22be.hcd dev=...
> Bluetooth: hci0: BCM: patch brcm/BCM20702A0-0a5c-22be.hcd not found
>
> If the .hcd file is found, btusb driver pushes it to the BCM Bluetooth adapter and
> it starts using the new firmware. Sample kernel log:
> hotplug: sys=firmware act=add fw=brcm/BCM20702A0-0a5c-22be.hcd dev=...
> Bluetooth: hci0: BCM: patching hci_ver=06 hci_rev=1000 lmp_ver=06 lmp_subver=220e
> Bluetooth: hci0: BCM: firmware hci_ver=06 hci_rev=1389 lmp_ver=06 lmp_subver=220e
>
> Above, we can see that hci_rev goes from 1000 to 1389 as a result of the upgrade.

so I tested this with an off the shelf USB dongle now and a firmware converted with the hex2hcd utility (which we most likely should just include in bluez.git).

[193812.000971] Bluetooth: hci0: BCM: patching hci_ver=06 hci_rev=1000 lmp_ver=06 lmp_subver=220e
[193812.738603] Bluetooth: hci0: BCM: firmware hci_ver=06 hci_rev=153a lmp_ver=06 lmp_subver=220e

Of course I have no idea what kind of bugs this new firmware now fixes. I am also almost certain that just based on the lmp_subver and hci_rev we could pick a firmware to load. And it would be nice if Broadcom starts putting firmware files in linux-firmware.git like any other company.

Anyhow, one minor thing that bugs me is this:

[193452.790194] Bluetooth: hci0: BCM: patch brcm/BCM20702A0-0a5c-21e8.hcd not found

While I want the firmware loading available for all Broadcom controllers, I do think we might have to mark some of them as firmware absolutely required since otherwise they will not work. And in that case we should print the error. Otherwise we might just silently not print anything. Thoughts?

> Signed-off-by: Petri Gynther <[email protected]>
> ---
> drivers/bluetooth/btusb.c | 155 +++++++++++++++++++++++++++++++++++++++++++++-
> 1 file changed, 154 insertions(+), 1 deletion(-)

Patch has been applied to bluetooth-next tree.

Regards

Marcel