[T106][ZXW-22]7520V3SCV2.01.01.02P42U09_VEC_V0.8_AP_VEC origin source commit

Change-Id: Ic6e05d89ecd62fc34f82b23dcf306c93764aec4b
diff --git a/ap/os/linux/linux-3.4.x/drivers/bluetooth/Kconfig b/ap/os/linux/linux-3.4.x/drivers/bluetooth/Kconfig
new file mode 100644
index 0000000..5ccf142
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/bluetooth/Kconfig
@@ -0,0 +1,232 @@
+
+menu "Bluetooth device drivers"
+	depends on BT
+
+config BT_HCIBTUSB
+	tristate "HCI USB driver"
+	depends on USB
+	help
+	  Bluetooth HCI USB driver.
+	  This driver is required if you want to use Bluetooth devices with
+	  USB interface.
+
+	  Say Y here to compile support for Bluetooth USB devices into the
+	  kernel or say M to compile it as module (btusb).
+
+config BT_HCIBTSDIO
+	tristate "HCI SDIO driver"
+	depends on MMC
+	help
+	  Bluetooth HCI SDIO driver.
+	  This driver is required if you want to use Bluetooth device with
+	  SDIO interface.
+
+	  Say Y here to compile support for Bluetooth SDIO devices into the
+	  kernel or say M to compile it as module (btsdio).
+
+config BT_HCIUART
+	tristate "HCI UART driver"
+	help
+	  Bluetooth HCI UART driver.
+	  This driver is required if you want to use Bluetooth devices with
+	  serial port interface. You will also need this driver if you have 
+	  UART based Bluetooth PCMCIA and CF devices like Xircom Credit Card 
+	  adapter and BrainBoxes Bluetooth PC Card.
+
+	  Say Y here to compile support for Bluetooth UART devices into the
+	  kernel or say M to compile it as module (hci_uart).
+
+config BT_HCIUART_H4
+	bool "UART (H4) protocol support"
+	depends on BT_HCIUART
+	help
+	  UART (H4) is serial protocol for communication between Bluetooth 
+	  device and host. This protocol is required for most Bluetooth devices 
+	  with UART interface, including PCMCIA and CF cards. 
+
+	  Say Y here to compile support for HCI UART (H4) protocol.
+
+config BT_HCIUART_BCSP
+	bool "BCSP protocol support"
+	depends on BT_HCIUART
+	select BITREVERSE
+	help
+	  BCSP (BlueCore Serial Protocol) is serial protocol for communication 
+	  between Bluetooth device and host. This protocol is required for non
+	  USB Bluetooth devices based on CSR BlueCore chip, including PCMCIA and
+	  CF cards.
+
+	  Say Y here to compile support for HCI BCSP protocol.
+
+config BT_HCIUART_ATH3K
+	bool "Atheros AR300x serial support"
+	depends on BT_HCIUART
+	help
+	  HCIATH3K (HCI Atheros AR300x) is a serial protocol for
+	  communication between host and Atheros AR300x Bluetooth devices.
+	  This protocol enables AR300x chips to be enabled with
+	  power management support.
+	  Enable this if you have Atheros AR300x serial Bluetooth device.
+
+	  Say Y here to compile support for HCI UART ATH3K protocol.
+
+config BT_HCIUART_LL
+	bool "HCILL protocol support"
+	depends on BT_HCIUART
+	help
+	  HCILL (HCI Low Level) is a serial protocol for communication
+	  between Bluetooth device and host. This protocol is required for
+	  serial Bluetooth devices that are based on Texas Instruments'
+	  BRF chips.
+
+	  Say Y here to compile support for HCILL protocol.
+
+config BT_HCIBCM203X
+	tristate "HCI BCM203x USB driver"
+	depends on USB
+	select FW_LOADER
+	help
+	  Bluetooth HCI BCM203x USB driver.
+	  This driver provides the firmware loading mechanism for the Broadcom
+	  Blutonium based devices.
+
+	  Say Y here to compile support for HCI BCM203x devices into the
+	  kernel or say M to compile it as module (bcm203x).
+
+config BT_HCIBPA10X
+	tristate "HCI BPA10x USB driver"
+	depends on USB
+	help
+	  Bluetooth HCI BPA10x USB driver.
+	  This driver provides support for the Digianswer BPA 100/105 Bluetooth
+	  sniffer devices.
+
+	  Say Y here to compile support for HCI BPA10x devices into the
+	  kernel or say M to compile it as module (bpa10x).
+
+config BT_HCIBFUSB
+	tristate "HCI BlueFRITZ! USB driver"
+	depends on USB
+	select FW_LOADER
+	help
+	  Bluetooth HCI BlueFRITZ! USB driver.
+	  This driver provides support for Bluetooth USB devices with AVM
+	  interface:
+	     AVM BlueFRITZ! USB
+
+	  Say Y here to compile support for HCI BFUSB devices into the
+	  kernel or say M to compile it as module (bfusb).
+
+config BT_HCIDTL1
+	tristate "HCI DTL1 (PC Card) driver"
+	depends on PCMCIA
+	help
+	  Bluetooth HCI DTL1 (PC Card) driver.
+	  This driver provides support for Bluetooth PCMCIA devices with
+	  Nokia DTL1 interface:
+	     Nokia Bluetooth Card
+	     Socket Bluetooth CF Card
+
+	  Say Y here to compile support for HCI DTL1 devices into the
+	  kernel or say M to compile it as module (dtl1_cs).
+
+config BT_HCIBT3C
+	tristate "HCI BT3C (PC Card) driver"
+	depends on PCMCIA
+	select FW_LOADER
+	help
+	  Bluetooth HCI BT3C (PC Card) driver.
+	  This driver provides support for Bluetooth PCMCIA devices with
+	  3Com BT3C interface:
+	     3Com Bluetooth Card (3CRWB6096)
+	     HP Bluetooth Card
+
+	  Say Y here to compile support for HCI BT3C devices into the
+	  kernel or say M to compile it as module (bt3c_cs).
+
+config BT_HCIBLUECARD
+	tristate "HCI BlueCard (PC Card) driver"
+	depends on PCMCIA
+	help
+	  Bluetooth HCI BlueCard (PC Card) driver.
+	  This driver provides support for Bluetooth PCMCIA devices with
+	  Anycom BlueCard interface:
+	     Anycom Bluetooth PC Card
+	     Anycom Bluetooth CF Card
+
+	  Say Y here to compile support for HCI BlueCard devices into the
+	  kernel or say M to compile it as module (bluecard_cs).
+
+config BT_HCIBTUART
+	tristate "HCI UART (PC Card) device driver"
+	depends on PCMCIA
+	help
+	  Bluetooth HCI UART (PC Card) driver.
+	  This driver provides support for Bluetooth PCMCIA devices with
+	  an UART interface:
+	     Xircom CreditCard Bluetooth Adapter
+	     Xircom RealPort2 Bluetooth Adapter
+	     Sphinx PICO Card
+	     H-Soft blue+Card
+	     Cyber-blue Compact Flash Card
+
+	  Say Y here to compile support for HCI UART devices into the
+	  kernel or say M to compile it as module (btuart_cs).
+
+config BT_HCIVHCI
+	tristate "HCI VHCI (Virtual HCI device) driver"
+	help
+	  Bluetooth Virtual HCI device driver.
+	  This driver is required if you want to use HCI Emulation software.
+
+	  Say Y here to compile support for virtual HCI devices into the
+	  kernel or say M to compile it as module (hci_vhci).
+
+config BT_MRVL
+	tristate "Marvell Bluetooth driver support"
+	help
+	  The core driver to support Marvell Bluetooth devices.
+
+	  This driver is required if you want to support
+	  Marvell Bluetooth devices, such as 8688/8787/8797.
+
+	  Say Y here to compile Marvell Bluetooth driver
+	  into the kernel or say M to compile it as module.
+
+config BT_MRVL_SDIO
+	tristate "Marvell BT-over-SDIO driver"
+	depends on BT_MRVL && MMC
+	select FW_LOADER
+	help
+	  The driver for Marvell Bluetooth chipsets with SDIO interface.
+
+	  This driver is required if you want to use Marvell Bluetooth
+	  devices with SDIO interface. Currently SD8688/SD8787/SD8797
+	  chipsets are supported.
+
+	  Say Y here to compile support for Marvell BT-over-SDIO driver
+	  into the kernel or say M to compile it as module.
+
+config BT_ATH3K
+	tristate "Atheros firmware download driver"
+	depends on BT_HCIBTUSB
+	select FW_LOADER
+	help
+	  Bluetooth firmware download driver.
+	  This driver loads the firmware into the Atheros Bluetooth
+	  chipset.
+
+	  Say Y here to compile support for "Atheros firmware download driver"
+	  into the kernel or say M to compile it as module (ath3k).
+
+config BT_WILINK
+	tristate "Texas Instruments WiLink7 driver"
+	depends on TI_ST
+	help
+	  This enables the Bluetooth driver for Texas Instrument's BT/FM/GPS
+	  combo devices. This makes use of shared transport line discipline
+	  core driver to communicate with the BT core of the combo chip.
+
+	  Say Y here to compile support for Texas Instrument's WiLink7 driver
+	  into the kernel or say M to compile it as module.
+endmenu
diff --git a/ap/os/linux/linux-3.4.x/drivers/bluetooth/Makefile b/ap/os/linux/linux-3.4.x/drivers/bluetooth/Makefile
new file mode 100644
index 0000000..f4460f4
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/bluetooth/Makefile
@@ -0,0 +1,31 @@
+#
+# Makefile for the Linux Bluetooth HCI device drivers.
+#
+
+obj-$(CONFIG_BT_HCIVHCI)	+= hci_vhci.o
+obj-$(CONFIG_BT_HCIUART)	+= hci_uart.o
+obj-$(CONFIG_BT_HCIBCM203X)	+= bcm203x.o
+obj-$(CONFIG_BT_HCIBPA10X)	+= bpa10x.o
+obj-$(CONFIG_BT_HCIBFUSB)	+= bfusb.o
+obj-$(CONFIG_BT_HCIDTL1)	+= dtl1_cs.o
+obj-$(CONFIG_BT_HCIBT3C)	+= bt3c_cs.o
+obj-$(CONFIG_BT_HCIBLUECARD)	+= bluecard_cs.o
+obj-$(CONFIG_BT_HCIBTUART)	+= btuart_cs.o
+
+obj-$(CONFIG_BT_HCIBTUSB)	+= btusb.o
+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
+
+btmrvl-y			:= btmrvl_main.o
+btmrvl-$(CONFIG_DEBUG_FS)	+= btmrvl_debugfs.o
+
+hci_uart-y				:= hci_ldisc.o
+hci_uart-$(CONFIG_BT_HCIUART_H4)	+= hci_h4.o
+hci_uart-$(CONFIG_BT_HCIUART_BCSP)	+= hci_bcsp.o
+hci_uart-$(CONFIG_BT_HCIUART_LL)	+= hci_ll.o
+hci_uart-$(CONFIG_BT_HCIUART_ATH3K)	+= hci_ath.o
+hci_uart-objs				:= $(hci_uart-y)
diff --git a/ap/os/linux/linux-3.4.x/drivers/bluetooth/ath3k.c b/ap/os/linux/linux-3.4.x/drivers/bluetooth/ath3k.c
new file mode 100644
index 0000000..4b8e03f
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/bluetooth/ath3k.c
@@ -0,0 +1,519 @@
+/*
+ * Copyright (c) 2008-2009 Atheros Communications Inc.
+ *
+ *  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/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/device.h>
+#include <linux/firmware.h>
+#include <linux/usb.h>
+#include <net/bluetooth/bluetooth.h>
+
+#define VERSION "1.0"
+#define ATH3K_FIRMWARE	"ath3k-1.fw"
+
+#define ATH3K_DNLOAD				0x01
+#define ATH3K_GETSTATE				0x05
+#define ATH3K_SET_NORMAL_MODE			0x07
+#define ATH3K_GETVERSION			0x09
+#define USB_REG_SWITCH_VID_PID			0x0a
+
+#define ATH3K_MODE_MASK				0x3F
+#define ATH3K_NORMAL_MODE			0x0E
+
+#define ATH3K_PATCH_UPDATE			0x80
+#define ATH3K_SYSCFG_UPDATE			0x40
+
+#define ATH3K_XTAL_FREQ_26M			0x00
+#define ATH3K_XTAL_FREQ_40M			0x01
+#define ATH3K_XTAL_FREQ_19P2			0x02
+#define ATH3K_NAME_LEN				0xFF
+
+struct ath3k_version {
+	unsigned int	rom_version;
+	unsigned int	build_version;
+	unsigned int	ram_version;
+	unsigned char	ref_clock;
+	unsigned char	reserved[0x07];
+};
+
+static struct usb_device_id ath3k_table[] = {
+	/* Atheros AR3011 */
+	{ USB_DEVICE(0x0CF3, 0x3000) },
+
+	/* Atheros AR3011 with sflash firmware*/
+	{ USB_DEVICE(0x0489, 0xE027) },
+	{ USB_DEVICE(0x0489, 0xE03D) },
+	{ USB_DEVICE(0x04F2, 0xAFF1) },
+	{ USB_DEVICE(0x0930, 0x0215) },
+	{ USB_DEVICE(0x0CF3, 0x3002) },
+	{ USB_DEVICE(0x0CF3, 0xE019) },
+	{ USB_DEVICE(0x13d3, 0x3304) },
+
+	/* Atheros AR9285 Malbec with sflash firmware */
+	{ USB_DEVICE(0x03F0, 0x311D) },
+
+	/* Atheros AR3012 with sflash firmware*/
+	{ USB_DEVICE(0x0489, 0xe04d) },
+	{ USB_DEVICE(0x0489, 0xe04e) },
+	{ USB_DEVICE(0x0489, 0xe057) },
+	{ USB_DEVICE(0x0489, 0xe056) },
+	{ USB_DEVICE(0x0489, 0xe078) },
+	{ USB_DEVICE(0x04c5, 0x1330) },
+	{ USB_DEVICE(0x04CA, 0x3004) },
+	{ USB_DEVICE(0x04CA, 0x3005) },
+	{ USB_DEVICE(0x04CA, 0x3006) },
+	{ USB_DEVICE(0x04CA, 0x3008) },
+	{ USB_DEVICE(0x04CA, 0x3010) },
+	{ USB_DEVICE(0x0930, 0x0219) },
+	{ USB_DEVICE(0x0930, 0x0227) },
+	{ USB_DEVICE(0x0b05, 0x17d0) },
+	{ USB_DEVICE(0x0CF3, 0x0036) },
+	{ USB_DEVICE(0x0CF3, 0x3004) },
+	{ USB_DEVICE(0x0CF3, 0x3008) },
+	{ USB_DEVICE(0x0CF3, 0x311D) },
+	{ USB_DEVICE(0x0cf3, 0x3121) },
+	{ USB_DEVICE(0x0CF3, 0x817a) },
+	{ USB_DEVICE(0x0cf3, 0xe003) },
+	{ USB_DEVICE(0x0CF3, 0xE004) },
+	{ USB_DEVICE(0x0CF3, 0xE005) },
+	{ USB_DEVICE(0x13d3, 0x3362) },
+	{ USB_DEVICE(0x13d3, 0x3375) },
+	{ USB_DEVICE(0x13d3, 0x3393) },
+	{ USB_DEVICE(0x13d3, 0x3402) },
+	{ USB_DEVICE(0x13d3, 0x3408) },
+	{ USB_DEVICE(0x13d3, 0x3423) },
+	{ USB_DEVICE(0x13d3, 0x3432) },
+	{ USB_DEVICE(0x13d3, 0x3474) },
+
+	/* Atheros AR5BBU12 with sflash firmware */
+	{ USB_DEVICE(0x0489, 0xE02C) },
+
+	/* Atheros AR5BBU22 with sflash firmware */
+	{ USB_DEVICE(0x0489, 0xE036) },
+	{ USB_DEVICE(0x0489, 0xE03C) },
+
+	{ }	/* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, ath3k_table);
+
+#define BTUSB_ATH3012		0x80
+/* This table is to load patch and sysconfig files
+ * for AR3012 */
+static struct usb_device_id ath3k_blist_tbl[] = {
+
+	/* Atheros AR3012 with sflash firmware*/
+	{ USB_DEVICE(0x0489, 0xe04e), .driver_info = BTUSB_ATH3012 },
+	{ USB_DEVICE(0x0489, 0xe04d), .driver_info = BTUSB_ATH3012 },
+	{ USB_DEVICE(0x0489, 0xe056), .driver_info = BTUSB_ATH3012 },
+	{ USB_DEVICE(0x0489, 0xe057), .driver_info = BTUSB_ATH3012 },
+	{ USB_DEVICE(0x0489, 0xe078), .driver_info = BTUSB_ATH3012 },
+	{ USB_DEVICE(0x04c5, 0x1330), .driver_info = BTUSB_ATH3012 },
+	{ USB_DEVICE(0x04ca, 0x3004), .driver_info = BTUSB_ATH3012 },
+	{ USB_DEVICE(0x04ca, 0x3005), .driver_info = BTUSB_ATH3012 },
+	{ USB_DEVICE(0x04ca, 0x3006), .driver_info = BTUSB_ATH3012 },
+	{ USB_DEVICE(0x04ca, 0x3008), .driver_info = BTUSB_ATH3012 },
+	{ USB_DEVICE(0x04ca, 0x3010), .driver_info = BTUSB_ATH3012 },
+	{ USB_DEVICE(0x0930, 0x0219), .driver_info = BTUSB_ATH3012 },
+	{ USB_DEVICE(0x0930, 0x0227), .driver_info = BTUSB_ATH3012 },
+	{ USB_DEVICE(0x0b05, 0x17d0), .driver_info = BTUSB_ATH3012 },
+	{ USB_DEVICE(0x0CF3, 0x0036), .driver_info = BTUSB_ATH3012 },
+	{ USB_DEVICE(0x0cf3, 0x3004), .driver_info = BTUSB_ATH3012 },
+	{ USB_DEVICE(0x0cf3, 0x3008), .driver_info = BTUSB_ATH3012 },
+	{ USB_DEVICE(0x0cf3, 0x311D), .driver_info = BTUSB_ATH3012 },
+	{ USB_DEVICE(0x0cf3, 0x3121), .driver_info = BTUSB_ATH3012 },
+	{ USB_DEVICE(0x0CF3, 0x817a), .driver_info = BTUSB_ATH3012 },
+	{ USB_DEVICE(0x0cf3, 0xe004), .driver_info = BTUSB_ATH3012 },
+	{ USB_DEVICE(0x0cf3, 0xe005), .driver_info = BTUSB_ATH3012 },
+	{ USB_DEVICE(0x0cf3, 0xe003), .driver_info = BTUSB_ATH3012 },
+	{ USB_DEVICE(0x13d3, 0x3362), .driver_info = BTUSB_ATH3012 },
+	{ USB_DEVICE(0x13d3, 0x3375), .driver_info = BTUSB_ATH3012 },
+	{ USB_DEVICE(0x13d3, 0x3393), .driver_info = BTUSB_ATH3012 },
+	{ USB_DEVICE(0x13d3, 0x3402), .driver_info = BTUSB_ATH3012 },
+	{ USB_DEVICE(0x13d3, 0x3408), .driver_info = BTUSB_ATH3012 },
+	{ USB_DEVICE(0x13d3, 0x3423), .driver_info = BTUSB_ATH3012 },
+	{ USB_DEVICE(0x13d3, 0x3432), .driver_info = BTUSB_ATH3012 },
+	{ USB_DEVICE(0x13d3, 0x3474), .driver_info = BTUSB_ATH3012 },
+
+	/* Atheros AR5BBU22 with sflash firmware */
+	{ USB_DEVICE(0x0489, 0xE03C), .driver_info = BTUSB_ATH3012 },
+	{ USB_DEVICE(0x0489, 0xE036), .driver_info = BTUSB_ATH3012 },
+
+	{ }	/* Terminating entry */
+};
+
+#define USB_REQ_DFU_DNLOAD	1
+#define BULK_SIZE		4096
+#define FW_HDR_SIZE		20
+#define TIMEGAP_USEC_MIN	50
+#define TIMEGAP_USEC_MAX	100
+
+static int ath3k_load_firmware(struct usb_device *udev,
+				const struct firmware *firmware)
+{
+	u8 *send_buf;
+	int err, pipe, len, size, sent = 0;
+	int count = firmware->size;
+
+	BT_DBG("udev %p", udev);
+
+	pipe = usb_sndctrlpipe(udev, 0);
+
+	send_buf = kmalloc(BULK_SIZE, GFP_KERNEL);
+	if (!send_buf) {
+		BT_ERR("Can't allocate memory chunk for firmware");
+		return -ENOMEM;
+	}
+
+	memcpy(send_buf, firmware->data, 20);
+	if ((err = usb_control_msg(udev, pipe,
+				USB_REQ_DFU_DNLOAD,
+				USB_TYPE_VENDOR, 0, 0,
+				send_buf, 20, USB_CTRL_SET_TIMEOUT)) < 0) {
+		BT_ERR("Can't change to loading configuration err");
+		goto error;
+	}
+	sent += 20;
+	count -= 20;
+
+	while (count) {
+		/* workaround the compatibility issue with xHCI controller*/
+		usleep_range(TIMEGAP_USEC_MIN, TIMEGAP_USEC_MAX);
+
+		size = min_t(uint, count, BULK_SIZE);
+		pipe = usb_sndbulkpipe(udev, 0x02);
+		memcpy(send_buf, firmware->data + sent, size);
+
+		err = usb_bulk_msg(udev, pipe, send_buf, size,
+					&len, 3000);
+
+		if (err || (len != size)) {
+			BT_ERR("Error in firmware loading err = %d,"
+				"len = %d, size = %d", err, len, size);
+			goto error;
+		}
+
+		sent  += size;
+		count -= size;
+	}
+
+error:
+	kfree(send_buf);
+	return err;
+}
+
+static int ath3k_get_state(struct usb_device *udev, unsigned char *state)
+{
+	int pipe = 0;
+
+	pipe = usb_rcvctrlpipe(udev, 0);
+	return usb_control_msg(udev, pipe, ATH3K_GETSTATE,
+			USB_TYPE_VENDOR | USB_DIR_IN, 0, 0,
+			state, 0x01, USB_CTRL_SET_TIMEOUT);
+}
+
+static int ath3k_get_version(struct usb_device *udev,
+			struct ath3k_version *version)
+{
+	int pipe = 0;
+
+	pipe = usb_rcvctrlpipe(udev, 0);
+	return usb_control_msg(udev, pipe, ATH3K_GETVERSION,
+			USB_TYPE_VENDOR | USB_DIR_IN, 0, 0, version,
+			sizeof(struct ath3k_version),
+			USB_CTRL_SET_TIMEOUT);
+}
+
+static int ath3k_load_fwfile(struct usb_device *udev,
+		const struct firmware *firmware)
+{
+	u8 *send_buf;
+	int err, pipe, len, size, count, sent = 0;
+	int ret;
+
+	count = firmware->size;
+
+	send_buf = kmalloc(BULK_SIZE, GFP_KERNEL);
+	if (!send_buf) {
+		BT_ERR("Can't allocate memory chunk for firmware");
+		return -ENOMEM;
+	}
+
+	size = min_t(uint, count, FW_HDR_SIZE);
+	memcpy(send_buf, firmware->data, size);
+
+	pipe = usb_sndctrlpipe(udev, 0);
+	ret = usb_control_msg(udev, pipe, ATH3K_DNLOAD,
+			USB_TYPE_VENDOR, 0, 0, send_buf,
+			size, USB_CTRL_SET_TIMEOUT);
+	if (ret < 0) {
+		BT_ERR("Can't change to loading configuration err");
+		kfree(send_buf);
+		return ret;
+	}
+
+	sent += size;
+	count -= size;
+
+	while (count) {
+		/* workaround the compatibility issue with xHCI controller*/
+		usleep_range(TIMEGAP_USEC_MIN, TIMEGAP_USEC_MAX);
+
+		size = min_t(uint, count, BULK_SIZE);
+		pipe = usb_sndbulkpipe(udev, 0x02);
+
+		memcpy(send_buf, firmware->data + sent, size);
+
+		err = usb_bulk_msg(udev, pipe, send_buf, size,
+					&len, 3000);
+		if (err || (len != size)) {
+			BT_ERR("Error in firmware loading err = %d,"
+				"len = %d, size = %d", err, len, size);
+			kfree(send_buf);
+			return err;
+		}
+		sent  += size;
+		count -= size;
+	}
+
+	kfree(send_buf);
+	return 0;
+}
+
+static int ath3k_switch_pid(struct usb_device *udev)
+{
+	int pipe = 0;
+
+	pipe = usb_sndctrlpipe(udev, 0);
+	return usb_control_msg(udev, pipe, USB_REG_SWITCH_VID_PID,
+			USB_TYPE_VENDOR, 0, 0,
+			NULL, 0, USB_CTRL_SET_TIMEOUT);
+}
+
+static int ath3k_set_normal_mode(struct usb_device *udev)
+{
+	unsigned char fw_state;
+	int pipe = 0, ret;
+
+	ret = ath3k_get_state(udev, &fw_state);
+	if (ret < 0) {
+		BT_ERR("Can't get state to change to normal mode err");
+		return ret;
+	}
+
+	if ((fw_state & ATH3K_MODE_MASK) == ATH3K_NORMAL_MODE) {
+		BT_DBG("firmware was already in normal mode");
+		return 0;
+	}
+
+	pipe = usb_sndctrlpipe(udev, 0);
+	return usb_control_msg(udev, pipe, ATH3K_SET_NORMAL_MODE,
+			USB_TYPE_VENDOR, 0, 0,
+			NULL, 0, USB_CTRL_SET_TIMEOUT);
+}
+
+static int ath3k_load_patch(struct usb_device *udev)
+{
+	unsigned char fw_state;
+	char filename[ATH3K_NAME_LEN] = {0};
+	const struct firmware *firmware;
+	struct ath3k_version fw_version, pt_version;
+	int ret;
+
+	ret = ath3k_get_state(udev, &fw_state);
+	if (ret < 0) {
+		BT_ERR("Can't get state to change to load ram patch err");
+		return ret;
+	}
+
+	if (fw_state & ATH3K_PATCH_UPDATE) {
+		BT_DBG("Patch was already downloaded");
+		return 0;
+	}
+
+	ret = ath3k_get_version(udev, &fw_version);
+	if (ret < 0) {
+		BT_ERR("Can't get version to change to load ram patch err");
+		return ret;
+	}
+
+	snprintf(filename, ATH3K_NAME_LEN, "ar3k/AthrBT_0x%08x.dfu",
+		fw_version.rom_version);
+
+	ret = request_firmware(&firmware, filename, &udev->dev);
+	if (ret < 0) {
+		BT_ERR("Patch file not found %s", filename);
+		return ret;
+	}
+
+	pt_version.rom_version = *(int *)(firmware->data + firmware->size - 8);
+	pt_version.build_version = *(int *)
+		(firmware->data + firmware->size - 4);
+
+	if ((pt_version.rom_version != fw_version.rom_version) ||
+		(pt_version.build_version <= fw_version.build_version)) {
+		BT_ERR("Patch file version did not match with firmware");
+		release_firmware(firmware);
+		return -EINVAL;
+	}
+
+	ret = ath3k_load_fwfile(udev, firmware);
+	release_firmware(firmware);
+
+	return ret;
+}
+
+static int ath3k_load_syscfg(struct usb_device *udev)
+{
+	unsigned char fw_state;
+	char filename[ATH3K_NAME_LEN] = {0};
+	const struct firmware *firmware;
+	struct ath3k_version fw_version;
+	int clk_value, ret;
+
+	ret = ath3k_get_state(udev, &fw_state);
+	if (ret < 0) {
+		BT_ERR("Can't get state to change to load configration err");
+		return -EBUSY;
+	}
+
+	ret = ath3k_get_version(udev, &fw_version);
+	if (ret < 0) {
+		BT_ERR("Can't get version to change to load ram patch err");
+		return ret;
+	}
+
+	switch (fw_version.ref_clock) {
+
+	case ATH3K_XTAL_FREQ_26M:
+		clk_value = 26;
+		break;
+	case ATH3K_XTAL_FREQ_40M:
+		clk_value = 40;
+		break;
+	case ATH3K_XTAL_FREQ_19P2:
+		clk_value = 19;
+		break;
+	default:
+		clk_value = 0;
+		break;
+	}
+
+	snprintf(filename, ATH3K_NAME_LEN, "ar3k/ramps_0x%08x_%d%s",
+		fw_version.rom_version, clk_value, ".dfu");
+
+	ret = request_firmware(&firmware, filename, &udev->dev);
+	if (ret < 0) {
+		BT_ERR("Configuration file not found %s", filename);
+		return ret;
+	}
+
+	ret = ath3k_load_fwfile(udev, firmware);
+	release_firmware(firmware);
+
+	return ret;
+}
+
+static int ath3k_probe(struct usb_interface *intf,
+			const struct usb_device_id *id)
+{
+	const struct firmware *firmware;
+	struct usb_device *udev = interface_to_usbdev(intf);
+	int ret;
+
+	BT_DBG("intf %p id %p", intf, id);
+
+	if (intf->cur_altsetting->desc.bInterfaceNumber != 0)
+		return -ENODEV;
+
+	/* match device ID in ath3k blacklist table */
+	if (!id->driver_info) {
+		const struct usb_device_id *match;
+		match = usb_match_id(intf, ath3k_blist_tbl);
+		if (match)
+			id = match;
+	}
+
+	/* load patch and sysconfig files for AR3012 */
+	if (id->driver_info & BTUSB_ATH3012) {
+
+		/* New firmware with patch and sysconfig files already loaded */
+		if (le16_to_cpu(udev->descriptor.bcdDevice) > 0x0001)
+			return -ENODEV;
+
+		ret = ath3k_load_patch(udev);
+		if (ret < 0) {
+			BT_ERR("Loading patch file failed");
+			return ret;
+		}
+		ret = ath3k_load_syscfg(udev);
+		if (ret < 0) {
+			BT_ERR("Loading sysconfig file failed");
+			return ret;
+		}
+		ret = ath3k_set_normal_mode(udev);
+		if (ret < 0) {
+			BT_ERR("Set normal mode failed");
+			return ret;
+		}
+		ath3k_switch_pid(udev);
+		return 0;
+	}
+
+	ret = request_firmware(&firmware, ATH3K_FIRMWARE, &udev->dev);
+	if (ret < 0) {
+		if (ret == -ENOENT)
+			BT_ERR("Firmware file \"%s\" not found",
+							ATH3K_FIRMWARE);
+		else
+			BT_ERR("Firmware file \"%s\" request failed (err=%d)",
+							ATH3K_FIRMWARE, ret);
+		return ret;
+	}
+
+	ret = ath3k_load_firmware(udev, firmware);
+	release_firmware(firmware);
+
+	return ret;
+}
+
+static void ath3k_disconnect(struct usb_interface *intf)
+{
+	BT_DBG("ath3k_disconnect intf %p", intf);
+}
+
+static struct usb_driver ath3k_driver = {
+	.name		= "ath3k",
+	.probe		= ath3k_probe,
+	.disconnect	= ath3k_disconnect,
+	.id_table	= ath3k_table,
+};
+
+module_usb_driver(ath3k_driver);
+
+MODULE_AUTHOR("Atheros Communications");
+MODULE_DESCRIPTION("Atheros AR30xx firmware driver");
+MODULE_VERSION(VERSION);
+MODULE_LICENSE("GPL");
+MODULE_FIRMWARE(ATH3K_FIRMWARE);
diff --git a/ap/os/linux/linux-3.4.x/drivers/bluetooth/bcm203x.c b/ap/os/linux/linux-3.4.x/drivers/bluetooth/bcm203x.c
new file mode 100644
index 0000000..1e742a5
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/bluetooth/bcm203x.c
@@ -0,0 +1,291 @@
+/*
+ *
+ *  Broadcom Blutonium firmware driver
+ *
+ *  Copyright (C) 2003  Maxim Krasnyansky <maxk@qualcomm.com>
+ *  Copyright (C) 2003  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/module.h>
+
+#include <linux/atomic.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+
+#include <linux/device.h>
+#include <linux/firmware.h>
+
+#include <linux/usb.h>
+
+#include <net/bluetooth/bluetooth.h>
+
+#define VERSION "1.2"
+
+static const struct usb_device_id bcm203x_table[] = {
+	/* Broadcom Blutonium (BCM2033) */
+	{ USB_DEVICE(0x0a5c, 0x2033) },
+
+	{ }	/* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, bcm203x_table);
+
+#define BCM203X_ERROR		0
+#define BCM203X_RESET		1
+#define BCM203X_LOAD_MINIDRV	2
+#define BCM203X_SELECT_MEMORY	3
+#define BCM203X_CHECK_MEMORY	4
+#define BCM203X_LOAD_FIRMWARE	5
+#define BCM203X_CHECK_FIRMWARE	6
+
+#define BCM203X_IN_EP		0x81
+#define BCM203X_OUT_EP		0x02
+
+struct bcm203x_data {
+	struct usb_device	*udev;
+
+	unsigned long		state;
+
+	struct work_struct	work;
+	atomic_t		shutdown;
+
+	struct urb		*urb;
+	unsigned char		*buffer;
+
+	unsigned char		*fw_data;
+	unsigned int		fw_size;
+	unsigned int		fw_sent;
+};
+
+static void bcm203x_complete(struct urb *urb)
+{
+	struct bcm203x_data *data = urb->context;
+	struct usb_device *udev = urb->dev;
+	int len;
+
+	BT_DBG("udev %p urb %p", udev, urb);
+
+	if (urb->status) {
+		BT_ERR("URB failed with status %d", urb->status);
+		data->state = BCM203X_ERROR;
+		return;
+	}
+
+	switch (data->state) {
+	case BCM203X_LOAD_MINIDRV:
+		memcpy(data->buffer, "#", 1);
+
+		usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, BCM203X_OUT_EP),
+				data->buffer, 1, bcm203x_complete, data);
+
+		data->state = BCM203X_SELECT_MEMORY;
+
+		/* use workqueue to have a small delay */
+		schedule_work(&data->work);
+		break;
+
+	case BCM203X_SELECT_MEMORY:
+		usb_fill_int_urb(urb, udev, usb_rcvintpipe(udev, BCM203X_IN_EP),
+				data->buffer, 32, bcm203x_complete, data, 1);
+
+		data->state = BCM203X_CHECK_MEMORY;
+
+		if (usb_submit_urb(data->urb, GFP_ATOMIC) < 0)
+			BT_ERR("Can't submit URB");
+		break;
+
+	case BCM203X_CHECK_MEMORY:
+		if (data->buffer[0] != '#') {
+			BT_ERR("Memory select failed");
+			data->state = BCM203X_ERROR;
+			break;
+		}
+
+		data->state = BCM203X_LOAD_FIRMWARE;
+
+	case BCM203X_LOAD_FIRMWARE:
+		if (data->fw_sent == data->fw_size) {
+			usb_fill_int_urb(urb, udev, usb_rcvintpipe(udev, BCM203X_IN_EP),
+				data->buffer, 32, bcm203x_complete, data, 1);
+
+			data->state = BCM203X_CHECK_FIRMWARE;
+		} else {
+			len = min_t(uint, data->fw_size - data->fw_sent, 4096);
+
+			usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, BCM203X_OUT_EP),
+				data->fw_data + data->fw_sent, len, bcm203x_complete, data);
+
+			data->fw_sent += len;
+		}
+
+		if (usb_submit_urb(data->urb, GFP_ATOMIC) < 0)
+			BT_ERR("Can't submit URB");
+		break;
+
+	case BCM203X_CHECK_FIRMWARE:
+		if (data->buffer[0] != '.') {
+			BT_ERR("Firmware loading failed");
+			data->state = BCM203X_ERROR;
+			break;
+		}
+
+		data->state = BCM203X_RESET;
+		break;
+	}
+}
+
+static void bcm203x_work(struct work_struct *work)
+{
+	struct bcm203x_data *data =
+		container_of(work, struct bcm203x_data, work);
+
+	if (atomic_read(&data->shutdown))
+		return;
+
+	if (usb_submit_urb(data->urb, GFP_KERNEL) < 0)
+		BT_ERR("Can't submit URB");
+}
+
+static int bcm203x_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+	const struct firmware *firmware;
+	struct usb_device *udev = interface_to_usbdev(intf);
+	struct bcm203x_data *data;
+	int size;
+
+	BT_DBG("intf %p id %p", intf, id);
+
+	if (intf->cur_altsetting->desc.bInterfaceNumber != 0)
+		return -ENODEV;
+
+	data = kzalloc(sizeof(*data), GFP_KERNEL);
+	if (!data) {
+		BT_ERR("Can't allocate memory for data structure");
+		return -ENOMEM;
+	}
+
+	data->udev  = udev;
+	data->state = BCM203X_LOAD_MINIDRV;
+
+	data->urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!data->urb) {
+		BT_ERR("Can't allocate URB");
+		kfree(data);
+		return -ENOMEM;
+	}
+
+	if (request_firmware(&firmware, "BCM2033-MD.hex", &udev->dev) < 0) {
+		BT_ERR("Mini driver request failed");
+		usb_free_urb(data->urb);
+		kfree(data);
+		return -EIO;
+	}
+
+	BT_DBG("minidrv data %p size %zu", firmware->data, firmware->size);
+
+	size = max_t(uint, firmware->size, 4096);
+
+	data->buffer = kmalloc(size, GFP_KERNEL);
+	if (!data->buffer) {
+		BT_ERR("Can't allocate memory for mini driver");
+		release_firmware(firmware);
+		usb_free_urb(data->urb);
+		kfree(data);
+		return -ENOMEM;
+	}
+
+	memcpy(data->buffer, firmware->data, firmware->size);
+
+	usb_fill_bulk_urb(data->urb, udev, usb_sndbulkpipe(udev, BCM203X_OUT_EP),
+			data->buffer, firmware->size, bcm203x_complete, data);
+
+	release_firmware(firmware);
+
+	if (request_firmware(&firmware, "BCM2033-FW.bin", &udev->dev) < 0) {
+		BT_ERR("Firmware request failed");
+		usb_free_urb(data->urb);
+		kfree(data->buffer);
+		kfree(data);
+		return -EIO;
+	}
+
+	BT_DBG("firmware data %p size %zu", firmware->data, firmware->size);
+
+	data->fw_data = kmemdup(firmware->data, firmware->size, GFP_KERNEL);
+	if (!data->fw_data) {
+		BT_ERR("Can't allocate memory for firmware image");
+		release_firmware(firmware);
+		usb_free_urb(data->urb);
+		kfree(data->buffer);
+		kfree(data);
+		return -ENOMEM;
+	}
+
+	data->fw_size = firmware->size;
+	data->fw_sent = 0;
+
+	release_firmware(firmware);
+
+	INIT_WORK(&data->work, bcm203x_work);
+
+	usb_set_intfdata(intf, data);
+
+	/* use workqueue to have a small delay */
+	schedule_work(&data->work);
+
+	return 0;
+}
+
+static void bcm203x_disconnect(struct usb_interface *intf)
+{
+	struct bcm203x_data *data = usb_get_intfdata(intf);
+
+	BT_DBG("intf %p", intf);
+
+	atomic_inc(&data->shutdown);
+	cancel_work_sync(&data->work);
+
+	usb_kill_urb(data->urb);
+
+	usb_set_intfdata(intf, NULL);
+
+	usb_free_urb(data->urb);
+	kfree(data->fw_data);
+	kfree(data->buffer);
+	kfree(data);
+}
+
+static struct usb_driver bcm203x_driver = {
+	.name		= "bcm203x",
+	.probe		= bcm203x_probe,
+	.disconnect	= bcm203x_disconnect,
+	.id_table	= bcm203x_table,
+};
+
+module_usb_driver(bcm203x_driver);
+
+MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
+MODULE_DESCRIPTION("Broadcom Blutonium firmware driver ver " VERSION);
+MODULE_VERSION(VERSION);
+MODULE_LICENSE("GPL");
+MODULE_FIRMWARE("BCM2033-MD.hex");
+MODULE_FIRMWARE("BCM2033-FW.bin");
diff --git a/ap/os/linux/linux-3.4.x/drivers/bluetooth/bfusb.c b/ap/os/linux/linux-3.4.x/drivers/bluetooth/bfusb.c
new file mode 100644
index 0000000..b8ac1c5
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/bluetooth/bfusb.c
@@ -0,0 +1,760 @@
+/*
+ *
+ *  AVM BlueFRITZ! USB driver
+ *
+ *  Copyright (C) 2003-2006  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/module.h>
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/skbuff.h>
+
+#include <linux/device.h>
+#include <linux/firmware.h>
+
+#include <linux/usb.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#define VERSION "1.2"
+
+static struct usb_driver bfusb_driver;
+
+static struct usb_device_id bfusb_table[] = {
+	/* AVM BlueFRITZ! USB */
+	{ USB_DEVICE(0x057c, 0x2200) },
+
+	{ }	/* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, bfusb_table);
+
+#define BFUSB_MAX_BLOCK_SIZE	256
+
+#define BFUSB_BLOCK_TIMEOUT	3000
+
+#define BFUSB_TX_PROCESS	1
+#define BFUSB_TX_WAKEUP		2
+
+#define BFUSB_MAX_BULK_TX	2
+#define BFUSB_MAX_BULK_RX	2
+
+struct bfusb_data {
+	struct hci_dev		*hdev;
+
+	unsigned long		state;
+
+	struct usb_device	*udev;
+
+	unsigned int		bulk_in_ep;
+	unsigned int		bulk_out_ep;
+	unsigned int		bulk_pkt_size;
+
+	rwlock_t		lock;
+
+	struct sk_buff_head	transmit_q;
+
+	struct sk_buff		*reassembly;
+
+	atomic_t		pending_tx;
+	struct sk_buff_head	pending_q;
+	struct sk_buff_head	completed_q;
+};
+
+struct bfusb_data_scb {
+	struct urb *urb;
+};
+
+static void bfusb_tx_complete(struct urb *urb);
+static void bfusb_rx_complete(struct urb *urb);
+
+static struct urb *bfusb_get_completed(struct bfusb_data *data)
+{
+	struct sk_buff *skb;
+	struct urb *urb = NULL;
+
+	BT_DBG("bfusb %p", data);
+
+	skb = skb_dequeue(&data->completed_q);
+	if (skb) {
+		urb = ((struct bfusb_data_scb *) skb->cb)->urb;
+		kfree_skb(skb);
+	}
+
+	return urb;
+}
+
+static void bfusb_unlink_urbs(struct bfusb_data *data)
+{
+	struct sk_buff *skb;
+	struct urb *urb;
+
+	BT_DBG("bfusb %p", data);
+
+	while ((skb = skb_dequeue(&data->pending_q))) {
+		urb = ((struct bfusb_data_scb *) skb->cb)->urb;
+		usb_kill_urb(urb);
+		skb_queue_tail(&data->completed_q, skb);
+	}
+
+	while ((urb = bfusb_get_completed(data)))
+		usb_free_urb(urb);
+}
+
+static int bfusb_send_bulk(struct bfusb_data *data, struct sk_buff *skb)
+{
+	struct bfusb_data_scb *scb = (void *) skb->cb;
+	struct urb *urb = bfusb_get_completed(data);
+	int err, pipe;
+
+	BT_DBG("bfusb %p skb %p len %d", data, skb, skb->len);
+
+	if (!urb && !(urb = usb_alloc_urb(0, GFP_ATOMIC)))
+		return -ENOMEM;
+
+	pipe = usb_sndbulkpipe(data->udev, data->bulk_out_ep);
+
+	usb_fill_bulk_urb(urb, data->udev, pipe, skb->data, skb->len,
+			bfusb_tx_complete, skb);
+
+	scb->urb = urb;
+
+	skb_queue_tail(&data->pending_q, skb);
+
+	err = usb_submit_urb(urb, GFP_ATOMIC);
+	if (err) {
+		BT_ERR("%s bulk tx submit failed urb %p err %d", 
+					data->hdev->name, urb, err);
+		skb_unlink(skb, &data->pending_q);
+		usb_free_urb(urb);
+	} else
+		atomic_inc(&data->pending_tx);
+
+	return err;
+}
+
+static void bfusb_tx_wakeup(struct bfusb_data *data)
+{
+	struct sk_buff *skb;
+
+	BT_DBG("bfusb %p", data);
+
+	if (test_and_set_bit(BFUSB_TX_PROCESS, &data->state)) {
+		set_bit(BFUSB_TX_WAKEUP, &data->state);
+		return;
+	}
+
+	do {
+		clear_bit(BFUSB_TX_WAKEUP, &data->state);
+
+		while ((atomic_read(&data->pending_tx) < BFUSB_MAX_BULK_TX) &&
+				(skb = skb_dequeue(&data->transmit_q))) {
+			if (bfusb_send_bulk(data, skb) < 0) {
+				skb_queue_head(&data->transmit_q, skb);
+				break;
+			}
+		}
+
+	} while (test_bit(BFUSB_TX_WAKEUP, &data->state));
+
+	clear_bit(BFUSB_TX_PROCESS, &data->state);
+}
+
+static void bfusb_tx_complete(struct urb *urb)
+{
+	struct sk_buff *skb = (struct sk_buff *) urb->context;
+	struct bfusb_data *data = (struct bfusb_data *) skb->dev;
+
+	BT_DBG("bfusb %p urb %p skb %p len %d", data, urb, skb, skb->len);
+
+	atomic_dec(&data->pending_tx);
+
+	if (!test_bit(HCI_RUNNING, &data->hdev->flags))
+		return;
+
+	if (!urb->status)
+		data->hdev->stat.byte_tx += skb->len;
+	else
+		data->hdev->stat.err_tx++;
+
+	read_lock(&data->lock);
+
+	skb_unlink(skb, &data->pending_q);
+	skb_queue_tail(&data->completed_q, skb);
+
+	bfusb_tx_wakeup(data);
+
+	read_unlock(&data->lock);
+}
+
+
+static int bfusb_rx_submit(struct bfusb_data *data, struct urb *urb)
+{
+	struct bfusb_data_scb *scb;
+	struct sk_buff *skb;
+	int err, pipe, size = HCI_MAX_FRAME_SIZE + 32;
+
+	BT_DBG("bfusb %p urb %p", data, urb);
+
+	if (!urb && !(urb = usb_alloc_urb(0, GFP_ATOMIC)))
+		return -ENOMEM;
+
+	skb = bt_skb_alloc(size, GFP_ATOMIC);
+	if (!skb) {
+		usb_free_urb(urb);
+		return -ENOMEM;
+	}
+
+	skb->dev = (void *) data;
+
+	scb = (struct bfusb_data_scb *) skb->cb;
+	scb->urb = urb;
+
+	pipe = usb_rcvbulkpipe(data->udev, data->bulk_in_ep);
+
+	usb_fill_bulk_urb(urb, data->udev, pipe, skb->data, size,
+			bfusb_rx_complete, skb);
+
+	skb_queue_tail(&data->pending_q, skb);
+
+	err = usb_submit_urb(urb, GFP_ATOMIC);
+	if (err) {
+		BT_ERR("%s bulk rx submit failed urb %p err %d",
+					data->hdev->name, urb, err);
+		skb_unlink(skb, &data->pending_q);
+		kfree_skb(skb);
+		usb_free_urb(urb);
+	}
+
+	return err;
+}
+
+static inline int bfusb_recv_block(struct bfusb_data *data, int hdr, unsigned char *buf, int len)
+{
+	BT_DBG("bfusb %p hdr 0x%02x data %p len %d", data, hdr, buf, len);
+
+	if (hdr & 0x10) {
+		BT_ERR("%s error in block", data->hdev->name);
+		kfree_skb(data->reassembly);
+		data->reassembly = NULL;
+		return -EIO;
+	}
+
+	if (hdr & 0x04) {
+		struct sk_buff *skb;
+		unsigned char pkt_type;
+		int pkt_len = 0;
+
+		if (data->reassembly) {
+			BT_ERR("%s unexpected start block", data->hdev->name);
+			kfree_skb(data->reassembly);
+			data->reassembly = NULL;
+		}
+
+		if (len < 1) {
+			BT_ERR("%s no packet type found", data->hdev->name);
+			return -EPROTO;
+		}
+
+		pkt_type = *buf++; len--;
+
+		switch (pkt_type) {
+		case HCI_EVENT_PKT:
+			if (len >= HCI_EVENT_HDR_SIZE) {
+				struct hci_event_hdr *hdr = (struct hci_event_hdr *) buf;
+				pkt_len = HCI_EVENT_HDR_SIZE + hdr->plen;
+			} else {
+				BT_ERR("%s event block is too short", data->hdev->name);
+				return -EILSEQ;
+			}
+			break;
+
+		case HCI_ACLDATA_PKT:
+			if (len >= HCI_ACL_HDR_SIZE) {
+				struct hci_acl_hdr *hdr = (struct hci_acl_hdr *) buf;
+				pkt_len = HCI_ACL_HDR_SIZE + __le16_to_cpu(hdr->dlen);
+			} else {
+				BT_ERR("%s data block is too short", data->hdev->name);
+				return -EILSEQ;
+			}
+			break;
+
+		case HCI_SCODATA_PKT:
+			if (len >= HCI_SCO_HDR_SIZE) {
+				struct hci_sco_hdr *hdr = (struct hci_sco_hdr *) buf;
+				pkt_len = HCI_SCO_HDR_SIZE + hdr->dlen;
+			} else {
+				BT_ERR("%s audio block is too short", data->hdev->name);
+				return -EILSEQ;
+			}
+			break;
+		}
+
+		skb = bt_skb_alloc(pkt_len, GFP_ATOMIC);
+		if (!skb) {
+			BT_ERR("%s no memory for the packet", data->hdev->name);
+			return -ENOMEM;
+		}
+
+		skb->dev = (void *) data->hdev;
+		bt_cb(skb)->pkt_type = pkt_type;
+
+		data->reassembly = skb;
+	} else {
+		if (!data->reassembly) {
+			BT_ERR("%s unexpected continuation block", data->hdev->name);
+			return -EIO;
+		}
+	}
+
+	if (len > 0)
+		memcpy(skb_put(data->reassembly, len), buf, len);
+
+	if (hdr & 0x08) {
+		hci_recv_frame(data->reassembly);
+		data->reassembly = NULL;
+	}
+
+	return 0;
+}
+
+static void bfusb_rx_complete(struct urb *urb)
+{
+	struct sk_buff *skb = (struct sk_buff *) urb->context;
+	struct bfusb_data *data = (struct bfusb_data *) skb->dev;
+	unsigned char *buf = urb->transfer_buffer;
+	int count = urb->actual_length;
+	int err, hdr, len;
+
+	BT_DBG("bfusb %p urb %p skb %p len %d", data, urb, skb, skb->len);
+
+	read_lock(&data->lock);
+
+	if (!test_bit(HCI_RUNNING, &data->hdev->flags))
+		goto unlock;
+
+	if (urb->status || !count)
+		goto resubmit;
+
+	data->hdev->stat.byte_rx += count;
+
+	skb_put(skb, count);
+
+	while (count) {
+		hdr = buf[0] | (buf[1] << 8);
+
+		if (hdr & 0x4000) {
+			len = 0;
+			count -= 2;
+			buf   += 2;
+		} else {
+			len = (buf[2] == 0) ? 256 : buf[2];
+			count -= 3;
+			buf   += 3;
+		}
+
+		if (count < len) {
+			BT_ERR("%s block extends over URB buffer ranges",
+					data->hdev->name);
+		}
+
+		if ((hdr & 0xe1) == 0xc1)
+			bfusb_recv_block(data, hdr, buf, len);
+
+		count -= len;
+		buf   += len;
+	}
+
+	skb_unlink(skb, &data->pending_q);
+	kfree_skb(skb);
+
+	bfusb_rx_submit(data, urb);
+
+	read_unlock(&data->lock);
+
+	return;
+
+resubmit:
+	urb->dev = data->udev;
+
+	err = usb_submit_urb(urb, GFP_ATOMIC);
+	if (err) {
+		BT_ERR("%s bulk resubmit failed urb %p err %d",
+					data->hdev->name, urb, err);
+	}
+
+unlock:
+	read_unlock(&data->lock);
+}
+
+static int bfusb_open(struct hci_dev *hdev)
+{
+	struct bfusb_data *data = hci_get_drvdata(hdev);
+	unsigned long flags;
+	int i, err;
+
+	BT_DBG("hdev %p bfusb %p", hdev, data);
+
+	if (test_and_set_bit(HCI_RUNNING, &hdev->flags))
+		return 0;
+
+	write_lock_irqsave(&data->lock, flags);
+
+	err = bfusb_rx_submit(data, NULL);
+	if (!err) {
+		for (i = 1; i < BFUSB_MAX_BULK_RX; i++)
+			bfusb_rx_submit(data, NULL);
+	} else {
+		clear_bit(HCI_RUNNING, &hdev->flags);
+	}
+
+	write_unlock_irqrestore(&data->lock, flags);
+
+	return err;
+}
+
+static int bfusb_flush(struct hci_dev *hdev)
+{
+	struct bfusb_data *data = hci_get_drvdata(hdev);
+
+	BT_DBG("hdev %p bfusb %p", hdev, data);
+
+	skb_queue_purge(&data->transmit_q);
+
+	return 0;
+}
+
+static int bfusb_close(struct hci_dev *hdev)
+{
+	struct bfusb_data *data = hci_get_drvdata(hdev);
+	unsigned long flags;
+
+	BT_DBG("hdev %p bfusb %p", hdev, data);
+
+	if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
+		return 0;
+
+	write_lock_irqsave(&data->lock, flags);
+	write_unlock_irqrestore(&data->lock, flags);
+
+	bfusb_unlink_urbs(data);
+	bfusb_flush(hdev);
+
+	return 0;
+}
+
+static int bfusb_send_frame(struct sk_buff *skb)
+{
+	struct hci_dev *hdev = (struct hci_dev *) skb->dev;
+	struct bfusb_data *data;
+	struct sk_buff *nskb;
+	unsigned char buf[3];
+	int sent = 0, size, count;
+
+	BT_DBG("hdev %p skb %p type %d len %d", hdev, skb, bt_cb(skb)->pkt_type, skb->len);
+
+	if (!hdev) {
+		BT_ERR("Frame for unknown HCI device (hdev=NULL)");
+		return -ENODEV;
+	}
+
+	if (!test_bit(HCI_RUNNING, &hdev->flags))
+		return -EBUSY;
+
+	data = hci_get_drvdata(hdev);
+
+	switch (bt_cb(skb)->pkt_type) {
+	case HCI_COMMAND_PKT:
+		hdev->stat.cmd_tx++;
+		break;
+	case HCI_ACLDATA_PKT:
+		hdev->stat.acl_tx++;
+		break;
+	case HCI_SCODATA_PKT:
+		hdev->stat.sco_tx++;
+		break;
+	};
+
+	/* Prepend skb with frame type */
+	memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
+
+	count = skb->len;
+
+	/* Max HCI frame size seems to be 1511 + 1 */
+	nskb = bt_skb_alloc(count + 32, GFP_ATOMIC);
+	if (!nskb) {
+		BT_ERR("Can't allocate memory for new packet");
+		return -ENOMEM;
+	}
+
+	nskb->dev = (void *) data;
+
+	while (count) {
+		size = min_t(uint, count, BFUSB_MAX_BLOCK_SIZE);
+
+		buf[0] = 0xc1 | ((sent == 0) ? 0x04 : 0) | ((count == size) ? 0x08 : 0);
+		buf[1] = 0x00;
+		buf[2] = (size == BFUSB_MAX_BLOCK_SIZE) ? 0 : size;
+
+		memcpy(skb_put(nskb, 3), buf, 3);
+		skb_copy_from_linear_data_offset(skb, sent, skb_put(nskb, size), size);
+
+		sent  += size;
+		count -= size;
+	}
+
+	/* Don't send frame with multiple size of bulk max packet */
+	if ((nskb->len % data->bulk_pkt_size) == 0) {
+		buf[0] = 0xdd;
+		buf[1] = 0x00;
+		memcpy(skb_put(nskb, 2), buf, 2);
+	}
+
+	read_lock(&data->lock);
+
+	skb_queue_tail(&data->transmit_q, nskb);
+	bfusb_tx_wakeup(data);
+
+	read_unlock(&data->lock);
+
+	kfree_skb(skb);
+
+	return 0;
+}
+
+static int bfusb_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg)
+{
+	return -ENOIOCTLCMD;
+}
+
+static int bfusb_load_firmware(struct bfusb_data *data,
+			       const unsigned char *firmware, int count)
+{
+	unsigned char *buf;
+	int err, pipe, len, size, sent = 0;
+
+	BT_DBG("bfusb %p udev %p", data, data->udev);
+
+	BT_INFO("BlueFRITZ! USB loading firmware");
+
+	buf = kmalloc(BFUSB_MAX_BLOCK_SIZE + 3, GFP_KERNEL);
+	if (!buf) {
+		BT_ERR("Can't allocate memory chunk for firmware");
+		return -ENOMEM;
+	}
+
+	pipe = usb_sndctrlpipe(data->udev, 0);
+
+	if (usb_control_msg(data->udev, pipe, USB_REQ_SET_CONFIGURATION,
+				0, 1, 0, NULL, 0, USB_CTRL_SET_TIMEOUT) < 0) {
+		BT_ERR("Can't change to loading configuration");
+		kfree(buf);
+		return -EBUSY;
+	}
+
+	data->udev->toggle[0] = data->udev->toggle[1] = 0;
+
+	pipe = usb_sndbulkpipe(data->udev, data->bulk_out_ep);
+
+	while (count) {
+		size = min_t(uint, count, BFUSB_MAX_BLOCK_SIZE + 3);
+
+		memcpy(buf, firmware + sent, size);
+
+		err = usb_bulk_msg(data->udev, pipe, buf, size,
+					&len, BFUSB_BLOCK_TIMEOUT);
+
+		if (err || (len != size)) {
+			BT_ERR("Error in firmware loading");
+			goto error;
+		}
+
+		sent  += size;
+		count -= size;
+	}
+
+	err = usb_bulk_msg(data->udev, pipe, NULL, 0,
+					&len, BFUSB_BLOCK_TIMEOUT);
+	if (err < 0) {
+		BT_ERR("Error in null packet request");
+		goto error;
+	}
+
+	pipe = usb_sndctrlpipe(data->udev, 0);
+
+	err = usb_control_msg(data->udev, pipe, USB_REQ_SET_CONFIGURATION,
+				0, 2, 0, NULL, 0, USB_CTRL_SET_TIMEOUT);
+	if (err < 0) {
+		BT_ERR("Can't change to running configuration");
+		goto error;
+	}
+
+	data->udev->toggle[0] = data->udev->toggle[1] = 0;
+
+	BT_INFO("BlueFRITZ! USB device ready");
+
+	kfree(buf);
+	return 0;
+
+error:
+	kfree(buf);
+
+	pipe = usb_sndctrlpipe(data->udev, 0);
+
+	usb_control_msg(data->udev, pipe, USB_REQ_SET_CONFIGURATION,
+				0, 0, 0, NULL, 0, USB_CTRL_SET_TIMEOUT);
+
+	return err;
+}
+
+static int bfusb_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+	const struct firmware *firmware;
+	struct usb_device *udev = interface_to_usbdev(intf);
+	struct usb_host_endpoint *bulk_out_ep;
+	struct usb_host_endpoint *bulk_in_ep;
+	struct hci_dev *hdev;
+	struct bfusb_data *data;
+
+	BT_DBG("intf %p id %p", intf, id);
+
+	/* Check number of endpoints */
+	if (intf->cur_altsetting->desc.bNumEndpoints < 2)
+		return -EIO;
+
+	bulk_out_ep = &intf->cur_altsetting->endpoint[0];
+	bulk_in_ep  = &intf->cur_altsetting->endpoint[1];
+
+	if (!bulk_out_ep || !bulk_in_ep) {
+		BT_ERR("Bulk endpoints not found");
+		goto done;
+	}
+
+	/* Initialize control structure and load firmware */
+	data = kzalloc(sizeof(struct bfusb_data), GFP_KERNEL);
+	if (!data) {
+		BT_ERR("Can't allocate memory for control structure");
+		goto done;
+	}
+
+	data->udev = udev;
+	data->bulk_in_ep    = bulk_in_ep->desc.bEndpointAddress;
+	data->bulk_out_ep   = bulk_out_ep->desc.bEndpointAddress;
+	data->bulk_pkt_size = le16_to_cpu(bulk_out_ep->desc.wMaxPacketSize);
+
+	rwlock_init(&data->lock);
+
+	data->reassembly = NULL;
+
+	skb_queue_head_init(&data->transmit_q);
+	skb_queue_head_init(&data->pending_q);
+	skb_queue_head_init(&data->completed_q);
+
+	if (request_firmware(&firmware, "bfubase.frm", &udev->dev) < 0) {
+		BT_ERR("Firmware request failed");
+		goto error;
+	}
+
+	BT_DBG("firmware data %p size %zu", firmware->data, firmware->size);
+
+	if (bfusb_load_firmware(data, firmware->data, firmware->size) < 0) {
+		BT_ERR("Firmware loading failed");
+		goto release;
+	}
+
+	release_firmware(firmware);
+
+	/* Initialize and register HCI device */
+	hdev = hci_alloc_dev();
+	if (!hdev) {
+		BT_ERR("Can't allocate HCI device");
+		goto error;
+	}
+
+	data->hdev = hdev;
+
+	hdev->bus = HCI_USB;
+	hci_set_drvdata(hdev, data);
+	SET_HCIDEV_DEV(hdev, &intf->dev);
+
+	hdev->open     = bfusb_open;
+	hdev->close    = bfusb_close;
+	hdev->flush    = bfusb_flush;
+	hdev->send     = bfusb_send_frame;
+	hdev->ioctl    = bfusb_ioctl;
+
+	if (hci_register_dev(hdev) < 0) {
+		BT_ERR("Can't register HCI device");
+		hci_free_dev(hdev);
+		goto error;
+	}
+
+	usb_set_intfdata(intf, data);
+
+	return 0;
+
+release:
+	release_firmware(firmware);
+
+error:
+	kfree(data);
+
+done:
+	return -EIO;
+}
+
+static void bfusb_disconnect(struct usb_interface *intf)
+{
+	struct bfusb_data *data = usb_get_intfdata(intf);
+	struct hci_dev *hdev = data->hdev;
+
+	BT_DBG("intf %p", intf);
+
+	if (!hdev)
+		return;
+
+	usb_set_intfdata(intf, NULL);
+
+	bfusb_close(hdev);
+
+	hci_unregister_dev(hdev);
+	hci_free_dev(hdev);
+	kfree(data);
+}
+
+static struct usb_driver bfusb_driver = {
+	.name		= "bfusb",
+	.probe		= bfusb_probe,
+	.disconnect	= bfusb_disconnect,
+	.id_table	= bfusb_table,
+};
+
+module_usb_driver(bfusb_driver);
+
+MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
+MODULE_DESCRIPTION("BlueFRITZ! USB driver ver " VERSION);
+MODULE_VERSION(VERSION);
+MODULE_LICENSE("GPL");
+MODULE_FIRMWARE("bfubase.frm");
diff --git a/ap/os/linux/linux-3.4.x/drivers/bluetooth/bluecard_cs.c b/ap/os/linux/linux-3.4.x/drivers/bluetooth/bluecard_cs.c
new file mode 100644
index 0000000..1fcd923
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/bluetooth/bluecard_cs.c
@@ -0,0 +1,951 @@
+/*
+ *
+ *  Bluetooth driver for the Anycom BlueCard (LSE039/LSE041)
+ *
+ *  Copyright (C) 2001-2002  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 version 2 as
+ *  published by the Free Software Foundation;
+ *
+ *  Software distributed under the License is distributed on an "AS
+ *  IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ *  implied. See the License for the specific language governing
+ *  rights and limitations under the License.
+ *
+ *  The initial developer of the original code is David A. Hinds
+ *  <dahinds@users.sourceforge.net>.  Portions created by David A. Hinds
+ *  are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
+ *
+ */
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/errno.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/spinlock.h>
+#include <linux/moduleparam.h>
+#include <linux/wait.h>
+
+#include <linux/skbuff.h>
+#include <linux/io.h>
+
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ciscode.h>
+#include <pcmcia/ds.h>
+#include <pcmcia/cisreg.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+
+
+/* ======================== Module parameters ======================== */
+
+
+MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
+MODULE_DESCRIPTION("Bluetooth driver for the Anycom BlueCard (LSE039/LSE041)");
+MODULE_LICENSE("GPL");
+
+
+
+/* ======================== Local structures ======================== */
+
+
+typedef struct bluecard_info_t {
+	struct pcmcia_device *p_dev;
+
+	struct hci_dev *hdev;
+
+	spinlock_t lock;		/* For serializing operations */
+	struct timer_list timer;	/* For LED control */
+
+	struct sk_buff_head txq;
+	unsigned long tx_state;
+
+	unsigned long rx_state;
+	unsigned long rx_count;
+	struct sk_buff *rx_skb;
+
+	unsigned char ctrl_reg;
+	unsigned long hw_state;		/* Status of the hardware and LED control */
+} bluecard_info_t;
+
+
+static int bluecard_config(struct pcmcia_device *link);
+static void bluecard_release(struct pcmcia_device *link);
+
+static void bluecard_detach(struct pcmcia_device *p_dev);
+
+
+/* Default baud rate: 57600, 115200, 230400 or 460800 */
+#define DEFAULT_BAUD_RATE  230400
+
+
+/* Hardware states */
+#define CARD_READY             1
+#define CARD_HAS_PCCARD_ID     4
+#define CARD_HAS_POWER_LED     5
+#define CARD_HAS_ACTIVITY_LED  6
+
+/* Transmit states  */
+#define XMIT_SENDING         1
+#define XMIT_WAKEUP          2
+#define XMIT_BUFFER_NUMBER   5	/* unset = buffer one, set = buffer two */
+#define XMIT_BUF_ONE_READY   6
+#define XMIT_BUF_TWO_READY   7
+#define XMIT_SENDING_READY   8
+
+/* Receiver states */
+#define RECV_WAIT_PACKET_TYPE   0
+#define RECV_WAIT_EVENT_HEADER  1
+#define RECV_WAIT_ACL_HEADER    2
+#define RECV_WAIT_SCO_HEADER    3
+#define RECV_WAIT_DATA          4
+
+/* Special packet types */
+#define PKT_BAUD_RATE_57600   0x80
+#define PKT_BAUD_RATE_115200  0x81
+#define PKT_BAUD_RATE_230400  0x82
+#define PKT_BAUD_RATE_460800  0x83
+
+
+/* These are the register offsets */
+#define REG_COMMAND     0x20
+#define REG_INTERRUPT   0x21
+#define REG_CONTROL     0x22
+#define REG_RX_CONTROL  0x24
+#define REG_CARD_RESET  0x30
+#define REG_LED_CTRL    0x30
+
+/* REG_COMMAND */
+#define REG_COMMAND_TX_BUF_ONE  0x01
+#define REG_COMMAND_TX_BUF_TWO  0x02
+#define REG_COMMAND_RX_BUF_ONE  0x04
+#define REG_COMMAND_RX_BUF_TWO  0x08
+#define REG_COMMAND_RX_WIN_ONE  0x00
+#define REG_COMMAND_RX_WIN_TWO  0x10
+
+/* REG_CONTROL */
+#define REG_CONTROL_BAUD_RATE_57600   0x00
+#define REG_CONTROL_BAUD_RATE_115200  0x01
+#define REG_CONTROL_BAUD_RATE_230400  0x02
+#define REG_CONTROL_BAUD_RATE_460800  0x03
+#define REG_CONTROL_RTS               0x04
+#define REG_CONTROL_BT_ON             0x08
+#define REG_CONTROL_BT_RESET          0x10
+#define REG_CONTROL_BT_RES_PU         0x20
+#define REG_CONTROL_INTERRUPT         0x40
+#define REG_CONTROL_CARD_RESET        0x80
+
+/* REG_RX_CONTROL */
+#define RTS_LEVEL_SHIFT_BITS  0x02
+
+
+
+/* ======================== LED handling routines ======================== */
+
+
+static void bluecard_activity_led_timeout(u_long arg)
+{
+	bluecard_info_t *info = (bluecard_info_t *)arg;
+	unsigned int iobase = info->p_dev->resource[0]->start;
+
+	if (!test_bit(CARD_HAS_PCCARD_ID, &(info->hw_state)))
+		return;
+
+	if (test_bit(CARD_HAS_ACTIVITY_LED, &(info->hw_state))) {
+		/* Disable activity LED */
+		outb(0x08 | 0x20, iobase + 0x30);
+	} else {
+		/* Disable power LED */
+		outb(0x00, iobase + 0x30);
+	}
+}
+
+
+static void bluecard_enable_activity_led(bluecard_info_t *info)
+{
+	unsigned int iobase = info->p_dev->resource[0]->start;
+
+	if (!test_bit(CARD_HAS_PCCARD_ID, &(info->hw_state)))
+		return;
+
+	if (test_bit(CARD_HAS_ACTIVITY_LED, &(info->hw_state))) {
+		/* Enable activity LED */
+		outb(0x10 | 0x40, iobase + 0x30);
+
+		/* Stop the LED after HZ/4 */
+		mod_timer(&(info->timer), jiffies + HZ / 4);
+	} else {
+		/* Enable power LED */
+		outb(0x08 | 0x20, iobase + 0x30);
+
+		/* Stop the LED after HZ/2 */
+		mod_timer(&(info->timer), jiffies + HZ / 2);
+	}
+}
+
+
+
+/* ======================== Interrupt handling ======================== */
+
+
+static int bluecard_write(unsigned int iobase, unsigned int offset, __u8 *buf, int len)
+{
+	int i, actual;
+
+	actual = (len > 15) ? 15 : len;
+
+	outb_p(actual, iobase + offset);
+
+	for (i = 0; i < actual; i++)
+		outb_p(buf[i], iobase + offset + i + 1);
+
+	return actual;
+}
+
+
+static void bluecard_write_wakeup(bluecard_info_t *info)
+{
+	if (!info) {
+		BT_ERR("Unknown device");
+		return;
+	}
+
+	if (!test_bit(XMIT_SENDING_READY, &(info->tx_state)))
+		return;
+
+	if (test_and_set_bit(XMIT_SENDING, &(info->tx_state))) {
+		set_bit(XMIT_WAKEUP, &(info->tx_state));
+		return;
+	}
+
+	do {
+		register unsigned int iobase = info->p_dev->resource[0]->start;
+		register unsigned int offset;
+		register unsigned char command;
+		register unsigned long ready_bit;
+		register struct sk_buff *skb;
+		register int len;
+
+		clear_bit(XMIT_WAKEUP, &(info->tx_state));
+
+		if (!pcmcia_dev_present(info->p_dev))
+			return;
+
+		if (test_bit(XMIT_BUFFER_NUMBER, &(info->tx_state))) {
+			if (!test_bit(XMIT_BUF_TWO_READY, &(info->tx_state)))
+				break;
+			offset = 0x10;
+			command = REG_COMMAND_TX_BUF_TWO;
+			ready_bit = XMIT_BUF_TWO_READY;
+		} else {
+			if (!test_bit(XMIT_BUF_ONE_READY, &(info->tx_state)))
+				break;
+			offset = 0x00;
+			command = REG_COMMAND_TX_BUF_ONE;
+			ready_bit = XMIT_BUF_ONE_READY;
+		}
+
+		if (!(skb = skb_dequeue(&(info->txq))))
+			break;
+
+		if (bt_cb(skb)->pkt_type & 0x80) {
+			/* Disable RTS */
+			info->ctrl_reg |= REG_CONTROL_RTS;
+			outb(info->ctrl_reg, iobase + REG_CONTROL);
+		}
+
+		/* Activate LED */
+		bluecard_enable_activity_led(info);
+
+		/* Send frame */
+		len = bluecard_write(iobase, offset, skb->data, skb->len);
+
+		/* Tell the FPGA to send the data */
+		outb_p(command, iobase + REG_COMMAND);
+
+		/* Mark the buffer as dirty */
+		clear_bit(ready_bit, &(info->tx_state));
+
+		if (bt_cb(skb)->pkt_type & 0x80) {
+			DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
+			DEFINE_WAIT(wait);
+
+			unsigned char baud_reg;
+
+			switch (bt_cb(skb)->pkt_type) {
+			case PKT_BAUD_RATE_460800:
+				baud_reg = REG_CONTROL_BAUD_RATE_460800;
+				break;
+			case PKT_BAUD_RATE_230400:
+				baud_reg = REG_CONTROL_BAUD_RATE_230400;
+				break;
+			case PKT_BAUD_RATE_115200:
+				baud_reg = REG_CONTROL_BAUD_RATE_115200;
+				break;
+			case PKT_BAUD_RATE_57600:
+				/* Fall through... */
+			default:
+				baud_reg = REG_CONTROL_BAUD_RATE_57600;
+				break;
+			}
+
+			/* Wait until the command reaches the baseband */
+			prepare_to_wait(&wq, &wait, TASK_INTERRUPTIBLE);
+			schedule_timeout(HZ/10);
+			finish_wait(&wq, &wait);
+
+			/* Set baud on baseband */
+			info->ctrl_reg &= ~0x03;
+			info->ctrl_reg |= baud_reg;
+			outb(info->ctrl_reg, iobase + REG_CONTROL);
+
+			/* Enable RTS */
+			info->ctrl_reg &= ~REG_CONTROL_RTS;
+			outb(info->ctrl_reg, iobase + REG_CONTROL);
+
+			/* Wait before the next HCI packet can be send */
+			prepare_to_wait(&wq, &wait, TASK_INTERRUPTIBLE);
+			schedule_timeout(HZ);
+			finish_wait(&wq, &wait);
+		}
+
+		if (len == skb->len) {
+			kfree_skb(skb);
+		} else {
+			skb_pull(skb, len);
+			skb_queue_head(&(info->txq), skb);
+		}
+
+		info->hdev->stat.byte_tx += len;
+
+		/* Change buffer */
+		change_bit(XMIT_BUFFER_NUMBER, &(info->tx_state));
+
+	} while (test_bit(XMIT_WAKEUP, &(info->tx_state)));
+
+	clear_bit(XMIT_SENDING, &(info->tx_state));
+}
+
+
+static int bluecard_read(unsigned int iobase, unsigned int offset, __u8 *buf, int size)
+{
+	int i, n, len;
+
+	outb(REG_COMMAND_RX_WIN_ONE, iobase + REG_COMMAND);
+
+	len = inb(iobase + offset);
+	n = 0;
+	i = 1;
+
+	while (n < len) {
+
+		if (i == 16) {
+			outb(REG_COMMAND_RX_WIN_TWO, iobase + REG_COMMAND);
+			i = 0;
+		}
+
+		buf[n] = inb(iobase + offset + i);
+
+		n++;
+		i++;
+
+	}
+
+	return len;
+}
+
+
+static void bluecard_receive(bluecard_info_t *info, unsigned int offset)
+{
+	unsigned int iobase;
+	unsigned char buf[31];
+	int i, len;
+
+	if (!info) {
+		BT_ERR("Unknown device");
+		return;
+	}
+
+	iobase = info->p_dev->resource[0]->start;
+
+	if (test_bit(XMIT_SENDING_READY, &(info->tx_state)))
+		bluecard_enable_activity_led(info);
+
+	len = bluecard_read(iobase, offset, buf, sizeof(buf));
+
+	for (i = 0; i < len; i++) {
+
+		/* Allocate packet */
+		if (info->rx_skb == NULL) {
+			info->rx_state = RECV_WAIT_PACKET_TYPE;
+			info->rx_count = 0;
+			if (!(info->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC))) {
+				BT_ERR("Can't allocate mem for new packet");
+				return;
+			}
+		}
+
+		if (info->rx_state == RECV_WAIT_PACKET_TYPE) {
+
+			info->rx_skb->dev = (void *) info->hdev;
+			bt_cb(info->rx_skb)->pkt_type = buf[i];
+
+			switch (bt_cb(info->rx_skb)->pkt_type) {
+
+			case 0x00:
+				/* init packet */
+				if (offset != 0x00) {
+					set_bit(XMIT_BUF_ONE_READY, &(info->tx_state));
+					set_bit(XMIT_BUF_TWO_READY, &(info->tx_state));
+					set_bit(XMIT_SENDING_READY, &(info->tx_state));
+					bluecard_write_wakeup(info);
+				}
+
+				kfree_skb(info->rx_skb);
+				info->rx_skb = NULL;
+				break;
+
+			case HCI_EVENT_PKT:
+				info->rx_state = RECV_WAIT_EVENT_HEADER;
+				info->rx_count = HCI_EVENT_HDR_SIZE;
+				break;
+
+			case HCI_ACLDATA_PKT:
+				info->rx_state = RECV_WAIT_ACL_HEADER;
+				info->rx_count = HCI_ACL_HDR_SIZE;
+				break;
+
+			case HCI_SCODATA_PKT:
+				info->rx_state = RECV_WAIT_SCO_HEADER;
+				info->rx_count = HCI_SCO_HDR_SIZE;
+				break;
+
+			default:
+				/* unknown packet */
+				BT_ERR("Unknown HCI packet with type 0x%02x received", bt_cb(info->rx_skb)->pkt_type);
+				info->hdev->stat.err_rx++;
+
+				kfree_skb(info->rx_skb);
+				info->rx_skb = NULL;
+				break;
+
+			}
+
+		} else {
+
+			*skb_put(info->rx_skb, 1) = buf[i];
+			info->rx_count--;
+
+			if (info->rx_count == 0) {
+
+				int dlen;
+				struct hci_event_hdr *eh;
+				struct hci_acl_hdr *ah;
+				struct hci_sco_hdr *sh;
+
+				switch (info->rx_state) {
+
+				case RECV_WAIT_EVENT_HEADER:
+					eh = hci_event_hdr(info->rx_skb);
+					info->rx_state = RECV_WAIT_DATA;
+					info->rx_count = eh->plen;
+					break;
+
+				case RECV_WAIT_ACL_HEADER:
+					ah = hci_acl_hdr(info->rx_skb);
+					dlen = __le16_to_cpu(ah->dlen);
+					info->rx_state = RECV_WAIT_DATA;
+					info->rx_count = dlen;
+					break;
+
+				case RECV_WAIT_SCO_HEADER:
+					sh = hci_sco_hdr(info->rx_skb);
+					info->rx_state = RECV_WAIT_DATA;
+					info->rx_count = sh->dlen;
+					break;
+
+				case RECV_WAIT_DATA:
+					hci_recv_frame(info->rx_skb);
+					info->rx_skb = NULL;
+					break;
+
+				}
+
+			}
+
+		}
+
+
+	}
+
+	info->hdev->stat.byte_rx += len;
+}
+
+
+static irqreturn_t bluecard_interrupt(int irq, void *dev_inst)
+{
+	bluecard_info_t *info = dev_inst;
+	unsigned int iobase;
+	unsigned char reg;
+
+	if (!info || !info->hdev)
+		/* our irq handler is shared */
+		return IRQ_NONE;
+
+	if (!test_bit(CARD_READY, &(info->hw_state)))
+		return IRQ_HANDLED;
+
+	iobase = info->p_dev->resource[0]->start;
+
+	spin_lock(&(info->lock));
+
+	/* Disable interrupt */
+	info->ctrl_reg &= ~REG_CONTROL_INTERRUPT;
+	outb(info->ctrl_reg, iobase + REG_CONTROL);
+
+	reg = inb(iobase + REG_INTERRUPT);
+
+	if ((reg != 0x00) && (reg != 0xff)) {
+
+		if (reg & 0x04) {
+			bluecard_receive(info, 0x00);
+			outb(0x04, iobase + REG_INTERRUPT);
+			outb(REG_COMMAND_RX_BUF_ONE, iobase + REG_COMMAND);
+		}
+
+		if (reg & 0x08) {
+			bluecard_receive(info, 0x10);
+			outb(0x08, iobase + REG_INTERRUPT);
+			outb(REG_COMMAND_RX_BUF_TWO, iobase + REG_COMMAND);
+		}
+
+		if (reg & 0x01) {
+			set_bit(XMIT_BUF_ONE_READY, &(info->tx_state));
+			outb(0x01, iobase + REG_INTERRUPT);
+			bluecard_write_wakeup(info);
+		}
+
+		if (reg & 0x02) {
+			set_bit(XMIT_BUF_TWO_READY, &(info->tx_state));
+			outb(0x02, iobase + REG_INTERRUPT);
+			bluecard_write_wakeup(info);
+		}
+
+	}
+
+	/* Enable interrupt */
+	info->ctrl_reg |= REG_CONTROL_INTERRUPT;
+	outb(info->ctrl_reg, iobase + REG_CONTROL);
+
+	spin_unlock(&(info->lock));
+
+	return IRQ_HANDLED;
+}
+
+
+
+/* ======================== Device specific HCI commands ======================== */
+
+
+static int bluecard_hci_set_baud_rate(struct hci_dev *hdev, int baud)
+{
+	bluecard_info_t *info = hci_get_drvdata(hdev);
+	struct sk_buff *skb;
+
+	/* Ericsson baud rate command */
+	unsigned char cmd[] = { HCI_COMMAND_PKT, 0x09, 0xfc, 0x01, 0x03 };
+
+	if (!(skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC))) {
+		BT_ERR("Can't allocate mem for new packet");
+		return -1;
+	}
+
+	switch (baud) {
+	case 460800:
+		cmd[4] = 0x00;
+		bt_cb(skb)->pkt_type = PKT_BAUD_RATE_460800;
+		break;
+	case 230400:
+		cmd[4] = 0x01;
+		bt_cb(skb)->pkt_type = PKT_BAUD_RATE_230400;
+		break;
+	case 115200:
+		cmd[4] = 0x02;
+		bt_cb(skb)->pkt_type = PKT_BAUD_RATE_115200;
+		break;
+	case 57600:
+		/* Fall through... */
+	default:
+		cmd[4] = 0x03;
+		bt_cb(skb)->pkt_type = PKT_BAUD_RATE_57600;
+		break;
+	}
+
+	memcpy(skb_put(skb, sizeof(cmd)), cmd, sizeof(cmd));
+
+	skb_queue_tail(&(info->txq), skb);
+
+	bluecard_write_wakeup(info);
+
+	return 0;
+}
+
+
+
+/* ======================== HCI interface ======================== */
+
+
+static int bluecard_hci_flush(struct hci_dev *hdev)
+{
+	bluecard_info_t *info = hci_get_drvdata(hdev);
+
+	/* Drop TX queue */
+	skb_queue_purge(&(info->txq));
+
+	return 0;
+}
+
+
+static int bluecard_hci_open(struct hci_dev *hdev)
+{
+	bluecard_info_t *info = hci_get_drvdata(hdev);
+	unsigned int iobase = info->p_dev->resource[0]->start;
+
+	if (test_bit(CARD_HAS_PCCARD_ID, &(info->hw_state)))
+		bluecard_hci_set_baud_rate(hdev, DEFAULT_BAUD_RATE);
+
+	if (test_and_set_bit(HCI_RUNNING, &(hdev->flags)))
+		return 0;
+
+	if (test_bit(CARD_HAS_PCCARD_ID, &(info->hw_state))) {
+		/* Enable LED */
+		outb(0x08 | 0x20, iobase + 0x30);
+	}
+
+	return 0;
+}
+
+
+static int bluecard_hci_close(struct hci_dev *hdev)
+{
+	bluecard_info_t *info = hci_get_drvdata(hdev);
+	unsigned int iobase = info->p_dev->resource[0]->start;
+
+	if (!test_and_clear_bit(HCI_RUNNING, &(hdev->flags)))
+		return 0;
+
+	bluecard_hci_flush(hdev);
+
+	if (test_bit(CARD_HAS_PCCARD_ID, &(info->hw_state))) {
+		/* Disable LED */
+		outb(0x00, iobase + 0x30);
+	}
+
+	return 0;
+}
+
+
+static int bluecard_hci_send_frame(struct sk_buff *skb)
+{
+	bluecard_info_t *info;
+	struct hci_dev *hdev = (struct hci_dev *)(skb->dev);
+
+	if (!hdev) {
+		BT_ERR("Frame for unknown HCI device (hdev=NULL)");
+		return -ENODEV;
+	}
+
+	info = hci_get_drvdata(hdev);
+
+	switch (bt_cb(skb)->pkt_type) {
+	case HCI_COMMAND_PKT:
+		hdev->stat.cmd_tx++;
+		break;
+	case HCI_ACLDATA_PKT:
+		hdev->stat.acl_tx++;
+		break;
+	case HCI_SCODATA_PKT:
+		hdev->stat.sco_tx++;
+		break;
+	};
+
+	/* Prepend skb with frame type */
+	memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
+	skb_queue_tail(&(info->txq), skb);
+
+	bluecard_write_wakeup(info);
+
+	return 0;
+}
+
+
+static int bluecard_hci_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg)
+{
+	return -ENOIOCTLCMD;
+}
+
+
+
+/* ======================== Card services HCI interaction ======================== */
+
+
+static int bluecard_open(bluecard_info_t *info)
+{
+	unsigned int iobase = info->p_dev->resource[0]->start;
+	struct hci_dev *hdev;
+	unsigned char id;
+
+	spin_lock_init(&(info->lock));
+
+	init_timer(&(info->timer));
+	info->timer.function = &bluecard_activity_led_timeout;
+	info->timer.data = (u_long)info;
+
+	skb_queue_head_init(&(info->txq));
+
+	info->rx_state = RECV_WAIT_PACKET_TYPE;
+	info->rx_count = 0;
+	info->rx_skb = NULL;
+
+	/* Initialize HCI device */
+	hdev = hci_alloc_dev();
+	if (!hdev) {
+		BT_ERR("Can't allocate HCI device");
+		return -ENOMEM;
+	}
+
+	info->hdev = hdev;
+
+	hdev->bus = HCI_PCCARD;
+	hci_set_drvdata(hdev, info);
+	SET_HCIDEV_DEV(hdev, &info->p_dev->dev);
+
+	hdev->open     = bluecard_hci_open;
+	hdev->close    = bluecard_hci_close;
+	hdev->flush    = bluecard_hci_flush;
+	hdev->send     = bluecard_hci_send_frame;
+	hdev->ioctl    = bluecard_hci_ioctl;
+
+	id = inb(iobase + 0x30);
+
+	if ((id & 0x0f) == 0x02)
+		set_bit(CARD_HAS_PCCARD_ID, &(info->hw_state));
+
+	if (id & 0x10)
+		set_bit(CARD_HAS_POWER_LED, &(info->hw_state));
+
+	if (id & 0x20)
+		set_bit(CARD_HAS_ACTIVITY_LED, &(info->hw_state));
+
+	/* Reset card */
+	info->ctrl_reg = REG_CONTROL_BT_RESET | REG_CONTROL_CARD_RESET;
+	outb(info->ctrl_reg, iobase + REG_CONTROL);
+
+	/* Turn FPGA off */
+	outb(0x80, iobase + 0x30);
+
+	/* Wait some time */
+	msleep(10);
+
+	/* Turn FPGA on */
+	outb(0x00, iobase + 0x30);
+
+	/* Activate card */
+	info->ctrl_reg = REG_CONTROL_BT_ON | REG_CONTROL_BT_RES_PU;
+	outb(info->ctrl_reg, iobase + REG_CONTROL);
+
+	/* Enable interrupt */
+	outb(0xff, iobase + REG_INTERRUPT);
+	info->ctrl_reg |= REG_CONTROL_INTERRUPT;
+	outb(info->ctrl_reg, iobase + REG_CONTROL);
+
+	if ((id & 0x0f) == 0x03) {
+		/* Disable RTS */
+		info->ctrl_reg |= REG_CONTROL_RTS;
+		outb(info->ctrl_reg, iobase + REG_CONTROL);
+
+		/* Set baud rate */
+		info->ctrl_reg |= 0x03;
+		outb(info->ctrl_reg, iobase + REG_CONTROL);
+
+		/* Enable RTS */
+		info->ctrl_reg &= ~REG_CONTROL_RTS;
+		outb(info->ctrl_reg, iobase + REG_CONTROL);
+
+		set_bit(XMIT_BUF_ONE_READY, &(info->tx_state));
+		set_bit(XMIT_BUF_TWO_READY, &(info->tx_state));
+		set_bit(XMIT_SENDING_READY, &(info->tx_state));
+	}
+
+	/* Start the RX buffers */
+	outb(REG_COMMAND_RX_BUF_ONE, iobase + REG_COMMAND);
+	outb(REG_COMMAND_RX_BUF_TWO, iobase + REG_COMMAND);
+
+	/* Signal that the hardware is ready */
+	set_bit(CARD_READY, &(info->hw_state));
+
+	/* Drop TX queue */
+	skb_queue_purge(&(info->txq));
+
+	/* Control the point at which RTS is enabled */
+	outb((0x0f << RTS_LEVEL_SHIFT_BITS) | 1, iobase + REG_RX_CONTROL);
+
+	/* Timeout before it is safe to send the first HCI packet */
+	msleep(1250);
+
+	/* Register HCI device */
+	if (hci_register_dev(hdev) < 0) {
+		BT_ERR("Can't register HCI device");
+		info->hdev = NULL;
+		hci_free_dev(hdev);
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+
+static int bluecard_close(bluecard_info_t *info)
+{
+	unsigned int iobase = info->p_dev->resource[0]->start;
+	struct hci_dev *hdev = info->hdev;
+
+	if (!hdev)
+		return -ENODEV;
+
+	bluecard_hci_close(hdev);
+
+	clear_bit(CARD_READY, &(info->hw_state));
+
+	/* Reset card */
+	info->ctrl_reg = REG_CONTROL_BT_RESET | REG_CONTROL_CARD_RESET;
+	outb(info->ctrl_reg, iobase + REG_CONTROL);
+
+	/* Turn FPGA off */
+	outb(0x80, iobase + 0x30);
+
+	hci_unregister_dev(hdev);
+	hci_free_dev(hdev);
+
+	return 0;
+}
+
+static int bluecard_probe(struct pcmcia_device *link)
+{
+	bluecard_info_t *info;
+
+	/* Create new info device */
+	info = kzalloc(sizeof(*info), GFP_KERNEL);
+	if (!info)
+		return -ENOMEM;
+
+	info->p_dev = link;
+	link->priv = info;
+
+	link->config_flags |= CONF_ENABLE_IRQ;
+
+	return bluecard_config(link);
+}
+
+
+static void bluecard_detach(struct pcmcia_device *link)
+{
+	bluecard_info_t *info = link->priv;
+
+	bluecard_release(link);
+	kfree(info);
+}
+
+
+static int bluecard_config(struct pcmcia_device *link)
+{
+	bluecard_info_t *info = link->priv;
+	int i, n;
+
+	link->config_index = 0x20;
+
+	link->resource[0]->flags |= IO_DATA_PATH_WIDTH_8;
+	link->resource[0]->end = 64;
+	link->io_lines = 6;
+
+	for (n = 0; n < 0x400; n += 0x40) {
+		link->resource[0]->start = n ^ 0x300;
+		i = pcmcia_request_io(link);
+		if (i == 0)
+			break;
+	}
+
+	if (i != 0)
+		goto failed;
+
+	i = pcmcia_request_irq(link, bluecard_interrupt);
+	if (i != 0)
+		goto failed;
+
+	i = pcmcia_enable_device(link);
+	if (i != 0)
+		goto failed;
+
+	if (bluecard_open(info) != 0)
+		goto failed;
+
+	return 0;
+
+failed:
+	bluecard_release(link);
+	return -ENODEV;
+}
+
+
+static void bluecard_release(struct pcmcia_device *link)
+{
+	bluecard_info_t *info = link->priv;
+
+	bluecard_close(info);
+
+	del_timer(&(info->timer));
+
+	pcmcia_disable_device(link);
+}
+
+static const struct pcmcia_device_id bluecard_ids[] = {
+	PCMCIA_DEVICE_PROD_ID12("BlueCard", "LSE041", 0xbaf16fbf, 0x657cc15e),
+	PCMCIA_DEVICE_PROD_ID12("BTCFCARD", "LSE139", 0xe3987764, 0x2524b59c),
+	PCMCIA_DEVICE_PROD_ID12("WSS", "LSE039", 0x0a0736ec, 0x24e6dfab),
+	PCMCIA_DEVICE_NULL
+};
+MODULE_DEVICE_TABLE(pcmcia, bluecard_ids);
+
+static struct pcmcia_driver bluecard_driver = {
+	.owner		= THIS_MODULE,
+	.name		= "bluecard_cs",
+	.probe		= bluecard_probe,
+	.remove		= bluecard_detach,
+	.id_table	= bluecard_ids,
+};
+
+static int __init init_bluecard_cs(void)
+{
+	return pcmcia_register_driver(&bluecard_driver);
+}
+
+
+static void __exit exit_bluecard_cs(void)
+{
+	pcmcia_unregister_driver(&bluecard_driver);
+}
+
+module_init(init_bluecard_cs);
+module_exit(exit_bluecard_cs);
diff --git a/ap/os/linux/linux-3.4.x/drivers/bluetooth/bpa10x.c b/ap/os/linux/linux-3.4.x/drivers/bluetooth/bpa10x.c
new file mode 100644
index 0000000..d894340
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/bluetooth/bpa10x.c
@@ -0,0 +1,518 @@
+/*
+ *
+ *  Digianswer Bluetooth USB driver
+ *
+ *  Copyright (C) 2004-2007  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.10"
+
+static struct usb_device_id bpa10x_table[] = {
+	/* Tektronix BPA 100/105 (Digianswer) */
+	{ USB_DEVICE(0x08fd, 0x0002) },
+
+	{ }	/* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, bpa10x_table);
+
+struct bpa10x_data {
+	struct hci_dev    *hdev;
+	struct usb_device *udev;
+
+	struct usb_anchor tx_anchor;
+	struct usb_anchor rx_anchor;
+
+	struct sk_buff *rx_skb[2];
+};
+
+#define HCI_VENDOR_HDR_SIZE 5
+
+struct hci_vendor_hdr {
+	__u8    type;
+	__le16  snum;
+	__le16  dlen;
+} __packed;
+
+static int bpa10x_recv(struct hci_dev *hdev, int queue, void *buf, int count)
+{
+	struct bpa10x_data *data = hci_get_drvdata(hdev);
+
+	BT_DBG("%s queue %d buffer %p count %d", hdev->name,
+							queue, buf, count);
+
+	if (queue < 0 || queue > 1)
+		return -EILSEQ;
+
+	hdev->stat.byte_rx += count;
+
+	while (count) {
+		struct sk_buff *skb = data->rx_skb[queue];
+		struct { __u8 type; int expect; } *scb;
+		int type, len = 0;
+
+		if (!skb) {
+			/* Start of the frame */
+
+			type = *((__u8 *) buf);
+			count--; buf++;
+
+			switch (type) {
+			case HCI_EVENT_PKT:
+				if (count >= HCI_EVENT_HDR_SIZE) {
+					struct hci_event_hdr *h = buf;
+					len = HCI_EVENT_HDR_SIZE + h->plen;
+				} else
+					return -EILSEQ;
+				break;
+
+			case HCI_ACLDATA_PKT:
+				if (count >= HCI_ACL_HDR_SIZE) {
+					struct hci_acl_hdr *h = buf;
+					len = HCI_ACL_HDR_SIZE +
+							__le16_to_cpu(h->dlen);
+				} else
+					return -EILSEQ;
+				break;
+
+			case HCI_SCODATA_PKT:
+				if (count >= HCI_SCO_HDR_SIZE) {
+					struct hci_sco_hdr *h = buf;
+					len = HCI_SCO_HDR_SIZE + h->dlen;
+				} else
+					return -EILSEQ;
+				break;
+
+			case HCI_VENDOR_PKT:
+				if (count >= HCI_VENDOR_HDR_SIZE) {
+					struct hci_vendor_hdr *h = buf;
+					len = HCI_VENDOR_HDR_SIZE +
+							__le16_to_cpu(h->dlen);
+				} else
+					return -EILSEQ;
+				break;
+			}
+
+			skb = bt_skb_alloc(len, GFP_ATOMIC);
+			if (!skb) {
+				BT_ERR("%s no memory for packet", hdev->name);
+				return -ENOMEM;
+			}
+
+			skb->dev = (void *) hdev;
+
+			data->rx_skb[queue] = skb;
+
+			scb = (void *) skb->cb;
+			scb->type = type;
+			scb->expect = len;
+		} else {
+			/* Continuation */
+
+			scb = (void *) skb->cb;
+			len = scb->expect;
+		}
+
+		len = min(len, count);
+
+		memcpy(skb_put(skb, len), buf, len);
+
+		scb->expect -= len;
+
+		if (scb->expect == 0) {
+			/* Complete frame */
+
+			data->rx_skb[queue] = NULL;
+
+			bt_cb(skb)->pkt_type = scb->type;
+			hci_recv_frame(skb);
+		}
+
+		count -= len; buf += len;
+	}
+
+	return 0;
+}
+
+static void bpa10x_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 void bpa10x_rx_complete(struct urb *urb)
+{
+	struct hci_dev *hdev = urb->context;
+	struct bpa10x_data *data = hci_get_drvdata(hdev);
+	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) {
+		if (bpa10x_recv(hdev, usb_pipebulk(urb->pipe),
+						urb->transfer_buffer,
+						urb->actual_length) < 0) {
+			BT_ERR("%s corrupted event packet", hdev->name);
+			hdev->stat.err_rx++;
+		}
+	}
+
+	usb_anchor_urb(urb, &data->rx_anchor);
+
+	err = usb_submit_urb(urb, GFP_ATOMIC);
+	if (err < 0) {
+		BT_ERR("%s urb %p failed to resubmit (%d)",
+						hdev->name, urb, -err);
+		usb_unanchor_urb(urb);
+	}
+}
+
+static inline int bpa10x_submit_intr_urb(struct hci_dev *hdev)
+{
+	struct bpa10x_data *data = hci_get_drvdata(hdev);
+	struct urb *urb;
+	unsigned char *buf;
+	unsigned int pipe;
+	int err, size = 16;
+
+	BT_DBG("%s", hdev->name);
+
+	urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!urb)
+		return -ENOMEM;
+
+	buf = kmalloc(size, GFP_KERNEL);
+	if (!buf) {
+		usb_free_urb(urb);
+		return -ENOMEM;
+	}
+
+	pipe = usb_rcvintpipe(data->udev, 0x81);
+
+	usb_fill_int_urb(urb, data->udev, pipe, buf, size,
+						bpa10x_rx_complete, hdev, 1);
+
+	urb->transfer_flags |= URB_FREE_BUFFER;
+
+	usb_anchor_urb(urb, &data->rx_anchor);
+
+	err = usb_submit_urb(urb, GFP_KERNEL);
+	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 inline int bpa10x_submit_bulk_urb(struct hci_dev *hdev)
+{
+	struct bpa10x_data *data = hci_get_drvdata(hdev);
+	struct urb *urb;
+	unsigned char *buf;
+	unsigned int pipe;
+	int err, size = 64;
+
+	BT_DBG("%s", hdev->name);
+
+	urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!urb)
+		return -ENOMEM;
+
+	buf = kmalloc(size, GFP_KERNEL);
+	if (!buf) {
+		usb_free_urb(urb);
+		return -ENOMEM;
+	}
+
+	pipe = usb_rcvbulkpipe(data->udev, 0x82);
+
+	usb_fill_bulk_urb(urb, data->udev, pipe,
+					buf, size, bpa10x_rx_complete, hdev);
+
+	urb->transfer_flags |= URB_FREE_BUFFER;
+
+	usb_anchor_urb(urb, &data->rx_anchor);
+
+	err = usb_submit_urb(urb, GFP_KERNEL);
+	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 int bpa10x_open(struct hci_dev *hdev)
+{
+	struct bpa10x_data *data = hci_get_drvdata(hdev);
+	int err;
+
+	BT_DBG("%s", hdev->name);
+
+	if (test_and_set_bit(HCI_RUNNING, &hdev->flags))
+		return 0;
+
+	err = bpa10x_submit_intr_urb(hdev);
+	if (err < 0)
+		goto error;
+
+	err = bpa10x_submit_bulk_urb(hdev);
+	if (err < 0)
+		goto error;
+
+	return 0;
+
+error:
+	usb_kill_anchored_urbs(&data->rx_anchor);
+
+	clear_bit(HCI_RUNNING, &hdev->flags);
+
+	return err;
+}
+
+static int bpa10x_close(struct hci_dev *hdev)
+{
+	struct bpa10x_data *data = hci_get_drvdata(hdev);
+
+	BT_DBG("%s", hdev->name);
+
+	if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
+		return 0;
+
+	usb_kill_anchored_urbs(&data->rx_anchor);
+
+	return 0;
+}
+
+static int bpa10x_flush(struct hci_dev *hdev)
+{
+	struct bpa10x_data *data = hci_get_drvdata(hdev);
+
+	BT_DBG("%s", hdev->name);
+
+	usb_kill_anchored_urbs(&data->tx_anchor);
+
+	return 0;
+}
+
+static int bpa10x_send_frame(struct sk_buff *skb)
+{
+	struct hci_dev *hdev = (struct hci_dev *) skb->dev;
+	struct bpa10x_data *data = hci_get_drvdata(hdev);
+	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;
+
+	urb = usb_alloc_urb(0, GFP_ATOMIC);
+	if (!urb)
+		return -ENOMEM;
+
+	/* Prepend skb with frame type */
+	*skb_push(skb, 1) = bt_cb(skb)->pkt_type;
+
+	switch (bt_cb(skb)->pkt_type) {
+	case HCI_COMMAND_PKT:
+		dr = kmalloc(sizeof(*dr), GFP_ATOMIC);
+		if (!dr) {
+			usb_free_urb(urb);
+			return -ENOMEM;
+		}
+
+		dr->bRequestType = USB_TYPE_VENDOR;
+		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, bpa10x_tx_complete, skb);
+
+		hdev->stat.cmd_tx++;
+		break;
+
+	case HCI_ACLDATA_PKT:
+		pipe = usb_sndbulkpipe(data->udev, 0x02);
+
+		usb_fill_bulk_urb(urb, data->udev, pipe,
+				skb->data, skb->len, bpa10x_tx_complete, skb);
+
+		hdev->stat.acl_tx++;
+		break;
+
+	case HCI_SCODATA_PKT:
+		pipe = usb_sndbulkpipe(data->udev, 0x02);
+
+		usb_fill_bulk_urb(urb, data->udev, pipe,
+				skb->data, skb->len, bpa10x_tx_complete, skb);
+
+		hdev->stat.sco_tx++;
+		break;
+
+	default:
+		usb_free_urb(urb);
+		return -EILSEQ;
+	}
+
+	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);
+	}
+
+	usb_free_urb(urb);
+
+	return 0;
+}
+
+static int bpa10x_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+	struct bpa10x_data *data;
+	struct hci_dev *hdev;
+	int err;
+
+	BT_DBG("intf %p id %p", intf, id);
+
+	if (intf->cur_altsetting->desc.bInterfaceNumber != 0)
+		return -ENODEV;
+
+	data = kzalloc(sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	data->udev = interface_to_usbdev(intf);
+
+	init_usb_anchor(&data->tx_anchor);
+	init_usb_anchor(&data->rx_anchor);
+
+	hdev = hci_alloc_dev();
+	if (!hdev) {
+		kfree(data);
+		return -ENOMEM;
+	}
+
+	hdev->bus = HCI_USB;
+	hci_set_drvdata(hdev, data);
+
+	data->hdev = hdev;
+
+	SET_HCIDEV_DEV(hdev, &intf->dev);
+
+	hdev->open     = bpa10x_open;
+	hdev->close    = bpa10x_close;
+	hdev->flush    = bpa10x_flush;
+	hdev->send     = bpa10x_send_frame;
+
+	set_bit(HCI_QUIRK_NO_RESET, &hdev->quirks);
+
+	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 bpa10x_disconnect(struct usb_interface *intf)
+{
+	struct bpa10x_data *data = usb_get_intfdata(intf);
+
+	BT_DBG("intf %p", intf);
+
+	if (!data)
+		return;
+
+	usb_set_intfdata(intf, NULL);
+
+	hci_unregister_dev(data->hdev);
+
+	hci_free_dev(data->hdev);
+	kfree_skb(data->rx_skb[0]);
+	kfree_skb(data->rx_skb[1]);
+	kfree(data);
+}
+
+static struct usb_driver bpa10x_driver = {
+	.name		= "bpa10x",
+	.probe		= bpa10x_probe,
+	.disconnect	= bpa10x_disconnect,
+	.id_table	= bpa10x_table,
+};
+
+module_usb_driver(bpa10x_driver);
+
+MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
+MODULE_DESCRIPTION("Digianswer Bluetooth USB driver ver " VERSION);
+MODULE_VERSION(VERSION);
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/bluetooth/bt3c_cs.c b/ap/os/linux/linux-3.4.x/drivers/bluetooth/bt3c_cs.c
new file mode 100644
index 0000000..308c859
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/bluetooth/bt3c_cs.c
@@ -0,0 +1,779 @@
+/*
+ *
+ *  Driver for the 3Com Bluetooth PCMCIA card
+ *
+ *  Copyright (C) 2001-2002  Marcel Holtmann <marcel@holtmann.org>
+ *                           Jose Orlando Pereira <jop@di.uminho.pt>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation;
+ *
+ *  Software distributed under the License is distributed on an "AS
+ *  IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ *  implied. See the License for the specific language governing
+ *  rights and limitations under the License.
+ *
+ *  The initial developer of the original code is David A. Hinds
+ *  <dahinds@users.sourceforge.net>.  Portions created by David A. Hinds
+ *  are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
+ *
+ */
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/spinlock.h>
+#include <linux/moduleparam.h>
+
+#include <linux/skbuff.h>
+#include <linux/string.h>
+#include <linux/serial.h>
+#include <linux/serial_reg.h>
+#include <linux/bitops.h>
+#include <asm/io.h>
+
+#include <linux/device.h>
+#include <linux/firmware.h>
+
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ciscode.h>
+#include <pcmcia/ds.h>
+#include <pcmcia/cisreg.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+
+
+/* ======================== Module parameters ======================== */
+
+
+MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
+MODULE_DESCRIPTION("Bluetooth driver for the 3Com Bluetooth PCMCIA card");
+MODULE_LICENSE("GPL");
+MODULE_FIRMWARE("BT3CPCC.bin");
+
+
+
+/* ======================== Local structures ======================== */
+
+
+typedef struct bt3c_info_t {
+	struct pcmcia_device *p_dev;
+
+	struct hci_dev *hdev;
+
+	spinlock_t lock;		/* For serializing operations */
+
+	struct sk_buff_head txq;
+	unsigned long tx_state;
+
+	unsigned long rx_state;
+	unsigned long rx_count;
+	struct sk_buff *rx_skb;
+} bt3c_info_t;
+
+
+static int bt3c_config(struct pcmcia_device *link);
+static void bt3c_release(struct pcmcia_device *link);
+
+static void bt3c_detach(struct pcmcia_device *p_dev);
+
+
+/* Transmit states  */
+#define XMIT_SENDING  1
+#define XMIT_WAKEUP   2
+#define XMIT_WAITING  8
+
+/* Receiver states */
+#define RECV_WAIT_PACKET_TYPE   0
+#define RECV_WAIT_EVENT_HEADER  1
+#define RECV_WAIT_ACL_HEADER    2
+#define RECV_WAIT_SCO_HEADER    3
+#define RECV_WAIT_DATA          4
+
+
+
+/* ======================== Special I/O functions ======================== */
+
+
+#define DATA_L   0
+#define DATA_H   1
+#define ADDR_L   2
+#define ADDR_H   3
+#define CONTROL  4
+
+
+static inline void bt3c_address(unsigned int iobase, unsigned short addr)
+{
+	outb(addr & 0xff, iobase + ADDR_L);
+	outb((addr >> 8) & 0xff, iobase + ADDR_H);
+}
+
+
+static inline void bt3c_put(unsigned int iobase, unsigned short value)
+{
+	outb(value & 0xff, iobase + DATA_L);
+	outb((value >> 8) & 0xff, iobase + DATA_H);
+}
+
+
+static inline void bt3c_io_write(unsigned int iobase, unsigned short addr, unsigned short value)
+{
+	bt3c_address(iobase, addr);
+	bt3c_put(iobase, value);
+}
+
+
+static inline unsigned short bt3c_get(unsigned int iobase)
+{
+	unsigned short value = inb(iobase + DATA_L);
+
+	value |= inb(iobase + DATA_H) << 8;
+
+	return value;
+}
+
+
+static inline unsigned short bt3c_read(unsigned int iobase, unsigned short addr)
+{
+	bt3c_address(iobase, addr);
+
+	return bt3c_get(iobase);
+}
+
+
+
+/* ======================== Interrupt handling ======================== */
+
+
+static int bt3c_write(unsigned int iobase, int fifo_size, __u8 *buf, int len)
+{
+	int actual = 0;
+
+	bt3c_address(iobase, 0x7080);
+
+	/* Fill FIFO with current frame */
+	while (actual < len) {
+		/* Transmit next byte */
+		bt3c_put(iobase, buf[actual]);
+		actual++;
+	}
+
+	bt3c_io_write(iobase, 0x7005, actual);
+
+	return actual;
+}
+
+
+static void bt3c_write_wakeup(bt3c_info_t *info)
+{
+	if (!info) {
+		BT_ERR("Unknown device");
+		return;
+	}
+
+	if (test_and_set_bit(XMIT_SENDING, &(info->tx_state)))
+		return;
+
+	do {
+		register unsigned int iobase = info->p_dev->resource[0]->start;
+		register struct sk_buff *skb;
+		register int len;
+
+		if (!pcmcia_dev_present(info->p_dev))
+			break;
+
+
+		if (!(skb = skb_dequeue(&(info->txq)))) {
+			clear_bit(XMIT_SENDING, &(info->tx_state));
+			break;
+		}
+
+		/* Send frame */
+		len = bt3c_write(iobase, 256, skb->data, skb->len);
+
+		if (len != skb->len) {
+			BT_ERR("Very strange");
+		}
+
+		kfree_skb(skb);
+
+		info->hdev->stat.byte_tx += len;
+
+	} while (0);
+}
+
+
+static void bt3c_receive(bt3c_info_t *info)
+{
+	unsigned int iobase;
+	int size = 0, avail;
+
+	if (!info) {
+		BT_ERR("Unknown device");
+		return;
+	}
+
+	iobase = info->p_dev->resource[0]->start;
+
+	avail = bt3c_read(iobase, 0x7006);
+	//printk("bt3c_cs: receiving %d bytes\n", avail);
+
+	bt3c_address(iobase, 0x7480);
+	while (size < avail) {
+		size++;
+		info->hdev->stat.byte_rx++;
+
+		/* Allocate packet */
+		if (info->rx_skb == NULL) {
+			info->rx_state = RECV_WAIT_PACKET_TYPE;
+			info->rx_count = 0;
+			if (!(info->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC))) {
+				BT_ERR("Can't allocate mem for new packet");
+				return;
+			}
+		}
+
+
+		if (info->rx_state == RECV_WAIT_PACKET_TYPE) {
+
+			info->rx_skb->dev = (void *) info->hdev;
+			bt_cb(info->rx_skb)->pkt_type = inb(iobase + DATA_L);
+			inb(iobase + DATA_H);
+			//printk("bt3c: PACKET_TYPE=%02x\n", bt_cb(info->rx_skb)->pkt_type);
+
+			switch (bt_cb(info->rx_skb)->pkt_type) {
+
+			case HCI_EVENT_PKT:
+				info->rx_state = RECV_WAIT_EVENT_HEADER;
+				info->rx_count = HCI_EVENT_HDR_SIZE;
+				break;
+
+			case HCI_ACLDATA_PKT:
+				info->rx_state = RECV_WAIT_ACL_HEADER;
+				info->rx_count = HCI_ACL_HDR_SIZE;
+				break;
+
+			case HCI_SCODATA_PKT:
+				info->rx_state = RECV_WAIT_SCO_HEADER;
+				info->rx_count = HCI_SCO_HDR_SIZE;
+				break;
+
+			default:
+				/* Unknown packet */
+				BT_ERR("Unknown HCI packet with type 0x%02x received", bt_cb(info->rx_skb)->pkt_type);
+				info->hdev->stat.err_rx++;
+				clear_bit(HCI_RUNNING, &(info->hdev->flags));
+
+				kfree_skb(info->rx_skb);
+				info->rx_skb = NULL;
+				break;
+
+			}
+
+		} else {
+
+			__u8 x = inb(iobase + DATA_L);
+
+			*skb_put(info->rx_skb, 1) = x;
+			inb(iobase + DATA_H);
+			info->rx_count--;
+
+			if (info->rx_count == 0) {
+
+				int dlen;
+				struct hci_event_hdr *eh;
+				struct hci_acl_hdr *ah;
+				struct hci_sco_hdr *sh;
+
+				switch (info->rx_state) {
+
+				case RECV_WAIT_EVENT_HEADER:
+					eh = hci_event_hdr(info->rx_skb);
+					info->rx_state = RECV_WAIT_DATA;
+					info->rx_count = eh->plen;
+					break;
+
+				case RECV_WAIT_ACL_HEADER:
+					ah = hci_acl_hdr(info->rx_skb);
+					dlen = __le16_to_cpu(ah->dlen);
+					info->rx_state = RECV_WAIT_DATA;
+					info->rx_count = dlen;
+					break;
+
+				case RECV_WAIT_SCO_HEADER:
+					sh = hci_sco_hdr(info->rx_skb);
+					info->rx_state = RECV_WAIT_DATA;
+					info->rx_count = sh->dlen;
+					break;
+
+				case RECV_WAIT_DATA:
+					hci_recv_frame(info->rx_skb);
+					info->rx_skb = NULL;
+					break;
+
+				}
+
+			}
+
+		}
+
+	}
+
+	bt3c_io_write(iobase, 0x7006, 0x0000);
+}
+
+
+static irqreturn_t bt3c_interrupt(int irq, void *dev_inst)
+{
+	bt3c_info_t *info = dev_inst;
+	unsigned int iobase;
+	int iir;
+	irqreturn_t r = IRQ_NONE;
+
+	if (!info || !info->hdev)
+		/* our irq handler is shared */
+		return IRQ_NONE;
+
+	iobase = info->p_dev->resource[0]->start;
+
+	spin_lock(&(info->lock));
+
+	iir = inb(iobase + CONTROL);
+	if (iir & 0x80) {
+		int stat = bt3c_read(iobase, 0x7001);
+
+		if ((stat & 0xff) == 0x7f) {
+			BT_ERR("Very strange (stat=0x%04x)", stat);
+		} else if ((stat & 0xff) != 0xff) {
+			if (stat & 0x0020) {
+				int status = bt3c_read(iobase, 0x7002) & 0x10;
+				BT_INFO("%s: Antenna %s", info->hdev->name,
+							status ? "out" : "in");
+			}
+			if (stat & 0x0001)
+				bt3c_receive(info);
+			if (stat & 0x0002) {
+				//BT_ERR("Ack (stat=0x%04x)", stat);
+				clear_bit(XMIT_SENDING, &(info->tx_state));
+				bt3c_write_wakeup(info);
+			}
+
+			bt3c_io_write(iobase, 0x7001, 0x0000);
+
+			outb(iir, iobase + CONTROL);
+		}
+		r = IRQ_HANDLED;
+	}
+
+	spin_unlock(&(info->lock));
+
+	return r;
+}
+
+
+
+/* ======================== HCI interface ======================== */
+
+
+static int bt3c_hci_flush(struct hci_dev *hdev)
+{
+	bt3c_info_t *info = hci_get_drvdata(hdev);
+
+	/* Drop TX queue */
+	skb_queue_purge(&(info->txq));
+
+	return 0;
+}
+
+
+static int bt3c_hci_open(struct hci_dev *hdev)
+{
+	set_bit(HCI_RUNNING, &(hdev->flags));
+
+	return 0;
+}
+
+
+static int bt3c_hci_close(struct hci_dev *hdev)
+{
+	if (!test_and_clear_bit(HCI_RUNNING, &(hdev->flags)))
+		return 0;
+
+	bt3c_hci_flush(hdev);
+
+	return 0;
+}
+
+
+static int bt3c_hci_send_frame(struct sk_buff *skb)
+{
+	bt3c_info_t *info;
+	struct hci_dev *hdev = (struct hci_dev *)(skb->dev);
+	unsigned long flags;
+
+	if (!hdev) {
+		BT_ERR("Frame for unknown HCI device (hdev=NULL)");
+		return -ENODEV;
+	}
+
+	info = hci_get_drvdata(hdev);
+
+	switch (bt_cb(skb)->pkt_type) {
+	case HCI_COMMAND_PKT:
+		hdev->stat.cmd_tx++;
+		break;
+	case HCI_ACLDATA_PKT:
+		hdev->stat.acl_tx++;
+		break;
+	case HCI_SCODATA_PKT:
+		hdev->stat.sco_tx++;
+		break;
+	};
+
+	/* Prepend skb with frame type */
+	memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
+	skb_queue_tail(&(info->txq), skb);
+
+	spin_lock_irqsave(&(info->lock), flags);
+
+	bt3c_write_wakeup(info);
+
+	spin_unlock_irqrestore(&(info->lock), flags);
+
+	return 0;
+}
+
+
+static int bt3c_hci_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg)
+{
+	return -ENOIOCTLCMD;
+}
+
+
+
+/* ======================== Card services HCI interaction ======================== */
+
+
+static int bt3c_load_firmware(bt3c_info_t *info, const unsigned char *firmware,
+			      int count)
+{
+	char *ptr = (char *) firmware;
+	char b[9];
+	unsigned int iobase, size, addr, fcs, tmp;
+	int i, err = 0;
+
+	iobase = info->p_dev->resource[0]->start;
+
+	/* Reset */
+	bt3c_io_write(iobase, 0x8040, 0x0404);
+	bt3c_io_write(iobase, 0x8040, 0x0400);
+
+	udelay(1);
+
+	bt3c_io_write(iobase, 0x8040, 0x0404);
+
+	udelay(17);
+
+	/* Load */
+	while (count) {
+		if (ptr[0] != 'S') {
+			BT_ERR("Bad address in firmware");
+			err = -EFAULT;
+			goto error;
+		}
+
+		memset(b, 0, sizeof(b));
+		memcpy(b, ptr + 2, 2);
+		size = simple_strtoul(b, NULL, 16);
+
+		memset(b, 0, sizeof(b));
+		memcpy(b, ptr + 4, 8);
+		addr = simple_strtoul(b, NULL, 16);
+
+		memset(b, 0, sizeof(b));
+		memcpy(b, ptr + (size * 2) + 2, 2);
+		fcs = simple_strtoul(b, NULL, 16);
+
+		memset(b, 0, sizeof(b));
+		for (tmp = 0, i = 0; i < size; i++) {
+			memcpy(b, ptr + (i * 2) + 2, 2);
+			tmp += simple_strtol(b, NULL, 16);
+		}
+
+		if (((tmp + fcs) & 0xff) != 0xff) {
+			BT_ERR("Checksum error in firmware");
+			err = -EILSEQ;
+			goto error;
+		}
+
+		if (ptr[1] == '3') {
+			bt3c_address(iobase, addr);
+
+			memset(b, 0, sizeof(b));
+			for (i = 0; i < (size - 4) / 2; i++) {
+				memcpy(b, ptr + (i * 4) + 12, 4);
+				tmp = simple_strtoul(b, NULL, 16);
+				bt3c_put(iobase, tmp);
+			}
+		}
+
+		ptr   += (size * 2) + 6;
+		count -= (size * 2) + 6;
+	}
+
+	udelay(17);
+
+	/* Boot */
+	bt3c_address(iobase, 0x3000);
+	outb(inb(iobase + CONTROL) | 0x40, iobase + CONTROL);
+
+error:
+	udelay(17);
+
+	/* Clear */
+	bt3c_io_write(iobase, 0x7006, 0x0000);
+	bt3c_io_write(iobase, 0x7005, 0x0000);
+	bt3c_io_write(iobase, 0x7001, 0x0000);
+
+	return err;
+}
+
+
+static int bt3c_open(bt3c_info_t *info)
+{
+	const struct firmware *firmware;
+	struct hci_dev *hdev;
+	int err;
+
+	spin_lock_init(&(info->lock));
+
+	skb_queue_head_init(&(info->txq));
+
+	info->rx_state = RECV_WAIT_PACKET_TYPE;
+	info->rx_count = 0;
+	info->rx_skb = NULL;
+
+	/* Initialize HCI device */
+	hdev = hci_alloc_dev();
+	if (!hdev) {
+		BT_ERR("Can't allocate HCI device");
+		return -ENOMEM;
+	}
+
+	info->hdev = hdev;
+
+	hdev->bus = HCI_PCCARD;
+	hci_set_drvdata(hdev, info);
+	SET_HCIDEV_DEV(hdev, &info->p_dev->dev);
+
+	hdev->open     = bt3c_hci_open;
+	hdev->close    = bt3c_hci_close;
+	hdev->flush    = bt3c_hci_flush;
+	hdev->send     = bt3c_hci_send_frame;
+	hdev->ioctl    = bt3c_hci_ioctl;
+
+	/* Load firmware */
+	err = request_firmware(&firmware, "BT3CPCC.bin", &info->p_dev->dev);
+	if (err < 0) {
+		BT_ERR("Firmware request failed");
+		goto error;
+	}
+
+	err = bt3c_load_firmware(info, firmware->data, firmware->size);
+
+	release_firmware(firmware);
+
+	if (err < 0) {
+		BT_ERR("Firmware loading failed");
+		goto error;
+	}
+
+	/* Timeout before it is safe to send the first HCI packet */
+	msleep(1000);
+
+	/* Register HCI device */
+	err = hci_register_dev(hdev);
+	if (err < 0) {
+		BT_ERR("Can't register HCI device");
+		goto error;
+	}
+
+	return 0;
+
+error:
+	info->hdev = NULL;
+	hci_free_dev(hdev);
+	return err;
+}
+
+
+static int bt3c_close(bt3c_info_t *info)
+{
+	struct hci_dev *hdev = info->hdev;
+
+	if (!hdev)
+		return -ENODEV;
+
+	bt3c_hci_close(hdev);
+
+	hci_unregister_dev(hdev);
+	hci_free_dev(hdev);
+
+	return 0;
+}
+
+static int bt3c_probe(struct pcmcia_device *link)
+{
+	bt3c_info_t *info;
+
+	/* Create new info device */
+	info = kzalloc(sizeof(*info), GFP_KERNEL);
+	if (!info)
+		return -ENOMEM;
+
+	info->p_dev = link;
+	link->priv = info;
+
+	link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_VPP |
+		CONF_AUTO_SET_IO;
+
+	return bt3c_config(link);
+}
+
+
+static void bt3c_detach(struct pcmcia_device *link)
+{
+	bt3c_info_t *info = link->priv;
+
+	bt3c_release(link);
+	kfree(info);
+}
+
+static int bt3c_check_config(struct pcmcia_device *p_dev, void *priv_data)
+{
+	int *try = priv_data;
+
+	if (try == 0)
+		p_dev->io_lines = 16;
+
+	if ((p_dev->resource[0]->end != 8) || (p_dev->resource[0]->start == 0))
+		return -EINVAL;
+
+	p_dev->resource[0]->end = 8;
+	p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH;
+	p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8;
+
+	return pcmcia_request_io(p_dev);
+}
+
+static int bt3c_check_config_notpicky(struct pcmcia_device *p_dev,
+				      void *priv_data)
+{
+	static unsigned int base[5] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, 0x0 };
+	int j;
+
+	if (p_dev->io_lines > 3)
+		return -ENODEV;
+
+	p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH;
+	p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8;
+	p_dev->resource[0]->end = 8;
+
+	for (j = 0; j < 5; j++) {
+		p_dev->resource[0]->start = base[j];
+		p_dev->io_lines = base[j] ? 16 : 3;
+		if (!pcmcia_request_io(p_dev))
+			return 0;
+	}
+	return -ENODEV;
+}
+
+static int bt3c_config(struct pcmcia_device *link)
+{
+	bt3c_info_t *info = link->priv;
+	int i;
+	unsigned long try;
+
+	/* First pass: look for a config entry that looks normal.
+	   Two tries: without IO aliases, then with aliases */
+	for (try = 0; try < 2; try++)
+		if (!pcmcia_loop_config(link, bt3c_check_config, (void *) try))
+			goto found_port;
+
+	/* Second pass: try to find an entry that isn't picky about
+	   its base address, then try to grab any standard serial port
+	   address, and finally try to get any free port. */
+	if (!pcmcia_loop_config(link, bt3c_check_config_notpicky, NULL))
+		goto found_port;
+
+	BT_ERR("No usable port range found");
+	goto failed;
+
+found_port:
+	i = pcmcia_request_irq(link, &bt3c_interrupt);
+	if (i != 0)
+		goto failed;
+
+	i = pcmcia_enable_device(link);
+	if (i != 0)
+		goto failed;
+
+	if (bt3c_open(info) != 0)
+		goto failed;
+
+	return 0;
+
+failed:
+	bt3c_release(link);
+	return -ENODEV;
+}
+
+
+static void bt3c_release(struct pcmcia_device *link)
+{
+	bt3c_info_t *info = link->priv;
+
+	bt3c_close(info);
+
+	pcmcia_disable_device(link);
+}
+
+
+static const struct pcmcia_device_id bt3c_ids[] = {
+	PCMCIA_DEVICE_PROD_ID13("3COM", "Bluetooth PC Card", 0xefce0a31, 0xd4ce9b02),
+	PCMCIA_DEVICE_NULL
+};
+MODULE_DEVICE_TABLE(pcmcia, bt3c_ids);
+
+static struct pcmcia_driver bt3c_driver = {
+	.owner		= THIS_MODULE,
+	.name		= "bt3c_cs",
+	.probe		= bt3c_probe,
+	.remove		= bt3c_detach,
+	.id_table	= bt3c_ids,
+};
+
+static int __init init_bt3c_cs(void)
+{
+	return pcmcia_register_driver(&bt3c_driver);
+}
+
+
+static void __exit exit_bt3c_cs(void)
+{
+	pcmcia_unregister_driver(&bt3c_driver);
+}
+
+module_init(init_bt3c_cs);
+module_exit(exit_bt3c_cs);
diff --git a/ap/os/linux/linux-3.4.x/drivers/bluetooth/btmrvl_debugfs.c b/ap/os/linux/linux-3.4.x/drivers/bluetooth/btmrvl_debugfs.c
new file mode 100644
index 0000000..428dbb7
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/bluetooth/btmrvl_debugfs.c
@@ -0,0 +1,446 @@
+/**
+ * Marvell Bluetooth driver: debugfs related functions
+ *
+ * Copyright (C) 2009, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License").  You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available by writing to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the
+ * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+ *
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
+ * this warranty disclaimer.
+ **/
+
+#include <linux/debugfs.h>
+#include <linux/slab.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#include "btmrvl_drv.h"
+
+struct btmrvl_debugfs_data {
+	struct dentry *config_dir;
+	struct dentry *status_dir;
+
+	/* config */
+	struct dentry *psmode;
+	struct dentry *pscmd;
+	struct dentry *hsmode;
+	struct dentry *hscmd;
+	struct dentry *gpiogap;
+	struct dentry *hscfgcmd;
+
+	/* status */
+	struct dentry *curpsmode;
+	struct dentry *hsstate;
+	struct dentry *psstate;
+	struct dentry *txdnldready;
+};
+
+static ssize_t btmrvl_hscfgcmd_write(struct file *file,
+			const char __user *ubuf, size_t count, loff_t *ppos)
+{
+	struct btmrvl_private *priv = file->private_data;
+	char buf[16];
+	long result, ret;
+
+	memset(buf, 0, sizeof(buf));
+
+	if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
+		return -EFAULT;
+
+	ret = strict_strtol(buf, 10, &result);
+	if (ret)
+		return ret;
+
+	priv->btmrvl_dev.hscfgcmd = result;
+
+	if (priv->btmrvl_dev.hscfgcmd) {
+		btmrvl_prepare_command(priv);
+		wake_up_interruptible(&priv->main_thread.wait_q);
+	}
+
+	return count;
+}
+
+static ssize_t btmrvl_hscfgcmd_read(struct file *file, char __user *userbuf,
+						size_t count, loff_t *ppos)
+{
+	struct btmrvl_private *priv = file->private_data;
+	char buf[16];
+	int ret;
+
+	ret = snprintf(buf, sizeof(buf) - 1, "%d\n",
+						priv->btmrvl_dev.hscfgcmd);
+
+	return simple_read_from_buffer(userbuf, count, ppos, buf, ret);
+}
+
+static const struct file_operations btmrvl_hscfgcmd_fops = {
+	.read	= btmrvl_hscfgcmd_read,
+	.write	= btmrvl_hscfgcmd_write,
+	.open	= simple_open,
+	.llseek = default_llseek,
+};
+
+static ssize_t btmrvl_psmode_write(struct file *file, const char __user *ubuf,
+						size_t count, loff_t *ppos)
+{
+	struct btmrvl_private *priv = file->private_data;
+	char buf[16];
+	long result, ret;
+
+	memset(buf, 0, sizeof(buf));
+
+	if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
+		return -EFAULT;
+
+	ret = strict_strtol(buf, 10, &result);
+	if (ret)
+		return ret;
+
+	priv->btmrvl_dev.psmode = result;
+
+	return count;
+}
+
+static ssize_t btmrvl_psmode_read(struct file *file, char __user *userbuf,
+						size_t count, loff_t *ppos)
+{
+	struct btmrvl_private *priv = file->private_data;
+	char buf[16];
+	int ret;
+
+	ret = snprintf(buf, sizeof(buf) - 1, "%d\n",
+						priv->btmrvl_dev.psmode);
+
+	return simple_read_from_buffer(userbuf, count, ppos, buf, ret);
+}
+
+static const struct file_operations btmrvl_psmode_fops = {
+	.read	= btmrvl_psmode_read,
+	.write	= btmrvl_psmode_write,
+	.open	= simple_open,
+	.llseek = default_llseek,
+};
+
+static ssize_t btmrvl_pscmd_write(struct file *file, const char __user *ubuf,
+						size_t count, loff_t *ppos)
+{
+	struct btmrvl_private *priv = file->private_data;
+	char buf[16];
+	long result, ret;
+
+	memset(buf, 0, sizeof(buf));
+
+	if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
+		return -EFAULT;
+
+	ret = strict_strtol(buf, 10, &result);
+	if (ret)
+		return ret;
+
+	priv->btmrvl_dev.pscmd = result;
+
+	if (priv->btmrvl_dev.pscmd) {
+		btmrvl_prepare_command(priv);
+		wake_up_interruptible(&priv->main_thread.wait_q);
+	}
+
+	return count;
+
+}
+
+static ssize_t btmrvl_pscmd_read(struct file *file, char __user *userbuf,
+						size_t count, loff_t *ppos)
+{
+	struct btmrvl_private *priv = file->private_data;
+	char buf[16];
+	int ret;
+
+	ret = snprintf(buf, sizeof(buf) - 1, "%d\n", priv->btmrvl_dev.pscmd);
+
+	return simple_read_from_buffer(userbuf, count, ppos, buf, ret);
+}
+
+static const struct file_operations btmrvl_pscmd_fops = {
+	.read = btmrvl_pscmd_read,
+	.write = btmrvl_pscmd_write,
+	.open = simple_open,
+	.llseek = default_llseek,
+};
+
+static ssize_t btmrvl_gpiogap_write(struct file *file, const char __user *ubuf,
+						size_t count, loff_t *ppos)
+{
+	struct btmrvl_private *priv = file->private_data;
+	char buf[16];
+	long result, ret;
+
+	memset(buf, 0, sizeof(buf));
+
+	if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
+		return -EFAULT;
+
+	ret = strict_strtol(buf, 16, &result);
+	if (ret)
+		return ret;
+
+	priv->btmrvl_dev.gpio_gap = result;
+
+	return count;
+}
+
+static ssize_t btmrvl_gpiogap_read(struct file *file, char __user *userbuf,
+						size_t count, loff_t *ppos)
+{
+	struct btmrvl_private *priv = file->private_data;
+	char buf[16];
+	int ret;
+
+	ret = snprintf(buf, sizeof(buf) - 1, "0x%x\n",
+						priv->btmrvl_dev.gpio_gap);
+
+	return simple_read_from_buffer(userbuf, count, ppos, buf, ret);
+}
+
+static const struct file_operations btmrvl_gpiogap_fops = {
+	.read	= btmrvl_gpiogap_read,
+	.write	= btmrvl_gpiogap_write,
+	.open	= simple_open,
+	.llseek = default_llseek,
+};
+
+static ssize_t btmrvl_hscmd_write(struct file *file, const char __user *ubuf,
+						size_t count, loff_t *ppos)
+{
+	struct btmrvl_private *priv = file->private_data;
+	char buf[16];
+	long result, ret;
+
+	memset(buf, 0, sizeof(buf));
+
+	if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
+		return -EFAULT;
+
+	ret = strict_strtol(buf, 10, &result);
+	if (ret)
+		return ret;
+
+	priv->btmrvl_dev.hscmd = result;
+	if (priv->btmrvl_dev.hscmd) {
+		btmrvl_prepare_command(priv);
+		wake_up_interruptible(&priv->main_thread.wait_q);
+	}
+
+	return count;
+}
+
+static ssize_t btmrvl_hscmd_read(struct file *file, char __user *userbuf,
+						size_t count, loff_t *ppos)
+{
+	struct btmrvl_private *priv = file->private_data;
+	char buf[16];
+	int ret;
+
+	ret = snprintf(buf, sizeof(buf) - 1, "%d\n", priv->btmrvl_dev.hscmd);
+
+	return simple_read_from_buffer(userbuf, count, ppos, buf, ret);
+}
+
+static const struct file_operations btmrvl_hscmd_fops = {
+	.read	= btmrvl_hscmd_read,
+	.write	= btmrvl_hscmd_write,
+	.open	= simple_open,
+	.llseek = default_llseek,
+};
+
+static ssize_t btmrvl_hsmode_write(struct file *file, const char __user *ubuf,
+						size_t count, loff_t *ppos)
+{
+	struct btmrvl_private *priv = file->private_data;
+	char buf[16];
+	long result, ret;
+
+	memset(buf, 0, sizeof(buf));
+
+	if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
+		return -EFAULT;
+
+	ret = strict_strtol(buf, 10, &result);
+	if (ret)
+		return ret;
+
+	priv->btmrvl_dev.hsmode = result;
+
+	return count;
+}
+
+static ssize_t btmrvl_hsmode_read(struct file *file, char __user * userbuf,
+						size_t count, loff_t *ppos)
+{
+	struct btmrvl_private *priv = file->private_data;
+	char buf[16];
+	int ret;
+
+	ret = snprintf(buf, sizeof(buf) - 1, "%d\n", priv->btmrvl_dev.hsmode);
+
+	return simple_read_from_buffer(userbuf, count, ppos, buf, ret);
+}
+
+static const struct file_operations btmrvl_hsmode_fops = {
+	.read	= btmrvl_hsmode_read,
+	.write	= btmrvl_hsmode_write,
+	.open	= simple_open,
+	.llseek = default_llseek,
+};
+
+static ssize_t btmrvl_curpsmode_read(struct file *file, char __user *userbuf,
+						size_t count, loff_t *ppos)
+{
+	struct btmrvl_private *priv = file->private_data;
+	char buf[16];
+	int ret;
+
+	ret = snprintf(buf, sizeof(buf) - 1, "%d\n", priv->adapter->psmode);
+
+	return simple_read_from_buffer(userbuf, count, ppos, buf, ret);
+}
+
+static const struct file_operations btmrvl_curpsmode_fops = {
+	.read	= btmrvl_curpsmode_read,
+	.open	= simple_open,
+	.llseek = default_llseek,
+};
+
+static ssize_t btmrvl_psstate_read(struct file *file, char __user * userbuf,
+						size_t count, loff_t *ppos)
+{
+	struct btmrvl_private *priv = file->private_data;
+	char buf[16];
+	int ret;
+
+	ret = snprintf(buf, sizeof(buf) - 1, "%d\n", priv->adapter->ps_state);
+
+	return simple_read_from_buffer(userbuf, count, ppos, buf, ret);
+}
+
+static const struct file_operations btmrvl_psstate_fops = {
+	.read	= btmrvl_psstate_read,
+	.open	= simple_open,
+	.llseek = default_llseek,
+};
+
+static ssize_t btmrvl_hsstate_read(struct file *file, char __user *userbuf,
+						size_t count, loff_t *ppos)
+{
+	struct btmrvl_private *priv = file->private_data;
+	char buf[16];
+	int ret;
+
+	ret = snprintf(buf, sizeof(buf) - 1, "%d\n", priv->adapter->hs_state);
+
+	return simple_read_from_buffer(userbuf, count, ppos, buf, ret);
+}
+
+static const struct file_operations btmrvl_hsstate_fops = {
+	.read	= btmrvl_hsstate_read,
+	.open	= simple_open,
+	.llseek = default_llseek,
+};
+
+static ssize_t btmrvl_txdnldready_read(struct file *file, char __user *userbuf,
+						size_t count, loff_t *ppos)
+{
+	struct btmrvl_private *priv = file->private_data;
+	char buf[16];
+	int ret;
+
+	ret = snprintf(buf, sizeof(buf) - 1, "%d\n",
+					priv->btmrvl_dev.tx_dnld_rdy);
+
+	return simple_read_from_buffer(userbuf, count, ppos, buf, ret);
+}
+
+static const struct file_operations btmrvl_txdnldready_fops = {
+	.read	= btmrvl_txdnldready_read,
+	.open	= simple_open,
+	.llseek = default_llseek,
+};
+
+void btmrvl_debugfs_init(struct hci_dev *hdev)
+{
+	struct btmrvl_private *priv = hci_get_drvdata(hdev);
+	struct btmrvl_debugfs_data *dbg;
+
+	if (!hdev->debugfs)
+		return;
+
+	dbg = kzalloc(sizeof(*dbg), GFP_KERNEL);
+	priv->debugfs_data = dbg;
+
+	if (!dbg) {
+		BT_ERR("Can not allocate memory for btmrvl_debugfs_data.");
+		return;
+	}
+
+	dbg->config_dir = debugfs_create_dir("config", hdev->debugfs);
+
+	dbg->psmode = debugfs_create_file("psmode", 0644, dbg->config_dir,
+					  priv, &btmrvl_psmode_fops);
+	dbg->pscmd = debugfs_create_file("pscmd", 0644, dbg->config_dir,
+					 priv, &btmrvl_pscmd_fops);
+	dbg->gpiogap = debugfs_create_file("gpiogap", 0644, dbg->config_dir,
+					   priv, &btmrvl_gpiogap_fops);
+	dbg->hsmode =  debugfs_create_file("hsmode", 0644, dbg->config_dir,
+					   priv, &btmrvl_hsmode_fops);
+	dbg->hscmd = debugfs_create_file("hscmd", 0644, dbg->config_dir,
+					 priv, &btmrvl_hscmd_fops);
+	dbg->hscfgcmd = debugfs_create_file("hscfgcmd", 0644, dbg->config_dir,
+					    priv, &btmrvl_hscfgcmd_fops);
+
+	dbg->status_dir = debugfs_create_dir("status", hdev->debugfs);
+	dbg->curpsmode = debugfs_create_file("curpsmode", 0444,
+					     dbg->status_dir, priv,
+					     &btmrvl_curpsmode_fops);
+	dbg->psstate = debugfs_create_file("psstate", 0444, dbg->status_dir,
+					   priv, &btmrvl_psstate_fops);
+	dbg->hsstate = debugfs_create_file("hsstate", 0444, dbg->status_dir,
+					   priv, &btmrvl_hsstate_fops);
+	dbg->txdnldready = debugfs_create_file("txdnldready", 0444,
+					       dbg->status_dir, priv,
+					       &btmrvl_txdnldready_fops);
+}
+
+void btmrvl_debugfs_remove(struct hci_dev *hdev)
+{
+	struct btmrvl_private *priv = hci_get_drvdata(hdev);
+	struct btmrvl_debugfs_data *dbg = priv->debugfs_data;
+
+	if (!dbg)
+		return;
+
+	debugfs_remove(dbg->psmode);
+	debugfs_remove(dbg->pscmd);
+	debugfs_remove(dbg->gpiogap);
+	debugfs_remove(dbg->hsmode);
+	debugfs_remove(dbg->hscmd);
+	debugfs_remove(dbg->hscfgcmd);
+	debugfs_remove(dbg->config_dir);
+
+	debugfs_remove(dbg->curpsmode);
+	debugfs_remove(dbg->psstate);
+	debugfs_remove(dbg->hsstate);
+	debugfs_remove(dbg->txdnldready);
+	debugfs_remove(dbg->status_dir);
+
+	kfree(dbg);
+}
diff --git a/ap/os/linux/linux-3.4.x/drivers/bluetooth/btmrvl_drv.h b/ap/os/linux/linux-3.4.x/drivers/bluetooth/btmrvl_drv.h
new file mode 100644
index 0000000..90bda50
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/bluetooth/btmrvl_drv.h
@@ -0,0 +1,148 @@
+/*
+ * Marvell Bluetooth driver: global definitions & declarations
+ *
+ * Copyright (C) 2009, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License").  You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available by writing to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the
+ * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+ *
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
+ * this warranty disclaimer.
+ *
+ */
+
+#include <linux/kthread.h>
+#include <linux/bitops.h>
+#include <linux/slab.h>
+#include <net/bluetooth/bluetooth.h>
+
+#define BTM_HEADER_LEN			4
+#define BTM_UPLD_SIZE			2312
+
+/* Time to wait until Host Sleep state change in millisecond */
+#define WAIT_UNTIL_HS_STATE_CHANGED	5000
+/* Time to wait for command response in millisecond */
+#define WAIT_UNTIL_CMD_RESP		5000
+
+struct btmrvl_thread {
+	struct task_struct *task;
+	wait_queue_head_t wait_q;
+	void *priv;
+};
+
+struct btmrvl_device {
+	void *card;
+	struct hci_dev *hcidev;
+
+	u8 dev_type;
+
+	u8 tx_dnld_rdy;
+
+	u8 psmode;
+	u8 pscmd;
+	u8 hsmode;
+	u8 hscmd;
+
+	/* Low byte is gap, high byte is GPIO */
+	u16 gpio_gap;
+
+	u8 hscfgcmd;
+	u8 sendcmdflag;
+};
+
+struct btmrvl_adapter {
+	u32 int_count;
+	struct sk_buff_head tx_queue;
+	u8 psmode;
+	u8 ps_state;
+	u8 hs_state;
+	u8 wakeup_tries;
+	wait_queue_head_t cmd_wait_q;
+	u8 cmd_complete;
+};
+
+struct btmrvl_private {
+	struct btmrvl_device btmrvl_dev;
+	struct btmrvl_adapter *adapter;
+	struct btmrvl_thread main_thread;
+	int (*hw_host_to_card) (struct btmrvl_private *priv,
+				u8 *payload, u16 nb);
+	int (*hw_wakeup_firmware) (struct btmrvl_private *priv);
+	int (*hw_process_int_status) (struct btmrvl_private *priv);
+	spinlock_t driver_lock;		/* spinlock used by driver */
+#ifdef CONFIG_DEBUG_FS
+	void *debugfs_data;
+#endif
+};
+
+#define MRVL_VENDOR_PKT			0xFE
+
+/* Bluetooth commands  */
+#define BT_CMD_AUTO_SLEEP_MODE		0x23
+#define BT_CMD_HOST_SLEEP_CONFIG	0x59
+#define BT_CMD_HOST_SLEEP_ENABLE	0x5A
+#define BT_CMD_MODULE_CFG_REQ		0x5B
+
+/* Sub-commands: Module Bringup/Shutdown Request/Response */
+#define MODULE_BRINGUP_REQ		0xF1
+#define MODULE_BROUGHT_UP		0x00
+#define MODULE_ALREADY_UP		0x0C
+
+#define MODULE_SHUTDOWN_REQ		0xF2
+
+#define BT_EVENT_POWER_STATE		0x20
+
+/* Bluetooth Power States */
+#define BT_PS_ENABLE			0x02
+#define BT_PS_DISABLE			0x03
+#define BT_PS_SLEEP			0x01
+
+#define OGF				0x3F
+
+/* Host Sleep states */
+#define HS_ACTIVATED			0x01
+#define HS_DEACTIVATED			0x00
+
+/* Power Save modes */
+#define PS_SLEEP			0x01
+#define PS_AWAKE			0x00
+
+struct btmrvl_cmd {
+	__le16 ocf_ogf;
+	u8 length;
+	u8 data[4];
+} __packed;
+
+struct btmrvl_event {
+	u8 ec;		/* event counter */
+	u8 length;
+	u8 data[4];
+} __packed;
+
+/* Prototype of global function */
+
+int btmrvl_register_hdev(struct btmrvl_private *priv);
+struct btmrvl_private *btmrvl_add_card(void *card);
+int btmrvl_remove_card(struct btmrvl_private *priv);
+
+void btmrvl_interrupt(struct btmrvl_private *priv);
+
+void btmrvl_check_evtpkt(struct btmrvl_private *priv, struct sk_buff *skb);
+int btmrvl_process_event(struct btmrvl_private *priv, struct sk_buff *skb);
+
+int btmrvl_send_module_cfg_cmd(struct btmrvl_private *priv, int subcmd);
+int btmrvl_enable_ps(struct btmrvl_private *priv);
+int btmrvl_prepare_command(struct btmrvl_private *priv);
+
+#ifdef CONFIG_DEBUG_FS
+void btmrvl_debugfs_init(struct hci_dev *hdev);
+void btmrvl_debugfs_remove(struct hci_dev *hdev);
+#endif
diff --git a/ap/os/linux/linux-3.4.x/drivers/bluetooth/btmrvl_main.c b/ap/os/linux/linux-3.4.x/drivers/bluetooth/btmrvl_main.c
new file mode 100644
index 0000000..d1209ad
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/bluetooth/btmrvl_main.c
@@ -0,0 +1,655 @@
+/**
+ * Marvell Bluetooth driver
+ *
+ * Copyright (C) 2009, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License").  You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available by writing to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the
+ * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+ *
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
+ * this warranty disclaimer.
+ **/
+
+#include <linux/module.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#include "btmrvl_drv.h"
+
+#define VERSION "1.0"
+
+/*
+ * This function is called by interface specific interrupt handler.
+ * It updates Power Save & Host Sleep states, and wakes up the main
+ * thread.
+ */
+void btmrvl_interrupt(struct btmrvl_private *priv)
+{
+	priv->adapter->ps_state = PS_AWAKE;
+
+	priv->adapter->wakeup_tries = 0;
+
+	priv->adapter->int_count++;
+
+	wake_up_interruptible(&priv->main_thread.wait_q);
+}
+EXPORT_SYMBOL_GPL(btmrvl_interrupt);
+
+void btmrvl_check_evtpkt(struct btmrvl_private *priv, struct sk_buff *skb)
+{
+	struct hci_event_hdr *hdr = (void *) skb->data;
+	struct hci_ev_cmd_complete *ec;
+	u16 opcode, ocf;
+
+	if (hdr->evt == HCI_EV_CMD_COMPLETE) {
+		ec = (void *) (skb->data + HCI_EVENT_HDR_SIZE);
+		opcode = __le16_to_cpu(ec->opcode);
+		ocf = hci_opcode_ocf(opcode);
+		if (ocf == BT_CMD_MODULE_CFG_REQ &&
+					priv->btmrvl_dev.sendcmdflag) {
+			priv->btmrvl_dev.sendcmdflag = false;
+			priv->adapter->cmd_complete = true;
+			wake_up_interruptible(&priv->adapter->cmd_wait_q);
+		}
+	}
+}
+EXPORT_SYMBOL_GPL(btmrvl_check_evtpkt);
+
+int btmrvl_process_event(struct btmrvl_private *priv, struct sk_buff *skb)
+{
+	struct btmrvl_adapter *adapter = priv->adapter;
+	struct btmrvl_event *event;
+	int ret = 0;
+
+	event = (struct btmrvl_event *) skb->data;
+	if (event->ec != 0xff) {
+		BT_DBG("Not Marvell Event=%x", event->ec);
+		ret = -EINVAL;
+		goto exit;
+	}
+
+	switch (event->data[0]) {
+	case BT_CMD_AUTO_SLEEP_MODE:
+		if (!event->data[2]) {
+			if (event->data[1] == BT_PS_ENABLE)
+				adapter->psmode = 1;
+			else
+				adapter->psmode = 0;
+			BT_DBG("PS Mode:%s",
+				(adapter->psmode) ? "Enable" : "Disable");
+		} else {
+			BT_DBG("PS Mode command failed");
+		}
+		break;
+
+	case BT_CMD_HOST_SLEEP_CONFIG:
+		if (!event->data[3])
+			BT_DBG("gpio=%x, gap=%x", event->data[1],
+							event->data[2]);
+		else
+			BT_DBG("HSCFG command failed");
+		break;
+
+	case BT_CMD_HOST_SLEEP_ENABLE:
+		if (!event->data[1]) {
+			adapter->hs_state = HS_ACTIVATED;
+			if (adapter->psmode)
+				adapter->ps_state = PS_SLEEP;
+			wake_up_interruptible(&adapter->cmd_wait_q);
+			BT_DBG("HS ACTIVATED!");
+		} else {
+			BT_DBG("HS Enable failed");
+		}
+		break;
+
+	case BT_CMD_MODULE_CFG_REQ:
+		if (priv->btmrvl_dev.sendcmdflag &&
+				event->data[1] == MODULE_BRINGUP_REQ) {
+			BT_DBG("EVENT:%s",
+				((event->data[2] == MODULE_BROUGHT_UP) ||
+				(event->data[2] == MODULE_ALREADY_UP)) ?
+				"Bring-up succeed" : "Bring-up failed");
+
+			if (event->length > 3 && event->data[3])
+				priv->btmrvl_dev.dev_type = HCI_AMP;
+			else
+				priv->btmrvl_dev.dev_type = HCI_BREDR;
+
+			BT_DBG("dev_type: %d", priv->btmrvl_dev.dev_type);
+		} else if (priv->btmrvl_dev.sendcmdflag &&
+				event->data[1] == MODULE_SHUTDOWN_REQ) {
+			BT_DBG("EVENT:%s", (event->data[2]) ?
+				"Shutdown failed" : "Shutdown succeed");
+		} else {
+			BT_DBG("BT_CMD_MODULE_CFG_REQ resp for APP");
+			ret = -EINVAL;
+		}
+		break;
+
+	case BT_EVENT_POWER_STATE:
+		if (event->data[1] == BT_PS_SLEEP)
+			adapter->ps_state = PS_SLEEP;
+		BT_DBG("EVENT:%s",
+			(adapter->ps_state) ? "PS_SLEEP" : "PS_AWAKE");
+		break;
+
+	default:
+		BT_DBG("Unknown Event=%d", event->data[0]);
+		ret = -EINVAL;
+		break;
+	}
+
+exit:
+	if (!ret)
+		kfree_skb(skb);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(btmrvl_process_event);
+
+int btmrvl_send_module_cfg_cmd(struct btmrvl_private *priv, int subcmd)
+{
+	struct sk_buff *skb;
+	struct btmrvl_cmd *cmd;
+	int ret = 0;
+
+	skb = bt_skb_alloc(sizeof(*cmd), GFP_ATOMIC);
+	if (skb == NULL) {
+		BT_ERR("No free skb");
+		return -ENOMEM;
+	}
+
+	cmd = (struct btmrvl_cmd *) skb_put(skb, sizeof(*cmd));
+	cmd->ocf_ogf = cpu_to_le16(hci_opcode_pack(OGF, BT_CMD_MODULE_CFG_REQ));
+	cmd->length = 1;
+	cmd->data[0] = subcmd;
+
+	bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+
+	skb->dev = (void *) priv->btmrvl_dev.hcidev;
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+
+	priv->btmrvl_dev.sendcmdflag = true;
+
+	priv->adapter->cmd_complete = false;
+
+	BT_DBG("Queue module cfg Command");
+
+	wake_up_interruptible(&priv->main_thread.wait_q);
+
+	if (!wait_event_interruptible_timeout(priv->adapter->cmd_wait_q,
+				priv->adapter->cmd_complete,
+				msecs_to_jiffies(WAIT_UNTIL_CMD_RESP))) {
+		ret = -ETIMEDOUT;
+		BT_ERR("module_cfg_cmd(%x): timeout: %d",
+					subcmd, priv->btmrvl_dev.sendcmdflag);
+	}
+
+	BT_DBG("module cfg Command done");
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(btmrvl_send_module_cfg_cmd);
+
+int btmrvl_enable_ps(struct btmrvl_private *priv)
+{
+	struct sk_buff *skb;
+	struct btmrvl_cmd *cmd;
+
+	skb = bt_skb_alloc(sizeof(*cmd), GFP_ATOMIC);
+	if (skb == NULL) {
+		BT_ERR("No free skb");
+		return -ENOMEM;
+	}
+
+	cmd = (struct btmrvl_cmd *) skb_put(skb, sizeof(*cmd));
+	cmd->ocf_ogf = cpu_to_le16(hci_opcode_pack(OGF,
+					BT_CMD_AUTO_SLEEP_MODE));
+	cmd->length = 1;
+
+	if (priv->btmrvl_dev.psmode)
+		cmd->data[0] = BT_PS_ENABLE;
+	else
+		cmd->data[0] = BT_PS_DISABLE;
+
+	bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+
+	skb->dev = (void *) priv->btmrvl_dev.hcidev;
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+
+	BT_DBG("Queue PSMODE Command:%d", cmd->data[0]);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(btmrvl_enable_ps);
+
+static int btmrvl_enable_hs(struct btmrvl_private *priv)
+{
+	struct sk_buff *skb;
+	struct btmrvl_cmd *cmd;
+	int ret = 0;
+
+	skb = bt_skb_alloc(sizeof(*cmd), GFP_ATOMIC);
+	if (skb == NULL) {
+		BT_ERR("No free skb");
+		return -ENOMEM;
+	}
+
+	cmd = (struct btmrvl_cmd *) skb_put(skb, sizeof(*cmd));
+	cmd->ocf_ogf = cpu_to_le16(hci_opcode_pack(OGF, BT_CMD_HOST_SLEEP_ENABLE));
+	cmd->length = 0;
+
+	bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+
+	skb->dev = (void *) priv->btmrvl_dev.hcidev;
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+
+	BT_DBG("Queue hs enable Command");
+
+	wake_up_interruptible(&priv->main_thread.wait_q);
+
+	if (!wait_event_interruptible_timeout(priv->adapter->cmd_wait_q,
+			priv->adapter->hs_state,
+			msecs_to_jiffies(WAIT_UNTIL_HS_STATE_CHANGED))) {
+		ret = -ETIMEDOUT;
+		BT_ERR("timeout: %d, %d,%d", priv->adapter->hs_state,
+						priv->adapter->ps_state,
+						priv->adapter->wakeup_tries);
+	}
+
+	return ret;
+}
+
+int btmrvl_prepare_command(struct btmrvl_private *priv)
+{
+	struct sk_buff *skb = NULL;
+	struct btmrvl_cmd *cmd;
+	int ret = 0;
+
+	if (priv->btmrvl_dev.hscfgcmd) {
+		priv->btmrvl_dev.hscfgcmd = 0;
+
+		skb = bt_skb_alloc(sizeof(*cmd), GFP_ATOMIC);
+		if (skb == NULL) {
+			BT_ERR("No free skb");
+			return -ENOMEM;
+		}
+
+		cmd = (struct btmrvl_cmd *) skb_put(skb, sizeof(*cmd));
+		cmd->ocf_ogf = cpu_to_le16(hci_opcode_pack(OGF, BT_CMD_HOST_SLEEP_CONFIG));
+		cmd->length = 2;
+		cmd->data[0] = (priv->btmrvl_dev.gpio_gap & 0xff00) >> 8;
+		cmd->data[1] = (u8) (priv->btmrvl_dev.gpio_gap & 0x00ff);
+
+		bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+
+		skb->dev = (void *) priv->btmrvl_dev.hcidev;
+		skb_queue_head(&priv->adapter->tx_queue, skb);
+
+		BT_DBG("Queue HSCFG Command, gpio=0x%x, gap=0x%x",
+						cmd->data[0], cmd->data[1]);
+	}
+
+	if (priv->btmrvl_dev.pscmd) {
+		priv->btmrvl_dev.pscmd = 0;
+		btmrvl_enable_ps(priv);
+	}
+
+	if (priv->btmrvl_dev.hscmd) {
+		priv->btmrvl_dev.hscmd = 0;
+
+		if (priv->btmrvl_dev.hsmode) {
+			ret = btmrvl_enable_hs(priv);
+		} else {
+			ret = priv->hw_wakeup_firmware(priv);
+			priv->adapter->hs_state = HS_DEACTIVATED;
+		}
+	}
+
+	return ret;
+}
+
+static int btmrvl_tx_pkt(struct btmrvl_private *priv, struct sk_buff *skb)
+{
+	int ret = 0;
+
+	if (!skb || !skb->data)
+		return -EINVAL;
+
+	if (!skb->len || ((skb->len + BTM_HEADER_LEN) > BTM_UPLD_SIZE)) {
+		BT_ERR("Tx Error: Bad skb length %d : %d",
+						skb->len, BTM_UPLD_SIZE);
+		return -EINVAL;
+	}
+
+	if (skb_headroom(skb) < BTM_HEADER_LEN) {
+		struct sk_buff *tmp = skb;
+
+		skb = skb_realloc_headroom(skb, BTM_HEADER_LEN);
+		if (!skb) {
+			BT_ERR("Tx Error: realloc_headroom failed %d",
+				BTM_HEADER_LEN);
+			skb = tmp;
+			return -EINVAL;
+		}
+
+		kfree_skb(tmp);
+	}
+
+	skb_push(skb, BTM_HEADER_LEN);
+
+	/* header type: byte[3]
+	 * HCI_COMMAND = 1, ACL_DATA = 2, SCO_DATA = 3, 0xFE = Vendor
+	 * header length: byte[2][1][0]
+	 */
+
+	skb->data[0] = (skb->len & 0x0000ff);
+	skb->data[1] = (skb->len & 0x00ff00) >> 8;
+	skb->data[2] = (skb->len & 0xff0000) >> 16;
+	skb->data[3] = bt_cb(skb)->pkt_type;
+
+	if (priv->hw_host_to_card)
+		ret = priv->hw_host_to_card(priv, skb->data, skb->len);
+
+	return ret;
+}
+
+static void btmrvl_init_adapter(struct btmrvl_private *priv)
+{
+	skb_queue_head_init(&priv->adapter->tx_queue);
+
+	priv->adapter->ps_state = PS_AWAKE;
+
+	init_waitqueue_head(&priv->adapter->cmd_wait_q);
+}
+
+static void btmrvl_free_adapter(struct btmrvl_private *priv)
+{
+	skb_queue_purge(&priv->adapter->tx_queue);
+
+	kfree(priv->adapter);
+
+	priv->adapter = NULL;
+}
+
+static int btmrvl_ioctl(struct hci_dev *hdev,
+				unsigned int cmd, unsigned long arg)
+{
+	return -ENOIOCTLCMD;
+}
+
+static int btmrvl_send_frame(struct sk_buff *skb)
+{
+	struct hci_dev *hdev = (struct hci_dev *) skb->dev;
+	struct btmrvl_private *priv = NULL;
+
+	BT_DBG("type=%d, len=%d", skb->pkt_type, skb->len);
+
+	if (!hdev) {
+		BT_ERR("Frame for unknown HCI device");
+		return -ENODEV;
+	}
+
+	priv = hci_get_drvdata(hdev);
+
+	if (!test_bit(HCI_RUNNING, &hdev->flags)) {
+		BT_ERR("Failed testing HCI_RUNING, flags=%lx", hdev->flags);
+		print_hex_dump_bytes("data: ", DUMP_PREFIX_OFFSET,
+							skb->data, skb->len);
+		return -EBUSY;
+	}
+
+	switch (bt_cb(skb)->pkt_type) {
+	case HCI_COMMAND_PKT:
+		hdev->stat.cmd_tx++;
+		break;
+
+	case HCI_ACLDATA_PKT:
+		hdev->stat.acl_tx++;
+		break;
+
+	case HCI_SCODATA_PKT:
+		hdev->stat.sco_tx++;
+		break;
+	}
+
+	skb_queue_tail(&priv->adapter->tx_queue, skb);
+
+	wake_up_interruptible(&priv->main_thread.wait_q);
+
+	return 0;
+}
+
+static int btmrvl_flush(struct hci_dev *hdev)
+{
+	struct btmrvl_private *priv = hci_get_drvdata(hdev);
+
+	skb_queue_purge(&priv->adapter->tx_queue);
+
+	return 0;
+}
+
+static int btmrvl_close(struct hci_dev *hdev)
+{
+	struct btmrvl_private *priv = hci_get_drvdata(hdev);
+
+	if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
+		return 0;
+
+	skb_queue_purge(&priv->adapter->tx_queue);
+
+	return 0;
+}
+
+static int btmrvl_open(struct hci_dev *hdev)
+{
+	set_bit(HCI_RUNNING, &hdev->flags);
+
+	return 0;
+}
+
+/*
+ * This function handles the event generated by firmware, rx data
+ * received from firmware, and tx data sent from kernel.
+ */
+static int btmrvl_service_main_thread(void *data)
+{
+	struct btmrvl_thread *thread = data;
+	struct btmrvl_private *priv = thread->priv;
+	struct btmrvl_adapter *adapter = priv->adapter;
+	wait_queue_t wait;
+	struct sk_buff *skb;
+	ulong flags;
+
+	init_waitqueue_entry(&wait, current);
+
+	for (;;) {
+		add_wait_queue(&thread->wait_q, &wait);
+
+		set_current_state(TASK_INTERRUPTIBLE);
+
+		if (adapter->wakeup_tries ||
+				((!adapter->int_count) &&
+				(!priv->btmrvl_dev.tx_dnld_rdy ||
+				skb_queue_empty(&adapter->tx_queue)))) {
+			BT_DBG("main_thread is sleeping...");
+			schedule();
+		}
+
+		set_current_state(TASK_RUNNING);
+
+		remove_wait_queue(&thread->wait_q, &wait);
+
+		BT_DBG("main_thread woke up");
+
+		if (kthread_should_stop()) {
+			BT_DBG("main_thread: break from main thread");
+			break;
+		}
+
+		spin_lock_irqsave(&priv->driver_lock, flags);
+		if (adapter->int_count) {
+			adapter->int_count = 0;
+			spin_unlock_irqrestore(&priv->driver_lock, flags);
+			priv->hw_process_int_status(priv);
+		} else if (adapter->ps_state == PS_SLEEP &&
+					!skb_queue_empty(&adapter->tx_queue)) {
+			spin_unlock_irqrestore(&priv->driver_lock, flags);
+			adapter->wakeup_tries++;
+			priv->hw_wakeup_firmware(priv);
+			continue;
+		} else {
+			spin_unlock_irqrestore(&priv->driver_lock, flags);
+		}
+
+		if (adapter->ps_state == PS_SLEEP)
+			continue;
+
+		if (!priv->btmrvl_dev.tx_dnld_rdy)
+			continue;
+
+		skb = skb_dequeue(&adapter->tx_queue);
+		if (skb) {
+			if (btmrvl_tx_pkt(priv, skb))
+				priv->btmrvl_dev.hcidev->stat.err_tx++;
+			else
+				priv->btmrvl_dev.hcidev->stat.byte_tx += skb->len;
+
+			kfree_skb(skb);
+		}
+	}
+
+	return 0;
+}
+
+int btmrvl_register_hdev(struct btmrvl_private *priv)
+{
+	struct hci_dev *hdev = NULL;
+	int ret;
+
+	hdev = hci_alloc_dev();
+	if (!hdev) {
+		BT_ERR("Can not allocate HCI device");
+		goto err_hdev;
+	}
+
+	priv->btmrvl_dev.hcidev = hdev;
+	hci_set_drvdata(hdev, priv);
+
+	hdev->bus = HCI_SDIO;
+	hdev->open = btmrvl_open;
+	hdev->close = btmrvl_close;
+	hdev->flush = btmrvl_flush;
+	hdev->send = btmrvl_send_frame;
+	hdev->ioctl = btmrvl_ioctl;
+
+	btmrvl_send_module_cfg_cmd(priv, MODULE_BRINGUP_REQ);
+
+	hdev->dev_type = priv->btmrvl_dev.dev_type;
+
+	ret = hci_register_dev(hdev);
+	if (ret < 0) {
+		BT_ERR("Can not register HCI device");
+		goto err_hci_register_dev;
+	}
+
+#ifdef CONFIG_DEBUG_FS
+	btmrvl_debugfs_init(hdev);
+#endif
+
+	return 0;
+
+err_hci_register_dev:
+	hci_free_dev(hdev);
+
+err_hdev:
+	/* Stop the thread servicing the interrupts */
+	kthread_stop(priv->main_thread.task);
+
+	btmrvl_free_adapter(priv);
+	kfree(priv);
+
+	return -ENOMEM;
+}
+EXPORT_SYMBOL_GPL(btmrvl_register_hdev);
+
+struct btmrvl_private *btmrvl_add_card(void *card)
+{
+	struct btmrvl_private *priv;
+
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv) {
+		BT_ERR("Can not allocate priv");
+		goto err_priv;
+	}
+
+	priv->adapter = kzalloc(sizeof(*priv->adapter), GFP_KERNEL);
+	if (!priv->adapter) {
+		BT_ERR("Allocate buffer for btmrvl_adapter failed!");
+		goto err_adapter;
+	}
+
+	btmrvl_init_adapter(priv);
+
+	BT_DBG("Starting kthread...");
+	priv->main_thread.priv = priv;
+	spin_lock_init(&priv->driver_lock);
+
+	init_waitqueue_head(&priv->main_thread.wait_q);
+	priv->main_thread.task = kthread_run(btmrvl_service_main_thread,
+				&priv->main_thread, "btmrvl_main_service");
+
+	priv->btmrvl_dev.card = card;
+	priv->btmrvl_dev.tx_dnld_rdy = true;
+
+	return priv;
+
+err_adapter:
+	kfree(priv);
+
+err_priv:
+	return NULL;
+}
+EXPORT_SYMBOL_GPL(btmrvl_add_card);
+
+int btmrvl_remove_card(struct btmrvl_private *priv)
+{
+	struct hci_dev *hdev;
+
+	hdev = priv->btmrvl_dev.hcidev;
+
+	wake_up_interruptible(&priv->adapter->cmd_wait_q);
+
+	kthread_stop(priv->main_thread.task);
+
+#ifdef CONFIG_DEBUG_FS
+	btmrvl_debugfs_remove(hdev);
+#endif
+
+	hci_unregister_dev(hdev);
+
+	hci_free_dev(hdev);
+
+	priv->btmrvl_dev.hcidev = NULL;
+
+	btmrvl_free_adapter(priv);
+
+	kfree(priv);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(btmrvl_remove_card);
+
+MODULE_AUTHOR("Marvell International Ltd.");
+MODULE_DESCRIPTION("Marvell Bluetooth driver ver " VERSION);
+MODULE_VERSION(VERSION);
+MODULE_LICENSE("GPL v2");
diff --git a/ap/os/linux/linux-3.4.x/drivers/bluetooth/btmrvl_sdio.c b/ap/os/linux/linux-3.4.x/drivers/bluetooth/btmrvl_sdio.c
new file mode 100644
index 0000000..27b74b0
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/bluetooth/btmrvl_sdio.c
@@ -0,0 +1,1089 @@
+/**
+ * Marvell BT-over-SDIO driver: SDIO interface related functions.
+ *
+ * Copyright (C) 2009, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License").  You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available by writing to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the
+ * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+ *
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
+ * this warranty disclaimer.
+ **/
+
+#include <linux/firmware.h>
+#include <linux/slab.h>
+
+#include <linux/mmc/sdio_ids.h>
+#include <linux/mmc/sdio_func.h>
+#include <linux/module.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#include "btmrvl_drv.h"
+#include "btmrvl_sdio.h"
+
+#define VERSION "1.0"
+
+/* The btmrvl_sdio_remove() callback function is called
+ * when user removes this module from kernel space or ejects
+ * the card from the slot. The driver handles these 2 cases
+ * differently.
+ * If the user is removing the module, a MODULE_SHUTDOWN_REQ
+ * command is sent to firmware and interrupt will be disabled.
+ * If the card is removed, there is no need to send command
+ * or disable interrupt.
+ *
+ * The variable 'user_rmmod' is used to distinguish these two
+ * scenarios. This flag is initialized as FALSE in case the card
+ * is removed, and will be set to TRUE for module removal when
+ * module_exit function is called.
+ */
+static u8 user_rmmod;
+static u8 sdio_ireg;
+
+static const struct btmrvl_sdio_card_reg btmrvl_reg_8688 = {
+	.cfg = 0x03,
+	.host_int_mask = 0x04,
+	.host_intstatus = 0x05,
+	.card_status = 0x20,
+	.sq_read_base_addr_a0 = 0x10,
+	.sq_read_base_addr_a1 = 0x11,
+	.card_fw_status0 = 0x40,
+	.card_fw_status1 = 0x41,
+	.card_rx_len = 0x42,
+	.card_rx_unit = 0x43,
+	.io_port_0 = 0x00,
+	.io_port_1 = 0x01,
+	.io_port_2 = 0x02,
+};
+static const struct btmrvl_sdio_card_reg btmrvl_reg_87xx = {
+	.cfg = 0x00,
+	.host_int_mask = 0x02,
+	.host_intstatus = 0x03,
+	.card_status = 0x30,
+	.sq_read_base_addr_a0 = 0x40,
+	.sq_read_base_addr_a1 = 0x41,
+	.card_revision = 0x5c,
+	.card_fw_status0 = 0x60,
+	.card_fw_status1 = 0x61,
+	.card_rx_len = 0x62,
+	.card_rx_unit = 0x63,
+	.io_port_0 = 0x78,
+	.io_port_1 = 0x79,
+	.io_port_2 = 0x7a,
+};
+
+static const struct btmrvl_sdio_device btmrvl_sdio_sd8688 = {
+	.helper		= "sd8688_helper.bin",
+	.firmware	= "sd8688.bin",
+	.reg		= &btmrvl_reg_8688,
+	.sd_blksz_fw_dl	= 64,
+};
+
+static const struct btmrvl_sdio_device btmrvl_sdio_sd8787 = {
+	.helper		= NULL,
+	.firmware	= "mrvl/sd8787_uapsta.bin",
+	.reg		= &btmrvl_reg_87xx,
+	.sd_blksz_fw_dl	= 256,
+};
+
+static const struct btmrvl_sdio_device btmrvl_sdio_sd8797 = {
+	.helper		= NULL,
+	.firmware	= "mrvl/sd8797_uapsta.bin",
+	.reg		= &btmrvl_reg_87xx,
+	.sd_blksz_fw_dl	= 256,
+};
+
+static const struct sdio_device_id btmrvl_sdio_ids[] = {
+	/* Marvell SD8688 Bluetooth device */
+	{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x9105),
+			.driver_data = (unsigned long) &btmrvl_sdio_sd8688 },
+	/* Marvell SD8787 Bluetooth device */
+	{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x911A),
+			.driver_data = (unsigned long) &btmrvl_sdio_sd8787 },
+	/* Marvell SD8797 Bluetooth device */
+	{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x912A),
+			.driver_data = (unsigned long) &btmrvl_sdio_sd8797 },
+
+	{ }	/* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(sdio, btmrvl_sdio_ids);
+
+static int btmrvl_sdio_get_rx_unit(struct btmrvl_sdio_card *card)
+{
+	u8 reg;
+	int ret;
+
+	reg = sdio_readb(card->func, card->reg->card_rx_unit, &ret);
+	if (!ret)
+		card->rx_unit = reg;
+
+	return ret;
+}
+
+static int btmrvl_sdio_read_fw_status(struct btmrvl_sdio_card *card, u16 *dat)
+{
+	u8 fws0, fws1;
+	int ret;
+
+	*dat = 0;
+
+	fws0 = sdio_readb(card->func, card->reg->card_fw_status0, &ret);
+	if (ret)
+		return -EIO;
+
+	fws1 = sdio_readb(card->func, card->reg->card_fw_status1, &ret);
+	if (ret)
+		return -EIO;
+
+	*dat = (((u16) fws1) << 8) | fws0;
+
+	return 0;
+}
+
+static int btmrvl_sdio_read_rx_len(struct btmrvl_sdio_card *card, u16 *dat)
+{
+	u8 reg;
+	int ret;
+
+	reg = sdio_readb(card->func, card->reg->card_rx_len, &ret);
+	if (!ret)
+		*dat = (u16) reg << card->rx_unit;
+
+	return ret;
+}
+
+static int btmrvl_sdio_enable_host_int_mask(struct btmrvl_sdio_card *card,
+								u8 mask)
+{
+	int ret;
+
+	sdio_writeb(card->func, mask, card->reg->host_int_mask, &ret);
+	if (ret) {
+		BT_ERR("Unable to enable the host interrupt!");
+		ret = -EIO;
+	}
+
+	return ret;
+}
+
+static int btmrvl_sdio_disable_host_int_mask(struct btmrvl_sdio_card *card,
+								u8 mask)
+{
+	u8 host_int_mask;
+	int ret;
+
+	host_int_mask = sdio_readb(card->func, card->reg->host_int_mask, &ret);
+	if (ret)
+		return -EIO;
+
+	host_int_mask &= ~mask;
+
+	sdio_writeb(card->func, host_int_mask, card->reg->host_int_mask, &ret);
+	if (ret < 0) {
+		BT_ERR("Unable to disable the host interrupt!");
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int btmrvl_sdio_poll_card_status(struct btmrvl_sdio_card *card, u8 bits)
+{
+	unsigned int tries;
+	u8 status;
+	int ret;
+
+	for (tries = 0; tries < MAX_POLL_TRIES * 1000; tries++) {
+		status = sdio_readb(card->func, card->reg->card_status,	&ret);
+		if (ret)
+			goto failed;
+		if ((status & bits) == bits)
+			return ret;
+
+		udelay(1);
+	}
+
+	ret = -ETIMEDOUT;
+
+failed:
+	BT_ERR("FAILED! ret=%d", ret);
+
+	return ret;
+}
+
+static int btmrvl_sdio_verify_fw_download(struct btmrvl_sdio_card *card,
+								int pollnum)
+{
+	int ret = -ETIMEDOUT;
+	u16 firmwarestat;
+	unsigned int tries;
+
+	 /* Wait for firmware to become ready */
+	for (tries = 0; tries < pollnum; tries++) {
+		if (btmrvl_sdio_read_fw_status(card, &firmwarestat) < 0)
+			continue;
+
+		if (firmwarestat == FIRMWARE_READY) {
+			ret = 0;
+			break;
+		} else {
+			msleep(10);
+		}
+	}
+
+	return ret;
+}
+
+static int btmrvl_sdio_download_helper(struct btmrvl_sdio_card *card)
+{
+	const struct firmware *fw_helper = NULL;
+	const u8 *helper = NULL;
+	int ret;
+	void *tmphlprbuf = NULL;
+	int tmphlprbufsz, hlprblknow, helperlen;
+	u8 *helperbuf;
+	u32 tx_len;
+
+	ret = request_firmware(&fw_helper, card->helper,
+						&card->func->dev);
+	if ((ret < 0) || !fw_helper) {
+		BT_ERR("request_firmware(helper) failed, error code = %d",
+									ret);
+		ret = -ENOENT;
+		goto done;
+	}
+
+	helper = fw_helper->data;
+	helperlen = fw_helper->size;
+
+	BT_DBG("Downloading helper image (%d bytes), block size %d bytes",
+						helperlen, SDIO_BLOCK_SIZE);
+
+	tmphlprbufsz = ALIGN_SZ(BTM_UPLD_SIZE, BTSDIO_DMA_ALIGN);
+
+	tmphlprbuf = kzalloc(tmphlprbufsz, GFP_KERNEL);
+	if (!tmphlprbuf) {
+		BT_ERR("Unable to allocate buffer for helper."
+			" Terminating download");
+		ret = -ENOMEM;
+		goto done;
+	}
+
+	helperbuf = (u8 *) ALIGN_ADDR(tmphlprbuf, BTSDIO_DMA_ALIGN);
+
+	/* Perform helper data transfer */
+	tx_len = (FIRMWARE_TRANSFER_NBLOCK * SDIO_BLOCK_SIZE)
+			- SDIO_HEADER_LEN;
+	hlprblknow = 0;
+
+	do {
+		ret = btmrvl_sdio_poll_card_status(card,
+					    CARD_IO_READY | DN_LD_CARD_RDY);
+		if (ret < 0) {
+			BT_ERR("Helper download poll status timeout @ %d",
+				hlprblknow);
+			goto done;
+		}
+
+		/* Check if there is more data? */
+		if (hlprblknow >= helperlen)
+			break;
+
+		if (helperlen - hlprblknow < tx_len)
+			tx_len = helperlen - hlprblknow;
+
+		/* Little-endian */
+		helperbuf[0] = ((tx_len & 0x000000ff) >> 0);
+		helperbuf[1] = ((tx_len & 0x0000ff00) >> 8);
+		helperbuf[2] = ((tx_len & 0x00ff0000) >> 16);
+		helperbuf[3] = ((tx_len & 0xff000000) >> 24);
+
+		memcpy(&helperbuf[SDIO_HEADER_LEN], &helper[hlprblknow],
+				tx_len);
+
+		/* Now send the data */
+		ret = sdio_writesb(card->func, card->ioport, helperbuf,
+				FIRMWARE_TRANSFER_NBLOCK * SDIO_BLOCK_SIZE);
+		if (ret < 0) {
+			BT_ERR("IO error during helper download @ %d",
+				hlprblknow);
+			goto done;
+		}
+
+		hlprblknow += tx_len;
+	} while (true);
+
+	BT_DBG("Transferring helper image EOF block");
+
+	memset(helperbuf, 0x0, SDIO_BLOCK_SIZE);
+
+	ret = sdio_writesb(card->func, card->ioport, helperbuf,
+							SDIO_BLOCK_SIZE);
+	if (ret < 0) {
+		BT_ERR("IO error in writing helper image EOF block");
+		goto done;
+	}
+
+	ret = 0;
+
+done:
+	kfree(tmphlprbuf);
+	if (fw_helper)
+		release_firmware(fw_helper);
+
+	return ret;
+}
+
+static int btmrvl_sdio_download_fw_w_helper(struct btmrvl_sdio_card *card)
+{
+	const struct firmware *fw_firmware = NULL;
+	const u8 *firmware = NULL;
+	int firmwarelen, tmpfwbufsz, ret;
+	unsigned int tries, offset;
+	u8 base0, base1;
+	void *tmpfwbuf = NULL;
+	u8 *fwbuf;
+	u16 len, blksz_dl = card->sd_blksz_fw_dl;
+	int txlen = 0, tx_blocks = 0, count = 0;
+
+	ret = request_firmware(&fw_firmware, card->firmware,
+							&card->func->dev);
+	if ((ret < 0) || !fw_firmware) {
+		BT_ERR("request_firmware(firmware) failed, error code = %d",
+									ret);
+		ret = -ENOENT;
+		goto done;
+	}
+
+	firmware = fw_firmware->data;
+	firmwarelen = fw_firmware->size;
+
+	BT_DBG("Downloading FW image (%d bytes)", firmwarelen);
+
+	tmpfwbufsz = ALIGN_SZ(BTM_UPLD_SIZE, BTSDIO_DMA_ALIGN);
+	tmpfwbuf = kzalloc(tmpfwbufsz, GFP_KERNEL);
+	if (!tmpfwbuf) {
+		BT_ERR("Unable to allocate buffer for firmware."
+		       " Terminating download");
+		ret = -ENOMEM;
+		goto done;
+	}
+
+	/* Ensure aligned firmware buffer */
+	fwbuf = (u8 *) ALIGN_ADDR(tmpfwbuf, BTSDIO_DMA_ALIGN);
+
+	/* Perform firmware data transfer */
+	offset = 0;
+	do {
+		ret = btmrvl_sdio_poll_card_status(card,
+					CARD_IO_READY | DN_LD_CARD_RDY);
+		if (ret < 0) {
+			BT_ERR("FW download with helper poll status"
+						" timeout @ %d", offset);
+			goto done;
+		}
+
+		/* Check if there is more data ? */
+		if (offset >= firmwarelen)
+			break;
+
+		for (tries = 0; tries < MAX_POLL_TRIES; tries++) {
+			base0 = sdio_readb(card->func,
+					card->reg->sq_read_base_addr_a0, &ret);
+			if (ret) {
+				BT_ERR("BASE0 register read failed:"
+					" base0 = 0x%04X(%d)."
+					" Terminating download",
+					base0, base0);
+				ret = -EIO;
+				goto done;
+			}
+			base1 = sdio_readb(card->func,
+					card->reg->sq_read_base_addr_a1, &ret);
+			if (ret) {
+				BT_ERR("BASE1 register read failed:"
+					" base1 = 0x%04X(%d)."
+					" Terminating download",
+					base1, base1);
+				ret = -EIO;
+				goto done;
+			}
+
+			len = (((u16) base1) << 8) | base0;
+			if (len)
+				break;
+
+			udelay(10);
+		}
+
+		if (!len)
+			break;
+		else if (len > BTM_UPLD_SIZE) {
+			BT_ERR("FW download failure @%d, invalid length %d",
+								offset, len);
+			ret = -EINVAL;
+			goto done;
+		}
+
+		txlen = len;
+
+		if (len & BIT(0)) {
+			count++;
+			if (count > MAX_WRITE_IOMEM_RETRY) {
+				BT_ERR("FW download failure @%d, "
+					"over max retry count", offset);
+				ret = -EIO;
+				goto done;
+			}
+			BT_ERR("FW CRC error indicated by the helper: "
+				"len = 0x%04X, txlen = %d", len, txlen);
+			len &= ~BIT(0);
+			/* Set txlen to 0 so as to resend from same offset */
+			txlen = 0;
+		} else {
+			count = 0;
+
+			/* Last block ? */
+			if (firmwarelen - offset < txlen)
+				txlen = firmwarelen - offset;
+
+			tx_blocks = (txlen + blksz_dl - 1) / blksz_dl;
+
+			memcpy(fwbuf, &firmware[offset], txlen);
+		}
+
+		ret = sdio_writesb(card->func, card->ioport, fwbuf,
+						tx_blocks * blksz_dl);
+
+		if (ret < 0) {
+			BT_ERR("FW download, writesb(%d) failed @%d",
+							count, offset);
+			sdio_writeb(card->func, HOST_CMD53_FIN,
+						card->reg->cfg, &ret);
+			if (ret)
+				BT_ERR("writeb failed (CFG)");
+		}
+
+		offset += txlen;
+	} while (true);
+
+	BT_DBG("FW download over, size %d bytes", offset);
+
+	ret = 0;
+
+done:
+	kfree(tmpfwbuf);
+
+	if (fw_firmware)
+		release_firmware(fw_firmware);
+
+	return ret;
+}
+
+static int btmrvl_sdio_card_to_host(struct btmrvl_private *priv)
+{
+	u16 buf_len = 0;
+	int ret, buf_block_len, blksz;
+	struct sk_buff *skb = NULL;
+	u32 type;
+	u8 *payload = NULL;
+	struct hci_dev *hdev = priv->btmrvl_dev.hcidev;
+	struct btmrvl_sdio_card *card = priv->btmrvl_dev.card;
+
+	if (!card || !card->func) {
+		BT_ERR("card or function is NULL!");
+		ret = -EINVAL;
+		goto exit;
+	}
+
+	/* Read the length of data to be transferred */
+	ret = btmrvl_sdio_read_rx_len(card, &buf_len);
+	if (ret < 0) {
+		BT_ERR("read rx_len failed");
+		ret = -EIO;
+		goto exit;
+	}
+
+	blksz = SDIO_BLOCK_SIZE;
+	buf_block_len = (buf_len + blksz - 1) / blksz;
+
+	if (buf_len <= SDIO_HEADER_LEN
+			|| (buf_block_len * blksz) > ALLOC_BUF_SIZE) {
+		BT_ERR("invalid packet length: %d", buf_len);
+		ret = -EINVAL;
+		goto exit;
+	}
+
+	/* Allocate buffer */
+	skb = bt_skb_alloc(buf_block_len * blksz + BTSDIO_DMA_ALIGN,
+								GFP_ATOMIC);
+	if (skb == NULL) {
+		BT_ERR("No free skb");
+		goto exit;
+	}
+
+	if ((unsigned long) skb->data & (BTSDIO_DMA_ALIGN - 1)) {
+		skb_put(skb, (unsigned long) skb->data &
+					(BTSDIO_DMA_ALIGN - 1));
+		skb_pull(skb, (unsigned long) skb->data &
+					(BTSDIO_DMA_ALIGN - 1));
+	}
+
+	payload = skb->data;
+
+	ret = sdio_readsb(card->func, payload, card->ioport,
+			  buf_block_len * blksz);
+	if (ret < 0) {
+		BT_ERR("readsb failed: %d", ret);
+		ret = -EIO;
+		goto exit;
+	}
+
+	/* This is SDIO specific header length: byte[2][1][0], type: byte[3]
+	 * (HCI_COMMAND = 1, ACL_DATA = 2, SCO_DATA = 3, 0xFE = Vendor)
+	 */
+
+	buf_len = payload[0];
+	buf_len |= (u16) payload[1] << 8;
+	type = payload[3];
+
+	switch (type) {
+	case HCI_ACLDATA_PKT:
+	case HCI_SCODATA_PKT:
+	case HCI_EVENT_PKT:
+		bt_cb(skb)->pkt_type = type;
+		skb->dev = (void *)hdev;
+		skb_put(skb, buf_len);
+		skb_pull(skb, SDIO_HEADER_LEN);
+
+		if (type == HCI_EVENT_PKT)
+			btmrvl_check_evtpkt(priv, skb);
+
+		hci_recv_frame(skb);
+		hdev->stat.byte_rx += buf_len;
+		break;
+
+	case MRVL_VENDOR_PKT:
+		bt_cb(skb)->pkt_type = HCI_VENDOR_PKT;
+		skb->dev = (void *)hdev;
+		skb_put(skb, buf_len);
+		skb_pull(skb, SDIO_HEADER_LEN);
+
+		if (btmrvl_process_event(priv, skb))
+			hci_recv_frame(skb);
+
+		hdev->stat.byte_rx += buf_len;
+		break;
+
+	default:
+		BT_ERR("Unknown packet type:%d", type);
+		print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, payload,
+						blksz * buf_block_len);
+
+		kfree_skb(skb);
+		skb = NULL;
+		break;
+	}
+
+exit:
+	if (ret) {
+		hdev->stat.err_rx++;
+		if (skb)
+			kfree_skb(skb);
+	}
+
+	return ret;
+}
+
+static int btmrvl_sdio_process_int_status(struct btmrvl_private *priv)
+{
+	ulong flags;
+	u8 ireg;
+	struct btmrvl_sdio_card *card = priv->btmrvl_dev.card;
+
+	spin_lock_irqsave(&priv->driver_lock, flags);
+	ireg = sdio_ireg;
+	sdio_ireg = 0;
+	spin_unlock_irqrestore(&priv->driver_lock, flags);
+
+	sdio_claim_host(card->func);
+	if (ireg & DN_LD_HOST_INT_STATUS) {
+		if (priv->btmrvl_dev.tx_dnld_rdy)
+			BT_DBG("tx_done already received: "
+				" int_status=0x%x", ireg);
+		else
+			priv->btmrvl_dev.tx_dnld_rdy = true;
+	}
+
+	if (ireg & UP_LD_HOST_INT_STATUS)
+		btmrvl_sdio_card_to_host(priv);
+
+	sdio_release_host(card->func);
+
+	return 0;
+}
+
+static void btmrvl_sdio_interrupt(struct sdio_func *func)
+{
+	struct btmrvl_private *priv;
+	struct btmrvl_sdio_card *card;
+	ulong flags;
+	u8 ireg = 0;
+	int ret;
+
+	card = sdio_get_drvdata(func);
+	if (!card || !card->priv) {
+		BT_ERR("sbi_interrupt(%p) card or priv is "
+				"NULL, card=%p\n", func, card);
+		return;
+	}
+
+	priv = card->priv;
+
+	ireg = sdio_readb(card->func, card->reg->host_intstatus, &ret);
+	if (ret) {
+		BT_ERR("sdio_readb: read int status register failed");
+		return;
+	}
+
+	if (ireg != 0) {
+		/*
+		 * DN_LD_HOST_INT_STATUS and/or UP_LD_HOST_INT_STATUS
+		 * Clear the interrupt status register and re-enable the
+		 * interrupt.
+		 */
+		BT_DBG("ireg = 0x%x", ireg);
+
+		sdio_writeb(card->func, ~(ireg) & (DN_LD_HOST_INT_STATUS |
+					UP_LD_HOST_INT_STATUS),
+				card->reg->host_intstatus, &ret);
+		if (ret) {
+			BT_ERR("sdio_writeb: clear int status register failed");
+			return;
+		}
+	}
+
+	spin_lock_irqsave(&priv->driver_lock, flags);
+	sdio_ireg |= ireg;
+	spin_unlock_irqrestore(&priv->driver_lock, flags);
+
+	btmrvl_interrupt(priv);
+}
+
+static int btmrvl_sdio_register_dev(struct btmrvl_sdio_card *card)
+{
+	struct sdio_func *func;
+	u8 reg;
+	int ret = 0;
+
+	if (!card || !card->func) {
+		BT_ERR("Error: card or function is NULL!");
+		ret = -EINVAL;
+		goto failed;
+	}
+
+	func = card->func;
+
+	sdio_claim_host(func);
+
+	ret = sdio_enable_func(func);
+	if (ret) {
+		BT_ERR("sdio_enable_func() failed: ret=%d", ret);
+		ret = -EIO;
+		goto release_host;
+	}
+
+	ret = sdio_claim_irq(func, btmrvl_sdio_interrupt);
+	if (ret) {
+		BT_ERR("sdio_claim_irq failed: ret=%d", ret);
+		ret = -EIO;
+		goto disable_func;
+	}
+
+	ret = sdio_set_block_size(card->func, SDIO_BLOCK_SIZE);
+	if (ret) {
+		BT_ERR("cannot set SDIO block size");
+		ret = -EIO;
+		goto release_irq;
+	}
+
+	reg = sdio_readb(func, card->reg->io_port_0, &ret);
+	if (ret < 0) {
+		ret = -EIO;
+		goto release_irq;
+	}
+
+	card->ioport = reg;
+
+	reg = sdio_readb(func, card->reg->io_port_1, &ret);
+	if (ret < 0) {
+		ret = -EIO;
+		goto release_irq;
+	}
+
+	card->ioport |= (reg << 8);
+
+	reg = sdio_readb(func, card->reg->io_port_2, &ret);
+	if (ret < 0) {
+		ret = -EIO;
+		goto release_irq;
+	}
+
+	card->ioport |= (reg << 16);
+
+	BT_DBG("SDIO FUNC%d IO port: 0x%x", func->num, card->ioport);
+
+	sdio_set_drvdata(func, card);
+
+	sdio_release_host(func);
+
+	return 0;
+
+release_irq:
+	sdio_release_irq(func);
+
+disable_func:
+	sdio_disable_func(func);
+
+release_host:
+	sdio_release_host(func);
+
+failed:
+	return ret;
+}
+
+static int btmrvl_sdio_unregister_dev(struct btmrvl_sdio_card *card)
+{
+	if (card && card->func) {
+		sdio_claim_host(card->func);
+		sdio_release_irq(card->func);
+		sdio_disable_func(card->func);
+		sdio_release_host(card->func);
+		sdio_set_drvdata(card->func, NULL);
+	}
+
+	return 0;
+}
+
+static int btmrvl_sdio_enable_host_int(struct btmrvl_sdio_card *card)
+{
+	int ret;
+
+	if (!card || !card->func)
+		return -EINVAL;
+
+	sdio_claim_host(card->func);
+
+	ret = btmrvl_sdio_enable_host_int_mask(card, HIM_ENABLE);
+
+	btmrvl_sdio_get_rx_unit(card);
+
+	sdio_release_host(card->func);
+
+	return ret;
+}
+
+static int btmrvl_sdio_disable_host_int(struct btmrvl_sdio_card *card)
+{
+	int ret;
+
+	if (!card || !card->func)
+		return -EINVAL;
+
+	sdio_claim_host(card->func);
+
+	ret = btmrvl_sdio_disable_host_int_mask(card, HIM_DISABLE);
+
+	sdio_release_host(card->func);
+
+	return ret;
+}
+
+static int btmrvl_sdio_host_to_card(struct btmrvl_private *priv,
+				u8 *payload, u16 nb)
+{
+	struct btmrvl_sdio_card *card = priv->btmrvl_dev.card;
+	int ret = 0;
+	int buf_block_len;
+	int blksz;
+	int i = 0;
+	u8 *buf = NULL;
+	void *tmpbuf = NULL;
+	int tmpbufsz;
+
+	if (!card || !card->func) {
+		BT_ERR("card or function is NULL!");
+		return -EINVAL;
+	}
+
+	buf = payload;
+	if ((unsigned long) payload & (BTSDIO_DMA_ALIGN - 1)) {
+		tmpbufsz = ALIGN_SZ(nb, BTSDIO_DMA_ALIGN);
+		tmpbuf = kzalloc(tmpbufsz, GFP_KERNEL);
+		if (!tmpbuf)
+			return -ENOMEM;
+		buf = (u8 *) ALIGN_ADDR(tmpbuf, BTSDIO_DMA_ALIGN);
+		memcpy(buf, payload, nb);
+	}
+
+	blksz = SDIO_BLOCK_SIZE;
+	buf_block_len = (nb + blksz - 1) / blksz;
+
+	sdio_claim_host(card->func);
+
+	do {
+		/* Transfer data to card */
+		ret = sdio_writesb(card->func, card->ioport, buf,
+				   buf_block_len * blksz);
+		if (ret < 0) {
+			i++;
+			BT_ERR("i=%d writesb failed: %d", i, ret);
+			print_hex_dump_bytes("", DUMP_PREFIX_OFFSET,
+						payload, nb);
+			ret = -EIO;
+			if (i > MAX_WRITE_IOMEM_RETRY)
+				goto exit;
+		}
+	} while (ret);
+
+	priv->btmrvl_dev.tx_dnld_rdy = false;
+
+exit:
+	sdio_release_host(card->func);
+	kfree(tmpbuf);
+
+	return ret;
+}
+
+static int btmrvl_sdio_download_fw(struct btmrvl_sdio_card *card)
+{
+	int ret = 0;
+	u8 fws0;
+	int pollnum = MAX_POLL_TRIES;
+
+	if (!card || !card->func) {
+		BT_ERR("card or function is NULL!");
+		return -EINVAL;
+	}
+	sdio_claim_host(card->func);
+
+	if (!btmrvl_sdio_verify_fw_download(card, 1)) {
+		BT_DBG("Firmware already downloaded!");
+		goto done;
+	}
+
+	/* Check if other function driver is downloading the firmware */
+	fws0 = sdio_readb(card->func, card->reg->card_fw_status0, &ret);
+	if (ret) {
+		BT_ERR("Failed to read FW downloading status!");
+		ret = -EIO;
+		goto done;
+	}
+	if (fws0) {
+		BT_DBG("BT not the winner (%#x). Skip FW downloading", fws0);
+
+		/* Give other function more time to download the firmware */
+		pollnum *= 10;
+	} else {
+		if (card->helper) {
+			ret = btmrvl_sdio_download_helper(card);
+			if (ret) {
+				BT_ERR("Failed to download helper!");
+				ret = -EIO;
+				goto done;
+			}
+		}
+
+		if (btmrvl_sdio_download_fw_w_helper(card)) {
+			BT_ERR("Failed to download firmware!");
+			ret = -EIO;
+			goto done;
+		}
+	}
+
+	if (btmrvl_sdio_verify_fw_download(card, pollnum)) {
+		BT_ERR("FW failed to be active in time!");
+		ret = -ETIMEDOUT;
+		goto done;
+	}
+
+done:
+	sdio_release_host(card->func);
+
+	return ret;
+}
+
+static int btmrvl_sdio_wakeup_fw(struct btmrvl_private *priv)
+{
+	struct btmrvl_sdio_card *card = priv->btmrvl_dev.card;
+	int ret = 0;
+
+	if (!card || !card->func) {
+		BT_ERR("card or function is NULL!");
+		return -EINVAL;
+	}
+
+	sdio_claim_host(card->func);
+
+	sdio_writeb(card->func, HOST_POWER_UP, card->reg->cfg, &ret);
+
+	sdio_release_host(card->func);
+
+	BT_DBG("wake up firmware");
+
+	return ret;
+}
+
+static int btmrvl_sdio_probe(struct sdio_func *func,
+					const struct sdio_device_id *id)
+{
+	int ret = 0;
+	struct btmrvl_private *priv = NULL;
+	struct btmrvl_sdio_card *card = NULL;
+
+	BT_INFO("vendor=0x%x, device=0x%x, class=%d, fn=%d",
+			id->vendor, id->device, id->class, func->num);
+
+	card = kzalloc(sizeof(*card), GFP_KERNEL);
+	if (!card) {
+		ret = -ENOMEM;
+		goto done;
+	}
+
+	card->func = func;
+
+	if (id->driver_data) {
+		struct btmrvl_sdio_device *data = (void *) id->driver_data;
+		card->helper = data->helper;
+		card->firmware = data->firmware;
+		card->reg = data->reg;
+		card->sd_blksz_fw_dl = data->sd_blksz_fw_dl;
+	}
+
+	if (btmrvl_sdio_register_dev(card) < 0) {
+		BT_ERR("Failed to register BT device!");
+		ret = -ENODEV;
+		goto free_card;
+	}
+
+	/* Disable the interrupts on the card */
+	btmrvl_sdio_disable_host_int(card);
+
+	if (btmrvl_sdio_download_fw(card)) {
+		BT_ERR("Downloading firmware failed!");
+		ret = -ENODEV;
+		goto unreg_dev;
+	}
+
+	msleep(100);
+
+	btmrvl_sdio_enable_host_int(card);
+
+	priv = btmrvl_add_card(card);
+	if (!priv) {
+		BT_ERR("Initializing card failed!");
+		ret = -ENODEV;
+		goto disable_host_int;
+	}
+
+	card->priv = priv;
+
+	/* Initialize the interface specific function pointers */
+	priv->hw_host_to_card = btmrvl_sdio_host_to_card;
+	priv->hw_wakeup_firmware = btmrvl_sdio_wakeup_fw;
+	priv->hw_process_int_status = btmrvl_sdio_process_int_status;
+
+	if (btmrvl_register_hdev(priv)) {
+		BT_ERR("Register hdev failed!");
+		ret = -ENODEV;
+		goto disable_host_int;
+	}
+
+	priv->btmrvl_dev.psmode = 1;
+	btmrvl_enable_ps(priv);
+
+	return 0;
+
+disable_host_int:
+	btmrvl_sdio_disable_host_int(card);
+unreg_dev:
+	btmrvl_sdio_unregister_dev(card);
+free_card:
+	kfree(card);
+done:
+	return ret;
+}
+
+static void btmrvl_sdio_remove(struct sdio_func *func)
+{
+	struct btmrvl_sdio_card *card;
+
+	if (func) {
+		card = sdio_get_drvdata(func);
+		if (card) {
+			/* Send SHUTDOWN command & disable interrupt
+			 * if user removes the module.
+			 */
+			if (user_rmmod) {
+				btmrvl_send_module_cfg_cmd(card->priv,
+							MODULE_SHUTDOWN_REQ);
+				btmrvl_sdio_disable_host_int(card);
+			}
+			BT_DBG("unregester dev");
+			btmrvl_sdio_unregister_dev(card);
+			btmrvl_remove_card(card->priv);
+			kfree(card);
+		}
+	}
+}
+
+static struct sdio_driver bt_mrvl_sdio = {
+	.name		= "btmrvl_sdio",
+	.id_table	= btmrvl_sdio_ids,
+	.probe		= btmrvl_sdio_probe,
+	.remove		= btmrvl_sdio_remove,
+};
+
+static int __init btmrvl_sdio_init_module(void)
+{
+	if (sdio_register_driver(&bt_mrvl_sdio) != 0) {
+		BT_ERR("SDIO Driver Registration Failed");
+		return -ENODEV;
+	}
+
+	/* Clear the flag in case user removes the card. */
+	user_rmmod = 0;
+
+	return 0;
+}
+
+static void __exit btmrvl_sdio_exit_module(void)
+{
+	/* Set the flag as user is removing this module. */
+	user_rmmod = 1;
+
+	sdio_unregister_driver(&bt_mrvl_sdio);
+}
+
+module_init(btmrvl_sdio_init_module);
+module_exit(btmrvl_sdio_exit_module);
+
+MODULE_AUTHOR("Marvell International Ltd.");
+MODULE_DESCRIPTION("Marvell BT-over-SDIO driver ver " VERSION);
+MODULE_VERSION(VERSION);
+MODULE_LICENSE("GPL v2");
+MODULE_FIRMWARE("sd8688_helper.bin");
+MODULE_FIRMWARE("sd8688.bin");
+MODULE_FIRMWARE("mrvl/sd8787_uapsta.bin");
+MODULE_FIRMWARE("mrvl/sd8797_uapsta.bin");
diff --git a/ap/os/linux/linux-3.4.x/drivers/bluetooth/btmrvl_sdio.h b/ap/os/linux/linux-3.4.x/drivers/bluetooth/btmrvl_sdio.h
new file mode 100644
index 0000000..43d35a6
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/bluetooth/btmrvl_sdio.h
@@ -0,0 +1,112 @@
+/**
+ * Marvell BT-over-SDIO driver: SDIO interface related definitions
+ *
+ * Copyright (C) 2009, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License").  You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available by writing to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the
+ * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+ *
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
+ * this warranty disclaimer.
+ *
+ **/
+
+#define SDIO_HEADER_LEN			4
+
+/* SD block size can not bigger than 64 due to buf size limit in firmware */
+/* define SD block size for data Tx/Rx */
+#define SDIO_BLOCK_SIZE			64
+
+/* Number of blocks for firmware transfer */
+#define FIRMWARE_TRANSFER_NBLOCK	2
+
+/* This is for firmware specific length */
+#define FW_EXTRA_LEN			36
+
+#define MRVDRV_SIZE_OF_CMD_BUFFER       (2 * 1024)
+
+#define MRVDRV_BT_RX_PACKET_BUFFER_SIZE \
+	(HCI_MAX_FRAME_SIZE + FW_EXTRA_LEN)
+
+#define ALLOC_BUF_SIZE	(((max_t (int, MRVDRV_BT_RX_PACKET_BUFFER_SIZE, \
+			MRVDRV_SIZE_OF_CMD_BUFFER) + SDIO_HEADER_LEN \
+			+ SDIO_BLOCK_SIZE - 1) / SDIO_BLOCK_SIZE) \
+			* SDIO_BLOCK_SIZE)
+
+/* The number of times to try when polling for status */
+#define MAX_POLL_TRIES			100
+
+/* Max retry number of CMD53 write */
+#define MAX_WRITE_IOMEM_RETRY		2
+
+/* register bitmasks */
+#define HOST_POWER_UP				BIT(1)
+#define HOST_CMD53_FIN				BIT(2)
+
+#define HIM_DISABLE				0xff
+#define HIM_ENABLE				(BIT(0) | BIT(1))
+
+#define UP_LD_HOST_INT_STATUS			BIT(0)
+#define DN_LD_HOST_INT_STATUS			BIT(1)
+
+#define DN_LD_CARD_RDY				BIT(0)
+#define CARD_IO_READY				BIT(3)
+
+#define FIRMWARE_READY				0xfedc
+
+
+struct btmrvl_sdio_card_reg {
+	u8 cfg;
+	u8 host_int_mask;
+	u8 host_intstatus;
+	u8 card_status;
+	u8 sq_read_base_addr_a0;
+	u8 sq_read_base_addr_a1;
+	u8 card_revision;
+	u8 card_fw_status0;
+	u8 card_fw_status1;
+	u8 card_rx_len;
+	u8 card_rx_unit;
+	u8 io_port_0;
+	u8 io_port_1;
+	u8 io_port_2;
+};
+
+struct btmrvl_sdio_card {
+	struct sdio_func *func;
+	u32 ioport;
+	const char *helper;
+	const char *firmware;
+	const struct btmrvl_sdio_card_reg *reg;
+	u16 sd_blksz_fw_dl;
+	u8 rx_unit;
+	struct btmrvl_private *priv;
+};
+
+struct btmrvl_sdio_device {
+	const char *helper;
+	const char *firmware;
+	const struct btmrvl_sdio_card_reg *reg;
+	u16 sd_blksz_fw_dl;
+};
+
+
+/* Platform specific DMA alignment */
+#define BTSDIO_DMA_ALIGN		8
+
+/* Macros for Data Alignment : size */
+#define ALIGN_SZ(p, a)	\
+	(((p) + ((a) - 1)) & ~((a) - 1))
+
+/* Macros for Data Alignment : address */
+#define ALIGN_ADDR(p, a)	\
+	((((unsigned long)(p)) + (((unsigned long)(a)) - 1)) & \
+					~(((unsigned long)(a)) - 1))
diff --git a/ap/os/linux/linux-3.4.x/drivers/bluetooth/btsdio.c b/ap/os/linux/linux-3.4.x/drivers/bluetooth/btsdio.c
new file mode 100644
index 0000000..e10ea03
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/bluetooth/btsdio.c
@@ -0,0 +1,397 @@
+/*
+ *
+ *  Generic Bluetooth SDIO driver
+ *
+ *  Copyright (C) 2007  Cambridge Silicon Radio Ltd.
+ *  Copyright (C) 2007  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/mmc/sdio_ids.h>
+#include <linux/mmc/sdio_func.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#define VERSION "0.1"
+
+static const struct sdio_device_id btsdio_table[] = {
+	/* Generic Bluetooth Type-A SDIO device */
+	{ SDIO_DEVICE_CLASS(SDIO_CLASS_BT_A) },
+
+	/* Generic Bluetooth Type-B SDIO device */
+	{ SDIO_DEVICE_CLASS(SDIO_CLASS_BT_B) },
+
+	/* Generic Bluetooth AMP controller */
+	{ SDIO_DEVICE_CLASS(SDIO_CLASS_BT_AMP) },
+
+	{ }	/* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(sdio, btsdio_table);
+
+struct btsdio_data {
+	struct hci_dev   *hdev;
+	struct sdio_func *func;
+
+	struct work_struct work;
+
+	struct sk_buff_head txq;
+};
+
+#define REG_RDAT     0x00	/* Receiver Data */
+#define REG_TDAT     0x00	/* Transmitter Data */
+#define REG_PC_RRT   0x10	/* Read Packet Control */
+#define REG_PC_WRT   0x11	/* Write Packet Control */
+#define REG_RTC_STAT 0x12	/* Retry Control Status */
+#define REG_RTC_SET  0x12	/* Retry Control Set */
+#define REG_INTRD    0x13	/* Interrupt Indication */
+#define REG_CL_INTRD 0x13	/* Interrupt Clear */
+#define REG_EN_INTRD 0x14	/* Interrupt Enable */
+#define REG_MD_STAT  0x20	/* Bluetooth Mode Status */
+
+static int btsdio_tx_packet(struct btsdio_data *data, struct sk_buff *skb)
+{
+	int err;
+
+	BT_DBG("%s", data->hdev->name);
+
+	/* Prepend Type-A header */
+	skb_push(skb, 4);
+	skb->data[0] = (skb->len & 0x0000ff);
+	skb->data[1] = (skb->len & 0x00ff00) >> 8;
+	skb->data[2] = (skb->len & 0xff0000) >> 16;
+	skb->data[3] = bt_cb(skb)->pkt_type;
+
+	err = sdio_writesb(data->func, REG_TDAT, skb->data, skb->len);
+	if (err < 0) {
+		skb_pull(skb, 4);
+		sdio_writeb(data->func, 0x01, REG_PC_WRT, NULL);
+		return err;
+	}
+
+	data->hdev->stat.byte_tx += skb->len;
+
+	kfree_skb(skb);
+
+	return 0;
+}
+
+static void btsdio_work(struct work_struct *work)
+{
+	struct btsdio_data *data = container_of(work, struct btsdio_data, work);
+	struct sk_buff *skb;
+	int err;
+
+	BT_DBG("%s", data->hdev->name);
+
+	sdio_claim_host(data->func);
+
+	while ((skb = skb_dequeue(&data->txq))) {
+		err = btsdio_tx_packet(data, skb);
+		if (err < 0) {
+			data->hdev->stat.err_tx++;
+			skb_queue_head(&data->txq, skb);
+			break;
+		}
+	}
+
+	sdio_release_host(data->func);
+}
+
+static int btsdio_rx_packet(struct btsdio_data *data)
+{
+	u8 hdr[4] __attribute__ ((aligned(4)));
+	struct sk_buff *skb;
+	int err, len;
+
+	BT_DBG("%s", data->hdev->name);
+
+	err = sdio_readsb(data->func, hdr, REG_RDAT, 4);
+	if (err < 0)
+		return err;
+
+	len = hdr[0] | (hdr[1] << 8) | (hdr[2] << 16);
+	if (len < 4 || len > 65543)
+		return -EILSEQ;
+
+	skb = bt_skb_alloc(len - 4, GFP_KERNEL);
+	if (!skb) {
+		/* Out of memory. Prepare a read retry and just
+		 * return with the expectation that the next time
+		 * we're called we'll have more memory. */
+		return -ENOMEM;
+	}
+
+	skb_put(skb, len - 4);
+
+	err = sdio_readsb(data->func, skb->data, REG_RDAT, len - 4);
+	if (err < 0) {
+		kfree_skb(skb);
+		return err;
+	}
+
+	data->hdev->stat.byte_rx += len;
+
+	skb->dev = (void *) data->hdev;
+	bt_cb(skb)->pkt_type = hdr[3];
+
+	err = hci_recv_frame(skb);
+	if (err < 0)
+		return err;
+
+	sdio_writeb(data->func, 0x00, REG_PC_RRT, NULL);
+
+	return 0;
+}
+
+static void btsdio_interrupt(struct sdio_func *func)
+{
+	struct btsdio_data *data = sdio_get_drvdata(func);
+	int intrd;
+
+	BT_DBG("%s", data->hdev->name);
+
+	intrd = sdio_readb(func, REG_INTRD, NULL);
+	if (intrd & 0x01) {
+		sdio_writeb(func, 0x01, REG_CL_INTRD, NULL);
+
+		if (btsdio_rx_packet(data) < 0) {
+			data->hdev->stat.err_rx++;
+			sdio_writeb(data->func, 0x01, REG_PC_RRT, NULL);
+		}
+	}
+}
+
+static int btsdio_open(struct hci_dev *hdev)
+{
+	struct btsdio_data *data = hci_get_drvdata(hdev);
+	int err;
+
+	BT_DBG("%s", hdev->name);
+
+	if (test_and_set_bit(HCI_RUNNING, &hdev->flags))
+		return 0;
+
+	sdio_claim_host(data->func);
+
+	err = sdio_enable_func(data->func);
+	if (err < 0) {
+		clear_bit(HCI_RUNNING, &hdev->flags);
+		goto release;
+	}
+
+	err = sdio_claim_irq(data->func, btsdio_interrupt);
+	if (err < 0) {
+		sdio_disable_func(data->func);
+		clear_bit(HCI_RUNNING, &hdev->flags);
+		goto release;
+	}
+
+	if (data->func->class == SDIO_CLASS_BT_B)
+		sdio_writeb(data->func, 0x00, REG_MD_STAT, NULL);
+
+	sdio_writeb(data->func, 0x01, REG_EN_INTRD, NULL);
+
+release:
+	sdio_release_host(data->func);
+
+	return err;
+}
+
+static int btsdio_close(struct hci_dev *hdev)
+{
+	struct btsdio_data *data = hci_get_drvdata(hdev);
+
+	BT_DBG("%s", hdev->name);
+
+	if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
+		return 0;
+
+	sdio_claim_host(data->func);
+
+	sdio_writeb(data->func, 0x00, REG_EN_INTRD, NULL);
+
+	sdio_release_irq(data->func);
+	sdio_disable_func(data->func);
+
+	sdio_release_host(data->func);
+
+	return 0;
+}
+
+static int btsdio_flush(struct hci_dev *hdev)
+{
+	struct btsdio_data *data = hci_get_drvdata(hdev);
+
+	BT_DBG("%s", hdev->name);
+
+	skb_queue_purge(&data->txq);
+
+	return 0;
+}
+
+static int btsdio_send_frame(struct sk_buff *skb)
+{
+	struct hci_dev *hdev = (struct hci_dev *) skb->dev;
+	struct btsdio_data *data = hci_get_drvdata(hdev);
+
+	BT_DBG("%s", hdev->name);
+
+	if (!test_bit(HCI_RUNNING, &hdev->flags))
+		return -EBUSY;
+
+	switch (bt_cb(skb)->pkt_type) {
+	case HCI_COMMAND_PKT:
+		hdev->stat.cmd_tx++;
+		break;
+
+	case HCI_ACLDATA_PKT:
+		hdev->stat.acl_tx++;
+		break;
+
+	case HCI_SCODATA_PKT:
+		hdev->stat.sco_tx++;
+		break;
+
+	default:
+		return -EILSEQ;
+	}
+
+	skb_queue_tail(&data->txq, skb);
+
+	schedule_work(&data->work);
+
+	return 0;
+}
+
+static int btsdio_probe(struct sdio_func *func,
+				const struct sdio_device_id *id)
+{
+	struct btsdio_data *data;
+	struct hci_dev *hdev;
+	struct sdio_func_tuple *tuple = func->tuples;
+	int err;
+
+	BT_DBG("func %p id %p class 0x%04x", func, id, func->class);
+
+	while (tuple) {
+		BT_DBG("code 0x%x size %d", tuple->code, tuple->size);
+		tuple = tuple->next;
+	}
+
+	data = kzalloc(sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	data->func = func;
+
+	INIT_WORK(&data->work, btsdio_work);
+
+	skb_queue_head_init(&data->txq);
+
+	hdev = hci_alloc_dev();
+	if (!hdev) {
+		kfree(data);
+		return -ENOMEM;
+	}
+
+	hdev->bus = HCI_SDIO;
+	hci_set_drvdata(hdev, data);
+
+	if (id->class == SDIO_CLASS_BT_AMP)
+		hdev->dev_type = HCI_AMP;
+	else
+		hdev->dev_type = HCI_BREDR;
+
+	data->hdev = hdev;
+
+	SET_HCIDEV_DEV(hdev, &func->dev);
+
+	hdev->open     = btsdio_open;
+	hdev->close    = btsdio_close;
+	hdev->flush    = btsdio_flush;
+	hdev->send     = btsdio_send_frame;
+
+	err = hci_register_dev(hdev);
+	if (err < 0) {
+		hci_free_dev(hdev);
+		kfree(data);
+		return err;
+	}
+
+	sdio_set_drvdata(func, data);
+
+	return 0;
+}
+
+static void btsdio_remove(struct sdio_func *func)
+{
+	struct btsdio_data *data = sdio_get_drvdata(func);
+	struct hci_dev *hdev;
+
+	BT_DBG("func %p", func);
+
+	if (!data)
+		return;
+
+	hdev = data->hdev;
+
+	sdio_set_drvdata(func, NULL);
+
+	hci_unregister_dev(hdev);
+
+	hci_free_dev(hdev);
+	kfree(data);
+}
+
+static struct sdio_driver btsdio_driver = {
+	.name		= "btsdio",
+	.probe		= btsdio_probe,
+	.remove		= btsdio_remove,
+	.id_table	= btsdio_table,
+};
+
+static int __init btsdio_init(void)
+{
+	BT_INFO("Generic Bluetooth SDIO driver ver %s", VERSION);
+
+	return sdio_register_driver(&btsdio_driver);
+}
+
+static void __exit btsdio_exit(void)
+{
+	sdio_unregister_driver(&btsdio_driver);
+}
+
+module_init(btsdio_init);
+module_exit(btsdio_exit);
+
+MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
+MODULE_DESCRIPTION("Generic Bluetooth SDIO driver ver " VERSION);
+MODULE_VERSION(VERSION);
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/bluetooth/btuart_cs.c b/ap/os/linux/linux-3.4.x/drivers/bluetooth/btuart_cs.c
new file mode 100644
index 0000000..c4fc2f3
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/bluetooth/btuart_cs.c
@@ -0,0 +1,707 @@
+/*
+ *
+ *  Driver for Bluetooth PCMCIA cards with HCI UART interface
+ *
+ *  Copyright (C) 2001-2002  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 version 2 as
+ *  published by the Free Software Foundation;
+ *
+ *  Software distributed under the License is distributed on an "AS
+ *  IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ *  implied. See the License for the specific language governing
+ *  rights and limitations under the License.
+ *
+ *  The initial developer of the original code is David A. Hinds
+ *  <dahinds@users.sourceforge.net>.  Portions created by David A. Hinds
+ *  are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
+ *
+ */
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/spinlock.h>
+#include <linux/moduleparam.h>
+
+#include <linux/skbuff.h>
+#include <linux/string.h>
+#include <linux/serial.h>
+#include <linux/serial_reg.h>
+#include <linux/bitops.h>
+#include <asm/io.h>
+
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ciscode.h>
+#include <pcmcia/ds.h>
+#include <pcmcia/cisreg.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+
+
+/* ======================== Module parameters ======================== */
+
+
+MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
+MODULE_DESCRIPTION("Bluetooth driver for Bluetooth PCMCIA cards with HCI UART interface");
+MODULE_LICENSE("GPL");
+
+
+
+/* ======================== Local structures ======================== */
+
+
+typedef struct btuart_info_t {
+	struct pcmcia_device *p_dev;
+
+	struct hci_dev *hdev;
+
+	spinlock_t lock;	/* For serializing operations */
+
+	struct sk_buff_head txq;
+	unsigned long tx_state;
+
+	unsigned long rx_state;
+	unsigned long rx_count;
+	struct sk_buff *rx_skb;
+} btuart_info_t;
+
+
+static int btuart_config(struct pcmcia_device *link);
+static void btuart_release(struct pcmcia_device *link);
+
+static void btuart_detach(struct pcmcia_device *p_dev);
+
+
+/* Maximum baud rate */
+#define SPEED_MAX  115200
+
+/* Default baud rate: 57600, 115200, 230400 or 460800 */
+#define DEFAULT_BAUD_RATE  115200
+
+
+/* Transmit states  */
+#define XMIT_SENDING	1
+#define XMIT_WAKEUP	2
+#define XMIT_WAITING	8
+
+/* Receiver states */
+#define RECV_WAIT_PACKET_TYPE	0
+#define RECV_WAIT_EVENT_HEADER	1
+#define RECV_WAIT_ACL_HEADER	2
+#define RECV_WAIT_SCO_HEADER	3
+#define RECV_WAIT_DATA		4
+
+
+
+/* ======================== Interrupt handling ======================== */
+
+
+static int btuart_write(unsigned int iobase, int fifo_size, __u8 *buf, int len)
+{
+	int actual = 0;
+
+	/* Tx FIFO should be empty */
+	if (!(inb(iobase + UART_LSR) & UART_LSR_THRE))
+		return 0;
+
+	/* Fill FIFO with current frame */
+	while ((fifo_size-- > 0) && (actual < len)) {
+		/* Transmit next byte */
+		outb(buf[actual], iobase + UART_TX);
+		actual++;
+	}
+
+	return actual;
+}
+
+
+static void btuart_write_wakeup(btuart_info_t *info)
+{
+	if (!info) {
+		BT_ERR("Unknown device");
+		return;
+	}
+
+	if (test_and_set_bit(XMIT_SENDING, &(info->tx_state))) {
+		set_bit(XMIT_WAKEUP, &(info->tx_state));
+		return;
+	}
+
+	do {
+		register unsigned int iobase = info->p_dev->resource[0]->start;
+		register struct sk_buff *skb;
+		register int len;
+
+		clear_bit(XMIT_WAKEUP, &(info->tx_state));
+
+		if (!pcmcia_dev_present(info->p_dev))
+			return;
+
+		if (!(skb = skb_dequeue(&(info->txq))))
+			break;
+
+		/* Send frame */
+		len = btuart_write(iobase, 16, skb->data, skb->len);
+		set_bit(XMIT_WAKEUP, &(info->tx_state));
+
+		if (len == skb->len) {
+			kfree_skb(skb);
+		} else {
+			skb_pull(skb, len);
+			skb_queue_head(&(info->txq), skb);
+		}
+
+		info->hdev->stat.byte_tx += len;
+
+	} while (test_bit(XMIT_WAKEUP, &(info->tx_state)));
+
+	clear_bit(XMIT_SENDING, &(info->tx_state));
+}
+
+
+static void btuart_receive(btuart_info_t *info)
+{
+	unsigned int iobase;
+	int boguscount = 0;
+
+	if (!info) {
+		BT_ERR("Unknown device");
+		return;
+	}
+
+	iobase = info->p_dev->resource[0]->start;
+
+	do {
+		info->hdev->stat.byte_rx++;
+
+		/* Allocate packet */
+		if (info->rx_skb == NULL) {
+			info->rx_state = RECV_WAIT_PACKET_TYPE;
+			info->rx_count = 0;
+			if (!(info->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC))) {
+				BT_ERR("Can't allocate mem for new packet");
+				return;
+			}
+		}
+
+		if (info->rx_state == RECV_WAIT_PACKET_TYPE) {
+
+			info->rx_skb->dev = (void *) info->hdev;
+			bt_cb(info->rx_skb)->pkt_type = inb(iobase + UART_RX);
+
+			switch (bt_cb(info->rx_skb)->pkt_type) {
+
+			case HCI_EVENT_PKT:
+				info->rx_state = RECV_WAIT_EVENT_HEADER;
+				info->rx_count = HCI_EVENT_HDR_SIZE;
+				break;
+
+			case HCI_ACLDATA_PKT:
+				info->rx_state = RECV_WAIT_ACL_HEADER;
+				info->rx_count = HCI_ACL_HDR_SIZE;
+				break;
+
+			case HCI_SCODATA_PKT:
+				info->rx_state = RECV_WAIT_SCO_HEADER;
+				info->rx_count = HCI_SCO_HDR_SIZE;
+				break;
+
+			default:
+				/* Unknown packet */
+				BT_ERR("Unknown HCI packet with type 0x%02x received", bt_cb(info->rx_skb)->pkt_type);
+				info->hdev->stat.err_rx++;
+				clear_bit(HCI_RUNNING, &(info->hdev->flags));
+
+				kfree_skb(info->rx_skb);
+				info->rx_skb = NULL;
+				break;
+
+			}
+
+		} else {
+
+			*skb_put(info->rx_skb, 1) = inb(iobase + UART_RX);
+			info->rx_count--;
+
+			if (info->rx_count == 0) {
+
+				int dlen;
+				struct hci_event_hdr *eh;
+				struct hci_acl_hdr *ah;
+				struct hci_sco_hdr *sh;
+
+
+				switch (info->rx_state) {
+
+				case RECV_WAIT_EVENT_HEADER:
+					eh = hci_event_hdr(info->rx_skb);
+					info->rx_state = RECV_WAIT_DATA;
+					info->rx_count = eh->plen;
+					break;
+
+				case RECV_WAIT_ACL_HEADER:
+					ah = hci_acl_hdr(info->rx_skb);
+					dlen = __le16_to_cpu(ah->dlen);
+					info->rx_state = RECV_WAIT_DATA;
+					info->rx_count = dlen;
+					break;
+
+				case RECV_WAIT_SCO_HEADER:
+					sh = hci_sco_hdr(info->rx_skb);
+					info->rx_state = RECV_WAIT_DATA;
+					info->rx_count = sh->dlen;
+					break;
+
+				case RECV_WAIT_DATA:
+					hci_recv_frame(info->rx_skb);
+					info->rx_skb = NULL;
+					break;
+
+				}
+
+			}
+
+		}
+
+		/* Make sure we don't stay here too long */
+		if (boguscount++ > 16)
+			break;
+
+	} while (inb(iobase + UART_LSR) & UART_LSR_DR);
+}
+
+
+static irqreturn_t btuart_interrupt(int irq, void *dev_inst)
+{
+	btuart_info_t *info = dev_inst;
+	unsigned int iobase;
+	int boguscount = 0;
+	int iir, lsr;
+	irqreturn_t r = IRQ_NONE;
+
+	if (!info || !info->hdev)
+		/* our irq handler is shared */
+		return IRQ_NONE;
+
+	iobase = info->p_dev->resource[0]->start;
+
+	spin_lock(&(info->lock));
+
+	iir = inb(iobase + UART_IIR) & UART_IIR_ID;
+	while (iir) {
+		r = IRQ_HANDLED;
+
+		/* Clear interrupt */
+		lsr = inb(iobase + UART_LSR);
+
+		switch (iir) {
+		case UART_IIR_RLSI:
+			BT_ERR("RLSI");
+			break;
+		case UART_IIR_RDI:
+			/* Receive interrupt */
+			btuart_receive(info);
+			break;
+		case UART_IIR_THRI:
+			if (lsr & UART_LSR_THRE) {
+				/* Transmitter ready for data */
+				btuart_write_wakeup(info);
+			}
+			break;
+		default:
+			BT_ERR("Unhandled IIR=%#x", iir);
+			break;
+		}
+
+		/* Make sure we don't stay here too long */
+		if (boguscount++ > 100)
+			break;
+
+		iir = inb(iobase + UART_IIR) & UART_IIR_ID;
+
+	}
+
+	spin_unlock(&(info->lock));
+
+	return r;
+}
+
+
+static void btuart_change_speed(btuart_info_t *info, unsigned int speed)
+{
+	unsigned long flags;
+	unsigned int iobase;
+	int fcr;		/* FIFO control reg */
+	int lcr;		/* Line control reg */
+	int divisor;
+
+	if (!info) {
+		BT_ERR("Unknown device");
+		return;
+	}
+
+	iobase = info->p_dev->resource[0]->start;
+
+	spin_lock_irqsave(&(info->lock), flags);
+
+	/* Turn off interrupts */
+	outb(0, iobase + UART_IER);
+
+	divisor = SPEED_MAX / speed;
+
+	fcr = UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT;
+
+	/* 
+	 * Use trigger level 1 to avoid 3 ms. timeout delay at 9600 bps, and
+	 * almost 1,7 ms at 19200 bps. At speeds above that we can just forget
+	 * about this timeout since it will always be fast enough. 
+	 */
+
+	if (speed < 38400)
+		fcr |= UART_FCR_TRIGGER_1;
+	else
+		fcr |= UART_FCR_TRIGGER_14;
+
+	/* Bluetooth cards use 8N1 */
+	lcr = UART_LCR_WLEN8;
+
+	outb(UART_LCR_DLAB | lcr, iobase + UART_LCR);	/* Set DLAB */
+	outb(divisor & 0xff, iobase + UART_DLL);	/* Set speed */
+	outb(divisor >> 8, iobase + UART_DLM);
+	outb(lcr, iobase + UART_LCR);	/* Set 8N1  */
+	outb(fcr, iobase + UART_FCR);	/* Enable FIFO's */
+
+	/* Turn on interrupts */
+	outb(UART_IER_RLSI | UART_IER_RDI | UART_IER_THRI, iobase + UART_IER);
+
+	spin_unlock_irqrestore(&(info->lock), flags);
+}
+
+
+
+/* ======================== HCI interface ======================== */
+
+
+static int btuart_hci_flush(struct hci_dev *hdev)
+{
+	btuart_info_t *info = hci_get_drvdata(hdev);
+
+	/* Drop TX queue */
+	skb_queue_purge(&(info->txq));
+
+	return 0;
+}
+
+
+static int btuart_hci_open(struct hci_dev *hdev)
+{
+	set_bit(HCI_RUNNING, &(hdev->flags));
+
+	return 0;
+}
+
+
+static int btuart_hci_close(struct hci_dev *hdev)
+{
+	if (!test_and_clear_bit(HCI_RUNNING, &(hdev->flags)))
+		return 0;
+
+	btuart_hci_flush(hdev);
+
+	return 0;
+}
+
+
+static int btuart_hci_send_frame(struct sk_buff *skb)
+{
+	btuart_info_t *info;
+	struct hci_dev *hdev = (struct hci_dev *)(skb->dev);
+
+	if (!hdev) {
+		BT_ERR("Frame for unknown HCI device (hdev=NULL)");
+		return -ENODEV;
+	}
+
+	info = hci_get_drvdata(hdev);
+
+	switch (bt_cb(skb)->pkt_type) {
+	case HCI_COMMAND_PKT:
+		hdev->stat.cmd_tx++;
+		break;
+	case HCI_ACLDATA_PKT:
+		hdev->stat.acl_tx++;
+		break;
+	case HCI_SCODATA_PKT:
+		hdev->stat.sco_tx++;
+		break;
+	};
+
+	/* Prepend skb with frame type */
+	memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
+	skb_queue_tail(&(info->txq), skb);
+
+	btuart_write_wakeup(info);
+
+	return 0;
+}
+
+
+static int btuart_hci_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg)
+{
+	return -ENOIOCTLCMD;
+}
+
+
+
+/* ======================== Card services HCI interaction ======================== */
+
+
+static int btuart_open(btuart_info_t *info)
+{
+	unsigned long flags;
+	unsigned int iobase = info->p_dev->resource[0]->start;
+	struct hci_dev *hdev;
+
+	spin_lock_init(&(info->lock));
+
+	skb_queue_head_init(&(info->txq));
+
+	info->rx_state = RECV_WAIT_PACKET_TYPE;
+	info->rx_count = 0;
+	info->rx_skb = NULL;
+
+	/* Initialize HCI device */
+	hdev = hci_alloc_dev();
+	if (!hdev) {
+		BT_ERR("Can't allocate HCI device");
+		return -ENOMEM;
+	}
+
+	info->hdev = hdev;
+
+	hdev->bus = HCI_PCCARD;
+	hci_set_drvdata(hdev, info);
+	SET_HCIDEV_DEV(hdev, &info->p_dev->dev);
+
+	hdev->open     = btuart_hci_open;
+	hdev->close    = btuart_hci_close;
+	hdev->flush    = btuart_hci_flush;
+	hdev->send     = btuart_hci_send_frame;
+	hdev->ioctl    = btuart_hci_ioctl;
+
+	spin_lock_irqsave(&(info->lock), flags);
+
+	/* Reset UART */
+	outb(0, iobase + UART_MCR);
+
+	/* Turn off interrupts */
+	outb(0, iobase + UART_IER);
+
+	/* Initialize UART */
+	outb(UART_LCR_WLEN8, iobase + UART_LCR);	/* Reset DLAB */
+	outb((UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2), iobase + UART_MCR);
+
+	/* Turn on interrupts */
+	// outb(UART_IER_RLSI | UART_IER_RDI | UART_IER_THRI, iobase + UART_IER);
+
+	spin_unlock_irqrestore(&(info->lock), flags);
+
+	btuart_change_speed(info, DEFAULT_BAUD_RATE);
+
+	/* Timeout before it is safe to send the first HCI packet */
+	msleep(1000);
+
+	/* Register HCI device */
+	if (hci_register_dev(hdev) < 0) {
+		BT_ERR("Can't register HCI device");
+		info->hdev = NULL;
+		hci_free_dev(hdev);
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+
+static int btuart_close(btuart_info_t *info)
+{
+	unsigned long flags;
+	unsigned int iobase = info->p_dev->resource[0]->start;
+	struct hci_dev *hdev = info->hdev;
+
+	if (!hdev)
+		return -ENODEV;
+
+	btuart_hci_close(hdev);
+
+	spin_lock_irqsave(&(info->lock), flags);
+
+	/* Reset UART */
+	outb(0, iobase + UART_MCR);
+
+	/* Turn off interrupts */
+	outb(0, iobase + UART_IER);
+
+	spin_unlock_irqrestore(&(info->lock), flags);
+
+	hci_unregister_dev(hdev);
+	hci_free_dev(hdev);
+
+	return 0;
+}
+
+static int btuart_probe(struct pcmcia_device *link)
+{
+	btuart_info_t *info;
+
+	/* Create new info device */
+	info = kzalloc(sizeof(*info), GFP_KERNEL);
+	if (!info)
+		return -ENOMEM;
+
+	info->p_dev = link;
+	link->priv = info;
+
+	link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_VPP |
+		CONF_AUTO_SET_IO;
+
+	return btuart_config(link);
+}
+
+
+static void btuart_detach(struct pcmcia_device *link)
+{
+	btuart_info_t *info = link->priv;
+
+	btuart_release(link);
+	kfree(info);
+}
+
+static int btuart_check_config(struct pcmcia_device *p_dev, void *priv_data)
+{
+	int *try = priv_data;
+
+	if (try == 0)
+		p_dev->io_lines = 16;
+
+	if ((p_dev->resource[0]->end != 8) || (p_dev->resource[0]->start == 0))
+		return -EINVAL;
+
+	p_dev->resource[0]->end = 8;
+	p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH;
+	p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8;
+
+	return pcmcia_request_io(p_dev);
+}
+
+static int btuart_check_config_notpicky(struct pcmcia_device *p_dev,
+					void *priv_data)
+{
+	static unsigned int base[5] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, 0x0 };
+	int j;
+
+	if (p_dev->io_lines > 3)
+		return -ENODEV;
+
+	p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH;
+	p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8;
+	p_dev->resource[0]->end = 8;
+
+	for (j = 0; j < 5; j++) {
+		p_dev->resource[0]->start = base[j];
+		p_dev->io_lines = base[j] ? 16 : 3;
+		if (!pcmcia_request_io(p_dev))
+			return 0;
+	}
+	return -ENODEV;
+}
+
+static int btuart_config(struct pcmcia_device *link)
+{
+	btuart_info_t *info = link->priv;
+	int i;
+	int try;
+
+	/* First pass: look for a config entry that looks normal.
+	   Two tries: without IO aliases, then with aliases */
+	for (try = 0; try < 2; try++)
+		if (!pcmcia_loop_config(link, btuart_check_config, &try))
+			goto found_port;
+
+	/* Second pass: try to find an entry that isn't picky about
+	   its base address, then try to grab any standard serial port
+	   address, and finally try to get any free port. */
+	if (!pcmcia_loop_config(link, btuart_check_config_notpicky, NULL))
+		goto found_port;
+
+	BT_ERR("No usable port range found");
+	goto failed;
+
+found_port:
+	i = pcmcia_request_irq(link, btuart_interrupt);
+	if (i != 0)
+		goto failed;
+
+	i = pcmcia_enable_device(link);
+	if (i != 0)
+		goto failed;
+
+	if (btuart_open(info) != 0)
+		goto failed;
+
+	return 0;
+
+failed:
+	btuart_release(link);
+	return -ENODEV;
+}
+
+
+static void btuart_release(struct pcmcia_device *link)
+{
+	btuart_info_t *info = link->priv;
+
+	btuart_close(info);
+
+	pcmcia_disable_device(link);
+}
+
+static const struct pcmcia_device_id btuart_ids[] = {
+	/* don't use this driver. Use serial_cs + hci_uart instead */
+	PCMCIA_DEVICE_NULL
+};
+MODULE_DEVICE_TABLE(pcmcia, btuart_ids);
+
+static struct pcmcia_driver btuart_driver = {
+	.owner		= THIS_MODULE,
+	.name		= "btuart_cs",
+	.probe		= btuart_probe,
+	.remove		= btuart_detach,
+	.id_table	= btuart_ids,
+};
+
+static int __init init_btuart_cs(void)
+{
+	return pcmcia_register_driver(&btuart_driver);
+}
+
+
+static void __exit exit_btuart_cs(void)
+{
+	pcmcia_unregister_driver(&btuart_driver);
+}
+
+module_init(init_btuart_cs);
+module_exit(exit_btuart_cs);
diff --git a/ap/os/linux/linux-3.4.x/drivers/bluetooth/btusb.c b/ap/os/linux/linux-3.4.x/drivers/bluetooth/btusb.c
new file mode 100644
index 0000000..bbd1e6c
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/bluetooth/btusb.c
@@ -0,0 +1,1306 @@
+/*
+ *
+ *  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 bool ignore_dga;
+static bool ignore_csr;
+static bool ignore_sniffer;
+static bool disable_scofix;
+static bool force_scofix;
+
+static bool 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
+#define BTUSB_ATH3012		0x80
+#define BTUSB_INTEL_BOOT	0x200
+
+static struct usb_device_id btusb_table[] = {
+	/* Generic Bluetooth USB device */
+	{ USB_DEVICE_INFO(0xe0, 0x01, 0x01) },
+
+	/* Apple-specific (Broadcom) devices */
+	{ USB_VENDOR_AND_INTERFACE_INFO(0x05ac, 0xff, 0x01, 0x01) },
+
+	/* MediaTek MT76x0E */
+	{ USB_DEVICE(0x0e8d, 0x763f) },
+
+	/* Broadcom SoftSailing reporting vendor specific */
+	{ USB_DEVICE(0x0a5c, 0x21e1) },
+
+	/* 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 MacBookAir4,1 */
+	{ USB_DEVICE(0x05ac, 0x821f) },
+
+	/* Apple MacBookPro8,2 */
+	{ USB_DEVICE(0x05ac, 0x821a) },
+
+	/* Apple MacMini5,1 */
+	{ USB_DEVICE(0x05ac, 0x8281) },
+
+	/* 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) },
+
+	/* Broadcom BCM20702A0 */
+	{ USB_DEVICE(0x0489, 0xe042) },
+	{ USB_DEVICE(0x04ca, 0x2003) },
+	{ USB_DEVICE(0x0b05, 0x17b5) },
+	{ USB_DEVICE(0x0b05, 0x17cb) },
+	{ USB_DEVICE(0x413c, 0x8197) },
+
+	/* Foxconn - Hon Hai */
+	{ USB_VENDOR_AND_INTERFACE_INFO(0x0489, 0xff, 0x01, 0x01) },
+
+	/* Broadcom devices with vendor specific id */
+	{ USB_VENDOR_AND_INTERFACE_INFO(0x0a5c, 0xff, 0x01, 0x01) },
+
+	/* ASUSTek Computer - Broadcom based */
+	{ USB_VENDOR_AND_INTERFACE_INFO(0x0b05, 0xff, 0x01, 0x01) },
+
+	/* Intel Bluetooth USB Bootloader (RAM module) */
+	{ USB_DEVICE(0x8087, 0x0a5a),
+	  .driver_info = BTUSB_INTEL_BOOT | BTUSB_BROKEN_ISOC },
+
+	{ }	/* 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(0x0489, 0xe027), .driver_info = BTUSB_IGNORE },
+	{ USB_DEVICE(0x0489, 0xe03d), .driver_info = BTUSB_IGNORE },
+	{ USB_DEVICE(0x04f2, 0xaff1), .driver_info = BTUSB_IGNORE },
+	{ USB_DEVICE(0x0930, 0x0215), .driver_info = BTUSB_IGNORE },
+	{ USB_DEVICE(0x0cf3, 0x3002), .driver_info = BTUSB_IGNORE },
+	{ USB_DEVICE(0x0cf3, 0xe019), .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(0x0489, 0xe04d), .driver_info = BTUSB_ATH3012 },
+	{ USB_DEVICE(0x0489, 0xe04e), .driver_info = BTUSB_ATH3012 },
+	{ USB_DEVICE(0x0489, 0xe056), .driver_info = BTUSB_ATH3012 },
+	{ USB_DEVICE(0x0489, 0xe057), .driver_info = BTUSB_ATH3012 },
+	{ USB_DEVICE(0x0489, 0xe078), .driver_info = BTUSB_ATH3012 },
+	{ USB_DEVICE(0x04c5, 0x1330), .driver_info = BTUSB_ATH3012 },
+	{ USB_DEVICE(0x04ca, 0x3004), .driver_info = BTUSB_ATH3012 },
+	{ USB_DEVICE(0x04ca, 0x3005), .driver_info = BTUSB_ATH3012 },
+	{ USB_DEVICE(0x04ca, 0x3006), .driver_info = BTUSB_ATH3012 },
+	{ USB_DEVICE(0x04ca, 0x3008), .driver_info = BTUSB_ATH3012 },
+	{ USB_DEVICE(0x04ca, 0x3010), .driver_info = BTUSB_ATH3012 },
+	{ USB_DEVICE(0x0930, 0x0219), .driver_info = BTUSB_ATH3012 },
+	{ USB_DEVICE(0x0930, 0x0227), .driver_info = BTUSB_ATH3012 },
+	{ USB_DEVICE(0x0b05, 0x17d0), .driver_info = BTUSB_ATH3012 },
+	{ USB_DEVICE(0x0cf3, 0x0036), .driver_info = BTUSB_ATH3012 },
+	{ USB_DEVICE(0x0cf3, 0x3004), .driver_info = BTUSB_ATH3012 },
+	{ USB_DEVICE(0x0cf3, 0x3008), .driver_info = BTUSB_ATH3012 },
+	{ USB_DEVICE(0x0cf3, 0x311d), .driver_info = BTUSB_ATH3012 },
+	{ USB_DEVICE(0x0cf3, 0x3121), .driver_info = BTUSB_ATH3012 },
+	{ USB_DEVICE(0x0cf3, 0x817a), .driver_info = BTUSB_ATH3012 },
+	{ USB_DEVICE(0x0cf3, 0xe003), .driver_info = BTUSB_ATH3012 },
+	{ USB_DEVICE(0x0cf3, 0xe004), .driver_info = BTUSB_ATH3012 },
+	{ USB_DEVICE(0x0cf3, 0xe005), .driver_info = BTUSB_ATH3012 },
+	{ USB_DEVICE(0x13d3, 0x3362), .driver_info = BTUSB_ATH3012 },
+	{ USB_DEVICE(0x13d3, 0x3375), .driver_info = BTUSB_ATH3012 },
+	{ USB_DEVICE(0x13d3, 0x3393), .driver_info = BTUSB_ATH3012 },
+	{ USB_DEVICE(0x13d3, 0x3402), .driver_info = BTUSB_ATH3012 },
+	{ USB_DEVICE(0x13d3, 0x3408), .driver_info = BTUSB_ATH3012 },
+	{ USB_DEVICE(0x13d3, 0x3423), .driver_info = BTUSB_ATH3012 },
+	{ USB_DEVICE(0x13d3, 0x3432), .driver_info = BTUSB_ATH3012 },
+	{ USB_DEVICE(0x13d3, 0x3474), .driver_info = BTUSB_ATH3012 },
+
+	/* Atheros AR5BBU12 with sflash firmware */
+	{ USB_DEVICE(0x0489, 0xe02c), .driver_info = BTUSB_IGNORE },
+
+	/* Atheros AR5BBU12 with sflash firmware */
+	{ USB_DEVICE(0x0489, 0xe036), .driver_info = BTUSB_ATH3012 },
+	{ USB_DEVICE(0x0489, 0xe03c), .driver_info = BTUSB_ATH3012 },
+
+	/* Broadcom BCM2035 */
+	{ USB_DEVICE(0x0a5c, 0x2009), .driver_info = BTUSB_BCM92035 },
+	{ USB_DEVICE(0x0a5c, 0x200a), .driver_info = BTUSB_WRONG_SCO_MTU },
+	{ USB_DEVICE(0x0a5c, 0x2035), .driver_info = BTUSB_WRONG_SCO_MTU },
+
+	/* 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 = hci_get_drvdata(hdev);
+	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++;
+		}
+	} else if (urb->status == -ENOENT) {
+		/* Avoid suspend failed when usb_kill_urb */
+		return;
+	}
+
+	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) {
+		/* -EPERM: urb is being killed;
+		 * -ENODEV: device got disconnected */
+		if (err != -EPERM && err != -ENODEV)
+			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 = hci_get_drvdata(hdev);
+	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) {
+		if (err != -EPERM && err != -ENODEV)
+			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 = hci_get_drvdata(hdev);
+	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++;
+		}
+	} else if (urb->status == -ENOENT) {
+		/* Avoid suspend failed when usb_kill_urb */
+		return;
+	}
+
+	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) {
+		/* -EPERM: urb is being killed;
+		 * -ENODEV: device got disconnected */
+		if (err != -EPERM && err != -ENODEV)
+			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 = hci_get_drvdata(hdev);
+	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) {
+		if (err != -EPERM && err != -ENODEV)
+			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 = hci_get_drvdata(hdev);
+	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++;
+			}
+		}
+	} else if (urb->status == -ENOENT) {
+		/* Avoid suspend failed when usb_kill_urb */
+		return;
+	}
+
+	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) {
+		/* -EPERM: urb is being killed;
+		 * -ENODEV: device got disconnected */
+		if (err != -EPERM && err != -ENODEV)
+			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 = hci_get_drvdata(hdev);
+	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);
+
+	usb_fill_int_urb(urb, data->udev, pipe, buf, size, btusb_isoc_complete,
+				hdev, data->isoc_rx_ep->bInterval);
+
+	urb->transfer_flags  = URB_FREE_BUFFER | URB_ISO_ASAP;
+
+	__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) {
+		if (err != -EPERM && err != -ENODEV)
+			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 = hci_get_drvdata(hdev);
+
+	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 = hci_get_drvdata(hdev);
+	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 = hci_get_drvdata(hdev);
+	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 = hci_get_drvdata(hdev);
+
+	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 = hci_get_drvdata(hdev);
+	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)
+			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) {
+		if (err != -EPERM && err != -ENODEV)
+			BT_ERR("%s urb %p submission failed (%d)",
+						hdev->name, urb, -err);
+		kfree(urb->setup_packet);
+		usb_unanchor_urb(urb);
+	} else {
+		usb_mark_last_busy(data->udev);
+	}
+
+done:
+	usb_free_urb(urb);
+	return err;
+}
+
+static void btusb_notify(struct hci_dev *hdev, unsigned int evt)
+{
+	struct btusb_data *data = hci_get_drvdata(hdev);
+
+	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 = hci_get_drvdata(hdev);
+	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;
+
+	if (id->driver_info & BTUSB_ATH3012) {
+		struct usb_device *udev = interface_to_usbdev(intf);
+
+		/* Old firmware would otherwise let ath3k driver load
+		 * patch and sysconfig files */
+		if (le16_to_cpu(udev->descriptor.bcdDevice) <= 0x0001)
+			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;
+	hci_set_drvdata(hdev, 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->notify   = btusb_notify;
+
+	if (id->driver_info & BTUSB_INTEL_BOOT)
+		set_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks);
+
+	/* 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;
+	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_free_dev(hdev);
+	kfree(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);
+
+	if (data->suspend_count++)
+		return 0;
+
+	spin_lock_irq(&data->txlock);
+	if (!(PMSG_IS_AUTO(message) && 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,
+};
+
+module_usb_driver(btusb_driver);
+
+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/ap/os/linux/linux-3.4.x/drivers/bluetooth/btwilink.c b/ap/os/linux/linux-3.4.x/drivers/bluetooth/btwilink.c
new file mode 100644
index 0000000..8869469
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/bluetooth/btwilink.c
@@ -0,0 +1,386 @@
+/*
+ *  Texas Instrument's Bluetooth Driver For Shared Transport.
+ *
+ *  Bluetooth Driver acts as interface between HCI core and
+ *  TI Shared Transport Layer.
+ *
+ *  Copyright (C) 2009-2010 Texas Instruments
+ *  Author: Raja Mani <raja_mani@ti.com>
+ *	Pavan Savoy <pavan_savoy@ti.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  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 DEBUG
+#include <linux/platform_device.h>
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+#include <net/bluetooth/hci.h>
+
+#include <linux/ti_wilink_st.h>
+#include <linux/module.h>
+
+/* Bluetooth Driver Version */
+#define VERSION               "1.0"
+#define MAX_BT_CHNL_IDS		3
+
+/* Number of seconds to wait for registration completion
+ * when ST returns PENDING status.
+ */
+#define BT_REGISTER_TIMEOUT   6000	/* 6 sec */
+
+/**
+ * struct ti_st - driver operation structure
+ * @hdev: hci device pointer which binds to bt driver
+ * @reg_status: ST registration callback status
+ * @st_write: write function provided by the ST driver
+ *	to be used by the driver during send_frame.
+ * @wait_reg_completion - completion sync between ti_st_open
+ *	and st_reg_completion_cb.
+ */
+struct ti_st {
+	struct hci_dev *hdev;
+	char reg_status;
+	long (*st_write) (struct sk_buff *);
+	struct completion wait_reg_completion;
+};
+
+/* Increments HCI counters based on pocket ID (cmd,acl,sco) */
+static inline void ti_st_tx_complete(struct ti_st *hst, int pkt_type)
+{
+	struct hci_dev *hdev = hst->hdev;
+
+	/* Update HCI stat counters */
+	switch (pkt_type) {
+	case HCI_COMMAND_PKT:
+		hdev->stat.cmd_tx++;
+		break;
+
+	case HCI_ACLDATA_PKT:
+		hdev->stat.acl_tx++;
+		break;
+
+	case HCI_SCODATA_PKT:
+		hdev->stat.sco_tx++;
+		break;
+	}
+}
+
+/* ------- Interfaces to Shared Transport ------ */
+
+/* Called by ST layer to indicate protocol registration completion
+ * status.ti_st_open() function will wait for signal from this
+ * API when st_register() function returns ST_PENDING.
+ */
+static void st_reg_completion_cb(void *priv_data, char data)
+{
+	struct ti_st *lhst = priv_data;
+
+	/* Save registration status for use in ti_st_open() */
+	lhst->reg_status = data;
+	/* complete the wait in ti_st_open() */
+	complete(&lhst->wait_reg_completion);
+}
+
+/* Called by Shared Transport layer when receive data is
+ * available */
+static long st_receive(void *priv_data, struct sk_buff *skb)
+{
+	struct ti_st *lhst = priv_data;
+	int err;
+
+	if (!skb)
+		return -EFAULT;
+
+	if (!lhst) {
+		kfree_skb(skb);
+		return -EFAULT;
+	}
+
+	skb->dev = (void *) lhst->hdev;
+
+	/* Forward skb to HCI core layer */
+	err = hci_recv_frame(skb);
+	if (err < 0) {
+		BT_ERR("Unable to push skb to HCI core(%d)", err);
+		return err;
+	}
+
+	lhst->hdev->stat.byte_rx += skb->len;
+
+	return 0;
+}
+
+/* ------- Interfaces to HCI layer ------ */
+/* protocol structure registered with shared transport */
+static struct st_proto_s ti_st_proto[MAX_BT_CHNL_IDS] = {
+	{
+		.chnl_id = HCI_EVENT_PKT, /* HCI Events */
+		.hdr_len = sizeof(struct hci_event_hdr),
+		.offset_len_in_hdr = offsetof(struct hci_event_hdr, plen),
+		.len_size = 1, /* sizeof(plen) in struct hci_event_hdr */
+		.reserve = 8,
+	},
+	{
+		.chnl_id = HCI_ACLDATA_PKT, /* ACL */
+		.hdr_len = sizeof(struct hci_acl_hdr),
+		.offset_len_in_hdr = offsetof(struct hci_acl_hdr, dlen),
+		.len_size = 2,	/* sizeof(dlen) in struct hci_acl_hdr */
+		.reserve = 8,
+	},
+	{
+		.chnl_id = HCI_SCODATA_PKT, /* SCO */
+		.hdr_len = sizeof(struct hci_sco_hdr),
+		.offset_len_in_hdr = offsetof(struct hci_sco_hdr, dlen),
+		.len_size = 1, /* sizeof(dlen) in struct hci_sco_hdr */
+		.reserve = 8,
+	},
+};
+
+/* Called from HCI core to initialize the device */
+static int ti_st_open(struct hci_dev *hdev)
+{
+	unsigned long timeleft;
+	struct ti_st *hst;
+	int err, i;
+
+	BT_DBG("%s %p", hdev->name, hdev);
+
+	if (test_and_set_bit(HCI_RUNNING, &hdev->flags))
+		return -EBUSY;
+
+	/* provide contexts for callbacks from ST */
+	hst = hci_get_drvdata(hdev);
+
+	for (i = 0; i < MAX_BT_CHNL_IDS; i++) {
+		ti_st_proto[i].priv_data = hst;
+		ti_st_proto[i].max_frame_size = HCI_MAX_FRAME_SIZE;
+		ti_st_proto[i].recv = st_receive;
+		ti_st_proto[i].reg_complete_cb = st_reg_completion_cb;
+
+		/* Prepare wait-for-completion handler */
+		init_completion(&hst->wait_reg_completion);
+		/* Reset ST registration callback status flag,
+		 * this value will be updated in
+		 * st_reg_completion_cb()
+		 * function whenever it called from ST driver.
+		 */
+		hst->reg_status = -EINPROGRESS;
+
+		err = st_register(&ti_st_proto[i]);
+		if (!err)
+			goto done;
+
+		if (err != -EINPROGRESS) {
+			clear_bit(HCI_RUNNING, &hdev->flags);
+			BT_ERR("st_register failed %d", err);
+			return err;
+		}
+
+		/* ST is busy with either protocol
+		 * registration or firmware download.
+		 */
+		BT_DBG("waiting for registration "
+				"completion signal from ST");
+		timeleft = wait_for_completion_timeout
+			(&hst->wait_reg_completion,
+			 msecs_to_jiffies(BT_REGISTER_TIMEOUT));
+		if (!timeleft) {
+			clear_bit(HCI_RUNNING, &hdev->flags);
+			BT_ERR("Timeout(%d sec),didn't get reg "
+					"completion signal from ST",
+					BT_REGISTER_TIMEOUT / 1000);
+			return -ETIMEDOUT;
+		}
+
+		/* Is ST registration callback
+		 * called with ERROR status? */
+		if (hst->reg_status != 0) {
+			clear_bit(HCI_RUNNING, &hdev->flags);
+			BT_ERR("ST registration completed with invalid "
+					"status %d", hst->reg_status);
+			return -EAGAIN;
+		}
+
+done:
+		hst->st_write = ti_st_proto[i].write;
+		if (!hst->st_write) {
+			BT_ERR("undefined ST write function");
+			clear_bit(HCI_RUNNING, &hdev->flags);
+			for (i = 0; i < MAX_BT_CHNL_IDS; i++) {
+				/* Undo registration with ST */
+				err = st_unregister(&ti_st_proto[i]);
+				if (err)
+					BT_ERR("st_unregister() failed with "
+							"error %d", err);
+				hst->st_write = NULL;
+			}
+			return -EIO;
+		}
+	}
+	return 0;
+}
+
+/* Close device */
+static int ti_st_close(struct hci_dev *hdev)
+{
+	int err, i;
+	struct ti_st *hst = hci_get_drvdata(hdev);
+
+	if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
+		return 0;
+
+	for (i = MAX_BT_CHNL_IDS-1; i >= 0; i--) {
+		err = st_unregister(&ti_st_proto[i]);
+		if (err)
+			BT_ERR("st_unregister(%d) failed with error %d",
+					ti_st_proto[i].chnl_id, err);
+	}
+
+	hst->st_write = NULL;
+
+	return err;
+}
+
+static int ti_st_send_frame(struct sk_buff *skb)
+{
+	struct hci_dev *hdev;
+	struct ti_st *hst;
+	long len;
+
+	hdev = (struct hci_dev *)skb->dev;
+
+	if (!test_bit(HCI_RUNNING, &hdev->flags))
+		return -EBUSY;
+
+	hst = hci_get_drvdata(hdev);
+
+	/* Prepend skb with frame type */
+	memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
+
+	BT_DBG("%s: type %d len %d", hdev->name, bt_cb(skb)->pkt_type,
+			skb->len);
+
+	/* Insert skb to shared transport layer's transmit queue.
+	 * Freeing skb memory is taken care in shared transport layer,
+	 * so don't free skb memory here.
+	 */
+	len = hst->st_write(skb);
+	if (len < 0) {
+		kfree_skb(skb);
+		BT_ERR("ST write failed (%ld)", len);
+		/* Try Again, would only fail if UART has gone bad */
+		return -EAGAIN;
+	}
+
+	/* ST accepted our skb. So, Go ahead and do rest */
+	hdev->stat.byte_tx += len;
+	ti_st_tx_complete(hst, bt_cb(skb)->pkt_type);
+
+	return 0;
+}
+
+static int bt_ti_probe(struct platform_device *pdev)
+{
+	static struct ti_st *hst;
+	struct hci_dev *hdev;
+	int err;
+
+	hst = kzalloc(sizeof(struct ti_st), GFP_KERNEL);
+	if (!hst)
+		return -ENOMEM;
+
+	/* Expose "hciX" device to user space */
+	hdev = hci_alloc_dev();
+	if (!hdev) {
+		kfree(hst);
+		return -ENOMEM;
+	}
+
+	BT_DBG("hdev %p", hdev);
+
+	hst->hdev = hdev;
+	hdev->bus = HCI_UART;
+	hci_set_drvdata(hdev, hst);
+	hdev->open = ti_st_open;
+	hdev->close = ti_st_close;
+	hdev->flush = NULL;
+	hdev->send = ti_st_send_frame;
+
+	err = hci_register_dev(hdev);
+	if (err < 0) {
+		BT_ERR("Can't register HCI device error %d", err);
+		kfree(hst);
+		hci_free_dev(hdev);
+		return err;
+	}
+
+	BT_DBG("HCI device registered (hdev %p)", hdev);
+
+	dev_set_drvdata(&pdev->dev, hst);
+	return err;
+}
+
+static int bt_ti_remove(struct platform_device *pdev)
+{
+	struct hci_dev *hdev;
+	struct ti_st *hst = dev_get_drvdata(&pdev->dev);
+
+	if (!hst)
+		return -EFAULT;
+
+	BT_DBG("%s", hst->hdev->name);
+
+	hdev = hst->hdev;
+	ti_st_close(hdev);
+	hci_unregister_dev(hdev);
+
+	hci_free_dev(hdev);
+	kfree(hst);
+
+	dev_set_drvdata(&pdev->dev, NULL);
+	return 0;
+}
+
+static struct platform_driver btwilink_driver = {
+	.probe = bt_ti_probe,
+	.remove = bt_ti_remove,
+	.driver = {
+		.name = "btwilink",
+		.owner = THIS_MODULE,
+	},
+};
+
+/* ------- Module Init/Exit interfaces ------ */
+static int __init btwilink_init(void)
+{
+	BT_INFO("Bluetooth Driver for TI WiLink - Version %s", VERSION);
+
+	return platform_driver_register(&btwilink_driver);
+}
+
+static void __exit btwilink_exit(void)
+{
+	platform_driver_unregister(&btwilink_driver);
+}
+
+module_init(btwilink_init);
+module_exit(btwilink_exit);
+
+/* ------ Module Info ------ */
+
+MODULE_AUTHOR("Raja Mani <raja_mani@ti.com>");
+MODULE_DESCRIPTION("Bluetooth Driver for TI Shared Transport" VERSION);
+MODULE_VERSION(VERSION);
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/bluetooth/dtl1_cs.c b/ap/os/linux/linux-3.4.x/drivers/bluetooth/dtl1_cs.c
new file mode 100644
index 0000000..6e8d961
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/bluetooth/dtl1_cs.c
@@ -0,0 +1,643 @@
+/*
+ *
+ *  A driver for Nokia Connectivity Card DTL-1 devices
+ *
+ *  Copyright (C) 2001-2002  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 version 2 as
+ *  published by the Free Software Foundation;
+ *
+ *  Software distributed under the License is distributed on an "AS
+ *  IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ *  implied. See the License for the specific language governing
+ *  rights and limitations under the License.
+ *
+ *  The initial developer of the original code is David A. Hinds
+ *  <dahinds@users.sourceforge.net>.  Portions created by David A. Hinds
+ *  are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
+ *
+ */
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/spinlock.h>
+#include <linux/moduleparam.h>
+
+#include <linux/skbuff.h>
+#include <linux/string.h>
+#include <linux/serial.h>
+#include <linux/serial_reg.h>
+#include <linux/bitops.h>
+#include <asm/io.h>
+
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ciscode.h>
+#include <pcmcia/ds.h>
+#include <pcmcia/cisreg.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+
+
+/* ======================== Module parameters ======================== */
+
+
+MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
+MODULE_DESCRIPTION("Bluetooth driver for Nokia Connectivity Card DTL-1");
+MODULE_LICENSE("GPL");
+
+
+
+/* ======================== Local structures ======================== */
+
+
+typedef struct dtl1_info_t {
+	struct pcmcia_device *p_dev;
+
+	struct hci_dev *hdev;
+
+	spinlock_t lock;		/* For serializing operations */
+
+	unsigned long flowmask;		/* HCI flow mask */
+	int ri_latch;
+
+	struct sk_buff_head txq;
+	unsigned long tx_state;
+
+	unsigned long rx_state;
+	unsigned long rx_count;
+	struct sk_buff *rx_skb;
+} dtl1_info_t;
+
+
+static int dtl1_config(struct pcmcia_device *link);
+
+
+/* Transmit states  */
+#define XMIT_SENDING  1
+#define XMIT_WAKEUP   2
+#define XMIT_WAITING  8
+
+/* Receiver States */
+#define RECV_WAIT_NSH   0
+#define RECV_WAIT_DATA  1
+
+
+typedef struct {
+	u8 type;
+	u8 zero;
+	u16 len;
+} __packed nsh_t;	/* Nokia Specific Header */
+
+#define NSHL  4				/* Nokia Specific Header Length */
+
+
+
+/* ======================== Interrupt handling ======================== */
+
+
+static int dtl1_write(unsigned int iobase, int fifo_size, __u8 *buf, int len)
+{
+	int actual = 0;
+
+	/* Tx FIFO should be empty */
+	if (!(inb(iobase + UART_LSR) & UART_LSR_THRE))
+		return 0;
+
+	/* Fill FIFO with current frame */
+	while ((fifo_size-- > 0) && (actual < len)) {
+		/* Transmit next byte */
+		outb(buf[actual], iobase + UART_TX);
+		actual++;
+	}
+
+	return actual;
+}
+
+
+static void dtl1_write_wakeup(dtl1_info_t *info)
+{
+	if (!info) {
+		BT_ERR("Unknown device");
+		return;
+	}
+
+	if (test_bit(XMIT_WAITING, &(info->tx_state))) {
+		set_bit(XMIT_WAKEUP, &(info->tx_state));
+		return;
+	}
+
+	if (test_and_set_bit(XMIT_SENDING, &(info->tx_state))) {
+		set_bit(XMIT_WAKEUP, &(info->tx_state));
+		return;
+	}
+
+	do {
+		register unsigned int iobase = info->p_dev->resource[0]->start;
+		register struct sk_buff *skb;
+		register int len;
+
+		clear_bit(XMIT_WAKEUP, &(info->tx_state));
+
+		if (!pcmcia_dev_present(info->p_dev))
+			return;
+
+		if (!(skb = skb_dequeue(&(info->txq))))
+			break;
+
+		/* Send frame */
+		len = dtl1_write(iobase, 32, skb->data, skb->len);
+
+		if (len == skb->len) {
+			set_bit(XMIT_WAITING, &(info->tx_state));
+			kfree_skb(skb);
+		} else {
+			skb_pull(skb, len);
+			skb_queue_head(&(info->txq), skb);
+		}
+
+		info->hdev->stat.byte_tx += len;
+
+	} while (test_bit(XMIT_WAKEUP, &(info->tx_state)));
+
+	clear_bit(XMIT_SENDING, &(info->tx_state));
+}
+
+
+static void dtl1_control(dtl1_info_t *info, struct sk_buff *skb)
+{
+	u8 flowmask = *(u8 *)skb->data;
+	int i;
+
+	printk(KERN_INFO "Bluetooth: Nokia control data =");
+	for (i = 0; i < skb->len; i++) {
+		printk(" %02x", skb->data[i]);
+	}
+	printk("\n");
+
+	/* transition to active state */
+	if (((info->flowmask & 0x07) == 0) && ((flowmask & 0x07) != 0)) {
+		clear_bit(XMIT_WAITING, &(info->tx_state));
+		dtl1_write_wakeup(info);
+	}
+
+	info->flowmask = flowmask;
+
+	kfree_skb(skb);
+}
+
+
+static void dtl1_receive(dtl1_info_t *info)
+{
+	unsigned int iobase;
+	nsh_t *nsh;
+	int boguscount = 0;
+
+	if (!info) {
+		BT_ERR("Unknown device");
+		return;
+	}
+
+	iobase = info->p_dev->resource[0]->start;
+
+	do {
+		info->hdev->stat.byte_rx++;
+
+		/* Allocate packet */
+		if (info->rx_skb == NULL)
+			if (!(info->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC))) {
+				BT_ERR("Can't allocate mem for new packet");
+				info->rx_state = RECV_WAIT_NSH;
+				info->rx_count = NSHL;
+				return;
+			}
+
+		*skb_put(info->rx_skb, 1) = inb(iobase + UART_RX);
+		nsh = (nsh_t *)info->rx_skb->data;
+
+		info->rx_count--;
+
+		if (info->rx_count == 0) {
+
+			switch (info->rx_state) {
+			case RECV_WAIT_NSH:
+				info->rx_state = RECV_WAIT_DATA;
+				info->rx_count = nsh->len + (nsh->len & 0x0001);
+				break;
+			case RECV_WAIT_DATA:
+				bt_cb(info->rx_skb)->pkt_type = nsh->type;
+
+				/* remove PAD byte if it exists */
+				if (nsh->len & 0x0001) {
+					info->rx_skb->tail--;
+					info->rx_skb->len--;
+				}
+
+				/* remove NSH */
+				skb_pull(info->rx_skb, NSHL);
+
+				switch (bt_cb(info->rx_skb)->pkt_type) {
+				case 0x80:
+					/* control data for the Nokia Card */
+					dtl1_control(info, info->rx_skb);
+					break;
+				case 0x82:
+				case 0x83:
+				case 0x84:
+					/* send frame to the HCI layer */
+					info->rx_skb->dev = (void *) info->hdev;
+					bt_cb(info->rx_skb)->pkt_type &= 0x0f;
+					hci_recv_frame(info->rx_skb);
+					break;
+				default:
+					/* unknown packet */
+					BT_ERR("Unknown HCI packet with type 0x%02x received", bt_cb(info->rx_skb)->pkt_type);
+					kfree_skb(info->rx_skb);
+					break;
+				}
+
+				info->rx_state = RECV_WAIT_NSH;
+				info->rx_count = NSHL;
+				info->rx_skb = NULL;
+				break;
+			}
+
+		}
+
+		/* Make sure we don't stay here too long */
+		if (boguscount++ > 32)
+			break;
+
+	} while (inb(iobase + UART_LSR) & UART_LSR_DR);
+}
+
+
+static irqreturn_t dtl1_interrupt(int irq, void *dev_inst)
+{
+	dtl1_info_t *info = dev_inst;
+	unsigned int iobase;
+	unsigned char msr;
+	int boguscount = 0;
+	int iir, lsr;
+	irqreturn_t r = IRQ_NONE;
+
+	if (!info || !info->hdev)
+		/* our irq handler is shared */
+		return IRQ_NONE;
+
+	iobase = info->p_dev->resource[0]->start;
+
+	spin_lock(&(info->lock));
+
+	iir = inb(iobase + UART_IIR) & UART_IIR_ID;
+	while (iir) {
+
+		r = IRQ_HANDLED;
+		/* Clear interrupt */
+		lsr = inb(iobase + UART_LSR);
+
+		switch (iir) {
+		case UART_IIR_RLSI:
+			BT_ERR("RLSI");
+			break;
+		case UART_IIR_RDI:
+			/* Receive interrupt */
+			dtl1_receive(info);
+			break;
+		case UART_IIR_THRI:
+			if (lsr & UART_LSR_THRE) {
+				/* Transmitter ready for data */
+				dtl1_write_wakeup(info);
+			}
+			break;
+		default:
+			BT_ERR("Unhandled IIR=%#x", iir);
+			break;
+		}
+
+		/* Make sure we don't stay here too long */
+		if (boguscount++ > 100)
+			break;
+
+		iir = inb(iobase + UART_IIR) & UART_IIR_ID;
+
+	}
+
+	msr = inb(iobase + UART_MSR);
+
+	if (info->ri_latch ^ (msr & UART_MSR_RI)) {
+		info->ri_latch = msr & UART_MSR_RI;
+		clear_bit(XMIT_WAITING, &(info->tx_state));
+		dtl1_write_wakeup(info);
+		r = IRQ_HANDLED;
+	}
+
+	spin_unlock(&(info->lock));
+
+	return r;
+}
+
+
+
+/* ======================== HCI interface ======================== */
+
+
+static int dtl1_hci_open(struct hci_dev *hdev)
+{
+	set_bit(HCI_RUNNING, &(hdev->flags));
+
+	return 0;
+}
+
+
+static int dtl1_hci_flush(struct hci_dev *hdev)
+{
+	dtl1_info_t *info = hci_get_drvdata(hdev);
+
+	/* Drop TX queue */
+	skb_queue_purge(&(info->txq));
+
+	return 0;
+}
+
+
+static int dtl1_hci_close(struct hci_dev *hdev)
+{
+	if (!test_and_clear_bit(HCI_RUNNING, &(hdev->flags)))
+		return 0;
+
+	dtl1_hci_flush(hdev);
+
+	return 0;
+}
+
+
+static int dtl1_hci_send_frame(struct sk_buff *skb)
+{
+	dtl1_info_t *info;
+	struct hci_dev *hdev = (struct hci_dev *)(skb->dev);
+	struct sk_buff *s;
+	nsh_t nsh;
+
+	if (!hdev) {
+		BT_ERR("Frame for unknown HCI device (hdev=NULL)");
+		return -ENODEV;
+	}
+
+	info = hci_get_drvdata(hdev);
+
+	switch (bt_cb(skb)->pkt_type) {
+	case HCI_COMMAND_PKT:
+		hdev->stat.cmd_tx++;
+		nsh.type = 0x81;
+		break;
+	case HCI_ACLDATA_PKT:
+		hdev->stat.acl_tx++;
+		nsh.type = 0x82;
+		break;
+	case HCI_SCODATA_PKT:
+		hdev->stat.sco_tx++;
+		nsh.type = 0x83;
+		break;
+	default:
+		return -EILSEQ;
+	};
+
+	nsh.zero = 0;
+	nsh.len = skb->len;
+
+	s = bt_skb_alloc(NSHL + skb->len + 1, GFP_ATOMIC);
+	if (!s)
+		return -ENOMEM;
+
+	skb_reserve(s, NSHL);
+	skb_copy_from_linear_data(skb, skb_put(s, skb->len), skb->len);
+	if (skb->len & 0x0001)
+		*skb_put(s, 1) = 0;	/* PAD */
+
+	/* Prepend skb with Nokia frame header and queue */
+	memcpy(skb_push(s, NSHL), &nsh, NSHL);
+	skb_queue_tail(&(info->txq), s);
+
+	dtl1_write_wakeup(info);
+
+	kfree_skb(skb);
+
+	return 0;
+}
+
+
+static int dtl1_hci_ioctl(struct hci_dev *hdev, unsigned int cmd,  unsigned long arg)
+{
+	return -ENOIOCTLCMD;
+}
+
+
+
+/* ======================== Card services HCI interaction ======================== */
+
+
+static int dtl1_open(dtl1_info_t *info)
+{
+	unsigned long flags;
+	unsigned int iobase = info->p_dev->resource[0]->start;
+	struct hci_dev *hdev;
+
+	spin_lock_init(&(info->lock));
+
+	skb_queue_head_init(&(info->txq));
+
+	info->rx_state = RECV_WAIT_NSH;
+	info->rx_count = NSHL;
+	info->rx_skb = NULL;
+
+	set_bit(XMIT_WAITING, &(info->tx_state));
+
+	/* Initialize HCI device */
+	hdev = hci_alloc_dev();
+	if (!hdev) {
+		BT_ERR("Can't allocate HCI device");
+		return -ENOMEM;
+	}
+
+	info->hdev = hdev;
+
+	hdev->bus = HCI_PCCARD;
+	hci_set_drvdata(hdev, info);
+	SET_HCIDEV_DEV(hdev, &info->p_dev->dev);
+
+	hdev->open     = dtl1_hci_open;
+	hdev->close    = dtl1_hci_close;
+	hdev->flush    = dtl1_hci_flush;
+	hdev->send     = dtl1_hci_send_frame;
+	hdev->ioctl    = dtl1_hci_ioctl;
+
+	spin_lock_irqsave(&(info->lock), flags);
+
+	/* Reset UART */
+	outb(0, iobase + UART_MCR);
+
+	/* Turn off interrupts */
+	outb(0, iobase + UART_IER);
+
+	/* Initialize UART */
+	outb(UART_LCR_WLEN8, iobase + UART_LCR);	/* Reset DLAB */
+	outb((UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2), iobase + UART_MCR);
+
+	info->ri_latch = inb(info->p_dev->resource[0]->start + UART_MSR)
+				& UART_MSR_RI;
+
+	/* Turn on interrupts */
+	outb(UART_IER_RLSI | UART_IER_RDI | UART_IER_THRI, iobase + UART_IER);
+
+	spin_unlock_irqrestore(&(info->lock), flags);
+
+	/* Timeout before it is safe to send the first HCI packet */
+	msleep(2000);
+
+	/* Register HCI device */
+	if (hci_register_dev(hdev) < 0) {
+		BT_ERR("Can't register HCI device");
+		info->hdev = NULL;
+		hci_free_dev(hdev);
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+
+static int dtl1_close(dtl1_info_t *info)
+{
+	unsigned long flags;
+	unsigned int iobase = info->p_dev->resource[0]->start;
+	struct hci_dev *hdev = info->hdev;
+
+	if (!hdev)
+		return -ENODEV;
+
+	dtl1_hci_close(hdev);
+
+	spin_lock_irqsave(&(info->lock), flags);
+
+	/* Reset UART */
+	outb(0, iobase + UART_MCR);
+
+	/* Turn off interrupts */
+	outb(0, iobase + UART_IER);
+
+	spin_unlock_irqrestore(&(info->lock), flags);
+
+	hci_unregister_dev(hdev);
+	hci_free_dev(hdev);
+
+	return 0;
+}
+
+static int dtl1_probe(struct pcmcia_device *link)
+{
+	dtl1_info_t *info;
+
+	/* Create new info device */
+	info = kzalloc(sizeof(*info), GFP_KERNEL);
+	if (!info)
+		return -ENOMEM;
+
+	info->p_dev = link;
+	link->priv = info;
+
+	link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO;
+
+	return dtl1_config(link);
+}
+
+
+static void dtl1_detach(struct pcmcia_device *link)
+{
+	dtl1_info_t *info = link->priv;
+
+	dtl1_close(info);
+	pcmcia_disable_device(link);
+	kfree(info);
+}
+
+static int dtl1_confcheck(struct pcmcia_device *p_dev, void *priv_data)
+{
+	if ((p_dev->resource[1]->end) || (p_dev->resource[1]->end < 8))
+		return -ENODEV;
+
+	p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH;
+	p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8;
+
+	return pcmcia_request_io(p_dev);
+}
+
+static int dtl1_config(struct pcmcia_device *link)
+{
+	dtl1_info_t *info = link->priv;
+	int i;
+
+	/* Look for a generic full-sized window */
+	link->resource[0]->end = 8;
+	if (pcmcia_loop_config(link, dtl1_confcheck, NULL) < 0)
+		goto failed;
+
+	i = pcmcia_request_irq(link, dtl1_interrupt);
+	if (i != 0)
+		goto failed;
+
+	i = pcmcia_enable_device(link);
+	if (i != 0)
+		goto failed;
+
+	if (dtl1_open(info) != 0)
+		goto failed;
+
+	return 0;
+
+failed:
+	dtl1_detach(link);
+	return -ENODEV;
+}
+
+static const struct pcmcia_device_id dtl1_ids[] = {
+	PCMCIA_DEVICE_PROD_ID12("Nokia Mobile Phones", "DTL-1", 0xe1bfdd64, 0xe168480d),
+	PCMCIA_DEVICE_PROD_ID12("Nokia Mobile Phones", "DTL-4", 0xe1bfdd64, 0x9102bc82),
+	PCMCIA_DEVICE_PROD_ID12("Socket", "CF", 0xb38bcc2e, 0x44ebf863),
+	PCMCIA_DEVICE_PROD_ID12("Socket", "CF+ Personal Network Card", 0xb38bcc2e, 0xe732bae3),
+	PCMCIA_DEVICE_NULL
+};
+MODULE_DEVICE_TABLE(pcmcia, dtl1_ids);
+
+static struct pcmcia_driver dtl1_driver = {
+	.owner		= THIS_MODULE,
+	.name		= "dtl1_cs",
+	.probe		= dtl1_probe,
+	.remove		= dtl1_detach,
+	.id_table	= dtl1_ids,
+};
+
+static int __init init_dtl1_cs(void)
+{
+	return pcmcia_register_driver(&dtl1_driver);
+}
+
+
+static void __exit exit_dtl1_cs(void)
+{
+	pcmcia_unregister_driver(&dtl1_driver);
+}
+
+module_init(init_dtl1_cs);
+module_exit(exit_dtl1_cs);
diff --git a/ap/os/linux/linux-3.4.x/drivers/bluetooth/hci_ath.c b/ap/os/linux/linux-3.4.x/drivers/bluetooth/hci_ath.c
new file mode 100644
index 0000000..12172a6
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/bluetooth/hci_ath.c
@@ -0,0 +1,240 @@
+/*
+ *  Atheros Communication Bluetooth HCIATH3K UART protocol
+ *
+ *  HCIATH3K (HCI Atheros AR300x Protocol) is a Atheros Communication's
+ *  power management protocol extension to H4 to support AR300x Bluetooth Chip.
+ *
+ *  Copyright (c) 2009-2010 Atheros Communications Inc.
+ *
+ *  Acknowledgements:
+ *  This file is based on hci_h4.c, which was written
+ *  by Maxim Krasnyansky and Marcel Holtmann.
+ *
+ *  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/module.h>
+#include <linux/kernel.h>
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/errno.h>
+#include <linux/ioctl.h>
+#include <linux/skbuff.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#include "hci_uart.h"
+
+struct ath_struct {
+	struct hci_uart *hu;
+	unsigned int cur_sleep;
+
+	struct sk_buff_head txq;
+	struct work_struct ctxtsw;
+};
+
+static int ath_wakeup_ar3k(struct tty_struct *tty)
+{
+	struct ktermios ktermios;
+	int status = tty->driver->ops->tiocmget(tty);
+
+	if (status & TIOCM_CTS)
+		return status;
+
+	/* Disable Automatic RTSCTS */
+	memcpy(&ktermios, tty->termios, sizeof(ktermios));
+	ktermios.c_cflag &= ~CRTSCTS;
+	tty_set_termios(tty, &ktermios);
+
+	/* Clear RTS first */
+	status = tty->driver->ops->tiocmget(tty);
+	tty->driver->ops->tiocmset(tty, 0x00, TIOCM_RTS);
+	mdelay(20);
+
+	/* Set RTS, wake up board */
+	status = tty->driver->ops->tiocmget(tty);
+	tty->driver->ops->tiocmset(tty, TIOCM_RTS, 0x00);
+	mdelay(20);
+
+	status = tty->driver->ops->tiocmget(tty);
+
+	/* Disable Automatic RTSCTS */
+	ktermios.c_cflag |= CRTSCTS;
+	status = tty_set_termios(tty, &ktermios);
+
+	return status;
+}
+
+static void ath_hci_uart_work(struct work_struct *work)
+{
+	int status;
+	struct ath_struct *ath;
+	struct hci_uart *hu;
+	struct tty_struct *tty;
+
+	ath = container_of(work, struct ath_struct, ctxtsw);
+
+	hu = ath->hu;
+	tty = hu->tty;
+
+	/* verify and wake up controller */
+	if (ath->cur_sleep) {
+		status = ath_wakeup_ar3k(tty);
+		if (!(status & TIOCM_CTS))
+			return;
+	}
+
+	/* Ready to send Data */
+	clear_bit(HCI_UART_SENDING, &hu->tx_state);
+	hci_uart_tx_wakeup(hu);
+}
+
+/* Initialize protocol */
+static int ath_open(struct hci_uart *hu)
+{
+	struct ath_struct *ath;
+
+	BT_DBG("hu %p", hu);
+
+	ath = kzalloc(sizeof(*ath), GFP_KERNEL);
+	if (!ath)
+		return -ENOMEM;
+
+	skb_queue_head_init(&ath->txq);
+
+	hu->priv = ath;
+	ath->hu = hu;
+
+	INIT_WORK(&ath->ctxtsw, ath_hci_uart_work);
+
+	return 0;
+}
+
+/* Flush protocol data */
+static int ath_flush(struct hci_uart *hu)
+{
+	struct ath_struct *ath = hu->priv;
+
+	BT_DBG("hu %p", hu);
+
+	skb_queue_purge(&ath->txq);
+
+	return 0;
+}
+
+/* Close protocol */
+static int ath_close(struct hci_uart *hu)
+{
+	struct ath_struct *ath = hu->priv;
+
+	BT_DBG("hu %p", hu);
+
+	skb_queue_purge(&ath->txq);
+
+	cancel_work_sync(&ath->ctxtsw);
+
+	hu->priv = NULL;
+	kfree(ath);
+
+	return 0;
+}
+
+#define HCI_OP_ATH_SLEEP 0xFC04
+
+/* Enqueue frame for transmittion */
+static int ath_enqueue(struct hci_uart *hu, struct sk_buff *skb)
+{
+	struct ath_struct *ath = hu->priv;
+
+	if (bt_cb(skb)->pkt_type == HCI_SCODATA_PKT) {
+		kfree_skb(skb);
+		return 0;
+	}
+
+	/*
+	 * Update power management enable flag with parameters of
+	 * HCI sleep enable vendor specific HCI command.
+	 */
+	if (bt_cb(skb)->pkt_type == HCI_COMMAND_PKT) {
+		struct hci_command_hdr *hdr = (void *)skb->data;
+
+		if (__le16_to_cpu(hdr->opcode) == HCI_OP_ATH_SLEEP)
+			ath->cur_sleep = skb->data[HCI_COMMAND_HDR_SIZE];
+	}
+
+	BT_DBG("hu %p skb %p", hu, skb);
+
+	/* Prepend skb with frame type */
+	memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
+
+	skb_queue_tail(&ath->txq, skb);
+	set_bit(HCI_UART_SENDING, &hu->tx_state);
+
+	schedule_work(&ath->ctxtsw);
+
+	return 0;
+}
+
+static struct sk_buff *ath_dequeue(struct hci_uart *hu)
+{
+	struct ath_struct *ath = hu->priv;
+
+	return skb_dequeue(&ath->txq);
+}
+
+/* Recv data */
+static int ath_recv(struct hci_uart *hu, void *data, int count)
+{
+	int ret;
+
+	ret = hci_recv_stream_fragment(hu->hdev, data, count);
+	if (ret < 0) {
+		BT_ERR("Frame Reassembly Failed");
+		return ret;
+	}
+
+	return count;
+}
+
+static struct hci_uart_proto athp = {
+	.id = HCI_UART_ATH3K,
+	.open = ath_open,
+	.close = ath_close,
+	.recv = ath_recv,
+	.enqueue = ath_enqueue,
+	.dequeue = ath_dequeue,
+	.flush = ath_flush,
+};
+
+int __init ath_init(void)
+{
+	int err = hci_uart_register_proto(&athp);
+
+	if (!err)
+		BT_INFO("HCIATH3K protocol initialized");
+	else
+		BT_ERR("HCIATH3K protocol registration failed");
+
+	return err;
+}
+
+int __exit ath_deinit(void)
+{
+	return hci_uart_unregister_proto(&athp);
+}
diff --git a/ap/os/linux/linux-3.4.x/drivers/bluetooth/hci_bcsp.c b/ap/os/linux/linux-3.4.x/drivers/bluetooth/hci_bcsp.c
new file mode 100644
index 0000000..661a8dc
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/bluetooth/hci_bcsp.c
@@ -0,0 +1,763 @@
+/*
+ *
+ *  Bluetooth HCI UART driver
+ *
+ *  Copyright (C) 2002-2003  Fabrizio Gennari <fabrizio.gennari@philips.com>
+ *  Copyright (C) 2004-2005  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/module.h>
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/poll.h>
+
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/signal.h>
+#include <linux/ioctl.h>
+#include <linux/skbuff.h>
+#include <linux/bitrev.h>
+#include <asm/unaligned.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#include "hci_uart.h"
+
+#define VERSION "0.3"
+
+static bool txcrc = 1;
+static bool hciextn = 1;
+
+#define BCSP_TXWINSIZE	4
+
+#define BCSP_ACK_PKT	0x05
+#define BCSP_LE_PKT	0x06
+
+struct bcsp_struct {
+	struct sk_buff_head unack;	/* Unack'ed packets queue */
+	struct sk_buff_head rel;	/* Reliable packets queue */
+	struct sk_buff_head unrel;	/* Unreliable packets queue */
+
+	unsigned long rx_count;
+	struct	sk_buff *rx_skb;
+	u8	rxseq_txack;		/* rxseq == txack. */
+	u8	rxack;			/* Last packet sent by us that the peer ack'ed */
+	struct	timer_list tbcsp;
+
+	enum {
+		BCSP_W4_PKT_DELIMITER,
+		BCSP_W4_PKT_START,
+		BCSP_W4_BCSP_HDR,
+		BCSP_W4_DATA,
+		BCSP_W4_CRC
+	} rx_state;
+
+	enum {
+		BCSP_ESCSTATE_NOESC,
+		BCSP_ESCSTATE_ESC
+	} rx_esc_state;
+
+	u8	use_crc;
+	u16	message_crc;
+	u8	txack_req;		/* Do we need to send ack's to the peer? */
+
+	/* Reliable packet sequence number - used to assign seq to each rel pkt. */
+	u8	msgq_txseq;
+};
+
+/* ---- BCSP CRC calculation ---- */
+
+/* Table for calculating CRC for polynomial 0x1021, LSB processed first,
+initial value 0xffff, bits shifted in reverse order. */
+
+static const u16 crc_table[] = {
+	0x0000, 0x1081, 0x2102, 0x3183,
+	0x4204, 0x5285, 0x6306, 0x7387,
+	0x8408, 0x9489, 0xa50a, 0xb58b,
+	0xc60c, 0xd68d, 0xe70e, 0xf78f
+};
+
+/* Initialise the crc calculator */
+#define BCSP_CRC_INIT(x) x = 0xffff
+
+/*
+   Update crc with next data byte
+
+   Implementation note
+        The data byte is treated as two nibbles.  The crc is generated
+        in reverse, i.e., bits are fed into the register from the top.
+*/
+static void bcsp_crc_update(u16 *crc, u8 d)
+{
+	u16 reg = *crc;
+
+	reg = (reg >> 4) ^ crc_table[(reg ^ d) & 0x000f];
+	reg = (reg >> 4) ^ crc_table[(reg ^ (d >> 4)) & 0x000f];
+
+	*crc = reg;
+}
+
+/* ---- BCSP core ---- */
+
+static void bcsp_slip_msgdelim(struct sk_buff *skb)
+{
+	const char pkt_delim = 0xc0;
+
+	memcpy(skb_put(skb, 1), &pkt_delim, 1);
+}
+
+static void bcsp_slip_one_byte(struct sk_buff *skb, u8 c)
+{
+	const char esc_c0[2] = { 0xdb, 0xdc };
+	const char esc_db[2] = { 0xdb, 0xdd };
+
+	switch (c) {
+	case 0xc0:
+		memcpy(skb_put(skb, 2), &esc_c0, 2);
+		break;
+	case 0xdb:
+		memcpy(skb_put(skb, 2), &esc_db, 2);
+		break;
+	default:
+		memcpy(skb_put(skb, 1), &c, 1);
+	}
+}
+
+static int bcsp_enqueue(struct hci_uart *hu, struct sk_buff *skb)
+{
+	struct bcsp_struct *bcsp = hu->priv;
+
+	if (skb->len > 0xFFF) {
+		BT_ERR("Packet too long");
+		kfree_skb(skb);
+		return 0;
+	}
+
+	switch (bt_cb(skb)->pkt_type) {
+	case HCI_ACLDATA_PKT:
+	case HCI_COMMAND_PKT:
+		skb_queue_tail(&bcsp->rel, skb);
+		break;
+
+	case HCI_SCODATA_PKT:
+		skb_queue_tail(&bcsp->unrel, skb);
+		break;
+
+	default:
+		BT_ERR("Unknown packet type");
+		kfree_skb(skb);
+		break;
+	}
+
+	return 0;
+}
+
+static struct sk_buff *bcsp_prepare_pkt(struct bcsp_struct *bcsp, u8 *data,
+		int len, int pkt_type)
+{
+	struct sk_buff *nskb;
+	u8 hdr[4], chan;
+	u16 BCSP_CRC_INIT(bcsp_txmsg_crc);
+	int rel, i;
+
+	switch (pkt_type) {
+	case HCI_ACLDATA_PKT:
+		chan = 6;	/* BCSP ACL channel */
+		rel = 1;	/* reliable channel */
+		break;
+	case HCI_COMMAND_PKT:
+		chan = 5;	/* BCSP cmd/evt channel */
+		rel = 1;	/* reliable channel */
+		break;
+	case HCI_SCODATA_PKT:
+		chan = 7;	/* BCSP SCO channel */
+		rel = 0;	/* unreliable channel */
+		break;
+	case BCSP_LE_PKT:
+		chan = 1;	/* BCSP LE channel */
+		rel = 0;	/* unreliable channel */
+		break;
+	case BCSP_ACK_PKT:
+		chan = 0;	/* BCSP internal channel */
+		rel = 0;	/* unreliable channel */
+		break;
+	default:
+		BT_ERR("Unknown packet type");
+		return NULL;
+	}
+
+	if (hciextn && chan == 5) {
+		__le16 opcode = ((struct hci_command_hdr *)data)->opcode;
+
+		/* Vendor specific commands */
+		if (hci_opcode_ogf(__le16_to_cpu(opcode)) == 0x3f) {
+			u8 desc = *(data + HCI_COMMAND_HDR_SIZE);
+			if ((desc & 0xf0) == 0xc0) {
+				data += HCI_COMMAND_HDR_SIZE + 1;
+				len  -= HCI_COMMAND_HDR_SIZE + 1;
+				chan = desc & 0x0f;
+			}
+		}
+	}
+
+	/* Max len of packet: (original len +4(bcsp hdr) +2(crc))*2
+	   (because bytes 0xc0 and 0xdb are escaped, worst case is
+	   when the packet is all made of 0xc0 and 0xdb :) )
+	   + 2 (0xc0 delimiters at start and end). */
+
+	nskb = alloc_skb((len + 6) * 2 + 2, GFP_ATOMIC);
+	if (!nskb)
+		return NULL;
+
+	bt_cb(nskb)->pkt_type = pkt_type;
+
+	bcsp_slip_msgdelim(nskb);
+
+	hdr[0] = bcsp->rxseq_txack << 3;
+	bcsp->txack_req = 0;
+	BT_DBG("We request packet no %u to card", bcsp->rxseq_txack);
+
+	if (rel) {
+		hdr[0] |= 0x80 + bcsp->msgq_txseq;
+		BT_DBG("Sending packet with seqno %u", bcsp->msgq_txseq);
+		bcsp->msgq_txseq = (bcsp->msgq_txseq + 1) & 0x07;
+	}
+
+	if (bcsp->use_crc)
+		hdr[0] |= 0x40;
+
+	hdr[1] = ((len << 4) & 0xff) | chan;
+	hdr[2] = len >> 4;
+	hdr[3] = ~(hdr[0] + hdr[1] + hdr[2]);
+
+	/* Put BCSP header */
+	for (i = 0; i < 4; i++) {
+		bcsp_slip_one_byte(nskb, hdr[i]);
+
+		if (bcsp->use_crc)
+			bcsp_crc_update(&bcsp_txmsg_crc, hdr[i]);
+	}
+
+	/* Put payload */
+	for (i = 0; i < len; i++) {
+		bcsp_slip_one_byte(nskb, data[i]);
+
+		if (bcsp->use_crc)
+			bcsp_crc_update(&bcsp_txmsg_crc, data[i]);
+	}
+
+	/* Put CRC */
+	if (bcsp->use_crc) {
+		bcsp_txmsg_crc = bitrev16(bcsp_txmsg_crc);
+		bcsp_slip_one_byte(nskb, (u8) ((bcsp_txmsg_crc >> 8) & 0x00ff));
+		bcsp_slip_one_byte(nskb, (u8) (bcsp_txmsg_crc & 0x00ff));
+	}
+
+	bcsp_slip_msgdelim(nskb);
+	return nskb;
+}
+
+/* This is a rewrite of pkt_avail in ABCSP */
+static struct sk_buff *bcsp_dequeue(struct hci_uart *hu)
+{
+	struct bcsp_struct *bcsp = hu->priv;
+	unsigned long flags;
+	struct sk_buff *skb;
+	
+	/* First of all, check for unreliable messages in the queue,
+	   since they have priority */
+
+	if ((skb = skb_dequeue(&bcsp->unrel)) != NULL) {
+		struct sk_buff *nskb = bcsp_prepare_pkt(bcsp, skb->data, skb->len, bt_cb(skb)->pkt_type);
+		if (nskb) {
+			kfree_skb(skb);
+			return nskb;
+		} else {
+			skb_queue_head(&bcsp->unrel, skb);
+			BT_ERR("Could not dequeue pkt because alloc_skb failed");
+		}
+	}
+
+	/* Now, try to send a reliable pkt. We can only send a
+	   reliable packet if the number of packets sent but not yet ack'ed
+	   is < than the winsize */
+
+	spin_lock_irqsave_nested(&bcsp->unack.lock, flags, SINGLE_DEPTH_NESTING);
+
+	if (bcsp->unack.qlen < BCSP_TXWINSIZE && (skb = skb_dequeue(&bcsp->rel)) != NULL) {
+		struct sk_buff *nskb = bcsp_prepare_pkt(bcsp, skb->data, skb->len, bt_cb(skb)->pkt_type);
+		if (nskb) {
+			__skb_queue_tail(&bcsp->unack, skb);
+			mod_timer(&bcsp->tbcsp, jiffies + HZ / 4);
+			spin_unlock_irqrestore(&bcsp->unack.lock, flags);
+			return nskb;
+		} else {
+			skb_queue_head(&bcsp->rel, skb);
+			BT_ERR("Could not dequeue pkt because alloc_skb failed");
+		}
+	}
+
+	spin_unlock_irqrestore(&bcsp->unack.lock, flags);
+
+	/* We could not send a reliable packet, either because there are
+	   none or because there are too many unack'ed pkts. Did we receive
+	   any packets we have not acknowledged yet ? */
+
+	if (bcsp->txack_req) {
+		/* if so, craft an empty ACK pkt and send it on BCSP unreliable
+		   channel 0 */
+		struct sk_buff *nskb = bcsp_prepare_pkt(bcsp, NULL, 0, BCSP_ACK_PKT);
+		return nskb;
+	}
+
+	/* We have nothing to send */
+	return NULL;
+}
+
+static int bcsp_flush(struct hci_uart *hu)
+{
+	BT_DBG("hu %p", hu);
+	return 0;
+}
+
+/* Remove ack'ed packets */
+static void bcsp_pkt_cull(struct bcsp_struct *bcsp)
+{
+	struct sk_buff *skb, *tmp;
+	unsigned long flags;
+	int i, pkts_to_be_removed;
+	u8 seqno;
+
+	spin_lock_irqsave(&bcsp->unack.lock, flags);
+
+	pkts_to_be_removed = skb_queue_len(&bcsp->unack);
+	seqno = bcsp->msgq_txseq;
+
+	while (pkts_to_be_removed) {
+		if (bcsp->rxack == seqno)
+			break;
+		pkts_to_be_removed--;
+		seqno = (seqno - 1) & 0x07;
+	}
+
+	if (bcsp->rxack != seqno)
+		BT_ERR("Peer acked invalid packet");
+
+	BT_DBG("Removing %u pkts out of %u, up to seqno %u",
+	       pkts_to_be_removed, skb_queue_len(&bcsp->unack),
+	       (seqno - 1) & 0x07);
+
+	i = 0;
+	skb_queue_walk_safe(&bcsp->unack, skb, tmp) {
+		if (i >= pkts_to_be_removed)
+			break;
+		i++;
+
+		__skb_unlink(skb, &bcsp->unack);
+		kfree_skb(skb);
+	}
+
+	if (skb_queue_empty(&bcsp->unack))
+		del_timer(&bcsp->tbcsp);
+
+	spin_unlock_irqrestore(&bcsp->unack.lock, flags);
+
+	if (i != pkts_to_be_removed)
+		BT_ERR("Removed only %u out of %u pkts", i, pkts_to_be_removed);
+}
+
+/* Handle BCSP link-establishment packets. When we
+   detect a "sync" packet, symptom that the BT module has reset,
+   we do nothing :) (yet) */
+static void bcsp_handle_le_pkt(struct hci_uart *hu)
+{
+	struct bcsp_struct *bcsp = hu->priv;
+	u8 conf_pkt[4]     = { 0xad, 0xef, 0xac, 0xed };
+	u8 conf_rsp_pkt[4] = { 0xde, 0xad, 0xd0, 0xd0 };
+	u8 sync_pkt[4]     = { 0xda, 0xdc, 0xed, 0xed };
+
+	/* spot "conf" pkts and reply with a "conf rsp" pkt */
+	if (bcsp->rx_skb->data[1] >> 4 == 4 && bcsp->rx_skb->data[2] == 0 &&
+			!memcmp(&bcsp->rx_skb->data[4], conf_pkt, 4)) {
+		struct sk_buff *nskb = alloc_skb(4, GFP_ATOMIC);
+
+		BT_DBG("Found a LE conf pkt");
+		if (!nskb)
+			return;
+		memcpy(skb_put(nskb, 4), conf_rsp_pkt, 4);
+		bt_cb(nskb)->pkt_type = BCSP_LE_PKT;
+
+		skb_queue_head(&bcsp->unrel, nskb);
+		hci_uart_tx_wakeup(hu);
+	}
+	/* Spot "sync" pkts. If we find one...disaster! */
+	else if (bcsp->rx_skb->data[1] >> 4 == 4 && bcsp->rx_skb->data[2] == 0 &&
+			!memcmp(&bcsp->rx_skb->data[4], sync_pkt, 4)) {
+		BT_ERR("Found a LE sync pkt, card has reset");
+	}
+}
+
+static inline void bcsp_unslip_one_byte(struct bcsp_struct *bcsp, unsigned char byte)
+{
+	const u8 c0 = 0xc0, db = 0xdb;
+
+	switch (bcsp->rx_esc_state) {
+	case BCSP_ESCSTATE_NOESC:
+		switch (byte) {
+		case 0xdb:
+			bcsp->rx_esc_state = BCSP_ESCSTATE_ESC;
+			break;
+		default:
+			memcpy(skb_put(bcsp->rx_skb, 1), &byte, 1);
+			if ((bcsp->rx_skb-> data[0] & 0x40) != 0 && 
+					bcsp->rx_state != BCSP_W4_CRC)
+				bcsp_crc_update(&bcsp->message_crc, byte);
+			bcsp->rx_count--;
+		}
+		break;
+
+	case BCSP_ESCSTATE_ESC:
+		switch (byte) {
+		case 0xdc:
+			memcpy(skb_put(bcsp->rx_skb, 1), &c0, 1);
+			if ((bcsp->rx_skb-> data[0] & 0x40) != 0 && 
+					bcsp->rx_state != BCSP_W4_CRC)
+				bcsp_crc_update(&bcsp-> message_crc, 0xc0);
+			bcsp->rx_esc_state = BCSP_ESCSTATE_NOESC;
+			bcsp->rx_count--;
+			break;
+
+		case 0xdd:
+			memcpy(skb_put(bcsp->rx_skb, 1), &db, 1);
+			if ((bcsp->rx_skb-> data[0] & 0x40) != 0 && 
+					bcsp->rx_state != BCSP_W4_CRC) 
+				bcsp_crc_update(&bcsp-> message_crc, 0xdb);
+			bcsp->rx_esc_state = BCSP_ESCSTATE_NOESC;
+			bcsp->rx_count--;
+			break;
+
+		default:
+			BT_ERR ("Invalid byte %02x after esc byte", byte);
+			kfree_skb(bcsp->rx_skb);
+			bcsp->rx_skb = NULL;
+			bcsp->rx_state = BCSP_W4_PKT_DELIMITER;
+			bcsp->rx_count = 0;
+		}
+	}
+}
+
+static void bcsp_complete_rx_pkt(struct hci_uart *hu)
+{
+	struct bcsp_struct *bcsp = hu->priv;
+	int pass_up;
+
+	if (bcsp->rx_skb->data[0] & 0x80) {	/* reliable pkt */
+		BT_DBG("Received seqno %u from card", bcsp->rxseq_txack);
+		bcsp->rxseq_txack++;
+		bcsp->rxseq_txack %= 0x8;
+		bcsp->txack_req    = 1;
+
+		/* If needed, transmit an ack pkt */
+		hci_uart_tx_wakeup(hu);
+	}
+
+	bcsp->rxack = (bcsp->rx_skb->data[0] >> 3) & 0x07;
+	BT_DBG("Request for pkt %u from card", bcsp->rxack);
+
+	bcsp_pkt_cull(bcsp);
+	if ((bcsp->rx_skb->data[1] & 0x0f) == 6 &&
+			bcsp->rx_skb->data[0] & 0x80) {
+		bt_cb(bcsp->rx_skb)->pkt_type = HCI_ACLDATA_PKT;
+		pass_up = 1;
+	} else if ((bcsp->rx_skb->data[1] & 0x0f) == 5 &&
+			bcsp->rx_skb->data[0] & 0x80) {
+		bt_cb(bcsp->rx_skb)->pkt_type = HCI_EVENT_PKT;
+		pass_up = 1;
+	} else if ((bcsp->rx_skb->data[1] & 0x0f) == 7) {
+		bt_cb(bcsp->rx_skb)->pkt_type = HCI_SCODATA_PKT;
+		pass_up = 1;
+	} else if ((bcsp->rx_skb->data[1] & 0x0f) == 1 &&
+			!(bcsp->rx_skb->data[0] & 0x80)) {
+		bcsp_handle_le_pkt(hu);
+		pass_up = 0;
+	} else
+		pass_up = 0;
+
+	if (!pass_up) {
+		struct hci_event_hdr hdr;
+		u8 desc = (bcsp->rx_skb->data[1] & 0x0f);
+
+		if (desc != 0 && desc != 1) {
+			if (hciextn) {
+				desc |= 0xc0;
+				skb_pull(bcsp->rx_skb, 4);
+				memcpy(skb_push(bcsp->rx_skb, 1), &desc, 1);
+
+				hdr.evt = 0xff;
+				hdr.plen = bcsp->rx_skb->len;
+				memcpy(skb_push(bcsp->rx_skb, HCI_EVENT_HDR_SIZE), &hdr, HCI_EVENT_HDR_SIZE);
+				bt_cb(bcsp->rx_skb)->pkt_type = HCI_EVENT_PKT;
+
+				hci_recv_frame(bcsp->rx_skb);
+			} else {
+				BT_ERR ("Packet for unknown channel (%u %s)",
+					bcsp->rx_skb->data[1] & 0x0f,
+					bcsp->rx_skb->data[0] & 0x80 ? 
+					"reliable" : "unreliable");
+				kfree_skb(bcsp->rx_skb);
+			}
+		} else
+			kfree_skb(bcsp->rx_skb);
+	} else {
+		/* Pull out BCSP hdr */
+		skb_pull(bcsp->rx_skb, 4);
+
+		hci_recv_frame(bcsp->rx_skb);
+	}
+
+	bcsp->rx_state = BCSP_W4_PKT_DELIMITER;
+	bcsp->rx_skb = NULL;
+}
+
+static u16 bscp_get_crc(struct bcsp_struct *bcsp)
+{
+	return get_unaligned_be16(&bcsp->rx_skb->data[bcsp->rx_skb->len - 2]);
+}
+
+/* Recv data */
+static int bcsp_recv(struct hci_uart *hu, void *data, int count)
+{
+	struct bcsp_struct *bcsp = hu->priv;
+	register unsigned char *ptr;
+
+	BT_DBG("hu %p count %d rx_state %d rx_count %ld", 
+		hu, count, bcsp->rx_state, bcsp->rx_count);
+
+	ptr = data;
+	while (count) {
+		if (bcsp->rx_count) {
+			if (*ptr == 0xc0) {
+				BT_ERR("Short BCSP packet");
+				kfree_skb(bcsp->rx_skb);
+				bcsp->rx_state = BCSP_W4_PKT_START;
+				bcsp->rx_count = 0;
+			} else
+				bcsp_unslip_one_byte(bcsp, *ptr);
+
+			ptr++; count--;
+			continue;
+		}
+
+		switch (bcsp->rx_state) {
+		case BCSP_W4_BCSP_HDR:
+			if ((0xff & (u8) ~ (bcsp->rx_skb->data[0] + bcsp->rx_skb->data[1] +
+					bcsp->rx_skb->data[2])) != bcsp->rx_skb->data[3]) {
+				BT_ERR("Error in BCSP hdr checksum");
+				kfree_skb(bcsp->rx_skb);
+				bcsp->rx_state = BCSP_W4_PKT_DELIMITER;
+				bcsp->rx_count = 0;
+				continue;
+			}
+			if (bcsp->rx_skb->data[0] & 0x80	/* reliable pkt */
+			    		&& (bcsp->rx_skb->data[0] & 0x07) != bcsp->rxseq_txack) {
+				BT_ERR ("Out-of-order packet arrived, got %u expected %u",
+					bcsp->rx_skb->data[0] & 0x07, bcsp->rxseq_txack);
+
+				kfree_skb(bcsp->rx_skb);
+				bcsp->rx_state = BCSP_W4_PKT_DELIMITER;
+				bcsp->rx_count = 0;
+				continue;
+			}
+			bcsp->rx_state = BCSP_W4_DATA;
+			bcsp->rx_count = (bcsp->rx_skb->data[1] >> 4) + 
+					(bcsp->rx_skb->data[2] << 4);	/* May be 0 */
+			continue;
+
+		case BCSP_W4_DATA:
+			if (bcsp->rx_skb->data[0] & 0x40) {	/* pkt with crc */
+				bcsp->rx_state = BCSP_W4_CRC;
+				bcsp->rx_count = 2;
+			} else
+				bcsp_complete_rx_pkt(hu);
+			continue;
+
+		case BCSP_W4_CRC:
+			if (bitrev16(bcsp->message_crc) != bscp_get_crc(bcsp)) {
+				BT_ERR ("Checksum failed: computed %04x received %04x",
+					bitrev16(bcsp->message_crc),
+					bscp_get_crc(bcsp));
+
+				kfree_skb(bcsp->rx_skb);
+				bcsp->rx_state = BCSP_W4_PKT_DELIMITER;
+				bcsp->rx_count = 0;
+				continue;
+			}
+			skb_trim(bcsp->rx_skb, bcsp->rx_skb->len - 2);
+			bcsp_complete_rx_pkt(hu);
+			continue;
+
+		case BCSP_W4_PKT_DELIMITER:
+			switch (*ptr) {
+			case 0xc0:
+				bcsp->rx_state = BCSP_W4_PKT_START;
+				break;
+			default:
+				/*BT_ERR("Ignoring byte %02x", *ptr);*/
+				break;
+			}
+			ptr++; count--;
+			break;
+
+		case BCSP_W4_PKT_START:
+			switch (*ptr) {
+			case 0xc0:
+				ptr++; count--;
+				break;
+
+			default:
+				bcsp->rx_state = BCSP_W4_BCSP_HDR;
+				bcsp->rx_count = 4;
+				bcsp->rx_esc_state = BCSP_ESCSTATE_NOESC;
+				BCSP_CRC_INIT(bcsp->message_crc);
+
+				/* Do not increment ptr or decrement count
+				 * Allocate packet. Max len of a BCSP pkt= 
+				 * 0xFFF (payload) +4 (header) +2 (crc) */
+
+				bcsp->rx_skb = bt_skb_alloc(0x1005, GFP_ATOMIC);
+				if (!bcsp->rx_skb) {
+					BT_ERR("Can't allocate mem for new packet");
+					bcsp->rx_state = BCSP_W4_PKT_DELIMITER;
+					bcsp->rx_count = 0;
+					return 0;
+				}
+				bcsp->rx_skb->dev = (void *) hu->hdev;
+				break;
+			}
+			break;
+		}
+	}
+	return count;
+}
+
+	/* Arrange to retransmit all messages in the relq. */
+static void bcsp_timed_event(unsigned long arg)
+{
+	struct hci_uart *hu = (struct hci_uart *) arg;
+	struct bcsp_struct *bcsp = hu->priv;
+	struct sk_buff *skb;
+	unsigned long flags;
+
+	BT_DBG("hu %p retransmitting %u pkts", hu, bcsp->unack.qlen);
+
+	spin_lock_irqsave_nested(&bcsp->unack.lock, flags, SINGLE_DEPTH_NESTING);
+
+	while ((skb = __skb_dequeue_tail(&bcsp->unack)) != NULL) {
+		bcsp->msgq_txseq = (bcsp->msgq_txseq - 1) & 0x07;
+		skb_queue_head(&bcsp->rel, skb);
+	}
+
+	spin_unlock_irqrestore(&bcsp->unack.lock, flags);
+
+	hci_uart_tx_wakeup(hu);
+}
+
+static int bcsp_open(struct hci_uart *hu)
+{
+	struct bcsp_struct *bcsp;
+
+	BT_DBG("hu %p", hu);
+
+	bcsp = kzalloc(sizeof(*bcsp), GFP_KERNEL);
+	if (!bcsp)
+		return -ENOMEM;
+
+	hu->priv = bcsp;
+	skb_queue_head_init(&bcsp->unack);
+	skb_queue_head_init(&bcsp->rel);
+	skb_queue_head_init(&bcsp->unrel);
+
+	init_timer(&bcsp->tbcsp);
+	bcsp->tbcsp.function = bcsp_timed_event;
+	bcsp->tbcsp.data     = (u_long) hu;
+
+	bcsp->rx_state = BCSP_W4_PKT_DELIMITER;
+
+	if (txcrc)
+		bcsp->use_crc = 1;
+
+	return 0;
+}
+
+static int bcsp_close(struct hci_uart *hu)
+{
+	struct bcsp_struct *bcsp = hu->priv;
+	hu->priv = NULL;
+
+	BT_DBG("hu %p", hu);
+
+	skb_queue_purge(&bcsp->unack);
+	skb_queue_purge(&bcsp->rel);
+	skb_queue_purge(&bcsp->unrel);
+	del_timer(&bcsp->tbcsp);
+
+	kfree(bcsp);
+	return 0;
+}
+
+static struct hci_uart_proto bcsp = {
+	.id		= HCI_UART_BCSP,
+	.open		= bcsp_open,
+	.close		= bcsp_close,
+	.enqueue	= bcsp_enqueue,
+	.dequeue	= bcsp_dequeue,
+	.recv		= bcsp_recv,
+	.flush		= bcsp_flush
+};
+
+int __init bcsp_init(void)
+{
+	int err = hci_uart_register_proto(&bcsp);
+
+	if (!err)
+		BT_INFO("HCI BCSP protocol initialized");
+	else
+		BT_ERR("HCI BCSP protocol registration failed");
+
+	return err;
+}
+
+int __exit bcsp_deinit(void)
+{
+	return hci_uart_unregister_proto(&bcsp);
+}
+
+module_param(txcrc, bool, 0644);
+MODULE_PARM_DESC(txcrc, "Transmit CRC with every BCSP packet");
+
+module_param(hciextn, bool, 0644);
+MODULE_PARM_DESC(hciextn, "Convert HCI Extensions into BCSP packets");
diff --git a/ap/os/linux/linux-3.4.x/drivers/bluetooth/hci_h4.c b/ap/os/linux/linux-3.4.x/drivers/bluetooth/hci_h4.c
new file mode 100644
index 0000000..7483294
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/bluetooth/hci_h4.c
@@ -0,0 +1,196 @@
+/*
+ *
+ *  Bluetooth HCI UART driver
+ *
+ *  Copyright (C) 2000-2001  Qualcomm Incorporated
+ *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
+ *  Copyright (C) 2004-2005  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/module.h>
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/poll.h>
+
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/signal.h>
+#include <linux/ioctl.h>
+#include <linux/skbuff.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#include "hci_uart.h"
+
+#define VERSION "1.2"
+
+struct h4_struct {
+	unsigned long rx_state;
+	unsigned long rx_count;
+	struct sk_buff *rx_skb;
+	struct sk_buff_head txq;
+};
+
+/* H4 receiver States */
+#define H4_W4_PACKET_TYPE	0
+#define H4_W4_EVENT_HDR		1
+#define H4_W4_ACL_HDR		2
+#define H4_W4_SCO_HDR		3
+#define H4_W4_DATA		4
+
+/* Initialize protocol */
+static int h4_open(struct hci_uart *hu)
+{
+	struct h4_struct *h4;
+
+	BT_DBG("hu %p", hu);
+
+	h4 = kzalloc(sizeof(*h4), GFP_KERNEL);
+	if (!h4)
+		return -ENOMEM;
+
+	skb_queue_head_init(&h4->txq);
+
+	hu->priv = h4;
+	return 0;
+}
+
+/* Flush protocol data */
+static int h4_flush(struct hci_uart *hu)
+{
+	struct h4_struct *h4 = hu->priv;
+
+	BT_DBG("hu %p", hu);
+
+	skb_queue_purge(&h4->txq);
+
+	return 0;
+}
+
+/* Close protocol */
+static int h4_close(struct hci_uart *hu)
+{
+	struct h4_struct *h4 = hu->priv;
+
+	hu->priv = NULL;
+
+	BT_DBG("hu %p", hu);
+
+	skb_queue_purge(&h4->txq);
+
+	kfree_skb(h4->rx_skb);
+
+	hu->priv = NULL;
+	kfree(h4);
+
+	return 0;
+}
+
+/* Enqueue frame for transmittion (padding, crc, etc) */
+static int h4_enqueue(struct hci_uart *hu, struct sk_buff *skb)
+{
+	struct h4_struct *h4 = hu->priv;
+
+	BT_DBG("hu %p skb %p", hu, skb);
+
+	/* Prepend skb with frame type */
+	memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
+	skb_queue_tail(&h4->txq, skb);
+
+	return 0;
+}
+
+static inline int h4_check_data_len(struct h4_struct *h4, int len)
+{
+	register int room = skb_tailroom(h4->rx_skb);
+
+	BT_DBG("len %d room %d", len, room);
+
+	if (!len) {
+		hci_recv_frame(h4->rx_skb);
+	} else if (len > room) {
+		BT_ERR("Data length is too large");
+		kfree_skb(h4->rx_skb);
+	} else {
+		h4->rx_state = H4_W4_DATA;
+		h4->rx_count = len;
+		return len;
+	}
+
+	h4->rx_state = H4_W4_PACKET_TYPE;
+	h4->rx_skb   = NULL;
+	h4->rx_count = 0;
+
+	return 0;
+}
+
+/* Recv data */
+static int h4_recv(struct hci_uart *hu, void *data, int count)
+{
+	int ret;
+
+	ret = hci_recv_stream_fragment(hu->hdev, data, count);
+	if (ret < 0) {
+		BT_ERR("Frame Reassembly Failed");
+		return ret;
+	}
+
+	return count;
+}
+
+static struct sk_buff *h4_dequeue(struct hci_uart *hu)
+{
+	struct h4_struct *h4 = hu->priv;
+	return skb_dequeue(&h4->txq);
+}
+
+static struct hci_uart_proto h4p = {
+	.id		= HCI_UART_H4,
+	.open		= h4_open,
+	.close		= h4_close,
+	.recv		= h4_recv,
+	.enqueue	= h4_enqueue,
+	.dequeue	= h4_dequeue,
+	.flush		= h4_flush,
+};
+
+int __init h4_init(void)
+{
+	int err = hci_uart_register_proto(&h4p);
+
+	if (!err)
+		BT_INFO("HCI H4 protocol initialized");
+	else
+		BT_ERR("HCI H4 protocol registration failed");
+
+	return err;
+}
+
+int __exit h4_deinit(void)
+{
+	return hci_uart_unregister_proto(&h4p);
+}
diff --git a/ap/os/linux/linux-3.4.x/drivers/bluetooth/hci_ldisc.c b/ap/os/linux/linux-3.4.x/drivers/bluetooth/hci_ldisc.c
new file mode 100644
index 0000000..d4550f9
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/bluetooth/hci_ldisc.c
@@ -0,0 +1,609 @@
+/*
+ *
+ *  Bluetooth HCI UART driver
+ *
+ *  Copyright (C) 2000-2001  Qualcomm Incorporated
+ *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
+ *  Copyright (C) 2004-2005  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/module.h>
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/poll.h>
+
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/signal.h>
+#include <linux/ioctl.h>
+#include <linux/skbuff.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#include "hci_uart.h"
+
+#define VERSION "2.2"
+
+static struct hci_uart_proto *hup[HCI_UART_MAX_PROTO];
+
+int hci_uart_register_proto(struct hci_uart_proto *p)
+{
+	if (p->id >= HCI_UART_MAX_PROTO)
+		return -EINVAL;
+
+	if (hup[p->id])
+		return -EEXIST;
+
+	hup[p->id] = p;
+
+	return 0;
+}
+
+int hci_uart_unregister_proto(struct hci_uart_proto *p)
+{
+	if (p->id >= HCI_UART_MAX_PROTO)
+		return -EINVAL;
+
+	if (!hup[p->id])
+		return -EINVAL;
+
+	hup[p->id] = NULL;
+
+	return 0;
+}
+
+static struct hci_uart_proto *hci_uart_get_proto(unsigned int id)
+{
+	if (id >= HCI_UART_MAX_PROTO)
+		return NULL;
+
+	return hup[id];
+}
+
+static inline void hci_uart_tx_complete(struct hci_uart *hu, int pkt_type)
+{
+	struct hci_dev *hdev = hu->hdev;
+
+	/* Update HCI stat counters */
+	switch (pkt_type) {
+	case HCI_COMMAND_PKT:
+		hdev->stat.cmd_tx++;
+		break;
+
+	case HCI_ACLDATA_PKT:
+		hdev->stat.acl_tx++;
+		break;
+
+	case HCI_SCODATA_PKT:
+		hdev->stat.sco_tx++;
+		break;
+	}
+}
+
+static inline struct sk_buff *hci_uart_dequeue(struct hci_uart *hu)
+{
+	struct sk_buff *skb = hu->tx_skb;
+
+	if (!skb)
+		skb = hu->proto->dequeue(hu);
+	else
+		hu->tx_skb = NULL;
+
+	return skb;
+}
+
+int hci_uart_tx_wakeup(struct hci_uart *hu)
+{
+	if (test_and_set_bit(HCI_UART_SENDING, &hu->tx_state)) {
+		set_bit(HCI_UART_TX_WAKEUP, &hu->tx_state);
+		return 0;
+	}
+
+	BT_DBG("");
+
+	schedule_work(&hu->write_work);
+
+	return 0;
+}
+
+static void hci_uart_write_work(struct work_struct *work)
+{
+	struct hci_uart *hu = container_of(work, struct hci_uart, write_work);
+	struct tty_struct *tty = hu->tty;
+	struct hci_dev *hdev = hu->hdev;
+	struct sk_buff *skb;
+
+	/* REVISIT: should we cope with bad skbs or ->write() returning
+	 * and error value ?
+	 */
+
+restart:
+	clear_bit(HCI_UART_TX_WAKEUP, &hu->tx_state);
+
+	while ((skb = hci_uart_dequeue(hu))) {
+		int len;
+
+		set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
+		len = tty->ops->write(tty, skb->data, skb->len);
+		hdev->stat.byte_tx += len;
+
+		skb_pull(skb, len);
+		if (skb->len) {
+			hu->tx_skb = skb;
+			break;
+		}
+
+		hci_uart_tx_complete(hu, bt_cb(skb)->pkt_type);
+		kfree_skb(skb);
+	}
+
+	if (test_bit(HCI_UART_TX_WAKEUP, &hu->tx_state))
+		goto restart;
+
+	clear_bit(HCI_UART_SENDING, &hu->tx_state);
+}
+
+/* ------- Interface to HCI layer ------ */
+/* Initialize device */
+static int hci_uart_open(struct hci_dev *hdev)
+{
+	BT_DBG("%s %p", hdev->name, hdev);
+
+	/* Nothing to do for UART driver */
+
+	set_bit(HCI_RUNNING, &hdev->flags);
+
+	return 0;
+}
+
+/* Reset device */
+static int hci_uart_flush(struct hci_dev *hdev)
+{
+	struct hci_uart *hu  = hci_get_drvdata(hdev);
+	struct tty_struct *tty = hu->tty;
+
+	BT_DBG("hdev %p tty %p", hdev, tty);
+
+	if (hu->tx_skb) {
+		kfree_skb(hu->tx_skb); hu->tx_skb = NULL;
+	}
+
+	/* Flush any pending characters in the driver and discipline. */
+	tty_ldisc_flush(tty);
+	tty_driver_flush_buffer(tty);
+
+	if (test_bit(HCI_UART_PROTO_SET, &hu->flags))
+		hu->proto->flush(hu);
+
+	return 0;
+}
+
+/* Close device */
+static int hci_uart_close(struct hci_dev *hdev)
+{
+	BT_DBG("hdev %p", hdev);
+
+	if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
+		return 0;
+
+	hci_uart_flush(hdev);
+	hdev->flush = NULL;
+	return 0;
+}
+
+/* Send frames from HCI layer */
+static int hci_uart_send_frame(struct sk_buff *skb)
+{
+	struct hci_dev* hdev = (struct hci_dev *) skb->dev;
+	struct hci_uart *hu;
+
+	if (!hdev) {
+		BT_ERR("Frame for unknown device (hdev=NULL)");
+		return -ENODEV;
+	}
+
+	if (!test_bit(HCI_RUNNING, &hdev->flags))
+		return -EBUSY;
+
+	hu = hci_get_drvdata(hdev);
+
+	BT_DBG("%s: type %d len %d", hdev->name, bt_cb(skb)->pkt_type, skb->len);
+
+	hu->proto->enqueue(hu, skb);
+
+	hci_uart_tx_wakeup(hu);
+
+	return 0;
+}
+
+/* ------ LDISC part ------ */
+/* hci_uart_tty_open
+ * 
+ *     Called when line discipline changed to HCI_UART.
+ *
+ * Arguments:
+ *     tty    pointer to tty info structure
+ * Return Value:    
+ *     0 if success, otherwise error code
+ */
+static int hci_uart_tty_open(struct tty_struct *tty)
+{
+	struct hci_uart *hu = (void *) tty->disc_data;
+
+	BT_DBG("tty %p", tty);
+
+	/* FIXME: This btw is bogus, nothing requires the old ldisc to clear
+	   the pointer */
+	if (hu)
+		return -EEXIST;
+
+	/* Error if the tty has no write op instead of leaving an exploitable
+	   hole */
+	if (tty->ops->write == NULL)
+		return -EOPNOTSUPP;
+
+	if (!(hu = kzalloc(sizeof(struct hci_uart), GFP_KERNEL))) {
+		BT_ERR("Can't allocate control structure");
+		return -ENFILE;
+	}
+
+	tty->disc_data = hu;
+	hu->tty = tty;
+	tty->receive_room = 65536;
+
+	INIT_WORK(&hu->write_work, hci_uart_write_work);
+
+	spin_lock_init(&hu->rx_lock);
+
+	/* Flush any pending characters in the driver and line discipline. */
+
+	/* FIXME: why is this needed. Note don't use ldisc_ref here as the
+	   open path is before the ldisc is referencable */
+
+	if (tty->ldisc->ops->flush_buffer)
+		tty->ldisc->ops->flush_buffer(tty);
+	tty_driver_flush_buffer(tty);
+
+	return 0;
+}
+
+/* hci_uart_tty_close()
+ *
+ *    Called when the line discipline is changed to something
+ *    else, the tty is closed, or the tty detects a hangup.
+ */
+static void hci_uart_tty_close(struct tty_struct *tty)
+{
+	struct hci_uart *hu = (void *)tty->disc_data;
+
+	BT_DBG("tty %p", tty);
+
+	/* Detach from the tty */
+	tty->disc_data = NULL;
+
+	if (hu) {
+		struct hci_dev *hdev = hu->hdev;
+
+		if (hdev)
+			hci_uart_close(hdev);
+
+		cancel_work_sync(&hu->write_work);
+
+		if (test_and_clear_bit(HCI_UART_PROTO_SET, &hu->flags)) {
+			if (hdev) {
+				hci_unregister_dev(hdev);
+				hci_free_dev(hdev);
+			}
+			hu->proto->close(hu);
+		}
+
+		kfree(hu);
+	}
+}
+
+/* hci_uart_tty_wakeup()
+ *
+ *    Callback for transmit wakeup. Called when low level
+ *    device driver can accept more send data.
+ *
+ * Arguments:        tty    pointer to associated tty instance data
+ * Return Value:    None
+ */
+static void hci_uart_tty_wakeup(struct tty_struct *tty)
+{
+	struct hci_uart *hu = (void *)tty->disc_data;
+
+	BT_DBG("");
+
+	if (!hu)
+		return;
+
+	clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
+
+	if (tty != hu->tty)
+		return;
+
+	if (test_bit(HCI_UART_PROTO_SET, &hu->flags))
+		hci_uart_tx_wakeup(hu);
+}
+
+/* hci_uart_tty_receive()
+ * 
+ *     Called by tty low level driver when receive data is
+ *     available.
+ *     
+ * Arguments:  tty          pointer to tty isntance data
+ *             data         pointer to received data
+ *             flags        pointer to flags for data
+ *             count        count of received data in bytes
+ *     
+ * Return Value:    None
+ */
+static void hci_uart_tty_receive(struct tty_struct *tty, const u8 *data, char *flags, int count)
+{
+	struct hci_uart *hu = (void *)tty->disc_data;
+
+	if (!hu || tty != hu->tty)
+		return;
+
+	if (!test_bit(HCI_UART_PROTO_SET, &hu->flags))
+		return;
+
+	spin_lock(&hu->rx_lock);
+	hu->proto->recv(hu, (void *) data, count);
+	hu->hdev->stat.byte_rx += count;
+	spin_unlock(&hu->rx_lock);
+
+	tty_unthrottle(tty);
+}
+
+static int hci_uart_register_dev(struct hci_uart *hu)
+{
+	struct hci_dev *hdev;
+
+	BT_DBG("");
+
+	/* Initialize and register HCI device */
+	hdev = hci_alloc_dev();
+	if (!hdev) {
+		BT_ERR("Can't allocate HCI device");
+		return -ENOMEM;
+	}
+
+	hu->hdev = hdev;
+
+	hdev->bus = HCI_UART;
+	hci_set_drvdata(hdev, hu);
+
+	hdev->open  = hci_uart_open;
+	hdev->close = hci_uart_close;
+	hdev->flush = hci_uart_flush;
+	hdev->send  = hci_uart_send_frame;
+	hdev->parent = hu->tty->dev;
+
+	if (test_bit(HCI_UART_RAW_DEVICE, &hu->hdev_flags))
+		set_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks);
+
+	if (!test_bit(HCI_UART_RESET_ON_INIT, &hu->hdev_flags))
+		set_bit(HCI_QUIRK_NO_RESET, &hdev->quirks);
+
+	if (test_bit(HCI_UART_CREATE_AMP, &hu->hdev_flags))
+		hdev->dev_type = HCI_AMP;
+	else
+		hdev->dev_type = HCI_BREDR;
+
+	if (hci_register_dev(hdev) < 0) {
+		BT_ERR("Can't register HCI device");
+		hci_free_dev(hdev);
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static int hci_uart_set_proto(struct hci_uart *hu, int id)
+{
+	struct hci_uart_proto *p;
+	int err;
+
+	p = hci_uart_get_proto(id);
+	if (!p)
+		return -EPROTONOSUPPORT;
+
+	err = p->open(hu);
+	if (err)
+		return err;
+
+	hu->proto = p;
+
+	err = hci_uart_register_dev(hu);
+	if (err) {
+		p->close(hu);
+		return err;
+	}
+
+	return 0;
+}
+
+/* hci_uart_tty_ioctl()
+ *
+ *    Process IOCTL system call for the tty device.
+ *
+ * Arguments:
+ *
+ *    tty        pointer to tty instance data
+ *    file       pointer to open file object for device
+ *    cmd        IOCTL command code
+ *    arg        argument for IOCTL call (cmd dependent)
+ *
+ * Return Value:    Command dependent
+ */
+static int hci_uart_tty_ioctl(struct tty_struct *tty, struct file * file,
+					unsigned int cmd, unsigned long arg)
+{
+	struct hci_uart *hu = (void *)tty->disc_data;
+	int err = 0;
+
+	BT_DBG("");
+
+	/* Verify the status of the device */
+	if (!hu)
+		return -EBADF;
+
+	switch (cmd) {
+	case HCIUARTSETPROTO:
+		if (!test_and_set_bit(HCI_UART_PROTO_SET, &hu->flags)) {
+			err = hci_uart_set_proto(hu, arg);
+			if (err) {
+				clear_bit(HCI_UART_PROTO_SET, &hu->flags);
+				return err;
+			}
+		} else
+			return -EBUSY;
+		break;
+
+	case HCIUARTGETPROTO:
+		if (test_bit(HCI_UART_PROTO_SET, &hu->flags))
+			return hu->proto->id;
+		return -EUNATCH;
+
+	case HCIUARTGETDEVICE:
+		if (test_bit(HCI_UART_PROTO_SET, &hu->flags))
+			return hu->hdev->id;
+		return -EUNATCH;
+
+	case HCIUARTSETFLAGS:
+		if (test_bit(HCI_UART_PROTO_SET, &hu->flags))
+			return -EBUSY;
+		hu->hdev_flags = arg;
+		break;
+
+	case HCIUARTGETFLAGS:
+		return hu->hdev_flags;
+
+	default:
+		err = n_tty_ioctl_helper(tty, file, cmd, arg);
+		break;
+	};
+
+	return err;
+}
+
+/*
+ * We don't provide read/write/poll interface for user space.
+ */
+static ssize_t hci_uart_tty_read(struct tty_struct *tty, struct file *file,
+					unsigned char __user *buf, size_t nr)
+{
+	return 0;
+}
+
+static ssize_t hci_uart_tty_write(struct tty_struct *tty, struct file *file,
+					const unsigned char *data, size_t count)
+{
+	return 0;
+}
+
+static unsigned int hci_uart_tty_poll(struct tty_struct *tty,
+					struct file *filp, poll_table *wait)
+{
+	return 0;
+}
+
+static int __init hci_uart_init(void)
+{
+	static struct tty_ldisc_ops hci_uart_ldisc;
+	int err;
+
+	BT_INFO("HCI UART driver ver %s", VERSION);
+
+	/* Register the tty discipline */
+
+	memset(&hci_uart_ldisc, 0, sizeof (hci_uart_ldisc));
+	hci_uart_ldisc.magic		= TTY_LDISC_MAGIC;
+	hci_uart_ldisc.name		= "n_hci";
+	hci_uart_ldisc.open		= hci_uart_tty_open;
+	hci_uart_ldisc.close		= hci_uart_tty_close;
+	hci_uart_ldisc.read		= hci_uart_tty_read;
+	hci_uart_ldisc.write		= hci_uart_tty_write;
+	hci_uart_ldisc.ioctl		= hci_uart_tty_ioctl;
+	hci_uart_ldisc.poll		= hci_uart_tty_poll;
+	hci_uart_ldisc.receive_buf	= hci_uart_tty_receive;
+	hci_uart_ldisc.write_wakeup	= hci_uart_tty_wakeup;
+	hci_uart_ldisc.owner		= THIS_MODULE;
+
+	if ((err = tty_register_ldisc(N_HCI, &hci_uart_ldisc))) {
+		BT_ERR("HCI line discipline registration failed. (%d)", err);
+		return err;
+	}
+
+#ifdef CONFIG_BT_HCIUART_H4
+	h4_init();
+#endif
+#ifdef CONFIG_BT_HCIUART_BCSP
+	bcsp_init();
+#endif
+#ifdef CONFIG_BT_HCIUART_LL
+	ll_init();
+#endif
+#ifdef CONFIG_BT_HCIUART_ATH3K
+	ath_init();
+#endif
+
+	return 0;
+}
+
+static void __exit hci_uart_exit(void)
+{
+	int err;
+
+#ifdef CONFIG_BT_HCIUART_H4
+	h4_deinit();
+#endif
+#ifdef CONFIG_BT_HCIUART_BCSP
+	bcsp_deinit();
+#endif
+#ifdef CONFIG_BT_HCIUART_LL
+	ll_deinit();
+#endif
+#ifdef CONFIG_BT_HCIUART_ATH3K
+	ath_deinit();
+#endif
+
+	/* Release tty registration of line discipline */
+	if ((err = tty_unregister_ldisc(N_HCI)))
+		BT_ERR("Can't unregister HCI line discipline (%d)", err);
+}
+
+module_init(hci_uart_init);
+module_exit(hci_uart_exit);
+
+MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
+MODULE_DESCRIPTION("Bluetooth HCI UART driver ver " VERSION);
+MODULE_VERSION(VERSION);
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_LDISC(N_HCI);
diff --git a/ap/os/linux/linux-3.4.x/drivers/bluetooth/hci_ll.c b/ap/os/linux/linux-3.4.x/drivers/bluetooth/hci_ll.c
new file mode 100644
index 0000000..b874c0e
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/bluetooth/hci_ll.c
@@ -0,0 +1,535 @@
+/*
+ *  Texas Instruments' Bluetooth HCILL UART protocol
+ *
+ *  HCILL (HCI Low Level) is a Texas Instruments' power management
+ *  protocol extension to H4.
+ *
+ *  Copyright (C) 2007 Texas Instruments, Inc.
+ *
+ *  Written by Ohad Ben-Cohen <ohad@bencohen.org>
+ *
+ *  Acknowledgements:
+ *  This file is based on hci_h4.c, which was written
+ *  by Maxim Krasnyansky and Marcel Holtmann.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2
+ *  as published by the Free Software Foundation
+ *
+ *  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/module.h>
+#include <linux/kernel.h>
+
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/poll.h>
+
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/signal.h>
+#include <linux/ioctl.h>
+#include <linux/skbuff.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#include "hci_uart.h"
+
+/* HCILL commands */
+#define HCILL_GO_TO_SLEEP_IND	0x30
+#define HCILL_GO_TO_SLEEP_ACK	0x31
+#define HCILL_WAKE_UP_IND	0x32
+#define HCILL_WAKE_UP_ACK	0x33
+
+/* HCILL receiver States */
+#define HCILL_W4_PACKET_TYPE	0
+#define HCILL_W4_EVENT_HDR	1
+#define HCILL_W4_ACL_HDR	2
+#define HCILL_W4_SCO_HDR	3
+#define HCILL_W4_DATA		4
+
+/* HCILL states */
+enum hcill_states_e {
+	HCILL_ASLEEP,
+	HCILL_ASLEEP_TO_AWAKE,
+	HCILL_AWAKE,
+	HCILL_AWAKE_TO_ASLEEP
+};
+
+struct hcill_cmd {
+	u8 cmd;
+} __packed;
+
+struct ll_struct {
+	unsigned long rx_state;
+	unsigned long rx_count;
+	struct sk_buff *rx_skb;
+	struct sk_buff_head txq;
+	spinlock_t hcill_lock;		/* HCILL state lock	*/
+	unsigned long hcill_state;	/* HCILL power state	*/
+	struct sk_buff_head tx_wait_q;	/* HCILL wait queue	*/
+};
+
+/*
+ * Builds and sends an HCILL command packet.
+ * These are very simple packets with only 1 cmd byte
+ */
+static int send_hcill_cmd(u8 cmd, struct hci_uart *hu)
+{
+	int err = 0;
+	struct sk_buff *skb = NULL;
+	struct ll_struct *ll = hu->priv;
+	struct hcill_cmd *hcill_packet;
+
+	BT_DBG("hu %p cmd 0x%x", hu, cmd);
+
+	/* allocate packet */
+	skb = bt_skb_alloc(1, GFP_ATOMIC);
+	if (!skb) {
+		BT_ERR("cannot allocate memory for HCILL packet");
+		err = -ENOMEM;
+		goto out;
+	}
+
+	/* prepare packet */
+	hcill_packet = (struct hcill_cmd *) skb_put(skb, 1);
+	hcill_packet->cmd = cmd;
+	skb->dev = (void *) hu->hdev;
+
+	/* send packet */
+	skb_queue_tail(&ll->txq, skb);
+out:
+	return err;
+}
+
+/* Initialize protocol */
+static int ll_open(struct hci_uart *hu)
+{
+	struct ll_struct *ll;
+
+	BT_DBG("hu %p", hu);
+
+	ll = kzalloc(sizeof(*ll), GFP_KERNEL);
+	if (!ll)
+		return -ENOMEM;
+
+	skb_queue_head_init(&ll->txq);
+	skb_queue_head_init(&ll->tx_wait_q);
+	spin_lock_init(&ll->hcill_lock);
+
+	ll->hcill_state = HCILL_AWAKE;
+
+	hu->priv = ll;
+
+	return 0;
+}
+
+/* Flush protocol data */
+static int ll_flush(struct hci_uart *hu)
+{
+	struct ll_struct *ll = hu->priv;
+
+	BT_DBG("hu %p", hu);
+
+	skb_queue_purge(&ll->tx_wait_q);
+	skb_queue_purge(&ll->txq);
+
+	return 0;
+}
+
+/* Close protocol */
+static int ll_close(struct hci_uart *hu)
+{
+	struct ll_struct *ll = hu->priv;
+
+	BT_DBG("hu %p", hu);
+
+	skb_queue_purge(&ll->tx_wait_q);
+	skb_queue_purge(&ll->txq);
+
+	kfree_skb(ll->rx_skb);
+
+	hu->priv = NULL;
+
+	kfree(ll);
+
+	return 0;
+}
+
+/*
+ * internal function, which does common work of the device wake up process:
+ * 1. places all pending packets (waiting in tx_wait_q list) in txq list.
+ * 2. changes internal state to HCILL_AWAKE.
+ * Note: assumes that hcill_lock spinlock is taken,
+ * shouldn't be called otherwise!
+ */
+static void __ll_do_awake(struct ll_struct *ll)
+{
+	struct sk_buff *skb = NULL;
+
+	while ((skb = skb_dequeue(&ll->tx_wait_q)))
+		skb_queue_tail(&ll->txq, skb);
+
+	ll->hcill_state = HCILL_AWAKE;
+}
+
+/*
+ * Called upon a wake-up-indication from the device
+ */
+static void ll_device_want_to_wakeup(struct hci_uart *hu)
+{
+	unsigned long flags;
+	struct ll_struct *ll = hu->priv;
+
+	BT_DBG("hu %p", hu);
+
+	/* lock hcill state */
+	spin_lock_irqsave(&ll->hcill_lock, flags);
+
+	switch (ll->hcill_state) {
+	case HCILL_ASLEEP_TO_AWAKE:
+		/*
+		 * This state means that both the host and the BRF chip
+		 * have simultaneously sent a wake-up-indication packet.
+		 * Traditionally, in this case, receiving a wake-up-indication
+		 * was enough and an additional wake-up-ack wasn't needed.
+		 * This has changed with the BRF6350, which does require an
+		 * explicit wake-up-ack. Other BRF versions, which do not
+		 * require an explicit ack here, do accept it, thus it is
+		 * perfectly safe to always send one.
+		 */
+		BT_DBG("dual wake-up-indication");
+		/* deliberate fall-through - do not add break */
+	case HCILL_ASLEEP:
+		/* acknowledge device wake up */
+		if (send_hcill_cmd(HCILL_WAKE_UP_ACK, hu) < 0) {
+			BT_ERR("cannot acknowledge device wake up");
+			goto out;
+		}
+		break;
+	default:
+		/* any other state is illegal */
+		BT_ERR("received HCILL_WAKE_UP_IND in state %ld", ll->hcill_state);
+		break;
+	}
+
+	/* send pending packets and change state to HCILL_AWAKE */
+	__ll_do_awake(ll);
+
+out:
+	spin_unlock_irqrestore(&ll->hcill_lock, flags);
+
+	/* actually send the packets */
+	hci_uart_tx_wakeup(hu);
+}
+
+/*
+ * Called upon a sleep-indication from the device
+ */
+static void ll_device_want_to_sleep(struct hci_uart *hu)
+{
+	unsigned long flags;
+	struct ll_struct *ll = hu->priv;
+
+	BT_DBG("hu %p", hu);
+
+	/* lock hcill state */
+	spin_lock_irqsave(&ll->hcill_lock, flags);
+
+	/* sanity check */
+	if (ll->hcill_state != HCILL_AWAKE)
+		BT_ERR("ERR: HCILL_GO_TO_SLEEP_IND in state %ld", ll->hcill_state);
+
+	/* acknowledge device sleep */
+	if (send_hcill_cmd(HCILL_GO_TO_SLEEP_ACK, hu) < 0) {
+		BT_ERR("cannot acknowledge device sleep");
+		goto out;
+	}
+
+	/* update state */
+	ll->hcill_state = HCILL_ASLEEP;
+
+out:
+	spin_unlock_irqrestore(&ll->hcill_lock, flags);
+
+	/* actually send the sleep ack packet */
+	hci_uart_tx_wakeup(hu);
+}
+
+/*
+ * Called upon wake-up-acknowledgement from the device
+ */
+static void ll_device_woke_up(struct hci_uart *hu)
+{
+	unsigned long flags;
+	struct ll_struct *ll = hu->priv;
+
+	BT_DBG("hu %p", hu);
+
+	/* lock hcill state */
+	spin_lock_irqsave(&ll->hcill_lock, flags);
+
+	/* sanity check */
+	if (ll->hcill_state != HCILL_ASLEEP_TO_AWAKE)
+		BT_ERR("received HCILL_WAKE_UP_ACK in state %ld", ll->hcill_state);
+
+	/* send pending packets and change state to HCILL_AWAKE */
+	__ll_do_awake(ll);
+
+	spin_unlock_irqrestore(&ll->hcill_lock, flags);
+
+	/* actually send the packets */
+	hci_uart_tx_wakeup(hu);
+}
+
+/* Enqueue frame for transmittion (padding, crc, etc) */
+/* may be called from two simultaneous tasklets */
+static int ll_enqueue(struct hci_uart *hu, struct sk_buff *skb)
+{
+	unsigned long flags = 0;
+	struct ll_struct *ll = hu->priv;
+
+	BT_DBG("hu %p skb %p", hu, skb);
+
+	/* Prepend skb with frame type */
+	memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
+
+	/* lock hcill state */
+	spin_lock_irqsave(&ll->hcill_lock, flags);
+
+	/* act according to current state */
+	switch (ll->hcill_state) {
+	case HCILL_AWAKE:
+		BT_DBG("device awake, sending normally");
+		skb_queue_tail(&ll->txq, skb);
+		break;
+	case HCILL_ASLEEP:
+		BT_DBG("device asleep, waking up and queueing packet");
+		/* save packet for later */
+		skb_queue_tail(&ll->tx_wait_q, skb);
+		/* awake device */
+		if (send_hcill_cmd(HCILL_WAKE_UP_IND, hu) < 0) {
+			BT_ERR("cannot wake up device");
+			break;
+		}
+		ll->hcill_state = HCILL_ASLEEP_TO_AWAKE;
+		break;
+	case HCILL_ASLEEP_TO_AWAKE:
+		BT_DBG("device waking up, queueing packet");
+		/* transient state; just keep packet for later */
+		skb_queue_tail(&ll->tx_wait_q, skb);
+		break;
+	default:
+		BT_ERR("illegal hcill state: %ld (losing packet)", ll->hcill_state);
+		kfree_skb(skb);
+		break;
+	}
+
+	spin_unlock_irqrestore(&ll->hcill_lock, flags);
+
+	return 0;
+}
+
+static inline int ll_check_data_len(struct ll_struct *ll, int len)
+{
+	register int room = skb_tailroom(ll->rx_skb);
+
+	BT_DBG("len %d room %d", len, room);
+
+	if (!len) {
+		hci_recv_frame(ll->rx_skb);
+	} else if (len > room) {
+		BT_ERR("Data length is too large");
+		kfree_skb(ll->rx_skb);
+	} else {
+		ll->rx_state = HCILL_W4_DATA;
+		ll->rx_count = len;
+		return len;
+	}
+
+	ll->rx_state = HCILL_W4_PACKET_TYPE;
+	ll->rx_skb   = NULL;
+	ll->rx_count = 0;
+
+	return 0;
+}
+
+/* Recv data */
+static int ll_recv(struct hci_uart *hu, void *data, int count)
+{
+	struct ll_struct *ll = hu->priv;
+	register char *ptr;
+	struct hci_event_hdr *eh;
+	struct hci_acl_hdr   *ah;
+	struct hci_sco_hdr   *sh;
+	register int len, type, dlen;
+
+	BT_DBG("hu %p count %d rx_state %ld rx_count %ld", hu, count, ll->rx_state, ll->rx_count);
+
+	ptr = data;
+	while (count) {
+		if (ll->rx_count) {
+			len = min_t(unsigned int, ll->rx_count, count);
+			memcpy(skb_put(ll->rx_skb, len), ptr, len);
+			ll->rx_count -= len; count -= len; ptr += len;
+
+			if (ll->rx_count)
+				continue;
+
+			switch (ll->rx_state) {
+			case HCILL_W4_DATA:
+				BT_DBG("Complete data");
+				hci_recv_frame(ll->rx_skb);
+
+				ll->rx_state = HCILL_W4_PACKET_TYPE;
+				ll->rx_skb = NULL;
+				continue;
+
+			case HCILL_W4_EVENT_HDR:
+				eh = hci_event_hdr(ll->rx_skb);
+
+				BT_DBG("Event header: evt 0x%2.2x plen %d", eh->evt, eh->plen);
+
+				ll_check_data_len(ll, eh->plen);
+				continue;
+
+			case HCILL_W4_ACL_HDR:
+				ah = hci_acl_hdr(ll->rx_skb);
+				dlen = __le16_to_cpu(ah->dlen);
+
+				BT_DBG("ACL header: dlen %d", dlen);
+
+				ll_check_data_len(ll, dlen);
+				continue;
+
+			case HCILL_W4_SCO_HDR:
+				sh = hci_sco_hdr(ll->rx_skb);
+
+				BT_DBG("SCO header: dlen %d", sh->dlen);
+
+				ll_check_data_len(ll, sh->dlen);
+				continue;
+			}
+		}
+
+		/* HCILL_W4_PACKET_TYPE */
+		switch (*ptr) {
+		case HCI_EVENT_PKT:
+			BT_DBG("Event packet");
+			ll->rx_state = HCILL_W4_EVENT_HDR;
+			ll->rx_count = HCI_EVENT_HDR_SIZE;
+			type = HCI_EVENT_PKT;
+			break;
+
+		case HCI_ACLDATA_PKT:
+			BT_DBG("ACL packet");
+			ll->rx_state = HCILL_W4_ACL_HDR;
+			ll->rx_count = HCI_ACL_HDR_SIZE;
+			type = HCI_ACLDATA_PKT;
+			break;
+
+		case HCI_SCODATA_PKT:
+			BT_DBG("SCO packet");
+			ll->rx_state = HCILL_W4_SCO_HDR;
+			ll->rx_count = HCI_SCO_HDR_SIZE;
+			type = HCI_SCODATA_PKT;
+			break;
+
+		/* HCILL signals */
+		case HCILL_GO_TO_SLEEP_IND:
+			BT_DBG("HCILL_GO_TO_SLEEP_IND packet");
+			ll_device_want_to_sleep(hu);
+			ptr++; count--;
+			continue;
+
+		case HCILL_GO_TO_SLEEP_ACK:
+			/* shouldn't happen */
+			BT_ERR("received HCILL_GO_TO_SLEEP_ACK (in state %ld)", ll->hcill_state);
+			ptr++; count--;
+			continue;
+
+		case HCILL_WAKE_UP_IND:
+			BT_DBG("HCILL_WAKE_UP_IND packet");
+			ll_device_want_to_wakeup(hu);
+			ptr++; count--;
+			continue;
+
+		case HCILL_WAKE_UP_ACK:
+			BT_DBG("HCILL_WAKE_UP_ACK packet");
+			ll_device_woke_up(hu);
+			ptr++; count--;
+			continue;
+
+		default:
+			BT_ERR("Unknown HCI packet type %2.2x", (__u8)*ptr);
+			hu->hdev->stat.err_rx++;
+			ptr++; count--;
+			continue;
+		};
+
+		ptr++; count--;
+
+		/* Allocate packet */
+		ll->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC);
+		if (!ll->rx_skb) {
+			BT_ERR("Can't allocate mem for new packet");
+			ll->rx_state = HCILL_W4_PACKET_TYPE;
+			ll->rx_count = 0;
+			return -ENOMEM;
+		}
+
+		ll->rx_skb->dev = (void *) hu->hdev;
+		bt_cb(ll->rx_skb)->pkt_type = type;
+	}
+
+	return count;
+}
+
+static struct sk_buff *ll_dequeue(struct hci_uart *hu)
+{
+	struct ll_struct *ll = hu->priv;
+	return skb_dequeue(&ll->txq);
+}
+
+static struct hci_uart_proto llp = {
+	.id		= HCI_UART_LL,
+	.open		= ll_open,
+	.close		= ll_close,
+	.recv		= ll_recv,
+	.enqueue	= ll_enqueue,
+	.dequeue	= ll_dequeue,
+	.flush		= ll_flush,
+};
+
+int __init ll_init(void)
+{
+	int err = hci_uart_register_proto(&llp);
+
+	if (!err)
+		BT_INFO("HCILL protocol initialized");
+	else
+		BT_ERR("HCILL protocol registration failed");
+
+	return err;
+}
+
+int __exit ll_deinit(void)
+{
+	return hci_uart_unregister_proto(&llp);
+}
diff --git a/ap/os/linux/linux-3.4.x/drivers/bluetooth/hci_uart.h b/ap/os/linux/linux-3.4.x/drivers/bluetooth/hci_uart.h
new file mode 100644
index 0000000..af93d83
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/bluetooth/hci_uart.h
@@ -0,0 +1,108 @@
+/*
+ *
+ *  Bluetooth HCI UART driver
+ *
+ *  Copyright (C) 2000-2001  Qualcomm Incorporated
+ *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
+ *  Copyright (C) 2004-2005  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
+ *
+ */
+
+#ifndef N_HCI
+#define N_HCI	15
+#endif
+
+/* Ioctls */
+#define HCIUARTSETPROTO		_IOW('U', 200, int)
+#define HCIUARTGETPROTO		_IOR('U', 201, int)
+#define HCIUARTGETDEVICE	_IOR('U', 202, int)
+#define HCIUARTSETFLAGS		_IOW('U', 203, int)
+#define HCIUARTGETFLAGS		_IOR('U', 204, int)
+
+/* UART protocols */
+#define HCI_UART_MAX_PROTO	6
+
+#define HCI_UART_H4	0
+#define HCI_UART_BCSP	1
+#define HCI_UART_3WIRE	2
+#define HCI_UART_H4DS	3
+#define HCI_UART_LL	4
+#define HCI_UART_ATH3K	5
+
+#define HCI_UART_RAW_DEVICE	0
+#define HCI_UART_RESET_ON_INIT	1
+#define HCI_UART_CREATE_AMP	2
+
+struct hci_uart;
+
+struct hci_uart_proto {
+	unsigned int id;
+	int (*open)(struct hci_uart *hu);
+	int (*close)(struct hci_uart *hu);
+	int (*flush)(struct hci_uart *hu);
+	int (*recv)(struct hci_uart *hu, void *data, int len);
+	int (*enqueue)(struct hci_uart *hu, struct sk_buff *skb);
+	struct sk_buff *(*dequeue)(struct hci_uart *hu);
+};
+
+struct hci_uart {
+	struct tty_struct	*tty;
+	struct hci_dev		*hdev;
+	unsigned long		flags;
+	unsigned long		hdev_flags;
+
+	struct work_struct	write_work;
+
+	struct hci_uart_proto	*proto;
+	void			*priv;
+
+	struct sk_buff		*tx_skb;
+	unsigned long		tx_state;
+	spinlock_t		rx_lock;
+};
+
+/* HCI_UART proto flag bits */
+#define HCI_UART_PROTO_SET	0
+
+/* TX states  */
+#define HCI_UART_SENDING	1
+#define HCI_UART_TX_WAKEUP	2
+
+int hci_uart_register_proto(struct hci_uart_proto *p);
+int hci_uart_unregister_proto(struct hci_uart_proto *p);
+int hci_uart_tx_wakeup(struct hci_uart *hu);
+
+#ifdef CONFIG_BT_HCIUART_H4
+int h4_init(void);
+int h4_deinit(void);
+#endif
+
+#ifdef CONFIG_BT_HCIUART_BCSP
+int bcsp_init(void);
+int bcsp_deinit(void);
+#endif
+
+#ifdef CONFIG_BT_HCIUART_LL
+int ll_init(void);
+int ll_deinit(void);
+#endif
+
+#ifdef CONFIG_BT_HCIUART_ATH3K
+int ath_init(void);
+int ath_deinit(void);
+#endif
diff --git a/ap/os/linux/linux-3.4.x/drivers/bluetooth/hci_vhci.c b/ap/os/linux/linux-3.4.x/drivers/bluetooth/hci_vhci.c
new file mode 100644
index 0000000..158bfe5
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/bluetooth/hci_vhci.c
@@ -0,0 +1,310 @@
+/*
+ *
+ *  Bluetooth virtual HCI driver
+ *
+ *  Copyright (C) 2000-2001  Qualcomm Incorporated
+ *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
+ *  Copyright (C) 2004-2006  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/module.h>
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/poll.h>
+
+#include <linux/skbuff.h>
+#include <linux/miscdevice.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#define VERSION "1.3"
+
+static bool amp;
+
+struct vhci_data {
+	struct hci_dev *hdev;
+
+	unsigned long flags;
+
+	wait_queue_head_t read_wait;
+	struct sk_buff_head readq;
+};
+
+static int vhci_open_dev(struct hci_dev *hdev)
+{
+	set_bit(HCI_RUNNING, &hdev->flags);
+
+	return 0;
+}
+
+static int vhci_close_dev(struct hci_dev *hdev)
+{
+	struct vhci_data *data = hci_get_drvdata(hdev);
+
+	if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
+		return 0;
+
+	skb_queue_purge(&data->readq);
+
+	return 0;
+}
+
+static int vhci_flush(struct hci_dev *hdev)
+{
+	struct vhci_data *data = hci_get_drvdata(hdev);
+
+	skb_queue_purge(&data->readq);
+
+	return 0;
+}
+
+static int vhci_send_frame(struct sk_buff *skb)
+{
+	struct hci_dev* hdev = (struct hci_dev *) skb->dev;
+	struct vhci_data *data;
+
+	if (!hdev) {
+		BT_ERR("Frame for unknown HCI device (hdev=NULL)");
+		return -ENODEV;
+	}
+
+	if (!test_bit(HCI_RUNNING, &hdev->flags))
+		return -EBUSY;
+
+	data = hci_get_drvdata(hdev);
+
+	memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
+	skb_queue_tail(&data->readq, skb);
+
+	wake_up_interruptible(&data->read_wait);
+
+	return 0;
+}
+
+static inline ssize_t vhci_get_user(struct vhci_data *data,
+					const char __user *buf, size_t count)
+{
+	struct sk_buff *skb;
+
+	if (count > HCI_MAX_FRAME_SIZE)
+		return -EINVAL;
+
+	skb = bt_skb_alloc(count, GFP_KERNEL);
+	if (!skb)
+		return -ENOMEM;
+
+	if (copy_from_user(skb_put(skb, count), buf, count)) {
+		kfree_skb(skb);
+		return -EFAULT;
+	}
+
+	skb->dev = (void *) data->hdev;
+	bt_cb(skb)->pkt_type = *((__u8 *) skb->data);
+	skb_pull(skb, 1);
+
+	hci_recv_frame(skb);
+
+	return count;
+}
+
+static inline ssize_t vhci_put_user(struct vhci_data *data,
+			struct sk_buff *skb, char __user *buf, int count)
+{
+	char __user *ptr = buf;
+	int len, total = 0;
+
+	len = min_t(unsigned int, skb->len, count);
+
+	if (copy_to_user(ptr, skb->data, len))
+		return -EFAULT;
+
+	total += len;
+
+	data->hdev->stat.byte_tx += len;
+
+	switch (bt_cb(skb)->pkt_type) {
+	case HCI_COMMAND_PKT:
+		data->hdev->stat.cmd_tx++;
+		break;
+
+	case HCI_ACLDATA_PKT:
+		data->hdev->stat.acl_tx++;
+		break;
+
+	case HCI_SCODATA_PKT:
+		data->hdev->stat.sco_tx++;
+		break;
+	};
+
+	return total;
+}
+
+static ssize_t vhci_read(struct file *file,
+				char __user *buf, size_t count, loff_t *pos)
+{
+	struct vhci_data *data = file->private_data;
+	struct sk_buff *skb;
+	ssize_t ret = 0;
+
+	while (count) {
+		skb = skb_dequeue(&data->readq);
+		if (skb) {
+			ret = vhci_put_user(data, skb, buf, count);
+			if (ret < 0)
+				skb_queue_head(&data->readq, skb);
+			else
+				kfree_skb(skb);
+			break;
+		}
+
+		if (file->f_flags & O_NONBLOCK) {
+			ret = -EAGAIN;
+			break;
+		}
+
+		ret = wait_event_interruptible(data->read_wait,
+					!skb_queue_empty(&data->readq));
+		if (ret < 0)
+			break;
+	}
+
+	return ret;
+}
+
+static ssize_t vhci_write(struct file *file,
+			const char __user *buf, size_t count, loff_t *pos)
+{
+	struct vhci_data *data = file->private_data;
+
+	return vhci_get_user(data, buf, count);
+}
+
+static unsigned int vhci_poll(struct file *file, poll_table *wait)
+{
+	struct vhci_data *data = file->private_data;
+
+	poll_wait(file, &data->read_wait, wait);
+
+	if (!skb_queue_empty(&data->readq))
+		return POLLIN | POLLRDNORM;
+
+	return POLLOUT | POLLWRNORM;
+}
+
+static int vhci_open(struct inode *inode, struct file *file)
+{
+	struct vhci_data *data;
+	struct hci_dev *hdev;
+
+	data = kzalloc(sizeof(struct vhci_data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	skb_queue_head_init(&data->readq);
+	init_waitqueue_head(&data->read_wait);
+
+	hdev = hci_alloc_dev();
+	if (!hdev) {
+		kfree(data);
+		return -ENOMEM;
+	}
+
+	data->hdev = hdev;
+
+	hdev->bus = HCI_VIRTUAL;
+	hci_set_drvdata(hdev, data);
+
+	if (amp)
+		hdev->dev_type = HCI_AMP;
+
+	hdev->open     = vhci_open_dev;
+	hdev->close    = vhci_close_dev;
+	hdev->flush    = vhci_flush;
+	hdev->send     = vhci_send_frame;
+
+	if (hci_register_dev(hdev) < 0) {
+		BT_ERR("Can't register HCI device");
+		kfree(data);
+		hci_free_dev(hdev);
+		return -EBUSY;
+	}
+
+	file->private_data = data;
+
+	return nonseekable_open(inode, file);
+}
+
+static int vhci_release(struct inode *inode, struct file *file)
+{
+	struct vhci_data *data = file->private_data;
+	struct hci_dev *hdev = data->hdev;
+
+	hci_unregister_dev(hdev);
+	hci_free_dev(hdev);
+
+	file->private_data = NULL;
+	kfree(data);
+
+	return 0;
+}
+
+static const struct file_operations vhci_fops = {
+	.owner		= THIS_MODULE,
+	.read		= vhci_read,
+	.write		= vhci_write,
+	.poll		= vhci_poll,
+	.open		= vhci_open,
+	.release	= vhci_release,
+	.llseek		= no_llseek,
+};
+
+static struct miscdevice vhci_miscdev= {
+	.name	= "vhci",
+	.fops	= &vhci_fops,
+	.minor	= MISC_DYNAMIC_MINOR,
+};
+
+static int __init vhci_init(void)
+{
+	BT_INFO("Virtual HCI driver ver %s", VERSION);
+
+	return misc_register(&vhci_miscdev);
+}
+
+static void __exit vhci_exit(void)
+{
+	misc_deregister(&vhci_miscdev);
+}
+
+module_init(vhci_init);
+module_exit(vhci_exit);
+
+module_param(amp, bool, 0644);
+MODULE_PARM_DESC(amp, "Create AMP controller device");
+
+MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
+MODULE_DESCRIPTION("Bluetooth virtual HCI driver ver " VERSION);
+MODULE_VERSION(VERSION);
+MODULE_LICENSE("GPL");