Index: chromeos/compat-wireless/drivers/bluetooth/fwload.c |
diff --git a/chromeos/compat-wireless/drivers/bluetooth/fwload.c b/chromeos/compat-wireless/drivers/bluetooth/fwload.c |
new file mode 100644 |
index 0000000000000000000000000000000000000000..a9a586ae6f3bc207bf493ad65ca6a930ae698715 |
--- /dev/null |
+++ b/chromeos/compat-wireless/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"); |