This patch in crap directory enables btusb to load firmware to device RAM when it is plugged in. Signed-off-by: Bala Shanmugam <sbalashanmugam@xxxxxxxxxxx> --- crap/0003-btusb-Add-fw-load-support.patch | 424 +++++++++++++++++++++++++++++ 1 files changed, 424 insertions(+), 0 deletions(-) create mode 100644 crap/0003-btusb-Add-fw-load-support.patch diff --git a/crap/0003-btusb-Add-fw-load-support.patch b/crap/0003-btusb-Add-fw-load-support.patch new file mode 100644 index 0000000..6642d6b --- /dev/null +++ b/crap/0003-btusb-Add-fw-load-support.patch @@ -0,0 +1,424 @@ +Reason for not yet publishing: Marcel feels that Atheros sflash based BT device +doesn't follow bluetooth H:2 specification and HCI commands should be supported +in firmware if it is detected as bluetooth device. Using HCI command, firmware +should be loaded. + +In sflash based device there is not enough memory to support HCI commands in firmware. +So load firmware from btusb when the device comes up. + +From 4ac276c14578b380d0c6a27658eeaa364efe6432 Mon Sep 17 00:00:00 2001 +From: Bala Shanmugam <sbalashanmugam@xxxxxxxxxxx> +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@xxxxxxxxxxx> + +--- + 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 + -- 1.6.3.3 -- To unsubscribe from this list: send the line "unsubscribe linux-wireless" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html