compat-wireless: Added ath3k sflash workaround to crap directory
authorBala Shanmugam <sbalashanmugam@atheros.com>
Sun, 3 Oct 2010 15:00:02 +0000 (20:30 +0530)
committerLuis R. Rodriguez <lrodriguez@atheros.com>
Fri, 15 Oct 2010 04:07:24 +0000 (21:07 -0700)
This patch in crap directory enables btusb to load firmware
to device RAM when it is plugged in.

Reason for this not getting 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

Signed-off-by: Bala Shanmugam <sbalashanmugam@atheros.com>
crap/0003-btusb-Add-fw-load-support.patch [new file with mode: 0644]

diff --git a/crap/0003-btusb-Add-fw-load-support.patch b/crap/0003-btusb-Add-fw-load-support.patch
new file mode 100644 (file)
index 0000000..87276c9
--- /dev/null
@@ -0,0 +1,429 @@
+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
+