diff mbox

[Raring] UBUNTU: SAUCE: Bluetooth: use hci_send_cmd instead of usb_control_msg

Message ID 1378316549-26472-1-git-send-email-jesse.sung@canonical.com
State New
Headers show

Commit Message

Wen-chien Jesse Sung Sept. 4, 2013, 5:42 p.m. UTC
From: Wen-chien Jesse Sung <jesse.sung@canonical.com>

BugLink: https://launchpad.net/bugs/1065400

Calling usb_control_msg directly to send firmware file does not work
with new platforms. Use hci_send_cmd instead.

Signed-off-by: Wen-chien Jesse Sung <jesse.sung@canonical.com>
---
 drivers/bluetooth/btusb.c | 58 +++++++++++++++++++++++------------------------
 net/bluetooth/hci_core.c  |  1 +
 2 files changed, 29 insertions(+), 30 deletions(-)

Comments

Tim Gardner Sept. 4, 2013, 6:36 p.m. UTC | #1
On 09/04/2013 10:42 AM, Jesse Sung wrote:
> From: Wen-chien Jesse Sung <jesse.sung@canonical.com>
> 
> BugLink: https://launchpad.net/bugs/1065400
> 
> Calling usb_control_msg directly to send firmware file does not work
> with new platforms. Use hci_send_cmd instead.
> 
> Signed-off-by: Wen-chien Jesse Sung <jesse.sung@canonical.com>
> ---
>  drivers/bluetooth/btusb.c | 58 +++++++++++++++++++++++------------------------
>  net/bluetooth/hci_core.c  |  1 +
>  2 files changed, 29 insertions(+), 30 deletions(-)
> 
> diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
> index 5be9002..3a82e09 100644
> --- a/drivers/bluetooth/btusb.c
> +++ b/drivers/bluetooth/btusb.c
> @@ -49,7 +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
> +#define BTUSB_BCM_PATCHRAM	0x800
>  

This seems gratuitous.

>  static struct usb_device_id btusb_table[] = {
>  	/* Generic Bluetooth USB device */
> @@ -226,14 +226,13 @@ static struct usb_device_id blacklist_table[] = {
>  #define BTUSB_ISOC_RUNNING	2
>  #define BTUSB_SUSPENDING	3
>  #define BTUSB_DID_ISO_RESUME	4
> -#define BTUSB_FIRMWARE_DONE	5
> +#define BTUSB_FIRMWARE_DONE	7
>  

Gratuitous ?

>  struct btusb_data {
>  	struct hci_dev       *hdev;
>  	struct usb_device    *udev;
>  	struct usb_interface *intf;
>  	struct usb_interface *isoc;
> -	const struct usb_device_id *id;
>  
>  	spinlock_t lock;
>  
> @@ -938,24 +937,20 @@ static void btusb_waker(struct work_struct *work)
>  	usb_autopm_put_interface(data->intf);
>  }
>  
> -#define PATCHRAM_TIMEOUT	1000
>  #define PATCHRAM_NAME_LEN	20
> +#define PATCHRAM_BUF_SIZE	(256 + 4)
>  
>  static void btusb_load_firmware(struct hci_dev *hdev)
>  {
>  	struct btusb_data *data = hci_get_drvdata(hdev);
>  	struct usb_device *udev = data->udev;
> -	const struct usb_device_id *id = data->id;
>  	size_t pos = 0;
>  	int err = 0;
>  	char filename[PATCHRAM_NAME_LEN];
>  	const struct firmware *fw;
> +	u8 *buf = NULL;
> +	u8 val = 0;
>  
> -	unsigned char reset_cmd[] = { 0x03, 0x0c, 0x00 };
> -	unsigned char download_cmd[] = { 0x2e, 0xfc, 0x00 };
> -
> -	if (!(id->driver_info & BTUSB_BCM_PATCHRAM))
> -		return;
>  	if (test_and_set_bit(BTUSB_FIRMWARE_DONE, &data->flags))
>  		return;
>  
> @@ -967,40 +962,43 @@ static void btusb_load_firmware(struct hci_dev *hdev)
>  		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;
> +	buf = kmalloc(PATCHRAM_BUF_SIZE, GFP_KERNEL);
> +	if (!buf)
>  		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;
> +	err= hci_send_cmd(hdev, 0x0c03, 1, &val);

formatting ^

> +	if (err)
>  		goto out;
> -	}
>  	msleep(300);
>  

The only error that hci_send_cmd() returns is if the skb allocation
failed. Is there any way to know that the command was actually received
by the hardware ?

> +	err = hci_send_cmd(hdev, 0xfc2e, 1, &val);
> +	if (err)
> +		goto out;
> +	msleep(1000);
> +
>  	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;
> +		if (pos + len > fw->size) {
> +			err = -EINVAL;
>  			goto out;
>  		}
> +		memcpy(buf, fw->data + pos, len);
> +		err = hci_send_cmd(hdev, le16_to_cpu(*(u16*)buf),
> +					*(buf + sizeof(u16)), buf + sizeof(u16) + 1);
> +		if (err)
> +			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);
> +	err= hci_send_cmd(hdev, 0x0c03, 1, &val);

formatting ^

>  out:
>  	if (err)
> -		BT_INFO("fail to load firmware, may not work correctly");
> +		BT_INFO("fail to load firmware");
>  	else
>  		BT_INFO("firmware loaded");
> +	if (buf)
> +		kfree(buf);
>  	release_firmware(fw);
>  }
>  
> @@ -1089,8 +1087,6 @@ static int btusb_probe(struct usb_interface *intf,
>  	init_usb_anchor(&data->isoc_anchor);
>  	init_usb_anchor(&data->deferred);
>  
> -	data->id = id;
> -
>  	hdev = hci_alloc_dev();
>  	if (!hdev)
>  		return -ENOMEM;
> @@ -1107,7 +1103,9 @@ static int btusb_probe(struct usb_interface *intf,
>  	hdev->flush    = btusb_flush;
>  	hdev->send     = btusb_send_frame;
>  	hdev->notify   = btusb_notify;
> -	hdev->load_firmware = btusb_load_firmware;
> +
> +	if (id->driver_info & BTUSB_BCM_PATCHRAM)
> +		hdev->load_firmware = btusb_load_firmware;
>  
>  	/* Interface numbers are hardcoded in the specification */
>  	data->isoc = usb_ifnum_to_if(data->udev, 1);
> diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
> index 843f46f..927e55e 100644
> --- a/net/bluetooth/hci_core.c
> +++ b/net/bluetooth/hci_core.c
> @@ -2193,6 +2193,7 @@ int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen, void *param)
>  
>  	return 0;
>  }
> +EXPORT_SYMBOL(hci_send_cmd);
>  
>  /* Get data from the previously sent command */
>  void *hci_sent_cmd_data(struct hci_dev *hdev, __u16 opcode)
>
Wen-chien Jesse Sung Sept. 5, 2013, 2:26 p.m. UTC | #2
2013/9/5 Tim Gardner <tim.gardner@canonical.com>:
> On 09/04/2013 10:42 AM, Jesse Sung wrote:
>> From: Wen-chien Jesse Sung <jesse.sung@canonical.com>
>>
>> BugLink: https://launchpad.net/bugs/1065400
>>
>> Calling usb_control_msg directly to send firmware file does not work
>> with new platforms. Use hci_send_cmd instead.
>>
>> Signed-off-by: Wen-chien Jesse Sung <jesse.sung@canonical.com>
>> ---
>>  drivers/bluetooth/btusb.c | 58 +++++++++++++++++++++++------------------------
>>  net/bluetooth/hci_core.c  |  1 +
>>  2 files changed, 29 insertions(+), 30 deletions(-)
>>
>> diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
>> index 5be9002..3a82e09 100644
>> --- a/drivers/bluetooth/btusb.c
>> +++ b/drivers/bluetooth/btusb.c
>> @@ -49,7 +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
>> +#define BTUSB_BCM_PATCHRAM   0x800
>>
>
> This seems gratuitous.

Since new flags are introduced in upstream, if we decide to merge some
changes from them later, this may
reduce the chance of conflicts. If this is unwanted, please let me
know, and  I'll remove it in the second version.

>>  static struct usb_device_id btusb_table[] = {
>>       /* Generic Bluetooth USB device */
>> @@ -226,14 +226,13 @@ static struct usb_device_id blacklist_table[] = {
>>  #define BTUSB_ISOC_RUNNING   2
>>  #define BTUSB_SUSPENDING     3
>>  #define BTUSB_DID_ISO_RESUME 4
>> -#define BTUSB_FIRMWARE_DONE  5
>> +#define BTUSB_FIRMWARE_DONE  7
>>
>
> Gratuitous ?

Same as above.

>>  struct btusb_data {
>>       struct hci_dev       *hdev;
>>       struct usb_device    *udev;
>>       struct usb_interface *intf;
>>       struct usb_interface *isoc;
>> -     const struct usb_device_id *id;
>>
>>       spinlock_t lock;
>>
>> @@ -938,24 +937,20 @@ static void btusb_waker(struct work_struct *work)
>>       usb_autopm_put_interface(data->intf);
>>  }
>>
>> -#define PATCHRAM_TIMEOUT     1000
>>  #define PATCHRAM_NAME_LEN    20
>> +#define PATCHRAM_BUF_SIZE    (256 + 4)
>>
>>  static void btusb_load_firmware(struct hci_dev *hdev)
>>  {
>>       struct btusb_data *data = hci_get_drvdata(hdev);
>>       struct usb_device *udev = data->udev;
>> -     const struct usb_device_id *id = data->id;
>>       size_t pos = 0;
>>       int err = 0;
>>       char filename[PATCHRAM_NAME_LEN];
>>       const struct firmware *fw;
>> +     u8 *buf = NULL;
>> +     u8 val = 0;
>>
>> -     unsigned char reset_cmd[] = { 0x03, 0x0c, 0x00 };
>> -     unsigned char download_cmd[] = { 0x2e, 0xfc, 0x00 };
>> -
>> -     if (!(id->driver_info & BTUSB_BCM_PATCHRAM))
>> -             return;
>>       if (test_and_set_bit(BTUSB_FIRMWARE_DONE, &data->flags))
>>               return;
>>
>> @@ -967,40 +962,43 @@ static void btusb_load_firmware(struct hci_dev *hdev)
>>               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;
>> +     buf = kmalloc(PATCHRAM_BUF_SIZE, GFP_KERNEL);
>> +     if (!buf)
>>               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;
>> +     err= hci_send_cmd(hdev, 0x0c03, 1, &val);
>
> formatting ^

Will fix this, sorry.

>> +     if (err)
>>               goto out;
>> -     }
>>       msleep(300);
>>
>
> The only error that hci_send_cmd() returns is if the skb allocation
> failed. Is there any way to know that the command was actually received
> by the hardware ?

This is what previous pull request for. It introduced __hci_cmd_sync()
and __hci_cmd_sync_ev().
They will wait for the reply from the module to tell if the command is
received or not. If this is what
we want, we may either pull these changes in to make sure the command
is received by the module,
or we may do something similar to the tx_complete callback to make
sure that the command is
actually sent by the usb host IMHO.

Thanks,
Jesse
Tim Gardner Sept. 5, 2013, 4:45 p.m. UTC | #3
More comments below...

On 09/04/2013 10:42 AM, Jesse Sung wrote:
> From: Wen-chien Jesse Sung <jesse.sung@canonical.com>
> 
> BugLink: https://launchpad.net/bugs/1065400
> 
> Calling usb_control_msg directly to send firmware file does not work
> with new platforms. Use hci_send_cmd instead.
> 
> Signed-off-by: Wen-chien Jesse Sung <jesse.sung@canonical.com>
> ---
>  drivers/bluetooth/btusb.c | 58 +++++++++++++++++++++++------------------------
>  net/bluetooth/hci_core.c  |  1 +
>  2 files changed, 29 insertions(+), 30 deletions(-)
> 
> diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
> index 5be9002..3a82e09 100644
> --- a/drivers/bluetooth/btusb.c
> +++ b/drivers/bluetooth/btusb.c
> @@ -49,7 +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
> +#define BTUSB_BCM_PATCHRAM	0x800
>  
>  static struct usb_device_id btusb_table[] = {
>  	/* Generic Bluetooth USB device */
> @@ -226,14 +226,13 @@ static struct usb_device_id blacklist_table[] = {
>  #define BTUSB_ISOC_RUNNING	2
>  #define BTUSB_SUSPENDING	3
>  #define BTUSB_DID_ISO_RESUME	4
> -#define BTUSB_FIRMWARE_DONE	5
> +#define BTUSB_FIRMWARE_DONE	7
>  
>  struct btusb_data {
>  	struct hci_dev       *hdev;
>  	struct usb_device    *udev;
>  	struct usb_interface *intf;
>  	struct usb_interface *isoc;
> -	const struct usb_device_id *id;
>  
>  	spinlock_t lock;
>  
> @@ -938,24 +937,20 @@ static void btusb_waker(struct work_struct *work)
>  	usb_autopm_put_interface(data->intf);
>  }
>  
> -#define PATCHRAM_TIMEOUT	1000
>  #define PATCHRAM_NAME_LEN	20
> +#define PATCHRAM_BUF_SIZE	(256 + 4)
>  
>  static void btusb_load_firmware(struct hci_dev *hdev)
>  {
>  	struct btusb_data *data = hci_get_drvdata(hdev);
>  	struct usb_device *udev = data->udev;
> -	const struct usb_device_id *id = data->id;
>  	size_t pos = 0;
>  	int err = 0;
>  	char filename[PATCHRAM_NAME_LEN];
>  	const struct firmware *fw;
> +	u8 *buf = NULL;
> +	u8 val = 0;
>  
> -	unsigned char reset_cmd[] = { 0x03, 0x0c, 0x00 };
> -	unsigned char download_cmd[] = { 0x2e, 0xfc, 0x00 };
> -
> -	if (!(id->driver_info & BTUSB_BCM_PATCHRAM))
> -		return;

If you remove these 2 lines then it seems that all Bluetooth adapters
will get the PATCHRAM treatment, regardless of the quirk setting in
btusb_table[].

Have you done any regression testing on Bluetooth adapters that don't
require PATCHRAM support ?

rtg
Wen-chien Jesse Sung Sept. 5, 2013, 5:28 p.m. UTC | #4
2013/9/6 Tim Gardner <tim.gardner@canonical.com>:
> More comments below...
>
> On 09/04/2013 10:42 AM, Jesse Sung wrote:
>> From: Wen-chien Jesse Sung <jesse.sung@canonical.com>
>>
>> BugLink: https://launchpad.net/bugs/1065400
>>
>> Calling usb_control_msg directly to send firmware file does not work
>> with new platforms. Use hci_send_cmd instead.
>>
>> Signed-off-by: Wen-chien Jesse Sung <jesse.sung@canonical.com>
>> ---
>>  drivers/bluetooth/btusb.c | 58 +++++++++++++++++++++++------------------------
>>  net/bluetooth/hci_core.c  |  1 +
>>  2 files changed, 29 insertions(+), 30 deletions(-)
>>
>> diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
>> index 5be9002..3a82e09 100644
>> --- a/drivers/bluetooth/btusb.c
>> +++ b/drivers/bluetooth/btusb.c
>> @@ -49,7 +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
>> +#define BTUSB_BCM_PATCHRAM   0x800
>>
>>  static struct usb_device_id btusb_table[] = {
>>       /* Generic Bluetooth USB device */
>> @@ -226,14 +226,13 @@ static struct usb_device_id blacklist_table[] = {
>>  #define BTUSB_ISOC_RUNNING   2
>>  #define BTUSB_SUSPENDING     3
>>  #define BTUSB_DID_ISO_RESUME 4
>> -#define BTUSB_FIRMWARE_DONE  5
>> +#define BTUSB_FIRMWARE_DONE  7
>>
>>  struct btusb_data {
>>       struct hci_dev       *hdev;
>>       struct usb_device    *udev;
>>       struct usb_interface *intf;
>>       struct usb_interface *isoc;
>> -     const struct usb_device_id *id;
>>
>>       spinlock_t lock;
>>
>> @@ -938,24 +937,20 @@ static void btusb_waker(struct work_struct *work)
>>       usb_autopm_put_interface(data->intf);
>>  }
>>
>> -#define PATCHRAM_TIMEOUT     1000
>>  #define PATCHRAM_NAME_LEN    20
>> +#define PATCHRAM_BUF_SIZE    (256 + 4)
>>
>>  static void btusb_load_firmware(struct hci_dev *hdev)
>>  {
>>       struct btusb_data *data = hci_get_drvdata(hdev);
>>       struct usb_device *udev = data->udev;
>> -     const struct usb_device_id *id = data->id;
>>       size_t pos = 0;
>>       int err = 0;
>>       char filename[PATCHRAM_NAME_LEN];
>>       const struct firmware *fw;
>> +     u8 *buf = NULL;
>> +     u8 val = 0;
>>
>> -     unsigned char reset_cmd[] = { 0x03, 0x0c, 0x00 };
>> -     unsigned char download_cmd[] = { 0x2e, 0xfc, 0x00 };
>> -
>> -     if (!(id->driver_info & BTUSB_BCM_PATCHRAM))
>> -             return;
>
> If you remove these 2 lines then it seems that all Bluetooth adapters
> will get the PATCHRAM treatment, regardless of the quirk setting in
> btusb_table[].

Detection is moved to probe(), thus there's no need to save id in
private data anymore.

-       hdev->load_firmware = btusb_load_firmware;
+
+       if (id->driver_info & BTUSB_BCM_PATCHRAM)
+               hdev->load_firmware = btusb_load_firmware;

> Have you done any regression testing on Bluetooth adapters that don't
> require PATCHRAM support ?

Yes, tested with a 0a5c:2145 and it works.

T:  Bus=04 Lev=01 Prnt=01 Port=01 Cnt=02 Dev#=  4 Spd=12  MxCh= 0
D:  Ver= 2.00 Cls=e0(wlcon) Sub=01 Prot=01 MxPS=64 #Cfgs=  1
P:  Vendor=0a5c ProdID=2145 Rev=03.99
S:  Manufacturer=Lenovo Computer Corp
S:  Product=ThinkPad Bluetooth with Enhanced Data Rate II
C:  #Ifs= 4 Cfg#= 1 Atr=e0 MxPwr=100mA
I:  If#= 0 Alt= 0 #EPs= 3 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb
I:  If#= 1 Alt= 0 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb
I:  If#= 2 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=ff Driver=(none)
I:  If#= 3 Alt= 0 #EPs= 0 Cls=fe(app. ) Sub=01 Prot=00 Driver=(none)

Thanks,
Jesse
diff mbox

Patch

diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
index 5be9002..3a82e09 100644
--- a/drivers/bluetooth/btusb.c
+++ b/drivers/bluetooth/btusb.c
@@ -49,7 +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
+#define BTUSB_BCM_PATCHRAM	0x800
 
 static struct usb_device_id btusb_table[] = {
 	/* Generic Bluetooth USB device */
@@ -226,14 +226,13 @@  static struct usb_device_id blacklist_table[] = {
 #define BTUSB_ISOC_RUNNING	2
 #define BTUSB_SUSPENDING	3
 #define BTUSB_DID_ISO_RESUME	4
-#define BTUSB_FIRMWARE_DONE	5
+#define BTUSB_FIRMWARE_DONE	7
 
 struct btusb_data {
 	struct hci_dev       *hdev;
 	struct usb_device    *udev;
 	struct usb_interface *intf;
 	struct usb_interface *isoc;
-	const struct usb_device_id *id;
 
 	spinlock_t lock;
 
@@ -938,24 +937,20 @@  static void btusb_waker(struct work_struct *work)
 	usb_autopm_put_interface(data->intf);
 }
 
-#define PATCHRAM_TIMEOUT	1000
 #define PATCHRAM_NAME_LEN	20
+#define PATCHRAM_BUF_SIZE	(256 + 4)
 
 static void btusb_load_firmware(struct hci_dev *hdev)
 {
 	struct btusb_data *data = hci_get_drvdata(hdev);
 	struct usb_device *udev = data->udev;
-	const struct usb_device_id *id = data->id;
 	size_t pos = 0;
 	int err = 0;
 	char filename[PATCHRAM_NAME_LEN];
 	const struct firmware *fw;
+	u8 *buf = NULL;
+	u8 val = 0;
 
-	unsigned char reset_cmd[] = { 0x03, 0x0c, 0x00 };
-	unsigned char download_cmd[] = { 0x2e, 0xfc, 0x00 };
-
-	if (!(id->driver_info & BTUSB_BCM_PATCHRAM))
-		return;
 	if (test_and_set_bit(BTUSB_FIRMWARE_DONE, &data->flags))
 		return;
 
@@ -967,40 +962,43 @@  static void btusb_load_firmware(struct hci_dev *hdev)
 		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;
+	buf = kmalloc(PATCHRAM_BUF_SIZE, GFP_KERNEL);
+	if (!buf)
 		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;
+	err= hci_send_cmd(hdev, 0x0c03, 1, &val);
+	if (err)
 		goto out;
-	}
 	msleep(300);
 
+	err = hci_send_cmd(hdev, 0xfc2e, 1, &val);
+	if (err)
+		goto out;
+	msleep(1000);
+
 	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;
+		if (pos + len > fw->size) {
+			err = -EINVAL;
 			goto out;
 		}
+		memcpy(buf, fw->data + pos, len);
+		err = hci_send_cmd(hdev, le16_to_cpu(*(u16*)buf),
+					*(buf + sizeof(u16)), buf + sizeof(u16) + 1);
+		if (err)
+			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);
+	err= hci_send_cmd(hdev, 0x0c03, 1, &val);
 out:
 	if (err)
-		BT_INFO("fail to load firmware, may not work correctly");
+		BT_INFO("fail to load firmware");
 	else
 		BT_INFO("firmware loaded");
+	if (buf)
+		kfree(buf);
 	release_firmware(fw);
 }
 
@@ -1089,8 +1087,6 @@  static int btusb_probe(struct usb_interface *intf,
 	init_usb_anchor(&data->isoc_anchor);
 	init_usb_anchor(&data->deferred);
 
-	data->id = id;
-
 	hdev = hci_alloc_dev();
 	if (!hdev)
 		return -ENOMEM;
@@ -1107,7 +1103,9 @@  static int btusb_probe(struct usb_interface *intf,
 	hdev->flush    = btusb_flush;
 	hdev->send     = btusb_send_frame;
 	hdev->notify   = btusb_notify;
-	hdev->load_firmware = btusb_load_firmware;
+
+	if (id->driver_info & BTUSB_BCM_PATCHRAM)
+		hdev->load_firmware = btusb_load_firmware;
 
 	/* Interface numbers are hardcoded in the specification */
 	data->isoc = usb_ifnum_to_if(data->udev, 1);
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index 843f46f..927e55e 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -2193,6 +2193,7 @@  int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen, void *param)
 
 	return 0;
 }
+EXPORT_SYMBOL(hci_send_cmd);
 
 /* Get data from the previously sent command */
 void *hci_sent_cmd_data(struct hci_dev *hdev, __u16 opcode)