* [RFC][PATCH 1/2] Implement broadcom patchram firmware loader
2012-08-15 1:15 [RFC][PATCH 0/2] broadcom patchram firmware loader Jesse Sung
@ 2012-08-15 1:15 ` Jesse Sung
2012-08-15 1:16 ` [RFC][PATCH 2/2] Cache firmware images for later use Jesse Sung
1 sibling, 0 replies; 6+ messages in thread
From: Jesse Sung @ 2012-08-15 1:15 UTC (permalink / raw)
To: linux-bluetooth
From: Wen-chien Jesse Sung <jesse.sung@canonical.com>
Signed-off-by: Wen-chien Jesse Sung <jesse.sung@canonical.com>
---
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
^ permalink raw reply related [flat|nested] 6+ messages in thread* [RFC][PATCH 2/2] Cache firmware images for later use
2012-08-15 1:15 [RFC][PATCH 0/2] broadcom patchram firmware loader Jesse Sung
2012-08-15 1:15 ` [RFC][PATCH 1/2] Implement " Jesse Sung
@ 2012-08-15 1:16 ` Jesse Sung
1 sibling, 0 replies; 6+ messages in thread
From: Jesse Sung @ 2012-08-15 1:16 UTC (permalink / raw)
To: linux-bluetooth
From: Wen-chien Jesse Sung <jesse.sung@canonical.com>
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 <jesse.sung@canonical.com>
---
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
^ permalink raw reply related [flat|nested] 6+ messages in thread