2012-08-15 01:15:58

by Jesse Sung

[permalink] [raw]
Subject: [RFC][PATCH 0/2] broadcom patchram firmware loader

From: Wen-chien Jesse Sung <[email protected]>

There is an user space firmware loading tool which can be found at
http://marc.info/?l=linux-bluetooth&m=132039175324993&w=2

This tool requires hci device to do the firmware loading, but this
may cause some race condition between patchram tool and bluetoothd
or something that also works on hci interface.

Also it needs some hooks to make firmware loads after bootup, s3,
s4, rfkill, and device hotplug events. Implement this loader in kernel
module would make things more easier.

Wen-chien Jesse Sung (2):
Implement broadcom patchram firmware loader
Cache firmware images for later use

drivers/bluetooth/btusb.c | 154 ++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 147 insertions(+), 7 deletions(-)

--
1.7.9.5



2012-08-15 01:16:00

by Jesse Sung

[permalink] [raw]
Subject: [RFC][PATCH 2/2] Cache firmware images for later use

From: Wen-chien Jesse Sung <[email protected]>

Since request_firmware() may fail when resume from suspend, store
used firmware image in ram to make sure that we can have what we need
on resume.

Signed-off-by: Wen-chien Jesse Sung <[email protected]>
---
drivers/bluetooth/btusb.c | 77 +++++++++++++++++++++++++++++++++++----------
1 file changed, 60 insertions(+), 17 deletions(-)

diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
index b60a2ae..6559c1b 100644
--- a/drivers/bluetooth/btusb.c
+++ b/drivers/bluetooth/btusb.c
@@ -218,18 +218,34 @@ MODULE_FIRMWARE(FW_0A5C_21F3);
MODULE_FIRMWARE(FW_0A5C_21F4);
MODULE_FIRMWARE(FW_413C_8197);

+struct firmware_cache {
+ const char *filename;
+ u8* data;
+ size_t size;
+};
+
+static struct firmware_cache firmware[] = {
+ { .filename = FW_0A5C_21D3, },
+ { .filename = FW_0A5C_21D7, },
+ { .filename = FW_413C_8197, },
+ { .filename = FW_0489_E031, },
+ { .filename = FW_0A5C_21E6, },
+ { .filename = FW_0A5C_21F3, },
+ { .filename = FW_0A5C_21F4, },
+};
+
static struct usb_device_id patchram_table[] = {
/* Dell DW1704 */
- { USB_DEVICE(0x0a5c, 0x21d3), .driver_info = (kernel_ulong_t) FW_0A5C_21D3 },
- { USB_DEVICE(0x0a5c, 0x21d7), .driver_info = (kernel_ulong_t) FW_0A5C_21D7 },
+ { USB_DEVICE(0x0a5c, 0x21d3), .driver_info = (kernel_ulong_t) &firmware[0] },
+ { USB_DEVICE(0x0a5c, 0x21d7), .driver_info = (kernel_ulong_t) &firmware[1] },
/* Dell DW380 */
- { USB_DEVICE(0x413c, 0x8197), .driver_info = (kernel_ulong_t) FW_413C_8197 },
+ { USB_DEVICE(0x413c, 0x8197), .driver_info = (kernel_ulong_t) &firmware[2] },
/* FoxConn Hon Hai */
- { USB_DEVICE(0x0489, 0xe031), .driver_info = (kernel_ulong_t) FW_0489_E031 },
+ { USB_DEVICE(0x0489, 0xe031), .driver_info = (kernel_ulong_t) &firmware[3] },
/* Lenovo */
- { USB_DEVICE(0x0a5c, 0x21e6), .driver_info = (kernel_ulong_t) FW_0A5C_21E6 },
- { USB_DEVICE(0x0a5c, 0x21f3), .driver_info = (kernel_ulong_t) FW_0A5C_21F3 },
- { USB_DEVICE(0x0a5c, 0x21f4), .driver_info = (kernel_ulong_t) FW_0A5C_21F4 },
+ { USB_DEVICE(0x0a5c, 0x21e6), .driver_info = (kernel_ulong_t) &firmware[4] },
+ { USB_DEVICE(0x0a5c, 0x21f3), .driver_info = (kernel_ulong_t) &firmware[5] },
+ { USB_DEVICE(0x0a5c, 0x21f4), .driver_info = (kernel_ulong_t) &firmware[6] },
};

#define BTUSB_MAX_ISOC_FRAMES 10
@@ -953,14 +969,27 @@ static inline void load_patchram_fw(struct usb_device *udev, const struct usb_de
{
size_t pos = 0;
int err = 0;
- const struct firmware *fw;
+ struct firmware_cache *fwcache;

unsigned char reset_cmd[] = { 0x03, 0x0c, 0x00 };
unsigned char download_cmd[] = { 0x2e, 0xfc, 0x00 };

- if (request_firmware(&fw, (const char *) id->driver_info, &udev->dev) < 0) {
- BT_INFO("can't load firmware, may not work correctly");
- return;
+ fwcache = (struct firmware_cache *)id->driver_info;
+ if (!fwcache->data) {
+ const struct firmware *fw;
+ if (request_firmware(&fw, fwcache->filename, &udev->dev) < 0) {
+ BT_INFO("can't load firmware, may not work correctly");
+ return;
+ }
+ fwcache->data = kmalloc(fw->size, GFP_KERNEL);
+ if (!fwcache->data) {
+ BT_INFO("OOM");
+ release_firmware(fw);
+ return;
+ }
+ fwcache->size = fw->size;
+ memcpy(fwcache->data, fw->data, fwcache->size);
+ release_firmware(fw);
}

if (usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0, USB_TYPE_CLASS, 0, 0,
@@ -977,12 +1006,12 @@ static inline void load_patchram_fw(struct usb_device *udev, const struct usb_de
}
msleep(300);

- while (pos < fw->size) {
+ while (pos < fwcache->size) {
size_t len;
- len = fw->data[pos + 2] + 3;
- if ((pos + len > fw->size) ||
+ len = fwcache->data[pos + 2] + 3;
+ if ((pos + len > fwcache->size) ||
(usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0,
- USB_TYPE_CLASS, 0, 0, (void *)fw->data + pos, len,
+ USB_TYPE_CLASS, 0, 0, (void *)(fwcache->data + pos), len,
PATCHRAM_TIMEOUT) < 0)) {
err = -1;
goto out;
@@ -995,7 +1024,6 @@ static inline void load_patchram_fw(struct usb_device *udev, const struct usb_de
out:
if (err)
BT_INFO("fail to load firmware, may not work correctly");
- release_firmware(fw);
}

static int btusb_probe(struct usb_interface *intf,
@@ -1326,7 +1354,22 @@ static struct usb_driver btusb_driver = {
.disable_hub_initiated_lpm = 1,
};

-module_usb_driver(btusb_driver);
+static int __init btusb_init(void)
+{
+ return usb_register(&btusb_driver);
+}
+
+static void __exit btusb_exit(void)
+{
+ int i;
+ for (i = 0; i < sizeof(firmware) / sizeof(firmware[0]); i++)
+ if (firmware[i].data)
+ kfree(firmware[i].data);
+ usb_deregister(&btusb_driver);
+}
+
+module_init(btusb_init);
+module_exit(btusb_exit);

module_param(ignore_dga, bool, 0644);
MODULE_PARM_DESC(ignore_dga, "Ignore devices with id 08fd:0001");
--
1.7.9.5


2012-08-15 01:15:59

by Jesse Sung

[permalink] [raw]
Subject: [RFC][PATCH 1/2] Implement broadcom patchram firmware loader

From: Wen-chien Jesse Sung <[email protected]>


Signed-off-by: Wen-chien Jesse Sung <[email protected]>
---
drivers/bluetooth/btusb.c | 109 ++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 103 insertions(+), 6 deletions(-)

diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
index cef3bac..b60a2ae 100644
--- a/drivers/bluetooth/btusb.c
+++ b/drivers/bluetooth/btusb.c
@@ -23,6 +23,8 @@

#include <linux/module.h>
#include <linux/usb.h>
+#include <linux/delay.h>
+#include <linux/firmware.h>

#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
@@ -47,6 +49,7 @@ static struct usb_driver btusb_driver;
#define BTUSB_BROKEN_ISOC 0x20
#define BTUSB_WRONG_SCO_MTU 0x40
#define BTUSB_ATH3012 0x80
+#define BTUSB_BCM_PATCHRAM 0x100

static struct usb_device_id btusb_table[] = {
/* Generic Bluetooth USB device */
@@ -93,13 +96,16 @@ static struct usb_device_id btusb_table[] = {
{ USB_DEVICE(0x0c10, 0x0000) },

/* Broadcom BCM20702A0 */
+ { USB_DEVICE(0x0489, 0xe031), .driver_info = BTUSB_BCM_PATCHRAM },
{ USB_DEVICE(0x0489, 0xe042) },
+ { USB_DEVICE(0x0a5c, 0x21d3), .driver_info = BTUSB_BCM_PATCHRAM },
+ { USB_DEVICE(0x0a5c, 0x21d7), .driver_info = BTUSB_BCM_PATCHRAM },
{ USB_DEVICE(0x0a5c, 0x21e3) },
- { USB_DEVICE(0x0a5c, 0x21e6) },
+ { USB_DEVICE(0x0a5c, 0x21e6), .driver_info = BTUSB_BCM_PATCHRAM },
{ USB_DEVICE(0x0a5c, 0x21e8) },
- { USB_DEVICE(0x0a5c, 0x21f3) },
- { USB_DEVICE(0x0a5c, 0x21f4) },
- { USB_DEVICE(0x413c, 0x8197) },
+ { USB_DEVICE(0x0a5c, 0x21f3), .driver_info = BTUSB_BCM_PATCHRAM },
+ { USB_DEVICE(0x0a5c, 0x21f4), .driver_info = BTUSB_BCM_PATCHRAM },
+ { USB_DEVICE(0x413c, 0x8197), .driver_info = BTUSB_BCM_PATCHRAM },

/* Foxconn - Hon Hai */
{ USB_DEVICE(0x0489, 0xe033) },
@@ -195,6 +201,37 @@ static struct usb_device_id blacklist_table[] = {
{ } /* Terminating entry */
};

+#define PATCHRAM_TIMEOUT 1000
+#define FW_0489_E031 "fw-0489_e031.hcd"
+#define FW_0A5C_21D3 "fw-0a5c_21d3.hcd"
+#define FW_0A5C_21D7 "fw-0a5c_21d7.hcd"
+#define FW_0A5C_21E6 "fw-0a5c_21e6.hcd"
+#define FW_0A5C_21F3 "fw-0a5c_21f3.hcd"
+#define FW_0A5C_21F4 "fw-0a5c_21f4.hcd"
+#define FW_413C_8197 "fw-413c_8197.hcd"
+
+MODULE_FIRMWARE(FW_0489_E031);
+MODULE_FIRMWARE(FW_0A5C_21D3);
+MODULE_FIRMWARE(FW_0A5C_21D7);
+MODULE_FIRMWARE(FW_0A5C_21E6);
+MODULE_FIRMWARE(FW_0A5C_21F3);
+MODULE_FIRMWARE(FW_0A5C_21F4);
+MODULE_FIRMWARE(FW_413C_8197);
+
+static struct usb_device_id patchram_table[] = {
+ /* Dell DW1704 */
+ { USB_DEVICE(0x0a5c, 0x21d3), .driver_info = (kernel_ulong_t) FW_0A5C_21D3 },
+ { USB_DEVICE(0x0a5c, 0x21d7), .driver_info = (kernel_ulong_t) FW_0A5C_21D7 },
+ /* Dell DW380 */
+ { USB_DEVICE(0x413c, 0x8197), .driver_info = (kernel_ulong_t) FW_413C_8197 },
+ /* FoxConn Hon Hai */
+ { USB_DEVICE(0x0489, 0xe031), .driver_info = (kernel_ulong_t) FW_0489_E031 },
+ /* Lenovo */
+ { USB_DEVICE(0x0a5c, 0x21e6), .driver_info = (kernel_ulong_t) FW_0A5C_21E6 },
+ { USB_DEVICE(0x0a5c, 0x21f3), .driver_info = (kernel_ulong_t) FW_0A5C_21F3 },
+ { USB_DEVICE(0x0a5c, 0x21f4), .driver_info = (kernel_ulong_t) FW_0A5C_21F4 },
+};
+
#define BTUSB_MAX_ISOC_FRAMES 10

#define BTUSB_INTR_RUNNING 0
@@ -912,6 +949,55 @@ static void btusb_waker(struct work_struct *work)
usb_autopm_put_interface(data->intf);
}

+static inline void load_patchram_fw(struct usb_device *udev, const struct usb_device_id *id)
+{
+ size_t pos = 0;
+ int err = 0;
+ const struct firmware *fw;
+
+ unsigned char reset_cmd[] = { 0x03, 0x0c, 0x00 };
+ unsigned char download_cmd[] = { 0x2e, 0xfc, 0x00 };
+
+ if (request_firmware(&fw, (const char *) id->driver_info, &udev->dev) < 0) {
+ BT_INFO("can't load firmware, may not work correctly");
+ return;
+ }
+
+ if (usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0, USB_TYPE_CLASS, 0, 0,
+ reset_cmd, sizeof(reset_cmd), PATCHRAM_TIMEOUT) < 0) {
+ err = -1;
+ goto out;
+ }
+ msleep(300);
+
+ if (usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0, USB_TYPE_CLASS, 0, 0,
+ download_cmd, sizeof(download_cmd), PATCHRAM_TIMEOUT) < 0) {
+ err = -1;
+ goto out;
+ }
+ msleep(300);
+
+ while (pos < fw->size) {
+ size_t len;
+ len = fw->data[pos + 2] + 3;
+ if ((pos + len > fw->size) ||
+ (usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0,
+ USB_TYPE_CLASS, 0, 0, (void *)fw->data + pos, len,
+ PATCHRAM_TIMEOUT) < 0)) {
+ err = -1;
+ goto out;
+ }
+ pos += len;
+ }
+
+ err = (usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0, USB_TYPE_CLASS, 0, 0,
+ reset_cmd, sizeof(reset_cmd), PATCHRAM_TIMEOUT) < 0);
+out:
+ if (err)
+ BT_INFO("fail to load firmware, may not work correctly");
+ release_firmware(fw);
+}
+
static int btusb_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
@@ -1076,15 +1162,26 @@ static int btusb_probe(struct usb_interface *intf,
}
}

+ usb_set_intfdata(intf, data);
+
+ if (id->driver_info & BTUSB_BCM_PATCHRAM) {
+ const struct usb_device_id *match;
+ match = usb_match_id(intf, patchram_table);
+ if (match) {
+ btusb_open(hdev);
+ load_patchram_fw(interface_to_usbdev(intf), match);
+ btusb_close(hdev);
+ }
+ }
+
err = hci_register_dev(hdev);
if (err < 0) {
hci_free_dev(hdev);
+ usb_set_intfdata(intf, NULL);
kfree(data);
return err;
}

- usb_set_intfdata(intf, data);
-
return 0;
}

--
1.7.9.5


2012-08-15 01:14:10

by Jesse Sung

[permalink] [raw]
Subject: Re: [RFC][PATCH 0/2] broadcom patchram firmware loader

Hi Joao,

Thanks for your reply. I'll send these patches again and make them inline.

Regards,
Jesse

2012/8/15 Joao Paulo Rechi Vita <[email protected]>:
> Hello Jesse,
>
> On Tue, Aug 14, 2012 at 11:24 AM, Jesse Sung <[email protected]> wrote:
>>
>> There is a user space firmware loading tool which can be found at
>> http://article.gmane.org/gmane.linux.bluez.kernel/17932
>>
>> This tool requires hci device to do the firmware loading, but this
>> may cause some race condition between patchram tool and bluetoothd
>> or something that also works on hci interface.
>>
>> Also it needs some hooks to make firmware loads after bootup, s3,
>> s4, rfkill, and device hotplug events. Implement this loader in kernel
>> module would make things more easier.
>>
>>
>> Wen-chien Jesse Sung (2):
>> Implement broadcom patchram firmware loader
>> Cache firmware images for later use
>>
>
> Can you please re-send the patches inline? This way it's easier for
> people to comment on the mailing list and there is increased chance it
> gets merged sooner. 'git send-email' is the best way to do that.
>
> Thanks.
>
> --
> João Paulo Rechi Vita
> Openbossa Labs - INdT

2012-08-14 17:14:57

by Joao Paulo Rechi Vita

[permalink] [raw]
Subject: Re: [RFC][PATCH 0/2] broadcom patchram firmware loader

Hello Jesse,

On Tue, Aug 14, 2012 at 11:24 AM, Jesse Sung <[email protected]> wrote:
>
> There is a user space firmware loading tool which can be found at
> http://article.gmane.org/gmane.linux.bluez.kernel/17932
>
> This tool requires hci device to do the firmware loading, but this
> may cause some race condition between patchram tool and bluetoothd
> or something that also works on hci interface.
>
> Also it needs some hooks to make firmware loads after bootup, s3,
> s4, rfkill, and device hotplug events. Implement this loader in kernel
> module would make things more easier.
>
>
> Wen-chien Jesse Sung (2):
> Implement broadcom patchram firmware loader
> Cache firmware images for later use
>

Can you please re-send the patches inline? This way it's easier for
people to comment on the mailing list and there is increased chance it
gets merged sooner. 'git send-email' is the best way to do that.

Thanks.

--
João Paulo Rechi Vita
Openbossa Labs - INdT