Linux bluetooth development
 help / color / mirror / Atom feed
* Re: Bluetooth USB device keeps same PID/VID after DFU
       [not found] <6AAC4214B9104E409FF4885ACB62A96101A78AD852@SHEXMB-01.global.atheros.com>
@ 2011-05-25 13:34 ` Jiri Kosina
  0 siblings, 0 replies; 3+ messages in thread
From: Jiri Kosina @ 2011-05-25 13:34 UTC (permalink / raw)
  To: Steven Li
  Cc: linux-kernel@vger.kernel.org, Haijun Liu, Hong Fan, Vic Wei,
	Jack Chen(Taipei), Robert Chang, Gustavo F. Padovan,
	linux-bluetooth


[ adding some relevant CCs ]

On Wed, 25 May 2011, Steven Li wrote:

> Hi Marcel:
>
> Here is steve from Atheros.  Glad to contact you here. we have some concerns to the btusb device driver.
> Present USB Bluetooth implementation in Linux has some limitations. Many vendors need to load patch and other specific operations during device probe or disconnect to their Bluetooth device, but btusb driver gives limitation to this behavior.  It needs to add the Bluetooth device PID/VID into the btusb blacklist, and use the vendor specific driver to load firmware. By this, it causes the Bluetooth device "MUST" have two PID/VIDs in Linux.  But on windows platform, the device can always keep one same PID/VID.  It is hard for us to handle this issue with current btusb design.
> Moreover in our Atheros 3012 chip, we also need to switch the 3012 chip between "normal" mode and "pre-boot" mode during PC reboot,  with current btusb design, we have to make changes in btusb.c directly, but it is our chip specific requirements.  Therefore, we’d like to propose some methods to make the Bluetooth device has only one PID/VID by proper btusb modification and also for other vendor extensions such as mode switch.
>
> Currently, we have three proposals. And I attached our rough patches for these three proposals.
> They are totally not formal, I attached them just want to make you easy to understand.
>
> And from here, Let me explain more of  these proposal one by one.
>
> # Proposal 1.
> This option could be refer to what it’s working for lots of UART Bluetooth devices in Linux.
> Mainly, vendor driver would be built as an object of btusb.ko. This should be same as those UART Bluetooth drivers.
> In our case, ath3k would be built as an object file according to CONFIG_BT_ATH3K define.
> Then btusb kernel module will be composed of btusb.o and ath3k.o.  So it is easy to make one PID/VID device work.
> This concept was oriented by hci_uart driver.  It can jump to individual entry function based on different ID.
> And things like mode switch also could be handled by just adding function like switch_mode() in ath3k.c.
>
> # Proposal 2.
> This option is to export btusb common functions, and vendor driver (for example our ath3k) can be taken as the extension of btusb.
> , and it can first downloads the firmware and then use those functions exported in btusb driver
> to setup the hci Bluetooth device.  Moreover if it is possible, we are also propose to refine the btusb.c and split the btusb.c to btusb_core.c and btusb_generic.c,
> In the btusb_core.c. It includes all the  Bluetooth usb common operations and these Bluetooth usb functions will be exported,
> and the btusb_generic.c is the real generic Bluetooth driver remains support all current Bluetooth device ,which uses the btusb_core functions.
> And vendor driver can also call those usb common operations in the btusb_core.c.
> With this proposal, Since the ath3k driver are the only owner of the bluetooth device, only one PID/VID is necessary.
> And it is very easy to handle the mode switch case in itself either. Please check btusb_ath3k_2.patch for detail.
>
> # Proposal 3.
> This option is to add "quirk device" to btusb driver. We found actually you added lots of so-called “quirk” implementation in btusb driver for many vendors.
> http://git.kernel.org/?p=linux/kernel/git/next/linux-next.git;a=commit;h=cfeb414537b1d7c23ba202f198fa4154cd5a4856
> Vendor specific driver can call btusb_register_quirk_device() function to  register itself to the btusb driver.
> So during the device probe, the btusb will try to match the device id, and if it is a “quirk” device, it will call
> the “quirk” device’s specific function first.  Every vendor can use this quirk to download their firmware and keep the same PID/VID with proper codes in btusb.c.
> This proposal is also able to handle the mode switch case.
>
> Please give your comments and suggestion for our proposals.
> We are hoping to make one PID/VID work and contribute to the community.
> Thank you very much for  your helping !
>
> Best Regards,
> Steven,
>
>
>

-- 
Jiri Kosina


^ permalink raw reply	[flat|nested] 3+ messages in thread

* Bluetooth USB device keeps same PID/VID after DFU
@ 2011-05-26  1:32 Steven Li
  2011-05-26  2:47 ` Marcel Holtmann
  0 siblings, 1 reply; 3+ messages in thread
From: Steven Li @ 2011-05-26  1:32 UTC (permalink / raw)
  To: Marcel Holtmann, Gustavo F. Padovan
  Cc: Bluettooth Linux, Steven Li, Haijun Liu, Hong Fan, Vic Wei,
	Jack Chen(Taipei), Robert Chang

[-- Attachment #1: Type: text/plain, Size: 4005 bytes --]

Hi Marcel:

Here is steven from Atheros.  Glad to contact you here. we have some concerns to the btusb device driver.
Present USB Bluetooth implementation in Linux has some limitations. Many vendors need to load patch and other specific operations during device probe or disconnect to their Bluetooth device, but btusb driver gives limitation to this behavior.  It needs to add the Bluetooth device PID/VID into the btusb blacklist, and use the vendor specific driver to load firmware. By this, it causes the Bluetooth device "MUST" have two PID/VIDs in Linux.  But on windows platform, the device can always keep one same PID/VID.  It is hard for us to handle this issue with current btusb design.
Moreover in our Atheros 3012 chip, we also need to switch the 3012 chip between "normal" mode and "pre-boot" mode during PC reboot,  with current btusb design, we have to make changes in btusb.c directly, but it is our chip specific requirements.  Therefore, we’d like to propose some methods to make the Bluetooth device has only one PID/VID by proper btusb modification and also for other vendor extensions such as mode switch. 

Currently, we have three proposals. And I attached our rough patches for these three proposals.
They are totally not formal, I attached them just want to make you easy to understand.

And from here, Let me explain more of  these proposal one by one.

# Proposal 1.
This option could be refer to what it’s working for lots of UART Bluetooth devices in Linux.
Mainly, vendor driver would be built as an object of btusb.ko. This should be same as those UART Bluetooth drivers.
In our case, ath3k would be built as an object file according to CONFIG_BT_ATH3K define.  
Then btusb kernel module will be composed of btusb.o and ath3k.o.  So it is easy to make one PID/VID device work.
This concept was oriented by hci_uart driver.  It can jump to individual entry function based on different ID.
And things like mode switch also could be handled by just adding function like switch_mode() in ath3k.c.

# Proposal 2.
This option is to export btusb common functions, and vendor driver (for example our ath3k) can be taken as the extension of btusb. 
, and it can first downloads the firmware and then use those functions exported in btusb driver
to setup the hci Bluetooth device.  Moreover if it is possible, we are also propose to refine the btusb.c and split the btusb.c to btusb_core.c and btusb_generic.c, 
In the btusb_core.c. It includes all the  Bluetooth usb common operations and these Bluetooth usb functions will be exported, 
and the btusb_generic.c is the real generic Bluetooth driver remains support all current Bluetooth device ,which uses the btusb_core functions.
And vendor driver can also call those usb common operations in the btusb_core.c.
With this proposal, Since the ath3k driver are the only owner of the bluetooth device, only one PID/VID is necessary.
And it is very easy to handle the mode switch case in itself either. Please check btusb_ath3k_2.patch for detail.

# Proposal 3.
This option is to add "quirk device" to btusb driver. We found actually you added lots of so-called “quirk” implementation in btusb driver for many vendors.
http://git.kernel.org/?p=linux/kernel/git/next/linux-next.git;a=commit;h=cfeb414537b1d7c23ba202f198fa4154cd5a4856
Vendor specific driver can call btusb_register_quirk_device() function to  register itself to the btusb driver. 
So during the device probe, the btusb will try to match the device id, and if it is a “quirk” device, it will call 
the “quirk” device’s specific function first.  Every vendor can use this quirk to download their firmware and keep the same PID/VID with proper codes in btusb.c.
This proposal is also able to handle the mode switch case. 

Please give your comments and suggestion for our proposals. 
We are hoping to make one PID/VID work and contribute to the community.
Thank you very much for  your helping !

Best Regards,
Steven,

[-- Attachment #2: btusb_ath3k_3.diff --]
[-- Type: application/octet-stream, Size: 6054 bytes --]

diff --git a/drivers/bluetooth/ath3k.c b/drivers/bluetooth/ath3k.c
old mode 100644
new mode 100755
index 6bacef3..7d20946
--- a/drivers/bluetooth/ath3k.c
+++ b/drivers/bluetooth/ath3k.c
@@ -29,6 +29,8 @@
 #include <linux/usb.h>
 #include <net/bluetooth/bluetooth.h>
 
+#include "btusb.h"
+
 #define VERSION "1.0"
 
 #define ATH3K_DNLOAD				0x01
@@ -68,7 +70,7 @@ static struct usb_device_id ath3k_table[] = {
 	{ USB_DEVICE(0x03F0, 0x311D) },
 
 	/* Atheros AR3012 with sflash firmware*/
-	{ USB_DEVICE(0x0CF3, 0x3004) },
+	/* { USB_DEVICE(0x0CF3, 0x3004) }, */
 
 	/* Atheros AR5BBU12 with sflash firmware */
 	{ USB_DEVICE(0x0489, 0xE02C) },
@@ -390,7 +392,7 @@ static int ath3k_probe(struct usb_interface *intf,
 			BT_ERR("Set normal mode failed");
 			return ret;
 		}
-		ath3k_switch_pid(udev);
+		/* ath3k_switch_pid(udev); */
 		return 0;
 	}
 
@@ -417,15 +419,23 @@ static struct usb_driver ath3k_driver = {
 	.id_table	= ath3k_table,
 };
 
+static struct btusb_quirk_device ath3k_device = {
+	.id		= BTUSB_QUIRKY_ATH3k,
+	.probe		= ath3k_probe,
+	.disconnect	= ath3k_disconnect,
+};
+
 static int __init ath3k_init(void)
 {
 	BT_INFO("Atheros AR30xx firmware driver ver %s", VERSION);
+	btusb_register_quirky_device(&ath3k_device);
 	return usb_register(&ath3k_driver);
 }
 
 static void __exit ath3k_exit(void)
 {
 	usb_deregister(&ath3k_driver);
+	btusb_unregister_quirky_device(&ath3k_device);
 }
 
 module_init(ath3k_init);
diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
old mode 100644
new mode 100755
index c2de895..b7928c6
--- a/drivers/bluetooth/btusb.c
+++ b/drivers/bluetooth/btusb.c
@@ -35,6 +35,8 @@
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
 
+#include "btusb.h"
+
 #define VERSION "0.6"
 
 static int ignore_dga;
@@ -47,6 +49,8 @@ static int reset = 1;
 
 static struct usb_driver btusb_driver;
 
+static struct btusb_quirky_device *quirky_device[BTUSB_MAX_QUIRKY_DEVICE];
+
 #define BTUSB_IGNORE		0x01
 #define BTUSB_DIGIANSWER	0x02
 #define BTUSB_CSR		0x04
@@ -54,6 +58,7 @@ static struct usb_driver btusb_driver;
 #define BTUSB_BCM92035		0x10
 #define BTUSB_BROKEN_ISOC	0x20
 #define BTUSB_WRONG_SCO_MTU	0x40
+#define BTUSB_ATH301X		0x80
 
 static struct usb_device_id btusb_table[] = {
 	/* Generic Bluetooth USB device */
@@ -110,7 +115,7 @@ static struct usb_device_id blacklist_table[] = {
 	{ USB_DEVICE(0x03f0, 0x311d), .driver_info = BTUSB_IGNORE },
 
 	/* Atheros 3012 with sflash firmware */
-	{ USB_DEVICE(0x0cf3, 0x3004), .driver_info = BTUSB_IGNORE },
+	{ USB_DEVICE(0x0cf3, 0x3004), .driver_info = BTUSB_ATH301X },
 
 	/* Atheros AR5BBU12 with sflash firmware */
 	{ USB_DEVICE(0x0489, 0xe02c), .driver_info = BTUSB_IGNORE },
@@ -1029,6 +1034,13 @@ static int btusb_probe(struct usb_interface *intf,
 		}
 	}
 
+	if (id->driver_info & BTUSB_ATH301X) {
+		struct btusb_quirk_device *ath3k = quirky_device[BTUSB_QUIRKY_ATH3k];
+		if(ath3k) {
+			ath3k->probe(intf, id);
+		}
+	}
+
 	if (data->isoc) {
 		err = usb_driver_claim_interface(&btusb_driver,
 							data->isoc, data);
@@ -1055,6 +1067,7 @@ static void btusb_disconnect(struct usb_interface *intf)
 {
 	struct btusb_data *data = usb_get_intfdata(intf);
 	struct hci_dev *hdev;
+	struct usb_device_id *id;
 
 	BT_DBG("intf %p", intf);
 
@@ -1080,6 +1093,14 @@ static void btusb_disconnect(struct usb_interface *intf)
 	__hci_dev_put(hdev);
 
 	hci_free_dev(hdev);
+
+	id = usb_match_id(intf, blacklist_table);
+	if(id && (id->driver_info & BTUSB_ATH301X)) {
+		struct btusb_quirk_device *ath3k = quirky_device[BTUSB_QUIRKY_ATH3k];
+		if(ath3k) {
+			ath3k->disconnect(id);
+		}
+	}
 }
 
 #ifdef CONFIG_PM
@@ -1195,6 +1216,32 @@ static struct usb_driver btusb_driver = {
 	.supports_autosuspend = 1,
 };
 
+int btusb_register_quirky_device(struct btusb_quirk_device *dev)
+{
+	if (dev->id >= BTUSB_MAX_QUIRKY_DEVICE)
+		return -EINVAL;
+
+	if (quirky_device[dev->id])
+		return -EEXIST;
+
+	quirky_device[dev->id] = dev;
+
+	return 0;
+}
+
+int btusb_unregister_quirky_device(struct btusb_quirk_device *dev)
+{
+	if (dev->id >= BTUSB_MAX_QUIRKY_DEVICE)
+		return -EINVAL;
+
+	if (!quirky_device[dev->id])
+		return -EINVAL;
+
+	quirky_device[dev->id] = NULL;
+
+	return 0;
+}
+
 static int __init btusb_init(void)
 {
 	BT_INFO("Generic Bluetooth USB driver ver %s", VERSION);
@@ -1207,6 +1254,9 @@ static void __exit btusb_exit(void)
 	usb_deregister(&btusb_driver);
 }
 
+EXPORT_SYMBOL_GPL(btusb_register_quirky_device);
+EXPORT_SYMBOL_GPL(btusb_unregister_quirky_device);
+
 module_init(btusb_init);
 module_exit(btusb_exit);
 
diff --git a/drivers/bluetooth/btusb.h b/drivers/bluetooth/btusb.h
new file mode 100755
index 0000000..d1cbc75
--- /dev/null
+++ b/drivers/bluetooth/btusb.h
@@ -0,0 +1,36 @@
+/*
+ *
+ *  Generic Bluetooth USB driver
+ *
+ *  Copyright (C) 2005-2008  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+*/
+
+#define BTUSB_MAX_QUIRKY_DEVICE	1
+
+#define BTUSB_QUIRKY_ATH3k	0
+
+struct btusb_quirk_device {
+        unsigned int id;
+	int (*probe)(struct usb_interface *intf,
+			const struct usb_device_id *id);
+	void (*disconnect)(struct usb_interface *intf);
+};
+
+int btusb_register_quirky_device(struct btusb_quirk_device *dev);
+int btusb_unregister_quirky_device(struct btusb_quirk_device *dev);

[-- Attachment #3: btusb_ath3k_1.diff --]
[-- Type: application/octet-stream, Size: 7201 bytes --]

diff -aurN bluetooth.orig/ath3k.c bluetooth.new/ath3k.c
--- bluetooth.orig/ath3k.c	2011-05-24 12:50:00.073891580 +0800
+++ bluetooth.new/ath3k.c	2011-05-24 13:30:55.212893136 +0800
@@ -29,6 +29,8 @@
 #include <linux/usb.h>
 #include <net/bluetooth/bluetooth.h>
 
+#include "btusb.h"
+
 #define VERSION "1.0"
 
 #define ATH3K_DNLOAD				0x01
@@ -56,28 +58,6 @@
 	unsigned char	reserved[0x07];
 };
 
-static struct usb_device_id ath3k_table[] = {
-	/* Atheros AR3011 */
-	{ USB_DEVICE(0x0CF3, 0x3000) },
-
-	/* Atheros AR3011 with sflash firmware*/
-	{ USB_DEVICE(0x0CF3, 0x3002) },
-	{ USB_DEVICE(0x13d3, 0x3304) },
-
-	/* Atheros AR9285 Malbec with sflash firmware */
-	{ USB_DEVICE(0x03F0, 0x311D) },
-
-	/* Atheros AR3012 with sflash firmware*/
-	{ USB_DEVICE(0x0CF3, 0x3004) },
-
-	/* Atheros AR5BBU12 with sflash firmware */
-	{ USB_DEVICE(0x0489, 0xE02C) },
-
-	{ }	/* Terminating entry */
-};
-
-MODULE_DEVICE_TABLE(usb, ath3k_table);
-
 #define BTUSB_ATH3012		0x80
 /* This table is to load patch and sysconfig files
  * for AR3012 */
@@ -353,7 +333,7 @@
 	return ret;
 }
 
-static int ath3k_probe(struct usb_interface *intf,
+int ath3k_dfu_init(struct usb_interface *intf,
 			const struct usb_device_id *id)
 {
 	const struct firmware *firmware;
@@ -404,32 +384,13 @@
 
 	return ret;
 }
+EXPORT_SYMBOL_GPL(ath3k_dfu_init);
 
-static void ath3k_disconnect(struct usb_interface *intf)
+void ath3k_dfu_deinit(struct usb_interface *intf)
 {
-	BT_DBG("ath3k_disconnect intf %p", intf);
+	BT_DBG("ath3k_dfu_deinit intf %p", intf);
 }
-
-static struct usb_driver ath3k_driver = {
-	.name		= "ath3k",
-	.probe		= ath3k_probe,
-	.disconnect	= ath3k_disconnect,
-	.id_table	= ath3k_table,
-};
-
-static int __init ath3k_init(void)
-{
-	BT_INFO("Atheros AR30xx firmware driver ver %s", VERSION);
-	return usb_register(&ath3k_driver);
-}
-
-static void __exit ath3k_exit(void)
-{
-	usb_deregister(&ath3k_driver);
-}
-
-module_init(ath3k_init);
-module_exit(ath3k_exit);
+EXPORT_SYMBOL_GPL(ath3k_dfu_deinit);
 
 MODULE_AUTHOR("Atheros Communications");
 MODULE_DESCRIPTION("Atheros AR30xx firmware driver");
diff -aurN bluetooth.orig/btusb.c bluetooth.new/btusb.c
--- bluetooth.orig/btusb.c	2011-05-24 12:50:00.073891580 +0800
+++ bluetooth.new/btusb.c	2011-05-24 14:28:56.077896856 +0800
@@ -35,6 +35,8 @@
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
 
+#include "btusb.h"
+
 #define VERSION "0.6"
 
 static int ignore_dga;
@@ -54,6 +56,7 @@
 #define BTUSB_BCM92035		0x10
 #define BTUSB_BROKEN_ISOC	0x20
 #define BTUSB_WRONG_SCO_MTU	0x40
+#define BTUSB_AR301X		0x80
 
 static struct usb_device_id btusb_table[] = {
 	/* Generic Bluetooth USB device */
@@ -90,30 +93,29 @@
 	/* Canyon CN-BTU1 with HID interfaces */
 	{ USB_DEVICE(0x0c10, 0x0000) },
 
-	{ }	/* Terminating entry */
-};
-
-MODULE_DEVICE_TABLE(usb, btusb_table);
-
-static struct usb_device_id blacklist_table[] = {
 	/* CSR BlueCore devices */
 	{ USB_DEVICE(0x0a12, 0x0001), .driver_info = BTUSB_CSR },
 
 	/* Broadcom BCM2033 without firmware */
 	{ USB_DEVICE(0x0a5c, 0x2033), .driver_info = BTUSB_IGNORE },
 
+#ifdef CONFIG_BT_ATH3K
+	/* Atheros AR3011 */
+	{ USB_DEVICE(0x0CF3, 0x3000), .driver_info = BTUSB_AR301X },
+
 	/* Atheros 3011 with sflash firmware */
-	{ USB_DEVICE(0x0cf3, 0x3002), .driver_info = BTUSB_IGNORE },
-	{ USB_DEVICE(0x13d3, 0x3304), .driver_info = BTUSB_IGNORE },
+	{ USB_DEVICE(0x0cf3, 0x3002), .driver_info = BTUSB_AR301X },
+	{ USB_DEVICE(0x13d3, 0x3304), .driver_info = BTUSB_AR301X },
 
 	/* Atheros AR9285 Malbec with sflash firmware */
-	{ USB_DEVICE(0x03f0, 0x311d), .driver_info = BTUSB_IGNORE },
+	{ USB_DEVICE(0x03f0, 0x311d), .driver_info = BTUSB_AR301X },
 
 	/* Atheros 3012 with sflash firmware */
-	{ USB_DEVICE(0x0cf3, 0x3004), .driver_info = BTUSB_IGNORE },
+	{ USB_DEVICE(0x0cf3, 0x3004), .driver_info = BTUSB_AR301X },
 
 	/* Atheros AR5BBU12 with sflash firmware */
-	{ USB_DEVICE(0x0489, 0xe02c), .driver_info = BTUSB_IGNORE },
+	{ USB_DEVICE(0x0489, 0xe02c), .driver_info = BTUSB_AR301X },
+#endif
 
 	/* Broadcom BCM2035 */
 	{ USB_DEVICE(0x0a5c, 0x2035), .driver_info = BTUSB_WRONG_SCO_MTU },
@@ -168,6 +170,8 @@
 	{ }	/* Terminating entry */
 };
 
+MODULE_DEVICE_TABLE(usb, btusb_table);
+
 #define BTUSB_MAX_ISOC_FRAMES	10
 
 #define BTUSB_INTR_RUNNING	0
@@ -897,7 +901,7 @@
 
 	if (!id->driver_info) {
 		const struct usb_device_id *match;
-		match = usb_match_id(intf, blacklist_table);
+		match = usb_match_id(intf, btusb_table);
 		if (match)
 			id = match;
 	}
@@ -914,6 +918,13 @@
 	if (ignore_sniffer && id->driver_info & BTUSB_SNIFFER)
 		return -ENODEV;
 
+	if (id->driver_info & BTUSB_AR301X) {
+#ifdef CONFIG_BT_ATH3K
+		if(ath3k_dfu_init(intf, id))
+			return 0;
+#endif
+	}
+
 	data = kzalloc(sizeof(*data), GFP_KERNEL);
 	if (!data)
 		return -ENOMEM;
diff -aurN bluetooth.orig/btusb.h bluetooth.new/btusb.h
--- bluetooth.orig/btusb.h	1970-01-01 08:00:00.000000000 +0800
+++ bluetooth.new/btusb.h	2011-05-24 13:24:00.756916000 +0800
@@ -0,0 +1,29 @@
+/*
+ *
+ *  Generic Bluetooth USB driver
+ *
+ *  Copyright (C) 2005-2008  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#define CONFIG_BT_ATH3K
+#ifdef CONFIG_BT_ATH3K
+int ath3k_dfu_init(struct usb_interface *intf,
+                        const struct usb_device_id *id);
+void ath3k_dfu_deinit(struct usb_interface *intf);
+#endif
diff -aurN bluetooth.orig/Kconfig bluetooth.new/Kconfig
--- bluetooth.orig/Kconfig	2011-05-24 12:50:00.073891580 +0800
+++ bluetooth.new/Kconfig	2011-05-24 15:27:49.020894851 +0800
@@ -208,7 +208,7 @@
 	  into the kernel or say M to compile it as module.
 
 config BT_ATH3K
-	tristate "Atheros firmware download driver"
+	bool "Atheros firmware download driver"
 	depends on BT_HCIBTUSB
 	select FW_LOADER
 	help
diff -aurN bluetooth.orig/Makefile bluetooth.new/Makefile
--- bluetooth.orig/Makefile	2011-05-24 12:50:00.040898322 +0800
+++ bluetooth.new/Makefile	2011-05-24 13:51:03.436420000 +0800
@@ -12,10 +12,12 @@
 obj-$(CONFIG_BT_HCIBLUECARD)	+= bluecard_cs.o
 obj-$(CONFIG_BT_HCIBTUART)	+= btuart_cs.o
 
-obj-$(CONFIG_BT_HCIBTUSB)	+= btusb.o
+obj-$(CONFIG_BT_HCIBTUSB)       += btusb.o
+btusb-$(CONFIG_BT_ATH3K)        += ath3k.o
+btusb-objs                      := $(btusb-$(CONFIG_BT_HCIBTUSB))
+
 obj-$(CONFIG_BT_HCIBTSDIO)	+= btsdio.o
 
-obj-$(CONFIG_BT_ATH3K)		+= ath3k.o
 obj-$(CONFIG_BT_MRVL)		+= btmrvl.o
 obj-$(CONFIG_BT_MRVL_SDIO)	+= btmrvl_sdio.o
 obj-$(CONFIG_BT_WILINK)		+= btwilink.o

[-- Attachment #4: btusb_ath3k_2.patch --]
[-- Type: application/octet-stream, Size: 67301 bytes --]

From 405b93e60cb12d0ff79c3e46d9dbd343b924912b Mon Sep 17 00:00:00 2001
From: Steven.Li <Steven.Li@Atheros.com>
Date: Wed, 25 May 2011 18:34:22 +0800
Subject: [PATCH 1/3] Split the btusb.c to btusb_core.c and btusb_generic.c.


Signed-off-by: Steven.Li <Steven.Li@Atheros.com>
---
 drivers/bluetooth/Makefile        |    2 +
 drivers/bluetooth/ath3k.c         |   21 +-
 drivers/bluetooth/btusb.c         | 1234 ---------------------------------------------
 drivers/bluetooth/btusb.h         |   65 +++++++++++++++++++++++++++++++++++++++++++++
 drivers/bluetooth/btusb_core.c    |  958 +++++++++++++++++++++++++++++++++++++
 drivers/bluetooth/btusb_generic.c |  334 +++++++++++++
 6 files changed, 1378 insertions(+), 1236 deletions(-)
 delete mode 100644 drivers/bluetooth/btusb.c
 create mode 100755 drivers/bluetooth/btusb.h
 create mode 100644 drivers/bluetooth/btusb_core.c
 create mode 100644 drivers/bluetooth/btusb_generic.c

diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile
index f4460f4..3acd97a 100644
--- a/drivers/bluetooth/Makefile
+++ b/drivers/bluetooth/Makefile
@@ -13,6 +13,8 @@ obj-$(CONFIG_BT_HCIBLUECARD)	+= bluecard_cs.o
 obj-$(CONFIG_BT_HCIBTUART)	+= btuart_cs.o
 
 obj-$(CONFIG_BT_HCIBTUSB)	+= btusb.o
+btusb-y				:= btusb_core.o btusb_generic.o
+
 obj-$(CONFIG_BT_HCIBTSDIO)	+= btsdio.o
 
 obj-$(CONFIG_BT_ATH3K)		+= ath3k.o
diff --git a/drivers/bluetooth/ath3k.c b/drivers/bluetooth/ath3k.c
index 6bacef3..7bb62db 100644
--- a/drivers/bluetooth/ath3k.c
+++ b/drivers/bluetooth/ath3k.c
@@ -29,8 +29,12 @@
 #include <linux/usb.h>
 #include <net/bluetooth/bluetooth.h>
 
+#include "btusb.h"
+
 #define VERSION "1.0"
 
+static struct usb_driver ath3k_driver;
+
 #define ATH3K_DNLOAD				0x01
 #define ATH3K_GETSTATE				0x05
 #define ATH3K_SET_NORMAL_MODE			0x07
@@ -372,9 +376,10 @@ static int ath3k_probe(struct usb_interface *intf,
 		if (match)
 			id = match;
 	}
-
+		
 	/* load patch and sysconfig files for AR3012 */
 	if (id->driver_info & BTUSB_ATH3012) {
+			
 		ret = ath3k_load_patch(udev);
 		if (ret < 0) {
 			BT_ERR("Loading patch file failed");
@@ -390,7 +395,11 @@ static int ath3k_probe(struct usb_interface *intf,
 			BT_ERR("Set normal mode failed");
 			return ret;
 		}
-		ath3k_switch_pid(udev);
+		/* ath3k_switch_pid(udev); */
+		struct btusb_data *data = btusb_alloc_devdata(intf);
+		if (!data)
+			return -ENOMEM;
+		btusb_register_dev(&ath3k_driver, data);
 		return 0;
 	}
 
@@ -407,7 +416,15 @@ static int ath3k_probe(struct usb_interface *intf,
 
 static void ath3k_disconnect(struct usb_interface *intf)
 {
+
 	BT_DBG("ath3k_disconnect intf %p", intf);
+
+	/* To Be DONE */
+	/* 
+		struct btusb_data *data = usb_get_intfdata(intf);
+		btusb_release_dev(&ath3k_driver, data);
+		ath3k_set_preboot_mode();
+	*/
 }
 
 static struct usb_driver ath3k_driver = {
diff --git a/drivers/bluetooth/btusb.h b/drivers/bluetooth/btusb.h
new file mode 100755
index 0000000..72e27e1
--- /dev/null
+++ b/drivers/bluetooth/btusb.h
@@ -0,0 +1,65 @@
+/*
+ *
+ *  Generic Bluetooth USB driver
+ *
+ *  Copyright (C) 2005-2008  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+*/
+
+struct btusb_data {
+	struct hci_dev       *hdev;
+	struct usb_device    *udev;
+	struct usb_interface *intf;
+	struct usb_interface *isoc;
+
+	spinlock_t lock;
+
+	unsigned long flags;
+
+	struct work_struct work;
+	struct work_struct waker;
+
+	struct usb_anchor tx_anchor;
+	struct usb_anchor intr_anchor;
+	struct usb_anchor bulk_anchor;
+	struct usb_anchor isoc_anchor;
+	struct usb_anchor deferred;
+	int tx_in_flight;
+	spinlock_t txlock;
+
+	struct usb_endpoint_descriptor *intr_ep;
+	struct usb_endpoint_descriptor *bulk_tx_ep;
+	struct usb_endpoint_descriptor *bulk_rx_ep;
+	struct usb_endpoint_descriptor *isoc_tx_ep;
+	struct usb_endpoint_descriptor *isoc_rx_ep;
+
+	__u8 cmdreq_type;
+
+	unsigned int sco_num;
+	int isoc_altsetting;
+	int suspend_count;
+};
+
+struct btusb_data* btusb_alloc_devdata(struct usb_interface *intf);
+int btusb_register_dev(struct usb_driver *usbdrv, struct btusb_data *data);
+void btusb_release_dev(struct usb_driver *usbdrv, struct btusb_data *data);
+
+#ifdef CONFIG_PM
+int btusb_suspend_dev(struct btusb_data *data, pm_message_t message);
+int btusb_resume_dev(struct btusb_data *data);
+#endif
diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
deleted file mode 100644
index c2de895..0000000
--- a/drivers/bluetooth/btusb.c
+++ /dev/null
@@ -1,1234 +0,0 @@
-/*
- *
- *  Generic Bluetooth USB driver
- *
- *  Copyright (C) 2005-2008  Marcel Holtmann <marcel@holtmann.org>
- *
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- *
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/types.h>
-#include <linux/sched.h>
-#include <linux/errno.h>
-#include <linux/skbuff.h>
-
-#include <linux/usb.h>
-
-#include <net/bluetooth/bluetooth.h>
-#include <net/bluetooth/hci_core.h>
-
-#define VERSION "0.6"
-
-static int ignore_dga;
-static int ignore_csr;
-static int ignore_sniffer;
-static int disable_scofix;
-static int force_scofix;
-
-static int reset = 1;
-
-static struct usb_driver btusb_driver;
-
-#define BTUSB_IGNORE		0x01
-#define BTUSB_DIGIANSWER	0x02
-#define BTUSB_CSR		0x04
-#define BTUSB_SNIFFER		0x08
-#define BTUSB_BCM92035		0x10
-#define BTUSB_BROKEN_ISOC	0x20
-#define BTUSB_WRONG_SCO_MTU	0x40
-
-static struct usb_device_id btusb_table[] = {
-	/* Generic Bluetooth USB device */
-	{ USB_DEVICE_INFO(0xe0, 0x01, 0x01) },
-
-	/* Apple MacBookPro 7,1 */
-	{ USB_DEVICE(0x05ac, 0x8213) },
-
-	/* Apple iMac11,1 */
-	{ USB_DEVICE(0x05ac, 0x8215) },
-
-	/* Apple MacBookPro6,2 */
-	{ USB_DEVICE(0x05ac, 0x8218) },
-
-	/* Apple MacBookAir3,1, MacBookAir3,2 */
-	{ USB_DEVICE(0x05ac, 0x821b) },
-
-	/* Apple MacBookPro8,2 */
-	{ USB_DEVICE(0x05ac, 0x821a) },
-
-	/* AVM BlueFRITZ! USB v2.0 */
-	{ USB_DEVICE(0x057c, 0x3800) },
-
-	/* Bluetooth Ultraport Module from IBM */
-	{ USB_DEVICE(0x04bf, 0x030a) },
-
-	/* ALPS Modules with non-standard id */
-	{ USB_DEVICE(0x044e, 0x3001) },
-	{ USB_DEVICE(0x044e, 0x3002) },
-
-	/* Ericsson with non-standard id */
-	{ USB_DEVICE(0x0bdb, 0x1002) },
-
-	/* Canyon CN-BTU1 with HID interfaces */
-	{ USB_DEVICE(0x0c10, 0x0000) },
-
-	{ }	/* Terminating entry */
-};
-
-MODULE_DEVICE_TABLE(usb, btusb_table);
-
-static struct usb_device_id blacklist_table[] = {
-	/* CSR BlueCore devices */
-	{ USB_DEVICE(0x0a12, 0x0001), .driver_info = BTUSB_CSR },
-
-	/* Broadcom BCM2033 without firmware */
-	{ USB_DEVICE(0x0a5c, 0x2033), .driver_info = BTUSB_IGNORE },
-
-	/* Atheros 3011 with sflash firmware */
-	{ USB_DEVICE(0x0cf3, 0x3002), .driver_info = BTUSB_IGNORE },
-	{ USB_DEVICE(0x13d3, 0x3304), .driver_info = BTUSB_IGNORE },
-
-	/* Atheros AR9285 Malbec with sflash firmware */
-	{ USB_DEVICE(0x03f0, 0x311d), .driver_info = BTUSB_IGNORE },
-
-	/* Atheros 3012 with sflash firmware */
-	{ USB_DEVICE(0x0cf3, 0x3004), .driver_info = BTUSB_IGNORE },
-
-	/* Atheros AR5BBU12 with sflash firmware */
-	{ USB_DEVICE(0x0489, 0xe02c), .driver_info = BTUSB_IGNORE },
-
-	/* Broadcom BCM2035 */
-	{ USB_DEVICE(0x0a5c, 0x2035), .driver_info = BTUSB_WRONG_SCO_MTU },
-	{ USB_DEVICE(0x0a5c, 0x200a), .driver_info = BTUSB_WRONG_SCO_MTU },
-	{ USB_DEVICE(0x0a5c, 0x2009), .driver_info = BTUSB_BCM92035 },
-
-	/* Broadcom BCM2045 */
-	{ USB_DEVICE(0x0a5c, 0x2039), .driver_info = BTUSB_WRONG_SCO_MTU },
-	{ USB_DEVICE(0x0a5c, 0x2101), .driver_info = BTUSB_WRONG_SCO_MTU },
-
-	/* IBM/Lenovo ThinkPad with Broadcom chip */
-	{ USB_DEVICE(0x0a5c, 0x201e), .driver_info = BTUSB_WRONG_SCO_MTU },
-	{ USB_DEVICE(0x0a5c, 0x2110), .driver_info = BTUSB_WRONG_SCO_MTU },
-
-	/* HP laptop with Broadcom chip */
-	{ USB_DEVICE(0x03f0, 0x171d), .driver_info = BTUSB_WRONG_SCO_MTU },
-
-	/* Dell laptop with Broadcom chip */
-	{ USB_DEVICE(0x413c, 0x8126), .driver_info = BTUSB_WRONG_SCO_MTU },
-
-	/* Dell Wireless 370 and 410 devices */
-	{ USB_DEVICE(0x413c, 0x8152), .driver_info = BTUSB_WRONG_SCO_MTU },
-	{ USB_DEVICE(0x413c, 0x8156), .driver_info = BTUSB_WRONG_SCO_MTU },
-
-	/* Belkin F8T012 and F8T013 devices */
-	{ USB_DEVICE(0x050d, 0x0012), .driver_info = BTUSB_WRONG_SCO_MTU },
-	{ USB_DEVICE(0x050d, 0x0013), .driver_info = BTUSB_WRONG_SCO_MTU },
-
-	/* Asus WL-BTD202 device */
-	{ USB_DEVICE(0x0b05, 0x1715), .driver_info = BTUSB_WRONG_SCO_MTU },
-
-	/* Kensington Bluetooth USB adapter */
-	{ USB_DEVICE(0x047d, 0x105e), .driver_info = BTUSB_WRONG_SCO_MTU },
-
-	/* RTX Telecom based adapters with buggy SCO support */
-	{ USB_DEVICE(0x0400, 0x0807), .driver_info = BTUSB_BROKEN_ISOC },
-	{ USB_DEVICE(0x0400, 0x080a), .driver_info = BTUSB_BROKEN_ISOC },
-
-	/* CONWISE Technology based adapters with buggy SCO support */
-	{ USB_DEVICE(0x0e5e, 0x6622), .driver_info = BTUSB_BROKEN_ISOC },
-
-	/* Digianswer devices */
-	{ USB_DEVICE(0x08fd, 0x0001), .driver_info = BTUSB_DIGIANSWER },
-	{ USB_DEVICE(0x08fd, 0x0002), .driver_info = BTUSB_IGNORE },
-
-	/* CSR BlueCore Bluetooth Sniffer */
-	{ USB_DEVICE(0x0a12, 0x0002), .driver_info = BTUSB_SNIFFER },
-
-	/* Frontline ComProbe Bluetooth Sniffer */
-	{ USB_DEVICE(0x16d3, 0x0002), .driver_info = BTUSB_SNIFFER },
-
-	{ }	/* Terminating entry */
-};
-
-#define BTUSB_MAX_ISOC_FRAMES	10
-
-#define BTUSB_INTR_RUNNING	0
-#define BTUSB_BULK_RUNNING	1
-#define BTUSB_ISOC_RUNNING	2
-#define BTUSB_SUSPENDING	3
-#define BTUSB_DID_ISO_RESUME	4
-
-struct btusb_data {
-	struct hci_dev       *hdev;
-	struct usb_device    *udev;
-	struct usb_interface *intf;
-	struct usb_interface *isoc;
-
-	spinlock_t lock;
-
-	unsigned long flags;
-
-	struct work_struct work;
-	struct work_struct waker;
-
-	struct usb_anchor tx_anchor;
-	struct usb_anchor intr_anchor;
-	struct usb_anchor bulk_anchor;
-	struct usb_anchor isoc_anchor;
-	struct usb_anchor deferred;
-	int tx_in_flight;
-	spinlock_t txlock;
-
-	struct usb_endpoint_descriptor *intr_ep;
-	struct usb_endpoint_descriptor *bulk_tx_ep;
-	struct usb_endpoint_descriptor *bulk_rx_ep;
-	struct usb_endpoint_descriptor *isoc_tx_ep;
-	struct usb_endpoint_descriptor *isoc_rx_ep;
-
-	__u8 cmdreq_type;
-
-	unsigned int sco_num;
-	int isoc_altsetting;
-	int suspend_count;
-};
-
-static int inc_tx(struct btusb_data *data)
-{
-	unsigned long flags;
-	int rv;
-
-	spin_lock_irqsave(&data->txlock, flags);
-	rv = test_bit(BTUSB_SUSPENDING, &data->flags);
-	if (!rv)
-		data->tx_in_flight++;
-	spin_unlock_irqrestore(&data->txlock, flags);
-
-	return rv;
-}
-
-static void btusb_intr_complete(struct urb *urb)
-{
-	struct hci_dev *hdev = urb->context;
-	struct btusb_data *data = hdev->driver_data;
-	int err;
-
-	BT_DBG("%s urb %p status %d count %d", hdev->name,
-					urb, urb->status, urb->actual_length);
-
-	if (!test_bit(HCI_RUNNING, &hdev->flags))
-		return;
-
-	if (urb->status == 0) {
-		hdev->stat.byte_rx += urb->actual_length;
-
-		if (hci_recv_fragment(hdev, HCI_EVENT_PKT,
-						urb->transfer_buffer,
-						urb->actual_length) < 0) {
-			BT_ERR("%s corrupted event packet", hdev->name);
-			hdev->stat.err_rx++;
-		}
-	}
-
-	if (!test_bit(BTUSB_INTR_RUNNING, &data->flags))
-		return;
-
-	usb_mark_last_busy(data->udev);
-	usb_anchor_urb(urb, &data->intr_anchor);
-
-	err = usb_submit_urb(urb, GFP_ATOMIC);
-	if (err < 0) {
-		if (err != -EPERM)
-			BT_ERR("%s urb %p failed to resubmit (%d)",
-						hdev->name, urb, -err);
-		usb_unanchor_urb(urb);
-	}
-}
-
-static int btusb_submit_intr_urb(struct hci_dev *hdev, gfp_t mem_flags)
-{
-	struct btusb_data *data = hdev->driver_data;
-	struct urb *urb;
-	unsigned char *buf;
-	unsigned int pipe;
-	int err, size;
-
-	BT_DBG("%s", hdev->name);
-
-	if (!data->intr_ep)
-		return -ENODEV;
-
-	urb = usb_alloc_urb(0, mem_flags);
-	if (!urb)
-		return -ENOMEM;
-
-	size = le16_to_cpu(data->intr_ep->wMaxPacketSize);
-
-	buf = kmalloc(size, mem_flags);
-	if (!buf) {
-		usb_free_urb(urb);
-		return -ENOMEM;
-	}
-
-	pipe = usb_rcvintpipe(data->udev, data->intr_ep->bEndpointAddress);
-
-	usb_fill_int_urb(urb, data->udev, pipe, buf, size,
-						btusb_intr_complete, hdev,
-						data->intr_ep->bInterval);
-
-	urb->transfer_flags |= URB_FREE_BUFFER;
-
-	usb_anchor_urb(urb, &data->intr_anchor);
-
-	err = usb_submit_urb(urb, mem_flags);
-	if (err < 0) {
-		BT_ERR("%s urb %p submission failed (%d)",
-						hdev->name, urb, -err);
-		usb_unanchor_urb(urb);
-	}
-
-	usb_free_urb(urb);
-
-	return err;
-}
-
-static void btusb_bulk_complete(struct urb *urb)
-{
-	struct hci_dev *hdev = urb->context;
-	struct btusb_data *data = hdev->driver_data;
-	int err;
-
-	BT_DBG("%s urb %p status %d count %d", hdev->name,
-					urb, urb->status, urb->actual_length);
-
-	if (!test_bit(HCI_RUNNING, &hdev->flags))
-		return;
-
-	if (urb->status == 0) {
-		hdev->stat.byte_rx += urb->actual_length;
-
-		if (hci_recv_fragment(hdev, HCI_ACLDATA_PKT,
-						urb->transfer_buffer,
-						urb->actual_length) < 0) {
-			BT_ERR("%s corrupted ACL packet", hdev->name);
-			hdev->stat.err_rx++;
-		}
-	}
-
-	if (!test_bit(BTUSB_BULK_RUNNING, &data->flags))
-		return;
-
-	usb_anchor_urb(urb, &data->bulk_anchor);
-	usb_mark_last_busy(data->udev);
-
-	err = usb_submit_urb(urb, GFP_ATOMIC);
-	if (err < 0) {
-		if (err != -EPERM)
-			BT_ERR("%s urb %p failed to resubmit (%d)",
-						hdev->name, urb, -err);
-		usb_unanchor_urb(urb);
-	}
-}
-
-static int btusb_submit_bulk_urb(struct hci_dev *hdev, gfp_t mem_flags)
-{
-	struct btusb_data *data = hdev->driver_data;
-	struct urb *urb;
-	unsigned char *buf;
-	unsigned int pipe;
-	int err, size = HCI_MAX_FRAME_SIZE;
-
-	BT_DBG("%s", hdev->name);
-
-	if (!data->bulk_rx_ep)
-		return -ENODEV;
-
-	urb = usb_alloc_urb(0, mem_flags);
-	if (!urb)
-		return -ENOMEM;
-
-	buf = kmalloc(size, mem_flags);
-	if (!buf) {
-		usb_free_urb(urb);
-		return -ENOMEM;
-	}
-
-	pipe = usb_rcvbulkpipe(data->udev, data->bulk_rx_ep->bEndpointAddress);
-
-	usb_fill_bulk_urb(urb, data->udev, pipe,
-					buf, size, btusb_bulk_complete, hdev);
-
-	urb->transfer_flags |= URB_FREE_BUFFER;
-
-	usb_mark_last_busy(data->udev);
-	usb_anchor_urb(urb, &data->bulk_anchor);
-
-	err = usb_submit_urb(urb, mem_flags);
-	if (err < 0) {
-		BT_ERR("%s urb %p submission failed (%d)",
-						hdev->name, urb, -err);
-		usb_unanchor_urb(urb);
-	}
-
-	usb_free_urb(urb);
-
-	return err;
-}
-
-static void btusb_isoc_complete(struct urb *urb)
-{
-	struct hci_dev *hdev = urb->context;
-	struct btusb_data *data = hdev->driver_data;
-	int i, err;
-
-	BT_DBG("%s urb %p status %d count %d", hdev->name,
-					urb, urb->status, urb->actual_length);
-
-	if (!test_bit(HCI_RUNNING, &hdev->flags))
-		return;
-
-	if (urb->status == 0) {
-		for (i = 0; i < urb->number_of_packets; i++) {
-			unsigned int offset = urb->iso_frame_desc[i].offset;
-			unsigned int length = urb->iso_frame_desc[i].actual_length;
-
-			if (urb->iso_frame_desc[i].status)
-				continue;
-
-			hdev->stat.byte_rx += length;
-
-			if (hci_recv_fragment(hdev, HCI_SCODATA_PKT,
-						urb->transfer_buffer + offset,
-								length) < 0) {
-				BT_ERR("%s corrupted SCO packet", hdev->name);
-				hdev->stat.err_rx++;
-			}
-		}
-	}
-
-	if (!test_bit(BTUSB_ISOC_RUNNING, &data->flags))
-		return;
-
-	usb_anchor_urb(urb, &data->isoc_anchor);
-
-	err = usb_submit_urb(urb, GFP_ATOMIC);
-	if (err < 0) {
-		if (err != -EPERM)
-			BT_ERR("%s urb %p failed to resubmit (%d)",
-						hdev->name, urb, -err);
-		usb_unanchor_urb(urb);
-	}
-}
-
-static inline void __fill_isoc_descriptor(struct urb *urb, int len, int mtu)
-{
-	int i, offset = 0;
-
-	BT_DBG("len %d mtu %d", len, mtu);
-
-	for (i = 0; i < BTUSB_MAX_ISOC_FRAMES && len >= mtu;
-					i++, offset += mtu, len -= mtu) {
-		urb->iso_frame_desc[i].offset = offset;
-		urb->iso_frame_desc[i].length = mtu;
-	}
-
-	if (len && i < BTUSB_MAX_ISOC_FRAMES) {
-		urb->iso_frame_desc[i].offset = offset;
-		urb->iso_frame_desc[i].length = len;
-		i++;
-	}
-
-	urb->number_of_packets = i;
-}
-
-static int btusb_submit_isoc_urb(struct hci_dev *hdev, gfp_t mem_flags)
-{
-	struct btusb_data *data = hdev->driver_data;
-	struct urb *urb;
-	unsigned char *buf;
-	unsigned int pipe;
-	int err, size;
-
-	BT_DBG("%s", hdev->name);
-
-	if (!data->isoc_rx_ep)
-		return -ENODEV;
-
-	urb = usb_alloc_urb(BTUSB_MAX_ISOC_FRAMES, mem_flags);
-	if (!urb)
-		return -ENOMEM;
-
-	size = le16_to_cpu(data->isoc_rx_ep->wMaxPacketSize) *
-						BTUSB_MAX_ISOC_FRAMES;
-
-	buf = kmalloc(size, mem_flags);
-	if (!buf) {
-		usb_free_urb(urb);
-		return -ENOMEM;
-	}
-
-	pipe = usb_rcvisocpipe(data->udev, data->isoc_rx_ep->bEndpointAddress);
-
-	urb->dev      = data->udev;
-	urb->pipe     = pipe;
-	urb->context  = hdev;
-	urb->complete = btusb_isoc_complete;
-	urb->interval = data->isoc_rx_ep->bInterval;
-
-	urb->transfer_flags  = URB_FREE_BUFFER | URB_ISO_ASAP;
-	urb->transfer_buffer = buf;
-	urb->transfer_buffer_length = size;
-
-	__fill_isoc_descriptor(urb, size,
-			le16_to_cpu(data->isoc_rx_ep->wMaxPacketSize));
-
-	usb_anchor_urb(urb, &data->isoc_anchor);
-
-	err = usb_submit_urb(urb, mem_flags);
-	if (err < 0) {
-		BT_ERR("%s urb %p submission failed (%d)",
-						hdev->name, urb, -err);
-		usb_unanchor_urb(urb);
-	}
-
-	usb_free_urb(urb);
-
-	return err;
-}
-
-static void btusb_tx_complete(struct urb *urb)
-{
-	struct sk_buff *skb = urb->context;
-	struct hci_dev *hdev = (struct hci_dev *) skb->dev;
-	struct btusb_data *data = hdev->driver_data;
-
-	BT_DBG("%s urb %p status %d count %d", hdev->name,
-					urb, urb->status, urb->actual_length);
-
-	if (!test_bit(HCI_RUNNING, &hdev->flags))
-		goto done;
-
-	if (!urb->status)
-		hdev->stat.byte_tx += urb->transfer_buffer_length;
-	else
-		hdev->stat.err_tx++;
-
-done:
-	spin_lock(&data->txlock);
-	data->tx_in_flight--;
-	spin_unlock(&data->txlock);
-
-	kfree(urb->setup_packet);
-
-	kfree_skb(skb);
-}
-
-static void btusb_isoc_tx_complete(struct urb *urb)
-{
-	struct sk_buff *skb = urb->context;
-	struct hci_dev *hdev = (struct hci_dev *) skb->dev;
-
-	BT_DBG("%s urb %p status %d count %d", hdev->name,
-					urb, urb->status, urb->actual_length);
-
-	if (!test_bit(HCI_RUNNING, &hdev->flags))
-		goto done;
-
-	if (!urb->status)
-		hdev->stat.byte_tx += urb->transfer_buffer_length;
-	else
-		hdev->stat.err_tx++;
-
-done:
-	kfree(urb->setup_packet);
-
-	kfree_skb(skb);
-}
-
-static int btusb_open(struct hci_dev *hdev)
-{
-	struct btusb_data *data = hdev->driver_data;
-	int err;
-
-	BT_DBG("%s", hdev->name);
-
-	err = usb_autopm_get_interface(data->intf);
-	if (err < 0)
-		return err;
-
-	data->intf->needs_remote_wakeup = 1;
-
-	if (test_and_set_bit(HCI_RUNNING, &hdev->flags))
-		goto done;
-
-	if (test_and_set_bit(BTUSB_INTR_RUNNING, &data->flags))
-		goto done;
-
-	err = btusb_submit_intr_urb(hdev, GFP_KERNEL);
-	if (err < 0)
-		goto failed;
-
-	err = btusb_submit_bulk_urb(hdev, GFP_KERNEL);
-	if (err < 0) {
-		usb_kill_anchored_urbs(&data->intr_anchor);
-		goto failed;
-	}
-
-	set_bit(BTUSB_BULK_RUNNING, &data->flags);
-	btusb_submit_bulk_urb(hdev, GFP_KERNEL);
-
-done:
-	usb_autopm_put_interface(data->intf);
-	return 0;
-
-failed:
-	clear_bit(BTUSB_INTR_RUNNING, &data->flags);
-	clear_bit(HCI_RUNNING, &hdev->flags);
-	usb_autopm_put_interface(data->intf);
-	return err;
-}
-
-static void btusb_stop_traffic(struct btusb_data *data)
-{
-	usb_kill_anchored_urbs(&data->intr_anchor);
-	usb_kill_anchored_urbs(&data->bulk_anchor);
-	usb_kill_anchored_urbs(&data->isoc_anchor);
-}
-
-static int btusb_close(struct hci_dev *hdev)
-{
-	struct btusb_data *data = hdev->driver_data;
-	int err;
-
-	BT_DBG("%s", hdev->name);
-
-	if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
-		return 0;
-
-	cancel_work_sync(&data->work);
-	cancel_work_sync(&data->waker);
-
-	clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
-	clear_bit(BTUSB_BULK_RUNNING, &data->flags);
-	clear_bit(BTUSB_INTR_RUNNING, &data->flags);
-
-	btusb_stop_traffic(data);
-	err = usb_autopm_get_interface(data->intf);
-	if (err < 0)
-		goto failed;
-
-	data->intf->needs_remote_wakeup = 0;
-	usb_autopm_put_interface(data->intf);
-
-failed:
-	usb_scuttle_anchored_urbs(&data->deferred);
-	return 0;
-}
-
-static int btusb_flush(struct hci_dev *hdev)
-{
-	struct btusb_data *data = hdev->driver_data;
-
-	BT_DBG("%s", hdev->name);
-
-	usb_kill_anchored_urbs(&data->tx_anchor);
-
-	return 0;
-}
-
-static int btusb_send_frame(struct sk_buff *skb)
-{
-	struct hci_dev *hdev = (struct hci_dev *) skb->dev;
-	struct btusb_data *data = hdev->driver_data;
-	struct usb_ctrlrequest *dr;
-	struct urb *urb;
-	unsigned int pipe;
-	int err;
-
-	BT_DBG("%s", hdev->name);
-
-	if (!test_bit(HCI_RUNNING, &hdev->flags))
-		return -EBUSY;
-
-	switch (bt_cb(skb)->pkt_type) {
-	case HCI_COMMAND_PKT:
-		urb = usb_alloc_urb(0, GFP_ATOMIC);
-		if (!urb)
-			return -ENOMEM;
-
-		dr = kmalloc(sizeof(*dr), GFP_ATOMIC);
-		if (!dr) {
-			usb_free_urb(urb);
-			return -ENOMEM;
-		}
-
-		dr->bRequestType = data->cmdreq_type;
-		dr->bRequest     = 0;
-		dr->wIndex       = 0;
-		dr->wValue       = 0;
-		dr->wLength      = __cpu_to_le16(skb->len);
-
-		pipe = usb_sndctrlpipe(data->udev, 0x00);
-
-		usb_fill_control_urb(urb, data->udev, pipe, (void *) dr,
-				skb->data, skb->len, btusb_tx_complete, skb);
-
-		hdev->stat.cmd_tx++;
-		break;
-
-	case HCI_ACLDATA_PKT:
-		if (!data->bulk_tx_ep || (hdev->conn_hash.acl_num < 1 &&
-						hdev->conn_hash.le_num < 1))
-			return -ENODEV;
-
-		urb = usb_alloc_urb(0, GFP_ATOMIC);
-		if (!urb)
-			return -ENOMEM;
-
-		pipe = usb_sndbulkpipe(data->udev,
-					data->bulk_tx_ep->bEndpointAddress);
-
-		usb_fill_bulk_urb(urb, data->udev, pipe,
-				skb->data, skb->len, btusb_tx_complete, skb);
-
-		hdev->stat.acl_tx++;
-		break;
-
-	case HCI_SCODATA_PKT:
-		if (!data->isoc_tx_ep || hdev->conn_hash.sco_num < 1)
-			return -ENODEV;
-
-		urb = usb_alloc_urb(BTUSB_MAX_ISOC_FRAMES, GFP_ATOMIC);
-		if (!urb)
-			return -ENOMEM;
-
-		pipe = usb_sndisocpipe(data->udev,
-					data->isoc_tx_ep->bEndpointAddress);
-
-		usb_fill_int_urb(urb, data->udev, pipe,
-				skb->data, skb->len, btusb_isoc_tx_complete,
-				skb, data->isoc_tx_ep->bInterval);
-
-		urb->transfer_flags  = URB_ISO_ASAP;
-
-		__fill_isoc_descriptor(urb, skb->len,
-				le16_to_cpu(data->isoc_tx_ep->wMaxPacketSize));
-
-		hdev->stat.sco_tx++;
-		goto skip_waking;
-
-	default:
-		return -EILSEQ;
-	}
-
-	err = inc_tx(data);
-	if (err) {
-		usb_anchor_urb(urb, &data->deferred);
-		schedule_work(&data->waker);
-		err = 0;
-		goto done;
-	}
-
-skip_waking:
-	usb_anchor_urb(urb, &data->tx_anchor);
-
-	err = usb_submit_urb(urb, GFP_ATOMIC);
-	if (err < 0) {
-		BT_ERR("%s urb %p submission failed", hdev->name, urb);
-		kfree(urb->setup_packet);
-		usb_unanchor_urb(urb);
-	} else {
-		usb_mark_last_busy(data->udev);
-	}
-
-	usb_free_urb(urb);
-
-done:
-	return err;
-}
-
-static void btusb_destruct(struct hci_dev *hdev)
-{
-	struct btusb_data *data = hdev->driver_data;
-
-	BT_DBG("%s", hdev->name);
-
-	kfree(data);
-}
-
-static void btusb_notify(struct hci_dev *hdev, unsigned int evt)
-{
-	struct btusb_data *data = hdev->driver_data;
-
-	BT_DBG("%s evt %d", hdev->name, evt);
-
-	if (hdev->conn_hash.sco_num != data->sco_num) {
-		data->sco_num = hdev->conn_hash.sco_num;
-		schedule_work(&data->work);
-	}
-}
-
-static inline int __set_isoc_interface(struct hci_dev *hdev, int altsetting)
-{
-	struct btusb_data *data = hdev->driver_data;
-	struct usb_interface *intf = data->isoc;
-	struct usb_endpoint_descriptor *ep_desc;
-	int i, err;
-
-	if (!data->isoc)
-		return -ENODEV;
-
-	err = usb_set_interface(data->udev, 1, altsetting);
-	if (err < 0) {
-		BT_ERR("%s setting interface failed (%d)", hdev->name, -err);
-		return err;
-	}
-
-	data->isoc_altsetting = altsetting;
-
-	data->isoc_tx_ep = NULL;
-	data->isoc_rx_ep = NULL;
-
-	for (i = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++) {
-		ep_desc = &intf->cur_altsetting->endpoint[i].desc;
-
-		if (!data->isoc_tx_ep && usb_endpoint_is_isoc_out(ep_desc)) {
-			data->isoc_tx_ep = ep_desc;
-			continue;
-		}
-
-		if (!data->isoc_rx_ep && usb_endpoint_is_isoc_in(ep_desc)) {
-			data->isoc_rx_ep = ep_desc;
-			continue;
-		}
-	}
-
-	if (!data->isoc_tx_ep || !data->isoc_rx_ep) {
-		BT_ERR("%s invalid SCO descriptors", hdev->name);
-		return -ENODEV;
-	}
-
-	return 0;
-}
-
-static void btusb_work(struct work_struct *work)
-{
-	struct btusb_data *data = container_of(work, struct btusb_data, work);
-	struct hci_dev *hdev = data->hdev;
-	int err;
-
-	if (hdev->conn_hash.sco_num > 0) {
-		if (!test_bit(BTUSB_DID_ISO_RESUME, &data->flags)) {
-			err = usb_autopm_get_interface(data->isoc ? data->isoc : data->intf);
-			if (err < 0) {
-				clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
-				usb_kill_anchored_urbs(&data->isoc_anchor);
-				return;
-			}
-
-			set_bit(BTUSB_DID_ISO_RESUME, &data->flags);
-		}
-		if (data->isoc_altsetting != 2) {
-			clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
-			usb_kill_anchored_urbs(&data->isoc_anchor);
-
-			if (__set_isoc_interface(hdev, 2) < 0)
-				return;
-		}
-
-		if (!test_and_set_bit(BTUSB_ISOC_RUNNING, &data->flags)) {
-			if (btusb_submit_isoc_urb(hdev, GFP_KERNEL) < 0)
-				clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
-			else
-				btusb_submit_isoc_urb(hdev, GFP_KERNEL);
-		}
-	} else {
-		clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
-		usb_kill_anchored_urbs(&data->isoc_anchor);
-
-		__set_isoc_interface(hdev, 0);
-		if (test_and_clear_bit(BTUSB_DID_ISO_RESUME, &data->flags))
-			usb_autopm_put_interface(data->isoc ? data->isoc : data->intf);
-	}
-}
-
-static void btusb_waker(struct work_struct *work)
-{
-	struct btusb_data *data = container_of(work, struct btusb_data, waker);
-	int err;
-
-	err = usb_autopm_get_interface(data->intf);
-	if (err < 0)
-		return;
-
-	usb_autopm_put_interface(data->intf);
-}
-
-static int btusb_probe(struct usb_interface *intf,
-				const struct usb_device_id *id)
-{
-	struct usb_endpoint_descriptor *ep_desc;
-	struct btusb_data *data;
-	struct hci_dev *hdev;
-	int i, err;
-
-	BT_DBG("intf %p id %p", intf, id);
-
-	/* interface numbers are hardcoded in the spec */
-	if (intf->cur_altsetting->desc.bInterfaceNumber != 0)
-		return -ENODEV;
-
-	if (!id->driver_info) {
-		const struct usb_device_id *match;
-		match = usb_match_id(intf, blacklist_table);
-		if (match)
-			id = match;
-	}
-
-	if (id->driver_info == BTUSB_IGNORE)
-		return -ENODEV;
-
-	if (ignore_dga && id->driver_info & BTUSB_DIGIANSWER)
-		return -ENODEV;
-
-	if (ignore_csr && id->driver_info & BTUSB_CSR)
-		return -ENODEV;
-
-	if (ignore_sniffer && id->driver_info & BTUSB_SNIFFER)
-		return -ENODEV;
-
-	data = kzalloc(sizeof(*data), GFP_KERNEL);
-	if (!data)
-		return -ENOMEM;
-
-	for (i = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++) {
-		ep_desc = &intf->cur_altsetting->endpoint[i].desc;
-
-		if (!data->intr_ep && usb_endpoint_is_int_in(ep_desc)) {
-			data->intr_ep = ep_desc;
-			continue;
-		}
-
-		if (!data->bulk_tx_ep && usb_endpoint_is_bulk_out(ep_desc)) {
-			data->bulk_tx_ep = ep_desc;
-			continue;
-		}
-
-		if (!data->bulk_rx_ep && usb_endpoint_is_bulk_in(ep_desc)) {
-			data->bulk_rx_ep = ep_desc;
-			continue;
-		}
-	}
-
-	if (!data->intr_ep || !data->bulk_tx_ep || !data->bulk_rx_ep) {
-		kfree(data);
-		return -ENODEV;
-	}
-
-	data->cmdreq_type = USB_TYPE_CLASS;
-
-	data->udev = interface_to_usbdev(intf);
-	data->intf = intf;
-
-	spin_lock_init(&data->lock);
-
-	INIT_WORK(&data->work, btusb_work);
-	INIT_WORK(&data->waker, btusb_waker);
-	spin_lock_init(&data->txlock);
-
-	init_usb_anchor(&data->tx_anchor);
-	init_usb_anchor(&data->intr_anchor);
-	init_usb_anchor(&data->bulk_anchor);
-	init_usb_anchor(&data->isoc_anchor);
-	init_usb_anchor(&data->deferred);
-
-	hdev = hci_alloc_dev();
-	if (!hdev) {
-		kfree(data);
-		return -ENOMEM;
-	}
-
-	hdev->bus = HCI_USB;
-	hdev->driver_data = data;
-
-	data->hdev = hdev;
-
-	SET_HCIDEV_DEV(hdev, &intf->dev);
-
-	hdev->open     = btusb_open;
-	hdev->close    = btusb_close;
-	hdev->flush    = btusb_flush;
-	hdev->send     = btusb_send_frame;
-	hdev->destruct = btusb_destruct;
-	hdev->notify   = btusb_notify;
-
-	hdev->owner = THIS_MODULE;
-
-	/* Interface numbers are hardcoded in the specification */
-	data->isoc = usb_ifnum_to_if(data->udev, 1);
-
-	if (!reset)
-		set_bit(HCI_QUIRK_NO_RESET, &hdev->quirks);
-
-	if (force_scofix || id->driver_info & BTUSB_WRONG_SCO_MTU) {
-		if (!disable_scofix)
-			set_bit(HCI_QUIRK_FIXUP_BUFFER_SIZE, &hdev->quirks);
-	}
-
-	if (id->driver_info & BTUSB_BROKEN_ISOC)
-		data->isoc = NULL;
-
-	if (id->driver_info & BTUSB_DIGIANSWER) {
-		data->cmdreq_type = USB_TYPE_VENDOR;
-		set_bit(HCI_QUIRK_NO_RESET, &hdev->quirks);
-	}
-
-	if (id->driver_info & BTUSB_CSR) {
-		struct usb_device *udev = data->udev;
-
-		/* Old firmware would otherwise execute USB reset */
-		if (le16_to_cpu(udev->descriptor.bcdDevice) < 0x117)
-			set_bit(HCI_QUIRK_NO_RESET, &hdev->quirks);
-	}
-
-	if (id->driver_info & BTUSB_SNIFFER) {
-		struct usb_device *udev = data->udev;
-
-		/* New sniffer firmware has crippled HCI interface */
-		if (le16_to_cpu(udev->descriptor.bcdDevice) > 0x997)
-			set_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks);
-
-		data->isoc = NULL;
-	}
-
-	if (id->driver_info & BTUSB_BCM92035) {
-		unsigned char cmd[] = { 0x3b, 0xfc, 0x01, 0x00 };
-		struct sk_buff *skb;
-
-		skb = bt_skb_alloc(sizeof(cmd), GFP_KERNEL);
-		if (skb) {
-			memcpy(skb_put(skb, sizeof(cmd)), cmd, sizeof(cmd));
-			skb_queue_tail(&hdev->driver_init, skb);
-		}
-	}
-
-	if (data->isoc) {
-		err = usb_driver_claim_interface(&btusb_driver,
-							data->isoc, data);
-		if (err < 0) {
-			hci_free_dev(hdev);
-			kfree(data);
-			return err;
-		}
-	}
-
-	err = hci_register_dev(hdev);
-	if (err < 0) {
-		hci_free_dev(hdev);
-		kfree(data);
-		return err;
-	}
-
-	usb_set_intfdata(intf, data);
-
-	return 0;
-}
-
-static void btusb_disconnect(struct usb_interface *intf)
-{
-	struct btusb_data *data = usb_get_intfdata(intf);
-	struct hci_dev *hdev;
-
-	BT_DBG("intf %p", intf);
-
-	if (!data)
-		return;
-
-	hdev = data->hdev;
-
-	__hci_dev_hold(hdev);
-
-	usb_set_intfdata(data->intf, NULL);
-
-	if (data->isoc)
-		usb_set_intfdata(data->isoc, NULL);
-
-	hci_unregister_dev(hdev);
-
-	if (intf == data->isoc)
-		usb_driver_release_interface(&btusb_driver, data->intf);
-	else if (data->isoc)
-		usb_driver_release_interface(&btusb_driver, data->isoc);
-
-	__hci_dev_put(hdev);
-
-	hci_free_dev(hdev);
-}
-
-#ifdef CONFIG_PM
-static int btusb_suspend(struct usb_interface *intf, pm_message_t message)
-{
-	struct btusb_data *data = usb_get_intfdata(intf);
-
-	BT_DBG("intf %p", intf);
-
-	if (data->suspend_count++)
-		return 0;
-
-	spin_lock_irq(&data->txlock);
-	if (!((message.event & PM_EVENT_AUTO) && data->tx_in_flight)) {
-		set_bit(BTUSB_SUSPENDING, &data->flags);
-		spin_unlock_irq(&data->txlock);
-	} else {
-		spin_unlock_irq(&data->txlock);
-		data->suspend_count--;
-		return -EBUSY;
-	}
-
-	cancel_work_sync(&data->work);
-
-	btusb_stop_traffic(data);
-	usb_kill_anchored_urbs(&data->tx_anchor);
-
-	return 0;
-}
-
-static void play_deferred(struct btusb_data *data)
-{
-	struct urb *urb;
-	int err;
-
-	while ((urb = usb_get_from_anchor(&data->deferred))) {
-		err = usb_submit_urb(urb, GFP_ATOMIC);
-		if (err < 0)
-			break;
-
-		data->tx_in_flight++;
-	}
-	usb_scuttle_anchored_urbs(&data->deferred);
-}
-
-static int btusb_resume(struct usb_interface *intf)
-{
-	struct btusb_data *data = usb_get_intfdata(intf);
-	struct hci_dev *hdev = data->hdev;
-	int err = 0;
-
-	BT_DBG("intf %p", intf);
-
-	if (--data->suspend_count)
-		return 0;
-
-	if (!test_bit(HCI_RUNNING, &hdev->flags))
-		goto done;
-
-	if (test_bit(BTUSB_INTR_RUNNING, &data->flags)) {
-		err = btusb_submit_intr_urb(hdev, GFP_NOIO);
-		if (err < 0) {
-			clear_bit(BTUSB_INTR_RUNNING, &data->flags);
-			goto failed;
-		}
-	}
-
-	if (test_bit(BTUSB_BULK_RUNNING, &data->flags)) {
-		err = btusb_submit_bulk_urb(hdev, GFP_NOIO);
-		if (err < 0) {
-			clear_bit(BTUSB_BULK_RUNNING, &data->flags);
-			goto failed;
-		}
-
-		btusb_submit_bulk_urb(hdev, GFP_NOIO);
-	}
-
-	if (test_bit(BTUSB_ISOC_RUNNING, &data->flags)) {
-		if (btusb_submit_isoc_urb(hdev, GFP_NOIO) < 0)
-			clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
-		else
-			btusb_submit_isoc_urb(hdev, GFP_NOIO);
-	}
-
-	spin_lock_irq(&data->txlock);
-	play_deferred(data);
-	clear_bit(BTUSB_SUSPENDING, &data->flags);
-	spin_unlock_irq(&data->txlock);
-	schedule_work(&data->work);
-
-	return 0;
-
-failed:
-	usb_scuttle_anchored_urbs(&data->deferred);
-done:
-	spin_lock_irq(&data->txlock);
-	clear_bit(BTUSB_SUSPENDING, &data->flags);
-	spin_unlock_irq(&data->txlock);
-
-	return err;
-}
-#endif
-
-static struct usb_driver btusb_driver = {
-	.name		= "btusb",
-	.probe		= btusb_probe,
-	.disconnect	= btusb_disconnect,
-#ifdef CONFIG_PM
-	.suspend	= btusb_suspend,
-	.resume		= btusb_resume,
-#endif
-	.id_table	= btusb_table,
-	.supports_autosuspend = 1,
-};
-
-static int __init btusb_init(void)
-{
-	BT_INFO("Generic Bluetooth USB driver ver %s", VERSION);
-
-	return usb_register(&btusb_driver);
-}
-
-static void __exit btusb_exit(void)
-{
-	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");
-
-module_param(ignore_csr, bool, 0644);
-MODULE_PARM_DESC(ignore_csr, "Ignore devices with id 0a12:0001");
-
-module_param(ignore_sniffer, bool, 0644);
-MODULE_PARM_DESC(ignore_sniffer, "Ignore devices with id 0a12:0002");
-
-module_param(disable_scofix, bool, 0644);
-MODULE_PARM_DESC(disable_scofix, "Disable fixup of wrong SCO buffer size");
-
-module_param(force_scofix, bool, 0644);
-MODULE_PARM_DESC(force_scofix, "Force fixup of wrong SCO buffers size");
-
-module_param(reset, bool, 0644);
-MODULE_PARM_DESC(reset, "Send HCI reset command on initialization");
-
-MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
-MODULE_DESCRIPTION("Generic Bluetooth USB driver ver " VERSION);
-MODULE_VERSION(VERSION);
-MODULE_LICENSE("GPL");
diff --git a/drivers/bluetooth/btusb_core.c b/drivers/bluetooth/btusb_core.c
new file mode 100644
index 0000000..1bd3219
--- /dev/null
+++ b/drivers/bluetooth/btusb_core.c
@@ -0,0 +1,958 @@
+/*
+ *
+ *  Generic Bluetooth USB driver
+ *
+ *  Copyright (C) 2005-2008  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/skbuff.h>
+
+#include <linux/usb.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#include "btusb.h"
+
+#define BTUSB_MAX_ISOC_FRAMES	10
+
+#define BTUSB_INTR_RUNNING	0
+#define BTUSB_BULK_RUNNING	1
+#define BTUSB_ISOC_RUNNING	2
+#define BTUSB_SUSPENDING	3
+#define BTUSB_DID_ISO_RESUME	4
+
+static int inc_tx(struct btusb_data *data)
+{
+	unsigned long flags;
+	int rv;
+
+	spin_lock_irqsave(&data->txlock, flags);
+	rv = test_bit(BTUSB_SUSPENDING, &data->flags);
+	if (!rv)
+		data->tx_in_flight++;
+	spin_unlock_irqrestore(&data->txlock, flags);
+
+	return rv;
+}
+
+static void btusb_intr_complete(struct urb *urb)
+{
+	struct hci_dev *hdev = urb->context;
+	struct btusb_data *data = hdev->driver_data;
+	int err;
+
+	BT_DBG("%s urb %p status %d count %d", hdev->name,
+					urb, urb->status, urb->actual_length);
+
+	if (!test_bit(HCI_RUNNING, &hdev->flags))
+		return;
+
+	if (urb->status == 0) {
+		hdev->stat.byte_rx += urb->actual_length;
+
+		if (hci_recv_fragment(hdev, HCI_EVENT_PKT,
+						urb->transfer_buffer,
+						urb->actual_length) < 0) {
+			BT_ERR("%s corrupted event packet", hdev->name);
+			hdev->stat.err_rx++;
+		}
+	}
+
+	if (!test_bit(BTUSB_INTR_RUNNING, &data->flags))
+		return;
+
+	usb_mark_last_busy(data->udev);
+	usb_anchor_urb(urb, &data->intr_anchor);
+
+	err = usb_submit_urb(urb, GFP_ATOMIC);
+	if (err < 0) {
+		if (err != -EPERM)
+			BT_ERR("%s urb %p failed to resubmit (%d)",
+						hdev->name, urb, -err);
+		usb_unanchor_urb(urb);
+	}
+}
+
+static int btusb_submit_intr_urb(struct hci_dev *hdev, gfp_t mem_flags)
+{
+	struct btusb_data *data = hdev->driver_data;
+	struct urb *urb;
+	unsigned char *buf;
+	unsigned int pipe;
+	int err, size;
+
+	BT_DBG("%s", hdev->name);
+
+	if (!data->intr_ep)
+		return -ENODEV;
+
+	urb = usb_alloc_urb(0, mem_flags);
+	if (!urb)
+		return -ENOMEM;
+
+	size = le16_to_cpu(data->intr_ep->wMaxPacketSize);
+
+	buf = kmalloc(size, mem_flags);
+	if (!buf) {
+		usb_free_urb(urb);
+		return -ENOMEM;
+	}
+
+	pipe = usb_rcvintpipe(data->udev, data->intr_ep->bEndpointAddress);
+
+	usb_fill_int_urb(urb, data->udev, pipe, buf, size,
+						btusb_intr_complete, hdev,
+						data->intr_ep->bInterval);
+
+	urb->transfer_flags |= URB_FREE_BUFFER;
+
+	usb_anchor_urb(urb, &data->intr_anchor);
+
+	err = usb_submit_urb(urb, mem_flags);
+	if (err < 0) {
+		BT_ERR("%s urb %p submission failed (%d)",
+						hdev->name, urb, -err);
+		usb_unanchor_urb(urb);
+	}
+
+	usb_free_urb(urb);
+
+	return err;
+}
+
+static void btusb_bulk_complete(struct urb *urb)
+{
+	struct hci_dev *hdev = urb->context;
+	struct btusb_data *data = hdev->driver_data;
+	int err;
+
+	BT_DBG("%s urb %p status %d count %d", hdev->name,
+					urb, urb->status, urb->actual_length);
+
+	if (!test_bit(HCI_RUNNING, &hdev->flags))
+		return;
+
+	if (urb->status == 0) {
+		hdev->stat.byte_rx += urb->actual_length;
+
+		if (hci_recv_fragment(hdev, HCI_ACLDATA_PKT,
+						urb->transfer_buffer,
+						urb->actual_length) < 0) {
+			BT_ERR("%s corrupted ACL packet", hdev->name);
+			hdev->stat.err_rx++;
+		}
+	}
+
+	if (!test_bit(BTUSB_BULK_RUNNING, &data->flags))
+		return;
+
+	usb_anchor_urb(urb, &data->bulk_anchor);
+	usb_mark_last_busy(data->udev);
+
+	err = usb_submit_urb(urb, GFP_ATOMIC);
+	if (err < 0) {
+		if (err != -EPERM)
+			BT_ERR("%s urb %p failed to resubmit (%d)",
+						hdev->name, urb, -err);
+		usb_unanchor_urb(urb);
+	}
+}
+
+static int btusb_submit_bulk_urb(struct hci_dev *hdev, gfp_t mem_flags)
+{
+	struct btusb_data *data = hdev->driver_data;
+	struct urb *urb;
+	unsigned char *buf;
+	unsigned int pipe;
+	int err, size = HCI_MAX_FRAME_SIZE;
+
+	BT_DBG("%s", hdev->name);
+
+	if (!data->bulk_rx_ep)
+		return -ENODEV;
+
+	urb = usb_alloc_urb(0, mem_flags);
+	if (!urb)
+		return -ENOMEM;
+
+	buf = kmalloc(size, mem_flags);
+	if (!buf) {
+		usb_free_urb(urb);
+		return -ENOMEM;
+	}
+
+	pipe = usb_rcvbulkpipe(data->udev, data->bulk_rx_ep->bEndpointAddress);
+
+	usb_fill_bulk_urb(urb, data->udev, pipe,
+					buf, size, btusb_bulk_complete, hdev);
+
+	urb->transfer_flags |= URB_FREE_BUFFER;
+
+	usb_mark_last_busy(data->udev);
+	usb_anchor_urb(urb, &data->bulk_anchor);
+
+	err = usb_submit_urb(urb, mem_flags);
+	if (err < 0) {
+		BT_ERR("%s urb %p submission failed (%d)",
+						hdev->name, urb, -err);
+		usb_unanchor_urb(urb);
+	}
+
+	usb_free_urb(urb);
+
+	return err;
+}
+
+static void btusb_isoc_complete(struct urb *urb)
+{
+	struct hci_dev *hdev = urb->context;
+	struct btusb_data *data = hdev->driver_data;
+	int i, err;
+
+	BT_DBG("%s urb %p status %d count %d", hdev->name,
+					urb, urb->status, urb->actual_length);
+
+	if (!test_bit(HCI_RUNNING, &hdev->flags))
+		return;
+
+	if (urb->status == 0) {
+		for (i = 0; i < urb->number_of_packets; i++) {
+			unsigned int offset = urb->iso_frame_desc[i].offset;
+			unsigned int length = urb->iso_frame_desc[i].actual_length;
+
+			if (urb->iso_frame_desc[i].status)
+				continue;
+
+			hdev->stat.byte_rx += length;
+
+			if (hci_recv_fragment(hdev, HCI_SCODATA_PKT,
+						urb->transfer_buffer + offset,
+								length) < 0) {
+				BT_ERR("%s corrupted SCO packet", hdev->name);
+				hdev->stat.err_rx++;
+			}
+		}
+	}
+
+	if (!test_bit(BTUSB_ISOC_RUNNING, &data->flags))
+		return;
+
+	usb_anchor_urb(urb, &data->isoc_anchor);
+
+	err = usb_submit_urb(urb, GFP_ATOMIC);
+	if (err < 0) {
+		if (err != -EPERM)
+			BT_ERR("%s urb %p failed to resubmit (%d)",
+						hdev->name, urb, -err);
+		usb_unanchor_urb(urb);
+	}
+}
+
+static inline void __fill_isoc_descriptor(struct urb *urb, int len, int mtu)
+{
+	int i, offset = 0;
+
+	BT_DBG("len %d mtu %d", len, mtu);
+
+	for (i = 0; i < BTUSB_MAX_ISOC_FRAMES && len >= mtu;
+					i++, offset += mtu, len -= mtu) {
+		urb->iso_frame_desc[i].offset = offset;
+		urb->iso_frame_desc[i].length = mtu;
+	}
+
+	if (len && i < BTUSB_MAX_ISOC_FRAMES) {
+		urb->iso_frame_desc[i].offset = offset;
+		urb->iso_frame_desc[i].length = len;
+		i++;
+	}
+
+	urb->number_of_packets = i;
+}
+
+static int btusb_submit_isoc_urb(struct hci_dev *hdev, gfp_t mem_flags)
+{
+	struct btusb_data *data = hdev->driver_data;
+	struct urb *urb;
+	unsigned char *buf;
+	unsigned int pipe;
+	int err, size;
+
+	BT_DBG("%s", hdev->name);
+
+	if (!data->isoc_rx_ep)
+		return -ENODEV;
+
+	urb = usb_alloc_urb(BTUSB_MAX_ISOC_FRAMES, mem_flags);
+	if (!urb)
+		return -ENOMEM;
+
+	size = le16_to_cpu(data->isoc_rx_ep->wMaxPacketSize) *
+						BTUSB_MAX_ISOC_FRAMES;
+
+	buf = kmalloc(size, mem_flags);
+	if (!buf) {
+		usb_free_urb(urb);
+		return -ENOMEM;
+	}
+
+	pipe = usb_rcvisocpipe(data->udev, data->isoc_rx_ep->bEndpointAddress);
+
+	urb->dev      = data->udev;
+	urb->pipe     = pipe;
+	urb->context  = hdev;
+	urb->complete = btusb_isoc_complete;
+	urb->interval = data->isoc_rx_ep->bInterval;
+
+	urb->transfer_flags  = URB_FREE_BUFFER | URB_ISO_ASAP;
+	urb->transfer_buffer = buf;
+	urb->transfer_buffer_length = size;
+
+	__fill_isoc_descriptor(urb, size,
+			le16_to_cpu(data->isoc_rx_ep->wMaxPacketSize));
+
+	usb_anchor_urb(urb, &data->isoc_anchor);
+
+	err = usb_submit_urb(urb, mem_flags);
+	if (err < 0) {
+		BT_ERR("%s urb %p submission failed (%d)",
+						hdev->name, urb, -err);
+		usb_unanchor_urb(urb);
+	}
+
+	usb_free_urb(urb);
+
+	return err;
+}
+
+static void btusb_tx_complete(struct urb *urb)
+{
+	struct sk_buff *skb = urb->context;
+	struct hci_dev *hdev = (struct hci_dev *) skb->dev;
+	struct btusb_data *data = hdev->driver_data;
+
+	BT_DBG("%s urb %p status %d count %d", hdev->name,
+					urb, urb->status, urb->actual_length);
+
+	if (!test_bit(HCI_RUNNING, &hdev->flags))
+		goto done;
+
+	if (!urb->status)
+		hdev->stat.byte_tx += urb->transfer_buffer_length;
+	else
+		hdev->stat.err_tx++;
+
+done:
+	spin_lock(&data->txlock);
+	data->tx_in_flight--;
+	spin_unlock(&data->txlock);
+
+	kfree(urb->setup_packet);
+
+	kfree_skb(skb);
+}
+
+static void btusb_isoc_tx_complete(struct urb *urb)
+{
+	struct sk_buff *skb = urb->context;
+	struct hci_dev *hdev = (struct hci_dev *) skb->dev;
+
+	BT_DBG("%s urb %p status %d count %d", hdev->name,
+					urb, urb->status, urb->actual_length);
+
+	if (!test_bit(HCI_RUNNING, &hdev->flags))
+		goto done;
+
+	if (!urb->status)
+		hdev->stat.byte_tx += urb->transfer_buffer_length;
+	else
+		hdev->stat.err_tx++;
+
+done:
+	kfree(urb->setup_packet);
+
+	kfree_skb(skb);
+}
+
+static int btusb_open(struct hci_dev *hdev)
+{
+	struct btusb_data *data = hdev->driver_data;
+	int err;
+
+	BT_DBG("%s", hdev->name);
+
+	err = usb_autopm_get_interface(data->intf);
+	if (err < 0)
+		return err;
+
+	data->intf->needs_remote_wakeup = 1;
+
+	if (test_and_set_bit(HCI_RUNNING, &hdev->flags))
+		goto done;
+
+	if (test_and_set_bit(BTUSB_INTR_RUNNING, &data->flags))
+		goto done;
+
+	err = btusb_submit_intr_urb(hdev, GFP_KERNEL);
+	if (err < 0)
+		goto failed;
+
+	err = btusb_submit_bulk_urb(hdev, GFP_KERNEL);
+	if (err < 0) {
+		usb_kill_anchored_urbs(&data->intr_anchor);
+		goto failed;
+	}
+
+	set_bit(BTUSB_BULK_RUNNING, &data->flags);
+	btusb_submit_bulk_urb(hdev, GFP_KERNEL);
+
+done:
+	usb_autopm_put_interface(data->intf);
+	return 0;
+
+failed:
+	clear_bit(BTUSB_INTR_RUNNING, &data->flags);
+	clear_bit(HCI_RUNNING, &hdev->flags);
+	usb_autopm_put_interface(data->intf);
+	return err;
+}
+
+static void btusb_stop_traffic(struct btusb_data *data)
+{
+	usb_kill_anchored_urbs(&data->intr_anchor);
+	usb_kill_anchored_urbs(&data->bulk_anchor);
+	usb_kill_anchored_urbs(&data->isoc_anchor);
+}
+
+static int btusb_close(struct hci_dev *hdev)
+{
+	struct btusb_data *data = hdev->driver_data;
+	int err;
+
+	BT_DBG("%s", hdev->name);
+
+	if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
+		return 0;
+
+	cancel_work_sync(&data->work);
+	cancel_work_sync(&data->waker);
+
+	clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
+	clear_bit(BTUSB_BULK_RUNNING, &data->flags);
+	clear_bit(BTUSB_INTR_RUNNING, &data->flags);
+
+	btusb_stop_traffic(data);
+	err = usb_autopm_get_interface(data->intf);
+	if (err < 0)
+		goto failed;
+
+	data->intf->needs_remote_wakeup = 0;
+	usb_autopm_put_interface(data->intf);
+
+failed:
+	usb_scuttle_anchored_urbs(&data->deferred);
+	return 0;
+}
+
+static int btusb_flush(struct hci_dev *hdev)
+{
+	struct btusb_data *data = hdev->driver_data;
+
+	BT_DBG("%s", hdev->name);
+
+	usb_kill_anchored_urbs(&data->tx_anchor);
+
+	return 0;
+}
+
+static int btusb_send_frame(struct sk_buff *skb)
+{
+	struct hci_dev *hdev = (struct hci_dev *) skb->dev;
+	struct btusb_data *data = hdev->driver_data;
+	struct usb_ctrlrequest *dr;
+	struct urb *urb;
+	unsigned int pipe;
+	int err;
+
+	BT_DBG("%s", hdev->name);
+
+	if (!test_bit(HCI_RUNNING, &hdev->flags))
+		return -EBUSY;
+
+	switch (bt_cb(skb)->pkt_type) {
+	case HCI_COMMAND_PKT:
+		urb = usb_alloc_urb(0, GFP_ATOMIC);
+		if (!urb)
+			return -ENOMEM;
+
+		dr = kmalloc(sizeof(*dr), GFP_ATOMIC);
+		if (!dr) {
+			usb_free_urb(urb);
+			return -ENOMEM;
+		}
+
+		dr->bRequestType = data->cmdreq_type;
+		dr->bRequest     = 0;
+		dr->wIndex       = 0;
+		dr->wValue       = 0;
+		dr->wLength      = __cpu_to_le16(skb->len);
+
+		pipe = usb_sndctrlpipe(data->udev, 0x00);
+
+		usb_fill_control_urb(urb, data->udev, pipe, (void *) dr,
+				skb->data, skb->len, btusb_tx_complete, skb);
+
+		hdev->stat.cmd_tx++;
+		break;
+
+	case HCI_ACLDATA_PKT:
+		if (!data->bulk_tx_ep || (hdev->conn_hash.acl_num < 1 &&
+						hdev->conn_hash.le_num < 1))
+			return -ENODEV;
+
+		urb = usb_alloc_urb(0, GFP_ATOMIC);
+		if (!urb)
+			return -ENOMEM;
+
+		pipe = usb_sndbulkpipe(data->udev,
+					data->bulk_tx_ep->bEndpointAddress);
+
+		usb_fill_bulk_urb(urb, data->udev, pipe,
+				skb->data, skb->len, btusb_tx_complete, skb);
+
+		hdev->stat.acl_tx++;
+		break;
+
+	case HCI_SCODATA_PKT:
+		if (!data->isoc_tx_ep || hdev->conn_hash.sco_num < 1)
+			return -ENODEV;
+
+		urb = usb_alloc_urb(BTUSB_MAX_ISOC_FRAMES, GFP_ATOMIC);
+		if (!urb)
+			return -ENOMEM;
+
+		pipe = usb_sndisocpipe(data->udev,
+					data->isoc_tx_ep->bEndpointAddress);
+
+		usb_fill_int_urb(urb, data->udev, pipe,
+				skb->data, skb->len, btusb_isoc_tx_complete,
+				skb, data->isoc_tx_ep->bInterval);
+
+		urb->transfer_flags  = URB_ISO_ASAP;
+
+		__fill_isoc_descriptor(urb, skb->len,
+				le16_to_cpu(data->isoc_tx_ep->wMaxPacketSize));
+
+		hdev->stat.sco_tx++;
+		goto skip_waking;
+
+	default:
+		return -EILSEQ;
+	}
+
+	err = inc_tx(data);
+	if (err) {
+		usb_anchor_urb(urb, &data->deferred);
+		schedule_work(&data->waker);
+		err = 0;
+		goto done;
+	}
+
+skip_waking:
+	usb_anchor_urb(urb, &data->tx_anchor);
+
+	err = usb_submit_urb(urb, GFP_ATOMIC);
+	if (err < 0) {
+		BT_ERR("%s urb %p submission failed", hdev->name, urb);
+		kfree(urb->setup_packet);
+		usb_unanchor_urb(urb);
+	} else {
+		usb_mark_last_busy(data->udev);
+	}
+
+	usb_free_urb(urb);
+
+done:
+	return err;
+}
+
+static void btusb_destruct(struct hci_dev *hdev)
+{
+	struct btusb_data *data = hdev->driver_data;
+
+	BT_DBG("%s", hdev->name);
+
+	kfree(data);
+}
+
+static void btusb_notify(struct hci_dev *hdev, unsigned int evt)
+{
+	struct btusb_data *data = hdev->driver_data;
+
+	BT_DBG("%s evt %d", hdev->name, evt);
+
+	if (hdev->conn_hash.sco_num != data->sco_num) {
+		data->sco_num = hdev->conn_hash.sco_num;
+		schedule_work(&data->work);
+	}
+}
+
+static inline int __set_isoc_interface(struct hci_dev *hdev, int altsetting)
+{
+	struct btusb_data *data = hdev->driver_data;
+	struct usb_interface *intf = data->isoc;
+	struct usb_endpoint_descriptor *ep_desc;
+	int i, err;
+
+	if (!data->isoc)
+		return -ENODEV;
+
+	err = usb_set_interface(data->udev, 1, altsetting);
+	if (err < 0) {
+		BT_ERR("%s setting interface failed (%d)", hdev->name, -err);
+		return err;
+	}
+
+	data->isoc_altsetting = altsetting;
+
+	data->isoc_tx_ep = NULL;
+	data->isoc_rx_ep = NULL;
+
+	for (i = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++) {
+		ep_desc = &intf->cur_altsetting->endpoint[i].desc;
+
+		if (!data->isoc_tx_ep && usb_endpoint_is_isoc_out(ep_desc)) {
+			data->isoc_tx_ep = ep_desc;
+			continue;
+		}
+
+		if (!data->isoc_rx_ep && usb_endpoint_is_isoc_in(ep_desc)) {
+			data->isoc_rx_ep = ep_desc;
+			continue;
+		}
+	}
+
+	if (!data->isoc_tx_ep || !data->isoc_rx_ep) {
+		BT_ERR("%s invalid SCO descriptors", hdev->name);
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static void btusb_work(struct work_struct *work)
+{
+	struct btusb_data *data = container_of(work, struct btusb_data, work);
+	struct hci_dev *hdev = data->hdev;
+	int err;
+
+	if (hdev->conn_hash.sco_num > 0) {
+		if (!test_bit(BTUSB_DID_ISO_RESUME, &data->flags)) {
+			err = usb_autopm_get_interface(data->isoc ? data->isoc : data->intf);
+			if (err < 0) {
+				clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
+				usb_kill_anchored_urbs(&data->isoc_anchor);
+				return;
+			}
+
+			set_bit(BTUSB_DID_ISO_RESUME, &data->flags);
+		}
+		if (data->isoc_altsetting != 2) {
+			clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
+			usb_kill_anchored_urbs(&data->isoc_anchor);
+
+			if (__set_isoc_interface(hdev, 2) < 0)
+				return;
+		}
+
+		if (!test_and_set_bit(BTUSB_ISOC_RUNNING, &data->flags)) {
+			if (btusb_submit_isoc_urb(hdev, GFP_KERNEL) < 0)
+				clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
+			else
+				btusb_submit_isoc_urb(hdev, GFP_KERNEL);
+		}
+	} else {
+		clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
+		usb_kill_anchored_urbs(&data->isoc_anchor);
+
+		__set_isoc_interface(hdev, 0);
+		if (test_and_clear_bit(BTUSB_DID_ISO_RESUME, &data->flags))
+			usb_autopm_put_interface(data->isoc ? data->isoc : data->intf);
+	}
+}
+
+static void btusb_waker(struct work_struct *work)
+{
+	struct btusb_data *data = container_of(work, struct btusb_data, waker);
+	int err;
+
+	err = usb_autopm_get_interface(data->intf);
+	if (err < 0)
+		return;
+
+	usb_autopm_put_interface(data->intf);
+}
+
+struct btusb_data* btusb_alloc_devdata(struct usb_interface *intf)
+{
+	struct usb_endpoint_descriptor *ep_desc;
+	struct btusb_data *data;
+	struct hci_dev *hdev;
+	int i;
+
+	data = kzalloc(sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return NULL;
+
+	for (i = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++) {
+		ep_desc = &intf->cur_altsetting->endpoint[i].desc;
+
+		if (!data->intr_ep && usb_endpoint_is_int_in(ep_desc)) {
+			data->intr_ep = ep_desc;
+			continue;
+		}
+
+		if (!data->bulk_tx_ep && usb_endpoint_is_bulk_out(ep_desc)) {
+			data->bulk_tx_ep = ep_desc;
+			continue;
+		}
+
+		if (!data->bulk_rx_ep && usb_endpoint_is_bulk_in(ep_desc)) {
+			data->bulk_rx_ep = ep_desc;
+			continue;
+		}
+	}
+
+	if (!data->intr_ep || !data->bulk_tx_ep || !data->bulk_rx_ep) {
+		kfree(data);
+		return NULL;
+	}
+
+	data->cmdreq_type = USB_TYPE_CLASS;
+
+	data->udev = interface_to_usbdev(intf);
+	data->intf = intf;
+
+	spin_lock_init(&data->lock);
+
+	INIT_WORK(&data->work, btusb_work);
+	INIT_WORK(&data->waker, btusb_waker);
+	spin_lock_init(&data->txlock);
+
+	init_usb_anchor(&data->tx_anchor);
+	init_usb_anchor(&data->intr_anchor);
+	init_usb_anchor(&data->bulk_anchor);
+	init_usb_anchor(&data->isoc_anchor);
+	init_usb_anchor(&data->deferred);
+
+	hdev = hci_alloc_dev();
+	if (!hdev) {
+		kfree(data);
+		return NULL;
+	}
+
+	hdev->bus = HCI_USB;
+	hdev->driver_data = data;
+
+	data->hdev = hdev;
+
+	SET_HCIDEV_DEV(hdev, &intf->dev);
+
+	hdev->open     = btusb_open;
+	hdev->close    = btusb_close;
+	hdev->flush    = btusb_flush;
+	hdev->send     = btusb_send_frame;
+	hdev->destruct = btusb_destruct;
+	hdev->notify   = btusb_notify;
+
+	hdev->owner = THIS_MODULE;
+
+	/* Interface numbers are hardcoded in the specification */
+	data->isoc = usb_ifnum_to_if(data->udev, 1);
+	
+	usb_set_intfdata(intf, data);
+	
+	return data; 
+}			
+
+int btusb_register_dev(struct usb_driver *usbdrv, struct btusb_data *data)
+{	
+	struct hci_dev *hdev;
+	int err;
+
+	if (!data)
+		return -ENODEV;
+	
+	hdev = data->hdev;
+	
+	if (data->isoc) {
+		err = usb_driver_claim_interface(usbdrv,
+							data->isoc, data);
+		if (err < 0) {
+			usb_set_intfdata(data->intf, NULL);
+			hci_free_dev(hdev);
+			kfree(data);
+			return err;
+		}
+	}
+
+	err = hci_register_dev(hdev);
+	if (err < 0) {
+		usb_set_intfdata(data->intf, NULL);
+		hci_free_dev(hdev);
+		kfree(data);
+		return err;
+	}
+	
+	return 0;
+}
+
+void btusb_release_dev(struct usb_driver *usbdrv, struct btusb_data *data)
+{
+	struct hci_dev *hdev;
+
+	if (!data)
+		return;
+
+	hdev = data->hdev;
+
+	__hci_dev_hold(hdev);
+
+	usb_set_intfdata(data->intf, NULL);
+
+	if (data->isoc)
+		usb_set_intfdata(data->isoc, NULL);
+
+	hci_unregister_dev(hdev);
+
+	if (data->intf == data->isoc)
+		usb_driver_release_interface(usbdrv, data->intf);
+	else if (data->isoc)
+		usb_driver_release_interface(usbdrv, data->isoc);
+
+	__hci_dev_put(hdev);
+
+	hci_free_dev(hdev);
+}
+
+#ifdef CONFIG_PM
+int btusb_suspend_dev(struct btusb_data *data, pm_message_t message)
+{
+	if (data->suspend_count++)
+		return 0;
+
+	spin_lock_irq(&data->txlock);
+	if (!((message.event & PM_EVENT_AUTO) && data->tx_in_flight)) {
+		set_bit(BTUSB_SUSPENDING, &data->flags);
+		spin_unlock_irq(&data->txlock);
+	} else {
+		spin_unlock_irq(&data->txlock);
+		data->suspend_count--;
+		return -EBUSY;
+	}
+
+	cancel_work_sync(&data->work);
+
+	btusb_stop_traffic(data);
+	usb_kill_anchored_urbs(&data->tx_anchor);
+
+	return 0;
+}
+
+static void play_deferred(struct btusb_data *data)
+{
+	struct urb *urb;
+	int err;
+
+	while ((urb = usb_get_from_anchor(&data->deferred))) {
+		err = usb_submit_urb(urb, GFP_ATOMIC);
+		if (err < 0)
+			break;
+
+		data->tx_in_flight++;
+	}
+	usb_scuttle_anchored_urbs(&data->deferred);
+}
+
+int btusb_resume_dev(struct btusb_data *data)
+{
+	struct hci_dev *hdev = data->hdev;
+	int err = 0;
+
+	if (--data->suspend_count)
+		return 0;
+
+	if (!test_bit(HCI_RUNNING, &hdev->flags))
+		goto done;
+
+	if (test_bit(BTUSB_INTR_RUNNING, &data->flags)) {
+		err = btusb_submit_intr_urb(hdev, GFP_NOIO);
+		if (err < 0) {
+			clear_bit(BTUSB_INTR_RUNNING, &data->flags);
+			goto failed;
+		}
+	}
+
+	if (test_bit(BTUSB_BULK_RUNNING, &data->flags)) {
+		err = btusb_submit_bulk_urb(hdev, GFP_NOIO);
+		if (err < 0) {
+			clear_bit(BTUSB_BULK_RUNNING, &data->flags);
+			goto failed;
+		}
+
+		btusb_submit_bulk_urb(hdev, GFP_NOIO);
+	}
+
+	if (test_bit(BTUSB_ISOC_RUNNING, &data->flags)) {
+		if (btusb_submit_isoc_urb(hdev, GFP_NOIO) < 0)
+			clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
+		else
+			btusb_submit_isoc_urb(hdev, GFP_NOIO);
+	}
+
+	spin_lock_irq(&data->txlock);
+	play_deferred(data);
+	clear_bit(BTUSB_SUSPENDING, &data->flags);
+	spin_unlock_irq(&data->txlock);
+	schedule_work(&data->work);
+
+	return 0;
+
+failed:
+	usb_scuttle_anchored_urbs(&data->deferred);
+done:
+	spin_lock_irq(&data->txlock);
+	clear_bit(BTUSB_SUSPENDING, &data->flags);
+	spin_unlock_irq(&data->txlock);
+
+	return err;
+}
+#endif
+
+EXPORT_SYMBOL(btusb_alloc_devdata);
+EXPORT_SYMBOL(btusb_register_dev);
+EXPORT_SYMBOL(btusb_release_dev);
+
diff --git a/drivers/bluetooth/btusb_generic.c b/drivers/bluetooth/btusb_generic.c
new file mode 100644
index 0000000..62f4048
--- /dev/null
+++ b/drivers/bluetooth/btusb_generic.c
@@ -0,0 +1,334 @@
+/*
+ *
+ *  Generic Bluetooth USB driver
+ *
+ *  Copyright (C) 2005-2008  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/skbuff.h>
+
+#include <linux/usb.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#include "btusb.h"
+
+#define VERSION "0.6"
+
+static int ignore_dga;
+static int ignore_csr;
+static int ignore_sniffer;
+static int disable_scofix;
+static int force_scofix;
+
+static int reset = 1;
+
+static struct usb_driver btusb_driver;
+
+#define BTUSB_IGNORE		0x01
+#define BTUSB_DIGIANSWER	0x02
+#define BTUSB_CSR		0x04
+#define BTUSB_SNIFFER		0x08
+#define BTUSB_BCM92035		0x10
+#define BTUSB_BROKEN_ISOC	0x20
+#define BTUSB_WRONG_SCO_MTU	0x40
+
+static struct usb_device_id btusb_table[] = {
+	/* Generic Bluetooth USB device */
+	{ USB_DEVICE_INFO(0xe0, 0x01, 0x01) },
+
+	/* Apple MacBookPro 7,1 */
+	{ USB_DEVICE(0x05ac, 0x8213) },
+
+	/* Apple iMac11,1 */
+	{ USB_DEVICE(0x05ac, 0x8215) },
+
+	/* Apple MacBookPro6,2 */
+	{ USB_DEVICE(0x05ac, 0x8218) },
+
+	/* Apple MacBookAir3,1, MacBookAir3,2 */
+	{ USB_DEVICE(0x05ac, 0x821b) },
+
+	/* Apple MacBookPro8,2 */
+	{ USB_DEVICE(0x05ac, 0x821a) },
+
+	/* AVM BlueFRITZ! USB v2.0 */
+	{ USB_DEVICE(0x057c, 0x3800) },
+
+	/* Bluetooth Ultraport Module from IBM */
+	{ USB_DEVICE(0x04bf, 0x030a) },
+
+	/* ALPS Modules with non-standard id */
+	{ USB_DEVICE(0x044e, 0x3001) },
+	{ USB_DEVICE(0x044e, 0x3002) },
+
+	/* Ericsson with non-standard id */
+	{ USB_DEVICE(0x0bdb, 0x1002) },
+
+	/* Canyon CN-BTU1 with HID interfaces */
+	{ USB_DEVICE(0x0c10, 0x0000) },
+
+	{ }	/* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, btusb_table);
+
+static struct usb_device_id blacklist_table[] = {
+	/* CSR BlueCore devices */
+	{ USB_DEVICE(0x0a12, 0x0001), .driver_info = BTUSB_CSR },
+
+	/* Broadcom BCM2033 without firmware */
+	{ USB_DEVICE(0x0a5c, 0x2033), .driver_info = BTUSB_IGNORE },
+
+	/* Atheros 3011 with sflash firmware */
+	{ USB_DEVICE(0x0cf3, 0x3002), .driver_info = BTUSB_IGNORE },
+	{ USB_DEVICE(0x13d3, 0x3304), .driver_info = BTUSB_IGNORE },
+
+	/* Atheros AR9285 Malbec with sflash firmware */
+	{ USB_DEVICE(0x03f0, 0x311d), .driver_info = BTUSB_IGNORE },
+
+	/* Atheros 3012 with sflash firmware */
+	{ USB_DEVICE(0x0cf3, 0x3004), .driver_info = BTUSB_IGNORE },
+
+	/* Atheros AR5BBU12 with sflash firmware */
+	{ USB_DEVICE(0x0489, 0xe02c), .driver_info = BTUSB_IGNORE },
+
+	/* Broadcom BCM2035 */
+	{ USB_DEVICE(0x0a5c, 0x2035), .driver_info = BTUSB_WRONG_SCO_MTU },
+	{ USB_DEVICE(0x0a5c, 0x200a), .driver_info = BTUSB_WRONG_SCO_MTU },
+	{ USB_DEVICE(0x0a5c, 0x2009), .driver_info = BTUSB_BCM92035 },
+
+	/* Broadcom BCM2045 */
+	{ USB_DEVICE(0x0a5c, 0x2039), .driver_info = BTUSB_WRONG_SCO_MTU },
+	{ USB_DEVICE(0x0a5c, 0x2101), .driver_info = BTUSB_WRONG_SCO_MTU },
+
+	/* IBM/Lenovo ThinkPad with Broadcom chip */
+	{ USB_DEVICE(0x0a5c, 0x201e), .driver_info = BTUSB_WRONG_SCO_MTU },
+	{ USB_DEVICE(0x0a5c, 0x2110), .driver_info = BTUSB_WRONG_SCO_MTU },
+
+	/* HP laptop with Broadcom chip */
+	{ USB_DEVICE(0x03f0, 0x171d), .driver_info = BTUSB_WRONG_SCO_MTU },
+
+	/* Dell laptop with Broadcom chip */
+	{ USB_DEVICE(0x413c, 0x8126), .driver_info = BTUSB_WRONG_SCO_MTU },
+
+	/* Dell Wireless 370 and 410 devices */
+	{ USB_DEVICE(0x413c, 0x8152), .driver_info = BTUSB_WRONG_SCO_MTU },
+	{ USB_DEVICE(0x413c, 0x8156), .driver_info = BTUSB_WRONG_SCO_MTU },
+
+	/* Belkin F8T012 and F8T013 devices */
+	{ USB_DEVICE(0x050d, 0x0012), .driver_info = BTUSB_WRONG_SCO_MTU },
+	{ USB_DEVICE(0x050d, 0x0013), .driver_info = BTUSB_WRONG_SCO_MTU },
+
+	/* Asus WL-BTD202 device */
+	{ USB_DEVICE(0x0b05, 0x1715), .driver_info = BTUSB_WRONG_SCO_MTU },
+
+	/* Kensington Bluetooth USB adapter */
+	{ USB_DEVICE(0x047d, 0x105e), .driver_info = BTUSB_WRONG_SCO_MTU },
+
+	/* RTX Telecom based adapters with buggy SCO support */
+	{ USB_DEVICE(0x0400, 0x0807), .driver_info = BTUSB_BROKEN_ISOC },
+	{ USB_DEVICE(0x0400, 0x080a), .driver_info = BTUSB_BROKEN_ISOC },
+
+	/* CONWISE Technology based adapters with buggy SCO support */
+	{ USB_DEVICE(0x0e5e, 0x6622), .driver_info = BTUSB_BROKEN_ISOC },
+
+	/* Digianswer devices */
+	{ USB_DEVICE(0x08fd, 0x0001), .driver_info = BTUSB_DIGIANSWER },
+	{ USB_DEVICE(0x08fd, 0x0002), .driver_info = BTUSB_IGNORE },
+
+	/* CSR BlueCore Bluetooth Sniffer */
+	{ USB_DEVICE(0x0a12, 0x0002), .driver_info = BTUSB_SNIFFER },
+
+	/* Frontline ComProbe Bluetooth Sniffer */
+	{ USB_DEVICE(0x16d3, 0x0002), .driver_info = BTUSB_SNIFFER },
+
+	{ }	/* Terminating entry */
+};
+
+static int btusb_probe(struct usb_interface *intf,
+				const struct usb_device_id *id)
+{
+	struct btusb_data *data;
+	struct hci_dev *hdev;
+	
+	BT_DBG("intf %p id %p", intf, id);
+
+	/* interface numbers are hardcoded in the spec */
+	if (intf->cur_altsetting->desc.bInterfaceNumber != 0)
+		return -ENODEV;
+
+	if (!id->driver_info) {
+		const struct usb_device_id *match;
+		match = usb_match_id(intf, blacklist_table);
+		if (match)
+			id = match;
+	}
+
+	if (id->driver_info == BTUSB_IGNORE)
+		return -ENODEV;
+
+	if (ignore_dga && id->driver_info & BTUSB_DIGIANSWER)
+		return -ENODEV;
+
+	if (ignore_csr && id->driver_info & BTUSB_CSR)
+		return -ENODEV;
+
+	if (ignore_sniffer && id->driver_info & BTUSB_SNIFFER)
+		return -ENODEV;
+
+	data = btusb_alloc_devdata(intf);
+	if (!data)
+		return -ENOMEM;
+	
+	hdev = data->hdev;
+	if (!reset)
+		set_bit(HCI_QUIRK_NO_RESET, &hdev->quirks);
+
+	if (force_scofix || id->driver_info & BTUSB_WRONG_SCO_MTU) {
+		if (!disable_scofix)
+			set_bit(HCI_QUIRK_FIXUP_BUFFER_SIZE, &hdev->quirks);
+	}
+
+	if (id->driver_info & BTUSB_BROKEN_ISOC)
+		data->isoc = NULL;
+
+	if (id->driver_info & BTUSB_DIGIANSWER) {
+		data->cmdreq_type = USB_TYPE_VENDOR;
+		set_bit(HCI_QUIRK_NO_RESET, &hdev->quirks);
+	}
+
+	if (id->driver_info & BTUSB_CSR) {
+		struct usb_device *udev = data->udev;
+
+		/* Old firmware would otherwise execute USB reset */
+		if (le16_to_cpu(udev->descriptor.bcdDevice) < 0x117)
+			set_bit(HCI_QUIRK_NO_RESET, &hdev->quirks);
+	}
+
+	if (id->driver_info & BTUSB_SNIFFER) {
+		struct usb_device *udev = data->udev;
+
+		/* New sniffer firmware has crippled HCI interface */
+		if (le16_to_cpu(udev->descriptor.bcdDevice) > 0x997)
+			set_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks);
+
+		data->isoc = NULL;
+	}
+
+	if (id->driver_info & BTUSB_BCM92035) {
+		unsigned char cmd[] = { 0x3b, 0xfc, 0x01, 0x00 };
+		struct sk_buff *skb;
+
+		skb = bt_skb_alloc(sizeof(cmd), GFP_KERNEL);
+		if (skb) {
+			memcpy(skb_put(skb, sizeof(cmd)), cmd, sizeof(cmd));
+			skb_queue_tail(&hdev->driver_init, skb);
+		}
+	}
+
+	return btusb_register_dev(&btusb_driver, data);
+}
+
+static void btusb_disconnect(struct usb_interface *intf)
+{
+	struct btusb_data *data = usb_get_intfdata(intf);
+
+	BT_DBG("intf %p", intf);
+	
+	btusb_release_dev(&btusb_driver, data);
+}
+
+#ifdef CONFIG_PM
+static int btusb_suspend(struct usb_interface *intf, pm_message_t message)
+{
+	struct btusb_data *data = usb_get_intfdata(intf);
+
+	BT_DBG("intf %p", intf);
+
+	return btusb_suspend_dev(data, message);
+}
+
+static int btusb_resume(struct usb_interface *intf)
+{
+	struct btusb_data *data = usb_get_intfdata(intf);
+	
+	BT_DBG("intf %p", intf);
+
+	return btusb_resume_dev(data);
+}
+#endif
+
+static struct usb_driver btusb_driver = {
+	.name		= "btusb",
+	.probe		= btusb_probe,
+	.disconnect	= btusb_disconnect,
+#ifdef CONFIG_PM
+	.suspend	= btusb_suspend,
+	.resume		= btusb_resume,
+#endif
+	.id_table	= btusb_table,
+	.supports_autosuspend = 1,
+};
+
+static int __init btusb_init(void)
+{
+	BT_INFO("Generic Bluetooth USB driver ver %s", VERSION);
+
+	return usb_register(&btusb_driver);
+}
+
+static void __exit btusb_exit(void)
+{
+	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");
+
+module_param(ignore_csr, bool, 0644);
+MODULE_PARM_DESC(ignore_csr, "Ignore devices with id 0a12:0001");
+
+module_param(ignore_sniffer, bool, 0644);
+MODULE_PARM_DESC(ignore_sniffer, "Ignore devices with id 0a12:0002");
+
+module_param(disable_scofix, bool, 0644);
+MODULE_PARM_DESC(disable_scofix, "Disable fixup of wrong SCO buffer size");
+
+module_param(force_scofix, bool, 0644);
+MODULE_PARM_DESC(force_scofix, "Force fixup of wrong SCO buffers size");
+
+module_param(reset, bool, 0644);
+MODULE_PARM_DESC(reset, "Send HCI reset command on initialization");
+
+MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
+MODULE_DESCRIPTION("Generic Bluetooth USB driver ver " VERSION);
+MODULE_VERSION(VERSION);
+MODULE_LICENSE("GPL");
-- 
1.7.1


^ permalink raw reply related	[flat|nested] 3+ messages in thread

* Re: Bluetooth USB device keeps same PID/VID after DFU
  2011-05-26  1:32 Bluetooth USB device keeps same PID/VID after DFU Steven Li
@ 2011-05-26  2:47 ` Marcel Holtmann
  0 siblings, 0 replies; 3+ messages in thread
From: Marcel Holtmann @ 2011-05-26  2:47 UTC (permalink / raw)
  To: Steven Li
  Cc: Gustavo F. Padovan, Bluettooth Linux, Haijun Liu, Hong Fan,
	Vic Wei, Jack Chen(Taipei), Robert Chang, Luis R. Rodriguez

Hi Steven,

> Here is steven from Atheros.  Glad to contact you here. we have some concerns to the btusb device driver.
> Present USB Bluetooth implementation in Linux has some limitations. Many vendors need to load patch and other specific operations during device probe or disconnect to their Bluetooth device, but btusb driver gives limitation to this behavior.  It needs to add the Bluetooth device PID/VID into the btusb blacklist, and use the vendor specific driver to load firmware. By this, it causes the Bluetooth device "MUST" have two PID/VIDs in Linux.  But on windows platform, the device can always keep one same PID/VID.  It is hard for us to handle this issue with current btusb design.
> Moreover in our Atheros 3012 chip, we also need to switch the 3012 chip between "normal" mode and "pre-boot" mode during PC reboot,  with current btusb design, we have to make changes in btusb.c directly, but it is our chip specific requirements.  Therefore, we’d like to propose some methods to make the Bluetooth device has only one PID/VID by proper btusb modification and also for other vendor extensions such as mode switch. 
> 
> Currently, we have three proposals. And I attached our rough patches for these three proposals.
> They are totally not formal, I attached them just want to make you easy to understand.
> 
> And from here, Let me explain more of  these proposal one by one.
> 
> # Proposal 1.
> This option could be refer to what it’s working for lots of UART Bluetooth devices in Linux.
> Mainly, vendor driver would be built as an object of btusb.ko. This should be same as those UART Bluetooth drivers.
> In our case, ath3k would be built as an object file according to CONFIG_BT_ATH3K define.  
> Then btusb kernel module will be composed of btusb.o and ath3k.o.  So it is easy to make one PID/VID device work.
> This concept was oriented by hci_uart driver.  It can jump to individual entry function based on different ID.
> And things like mode switch also could be handled by just adding function like switch_mode() in ath3k.c.
> 
> # Proposal 2.
> This option is to export btusb common functions, and vendor driver (for example our ath3k) can be taken as the extension of btusb. 
> , and it can first downloads the firmware and then use those functions exported in btusb driver
> to setup the hci Bluetooth device.  Moreover if it is possible, we are also propose to refine the btusb.c and split the btusb.c to btusb_core.c and btusb_generic.c, 
> In the btusb_core.c. It includes all the  Bluetooth usb common operations and these Bluetooth usb functions will be exported, 
> and the btusb_generic.c is the real generic Bluetooth driver remains support all current Bluetooth device ,which uses the btusb_core functions.
> And vendor driver can also call those usb common operations in the btusb_core.c.
> With this proposal, Since the ath3k driver are the only owner of the bluetooth device, only one PID/VID is necessary.
> And it is very easy to handle the mode switch case in itself either. Please check btusb_ath3k_2.patch for detail.
> 
> # Proposal 3.
> This option is to add "quirk device" to btusb driver. We found actually you added lots of so-called “quirk” implementation in btusb driver for many vendors.
> http://git.kernel.org/?p=linux/kernel/git/next/linux-next.git;a=commit;h=cfeb414537b1d7c23ba202f198fa4154cd5a4856
> Vendor specific driver can call btusb_register_quirk_device() function to  register itself to the btusb driver. 
> So during the device probe, the btusb will try to match the device id, and if it is a “quirk” device, it will call 
> the “quirk” device’s specific function first.  Every vendor can use this quirk to download their firmware and keep the same PID/VID with proper codes in btusb.c.
> This proposal is also able to handle the mode switch case. 
> 
> Please give your comments and suggestion for our proposals. 
> We are hoping to make one PID/VID work and contribute to the community.

why do you want this at all. You run a different protocol with different
endpoints on the device in DFU mode and on the device in H:2 mode.

So you actually have different functionality. And I personally do not
care what Windows is doing. This is Linux, so please follow what the
Linux community expect you to do.

Also in case of btusb, you can use the Bluetooth SIG (and for that
matter USB SIG) defined interface identifiers. The btusb driver does not
match by default to VID/PID. It uses a standard. If you wanna use the
same VID/PID here, then declare the interface identifiers properly and
everything would work out nicely.

You guys insist on lying to the USB subsystem and by that fact most
likely violating the USB standards. Don't do that and everything will
work out of the box with Linux. No extra patches or modification for
btusb needed.

Regards

Marcel



^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2011-05-26  2:47 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2011-05-26  1:32 Bluetooth USB device keeps same PID/VID after DFU Steven Li
2011-05-26  2:47 ` Marcel Holtmann
     [not found] <6AAC4214B9104E409FF4885ACB62A96101A78AD852@SHEXMB-01.global.atheros.com>
2011-05-25 13:34 ` Jiri Kosina

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox