+++ /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
-
---- a/drivers/bluetooth/Makefile
-+++ b/drivers/bluetooth/Makefile
-@@ -13,6 +13,7 @@ obj-$(CONFIG_BT_HCIBLUECARD) += bluecard
- 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
---- 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) },
-@@ -887,6 +908,7 @@ static int btusb_probe(struct usb_interf
- 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);
-
-@@ -946,6 +968,19 @@ static int btusb_probe(struct usb_interf
- 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);
-@@ -1054,12 +1089,26 @@ static void btusb_disconnect(struct usb_
- {
- 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);
-@@ -1085,12 +1134,22 @@ static void btusb_disconnect(struct usb_
- 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);
-@@ -1203,6 +1262,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);
- }
-
---- /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");
---- /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_ */