--- /dev/null
+Reason for this not being merged upstream:
+
+It was first rejected without any alternatives suggested. Then
+an alternative was finally suggested but too late for integration
+upstream.
+
+This is a work around until a new firmware and patch is being worked
+on which is acceptable upstream.
+
+For details refer to:
+
+http://lkml.org/lkml/2010/10/5/195
+
+From 4ac276c14578b380d0c6a27658eeaa364efe6432 Mon Sep 17 00:00:00 2001
+From: Bala Shanmugam <sbalashanmugam@atheros.com>
+Date: Fri, 1 Oct 2010 15:18:02 +0530
+Subject: [PATCH] Added support to load firmware to target RAM from btusb transport driver.
+ Each BT device vendor can add their product ID, firmware file, load and unload function
+ to btusb_fwcbs array. When the device is inserted btusb will call appropriate
+ firmware load function. This support will significantly reduce cost of
+ BT chip because of RAM based firmware.
+ Signed-off-by: Bala Shanmugam <sbalashanmugam@atheros.com>
+
+---
+ drivers/bluetooth/Makefile | 1 +
+ drivers/bluetooth/btusb.c | 67 +++++++++++++++
+ drivers/bluetooth/fwload.c | 199 ++++++++++++++++++++++++++++++++++++++++++++
+ drivers/bluetooth/fwload.h | 39 +++++++++
+ 4 files changed, 306 insertions(+), 0 deletions(-)
+ create mode 100644 drivers/bluetooth/fwload.c
+ create mode 100644 drivers/bluetooth/fwload.h
+
+diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile
+index 71bdf13..5a55cbb 100644
+--- a/drivers/bluetooth/Makefile
++++ b/drivers/bluetooth/Makefile
+@@ -13,6 +13,7 @@ obj-$(CONFIG_BT_HCIBLUECARD) += bluecard_cs.o
+ obj-$(CONFIG_BT_HCIBTUART) += btuart_cs.o
+
+ obj-$(CONFIG_BT_HCIBTUSB) += btusb.o
++obj-$(CONFIG_BT_HCIBTUSB) += fwload.o
+ obj-$(CONFIG_BT_HCIBTSDIO) += btsdio.o
+
+ obj-$(CONFIG_BT_ATH3K) += ath3k.o
+diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
+index d22ce3c..13e0fb8 100644
+--- a/drivers/bluetooth/btusb.c
++++ b/drivers/bluetooth/btusb.c
+@@ -34,6 +34,7 @@
+
+ #include <net/bluetooth/bluetooth.h>
+ #include <net/bluetooth/hci_core.h>
++#include "fwload.h"
+
+ #define VERSION "0.6"
+
+@@ -55,6 +56,26 @@ static struct usb_driver btusb_driver;
+ #define BTUSB_BROKEN_ISOC 0x20
+ #define BTUSB_WRONG_SCO_MTU 0x40
+
++static struct usb_device_id ath_table[] = {
++ /* Atheros AR3011 */
++ { USB_DEVICE(0x0CF3, 0x3002) },
++ { USB_DEVICE(0x13D3, 0x3304) },
++ { } /* Terminating entry */
++};
++
++/* Add firmware file, load and unload function
++ * to download the firmware to target RAM
++ */
++static struct fw_cb_config btusb_fwcbs[] = {
++ {
++ .fwfile = "ath3k-1.fw",
++ .usb_id_table = ath_table,
++ .fwload = ath_fw_load,
++ .fwunload = ath_fw_unload
++ },
++ {}
++};
++
+ static struct usb_device_id btusb_table[] = {
+ /* Generic Bluetooth USB device */
+ { USB_DEVICE_INFO(0xe0, 0x01, 0x01) },
+@@ -863,6 +884,7 @@ static int btusb_probe(struct usb_interface *intf,
+ struct btusb_data *data;
+ struct hci_dev *hdev;
+ int i, err;
++ const struct usb_device_id *match;
+
+ BT_DBG("intf %p id %p", intf, id);
+
+@@ -922,6 +944,19 @@ static int btusb_probe(struct usb_interface *intf,
+ data->udev = interface_to_usbdev(intf);
+ data->intf = intf;
+
++ for (i = 0; btusb_fwcbs[i].fwfile; i++) {
++ match = usb_match_id(intf, btusb_fwcbs[i].usb_id_table);
++ if (match) {
++ if (btusb_fwcbs[i].fwload) {
++ btusb_fwcbs[i].data =
++ btusb_fwcbs[i].fwload(intf,
++ btusb_fwcbs[i].fwfile,
++ &btusb_fwcbs[i].bsuspend);
++ }
++ break;
++ }
++ }
++
+ spin_lock_init(&data->lock);
+
+ INIT_WORK(&data->work, btusb_work);
+@@ -1030,12 +1065,26 @@ static void btusb_disconnect(struct usb_interface *intf)
+ {
+ struct btusb_data *data = usb_get_intfdata(intf);
+ struct hci_dev *hdev;
++ const struct usb_device_id *match;
++ int i;
+
+ BT_DBG("intf %p", intf);
+
+ if (!data)
+ return;
+
++ for (i = 0; btusb_fwcbs[i].fwfile; i++) {
++ match = usb_match_id(intf, btusb_fwcbs[i].usb_id_table);
++ if (match) {
++ if (btusb_fwcbs[i].fwunload) {
++ btusb_fwcbs[i].fwunload(btusb_fwcbs[i].data,
++ btusb_fwcbs[i].bsuspend);
++ btusb_fwcbs[i].data = NULL;
++ }
++ break;
++ }
++ }
++
+ hdev = data->hdev;
+
+ __hci_dev_hold(hdev);
+@@ -1061,12 +1110,22 @@ static void btusb_disconnect(struct usb_interface *intf)
+ static int btusb_suspend(struct usb_interface *intf, pm_message_t message)
+ {
+ struct btusb_data *data = usb_get_intfdata(intf);
++ const struct usb_device_id *match;
++ int i;
+
+ BT_DBG("intf %p", intf);
+
+ if (data->suspend_count++)
+ return 0;
+
++ for (i = 0; btusb_fwcbs[i].fwfile; i++) {
++ match = usb_match_id(intf, btusb_fwcbs[i].usb_id_table);
++ if (match) {
++ btusb_fwcbs[i].bsuspend = 1;
++ break;
++ }
++ }
++
+ spin_lock_irq(&data->txlock);
+ if (!((message.event & PM_EVENT_AUTO) && data->tx_in_flight)) {
+ set_bit(BTUSB_SUSPENDING, &data->flags);
+@@ -1179,6 +1238,14 @@ static int __init btusb_init(void)
+
+ static void __exit btusb_exit(void)
+ {
++ int i;
++ for (i = 0; btusb_fwcbs[i].fwfile; i++) {
++ if (btusb_fwcbs[i].fwunload && btusb_fwcbs[i].data) {
++ btusb_fwcbs[i].fwunload(btusb_fwcbs[i].data,
++ btusb_fwcbs[i].bsuspend);
++ btusb_fwcbs[i].data = NULL;
++ }
++ }
+ usb_deregister(&btusb_driver);
+ }
+
+diff --git a/drivers/bluetooth/fwload.c b/drivers/bluetooth/fwload.c
+new file mode 100644
+index 0000000..a9a586a
+--- /dev/null
++++ b/drivers/bluetooth/fwload.c
+@@ -0,0 +1,199 @@
++/*
++ *
++ * Generic Bluetooth USB DFU driver to download firmware to target RAM
++ *
++ * Copyright (c) 2009-2010 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/device.h>
++#include <linux/firmware.h>
++#include <linux/usb.h>
++#include <net/bluetooth/bluetooth.h>
++
++#define USB_REQ_DFU_DNLOAD 1
++#define USB_REQ_GET_STATE 5
++#define USB_FIRMWARE_RAM_MODE 11
++#define USB_FIRMWARE_FLASH_MODE 12
++#define BULK_SIZE 4096
++#define VERSION "1.0"
++
++struct firmware_data {
++ struct usb_device *udev;
++ u8 *fw_data;
++ u32 fw_size;
++ u32 fw_sent;
++};
++
++static int load_firmware(struct firmware_data *data,
++ unsigned char *firmware,
++ int count)
++{
++ u8 *send_buf;
++ int err, pipe, len, size, sent = 0;
++ char ucFirmware = 0;
++
++ BT_DBG("ath3k %p udev %p", data, data->udev);
++
++ if ((usb_control_msg(data->udev, usb_rcvctrlpipe(data->udev, 0),
++ USB_REQ_GET_STATE,
++ USB_TYPE_VENDOR | USB_DIR_IN, 0, 0,
++ &ucFirmware, 1, USB_CTRL_SET_TIMEOUT)) < 0) {
++ BT_ERR("Can't change to loading configuration err");
++ return -EBUSY;
++ }
++
++ if (ucFirmware == USB_FIRMWARE_RAM_MODE) {
++ /* RAM based firmware is available in the target.
++ * No need to load the firmware to RAM */
++ BT_DBG("RAM based firmware is available");
++ return 0;
++ }
++
++ pipe = usb_sndctrlpipe(data->udev, 0);
++ if ((usb_control_msg(data->udev, pipe,
++ USB_REQ_DFU_DNLOAD,
++ USB_TYPE_VENDOR, 0, 0,
++ firmware, 20, USB_CTRL_SET_TIMEOUT)) < 0) {
++ BT_ERR("Can't change to loading configuration err");
++ return -EBUSY;
++ }
++ sent += 20;
++ count -= 20;
++
++ send_buf = kmalloc(BULK_SIZE, GFP_ATOMIC);
++ if (!send_buf) {
++ BT_ERR("Can't allocate memory chunk for firmware");
++ return -ENOMEM;
++ }
++
++ while (count) {
++ size = min_t(uint, count, BULK_SIZE);
++ pipe = usb_sndbulkpipe(data->udev, 0x02);
++ memcpy(send_buf, firmware + sent, size);
++
++ err = usb_bulk_msg(data->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;
++ }
++
++ kfree(send_buf);
++ return 0;
++
++error:
++ kfree(send_buf);
++ return err;
++}
++
++void *ath_fw_load(struct usb_interface *intf,
++ const char *fwfile, bool *suspend)
++{
++ const struct firmware *firmware;
++ struct usb_device *udev = interface_to_usbdev(intf);
++ static struct firmware_data *data;
++ int size;
++
++ BT_DBG("\nintf %p suspend %d\n", intf, *suspend);
++
++ if (*suspend) {
++ load_firmware(data, data->fw_data, data->fw_size);
++ *suspend = 0;
++ return data;
++ }
++
++ if (intf->cur_altsetting->desc.bInterfaceNumber != 0)
++ return NULL;
++
++ data = kzalloc(sizeof(*data), GFP_KERNEL);
++ if (!data)
++ return NULL;
++ data->udev = udev;
++
++ if (request_firmware(&firmware, fwfile, &udev->dev) < 0) {
++ kfree(data);
++ return NULL;
++ }
++
++ size = max_t(uint, firmware->size, 4096);
++ data->fw_data = kmalloc(size, GFP_KERNEL);
++ if (!data->fw_data) {
++ release_firmware(firmware);
++ kfree(data);
++ return NULL;
++ }
++
++ memcpy(data->fw_data, firmware->data, firmware->size);
++ data->fw_size = firmware->size;
++ data->fw_sent = 0;
++ release_firmware(firmware);
++
++ if (load_firmware(data, data->fw_data, data->fw_size)) {
++ kfree(data->fw_data);
++ kfree(data);
++ return NULL;
++ }
++ return data;
++}
++EXPORT_SYMBOL(ath_fw_load);
++
++void ath_fw_unload(void *pdata, bool bsuspend)
++{
++ struct firmware_data *data = (struct firmware_data *)pdata;
++
++ if (data == NULL)
++ return;
++
++ /* do not free the data on suspend as we will
++ * use it on resume */
++ if (!bsuspend) {
++ kfree(data->fw_data);
++ kfree(data);
++ }
++}
++EXPORT_SYMBOL(ath_fw_unload);
++
++static int __init fwload_init(void)
++{
++ BT_INFO("Firmware load driver init. Version:%s", VERSION);
++ return 0;
++}
++
++static void __exit fwload_deinit(void)
++{
++ BT_INFO("Firmware load driver deinit");
++}
++
++module_init(fwload_init);
++module_exit(fwload_deinit);
++
++MODULE_AUTHOR("Atheros Communications");
++MODULE_DESCRIPTION("Firmware load driver");
++MODULE_VERSION(VERSION);
++MODULE_LICENSE("GPL");
+diff --git a/drivers/bluetooth/fwload.h b/drivers/bluetooth/fwload.h
+new file mode 100644
+index 0000000..5c1136a
+--- /dev/null
++++ b/drivers/bluetooth/fwload.h
+@@ -0,0 +1,39 @@
++/*
++ *
++ * Generic Bluetooth USB DFU driver to download firmware to target RAM
++ *
++ * Copyright (c) 2009-2010 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
++ *
++ */
++#ifndef _FWLOAD_H_
++#define _FWLOAD_H_
++
++/* callbacks to load firmware to BT device RAM
++ * when it is inserted */
++struct fw_cb_config {
++ const char *fwfile;
++ void * (*fwload)(struct usb_interface *intf, const char *fwfile,
++ bool *bsuspend);
++ void (*fwunload)(void *, bool);
++ const struct usb_device_id *usb_id_table;
++ void *data;
++ bool bsuspend;
++};
++void *ath_fw_load(struct usb_interface *intf, const char *, bool *);
++void ath_fw_unload(void *pdata, bool bsuspend);
++
++#endif /* _FWLOAD_H_ */
+--
+1.6.3.3
+