From: Tedd Ho-Jeong An <[email protected]>
This patch adds Intel Bluetooth Bootloader driver for Intel BT device.
It downloads the full FW image file from firmware/intel to the device.
Once downloading is done successfully, the device reset and bootup as
Bluetooth class device.
Signed-off-by: Tedd Ho-Jeong An <[email protected]>
Tested-by: Don Fry <[email protected]>
---
drivers/bluetooth/Kconfig | 13 ++
drivers/bluetooth/Makefile | 1 +
drivers/bluetooth/intel_bl.c | 370 ++++++++++++++++++++++++++++++++++++++++++
3 files changed, 384 insertions(+)
create mode 100644 drivers/bluetooth/intel_bl.c
diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig
index fdfd61a..060dd10 100644
--- a/drivers/bluetooth/Kconfig
+++ b/drivers/bluetooth/Kconfig
@@ -242,4 +242,17 @@ config BT_WILINK
Say Y here to compile support for Texas Instrument's WiLink7 driver
into the kernel or say M to compile it as module.
+
+config BT_INTEL_BL
+ tristate "Intel firmware download driver"
+ depends on USB
+ select FW_LOADER
+ help
+ Intel Bluetooth firmware download driver.
+ This driver loads the full firmware into the Intel Bluetooth
+ chipset.
+
+ Say Y here to compile support for "Intel firmware download driver"
+ into the kernel or say M to compile it as module.
+
endmenu
diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile
index 4afae20..edfcdb5 100644
--- a/drivers/bluetooth/Makefile
+++ b/drivers/bluetooth/Makefile
@@ -19,6 +19,7 @@ obj-$(CONFIG_BT_ATH3K) += ath3k.o
obj-$(CONFIG_BT_MRVL) += btmrvl.o
obj-$(CONFIG_BT_MRVL_SDIO) += btmrvl_sdio.o
obj-$(CONFIG_BT_WILINK) += btwilink.o
+obj-$(CONFIG_BT_INTEL_BL) += intel_bl.o
btmrvl-y := btmrvl_main.o
btmrvl-$(CONFIG_DEBUG_FS) += btmrvl_debugfs.o
diff --git a/drivers/bluetooth/intel_bl.c b/drivers/bluetooth/intel_bl.c
new file mode 100644
index 0000000..4c8296b
--- /dev/null
+++ b/drivers/bluetooth/intel_bl.c
@@ -0,0 +1,387 @@
+/*
+ *
+ * Intel Bluetooth BootLoader driver
+ *
+ * Copyright (C) 2012 Intel Corporation
+ *
+ *
+ * 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/errno.h>
+#include <linux/usb.h>
+#include <linux/firmware.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#define VERSION "1.0"
+
+static struct usb_driver intel_bl_driver;
+
+static const struct usb_device_id intel_bl_table[] = {
+ /* Intel Bluetooth USB BootLoader(RAM module) */
+ { USB_DEVICE(0x8087, 0x0A5A) },
+
+ { } /* Terminating entry */
+};
+MODULE_DEVICE_TABLE(usb, intel_bl_table);
+
+#define MAX_DATA_SIZE 260
+
+#define CMD_TIMEOUT msecs_to_jiffies(2000) /* 2 seconds */
+
+struct intel_version {
+ u8 status;
+ u8 hw_platform;
+ u8 hw_variant;
+ u8 hw_revision;
+ u8 fw_variant;
+ u8 fw_revision;
+ u8 fw_build_num;
+ u8 fw_build_ww;
+ u8 fw_build_yy;
+ u8 fw_patch_num;
+} __packed;
+
+struct intel_bl_data {
+ struct usb_device *udev;
+
+ u8 evt_buff[MAX_DATA_SIZE];
+ int evt_len;
+
+ struct work_struct work;
+ atomic_t shutdown;
+};
+
+static int intel_bl_send_cmd_sync(struct intel_bl_data *data,
+ const u8 *cmd_buff, int cmd_len, u32 timeout)
+{
+ u8 *buff;
+ int err;
+
+ BT_DBG("send cmd len %d", cmd_len);
+
+ buff = kmalloc(cmd_len, GFP_KERNEL);
+ if (!buff)
+ return -ENOMEM;
+
+ memcpy(buff, cmd_buff, cmd_len);
+
+ err = usb_control_msg(data->udev, usb_sndctrlpipe(data->udev, 0x00),
+ 0, USB_TYPE_CLASS, 0, 0,
+ buff, cmd_len, USB_CTRL_SET_TIMEOUT);
+ if (err < 0) {
+ BT_ERR("failed to send control message (%d)", err);
+ goto exit_error;
+ }
+
+ err = usb_interrupt_msg(data->udev, usb_rcvintpipe(data->udev, 0x81),
+ data->evt_buff, MAX_DATA_SIZE, &data->evt_len,
+ 5000);
+ if (err < 0) {
+ BT_ERR("failed to receive interrupt message (%d)", err);
+ goto exit_error;
+ }
+
+ BT_DBG("received event(%d): %02x %02x %02x %02x %02x %02x",
+ data->evt_len, data->evt_buff[0], data->evt_buff[1],
+ data->evt_buff[2], data->evt_buff[3], data->evt_buff[4],
+ data->evt_buff[5]);
+
+exit_error:
+ kfree(buff);
+
+ return err;
+}
+
+static const struct firmware *intel_bl_get_fw(struct intel_bl_data *data,
+ struct intel_version *ver)
+{
+ const struct firmware *fw;
+ char fwname[64];
+ int err;
+
+ snprintf(fwname, sizeof(fwname),
+ "intel/ibt-hw-%x.%x.%x-fw-%x.%x.%x.%x.%x.bseq",
+ ver->hw_platform, ver->hw_variant, ver->hw_revision,
+ ver->fw_variant, ver->fw_revision, ver->fw_build_num,
+ ver->fw_build_ww, ver->fw_build_yy);
+
+ BT_INFO("Intel Bluetooth firmware file: %s", fwname);
+
+ err = request_firmware(&fw, fwname, &data->udev->dev);
+ if (err < 0) {
+ BT_ERR("failed to open the Intel firmware file (%d)", err);
+ return NULL;
+ }
+
+ return fw;
+}
+
+static int intel_bl_downloading(struct intel_bl_data *data,
+ const struct firmware *fw,
+ const u8 **fw_ptr)
+{
+ struct hci_command_hdr *cmd;
+ const u8 *cmd_ptr;
+ struct hci_event_hdr *evt = NULL;
+ const u8 *evt_ptr = NULL;
+ int err;
+
+ int remain = fw->size - (*fw_ptr - fw->data);
+
+ if (atomic_read(&data->shutdown)) {
+ BT_ERR("firmware download aborted.");
+ return -EFAULT;
+ }
+
+ BT_DBG("downloading fw data: remain %d", remain);
+
+
+ if (remain <= HCI_COMMAND_HDR_SIZE || *fw_ptr[0] != 0x01) {
+ BT_ERR("fw file is corrupted: invalid cmd read");
+ return -EINVAL;
+ }
+ (*fw_ptr)++;
+ remain--;
+
+ cmd = (struct hci_command_hdr *)(*fw_ptr);
+ cmd_ptr = *fw_ptr;
+
+ /* push HCI command header */
+ *fw_ptr += sizeof(*cmd);
+ remain -= sizeof(*cmd);
+
+ if (remain < cmd->plen) {
+ BT_ERR("fw file is corrupted. invalid cmd len");
+ return -EFAULT;
+ }
+
+ /* push HCI command parameter */
+ *fw_ptr += cmd->plen;
+ remain -= cmd->plen;
+
+ /* read event */
+ if (remain <= HCI_EVENT_HDR_SIZE || *fw_ptr[0] != 0x02) {
+ BT_ERR("fw file is corrupted: invalid evt read");
+ return -EINVAL;
+ }
+
+ (*fw_ptr)++;
+ remain--;
+
+ evt = (struct hci_event_hdr *)(*fw_ptr);
+ evt_ptr = *fw_ptr;
+
+ /* push HCI event header */
+ *fw_ptr += sizeof(*evt);
+ remain -= sizeof(*evt);
+
+ if (remain < evt->plen) {
+ BT_ERR("fw file is corrupted: invalid evt len");
+ return -EFAULT;
+ }
+
+ *fw_ptr += evt->plen;
+ remain -= evt->plen;
+
+ err = intel_bl_send_cmd_sync(data, cmd_ptr, cmd->plen + 3, CMD_TIMEOUT);
+ if (err) {
+ BT_ERR("failed to send patch command %d", err);
+ return -EFAULT;
+ }
+
+ if (evt->plen + 2 != data->evt_len) {
+ BT_ERR("mismatch event length");
+ return -EFAULT;
+ }
+
+ if (memcmp(data->evt_buff, evt_ptr, evt->plen + 2)) {
+ BT_ERR("mismatch event parameter");
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+static void intel_bl_setup(struct work_struct *work)
+{
+ struct intel_bl_data *data =
+ container_of(work, struct intel_bl_data, work);
+ const struct firmware *fw;
+ const u8 *fw_ptr;
+ struct intel_version *ver;
+ int err;
+
+ const u8 get_version[] = { 0x05, 0xFC, 0x00 };
+ const u8 mfg_enable[] = { 0x11, 0xFC, 0x02, 0x01, 0x00 };
+ const u8 mfg_reset_deactivate[] = { 0x11, 0xFC, 0x02, 0x00, 0x01 };
+ const u8 mfg_reset_activate[] = { 0x11, 0xFC, 0x02, 0x00, 0x02 };
+
+ BT_DBG("start Intel fw setup: data %p", data);
+
+ /* get version */
+ err = intel_bl_send_cmd_sync(data, get_version, 3, CMD_TIMEOUT);
+ if (err < 0) {
+ BT_ERR("failed to send get_version command (%d)", err);
+ return;
+ }
+
+ BT_DBG("received get_version event");
+ if (data->evt_len != 15) {
+ BT_ERR("received invalid get_version event: (%d)",
+ data->evt_len);
+ return;
+ }
+
+ ver = (struct intel_version *)&data->evt_buff[5];
+ if (ver->status) {
+ BT_ERR("get_version event failed (%02x)", ver->status);
+ return;
+ }
+
+ BT_DBG("Intel fw version: %02x%02x%02x%02x%02x%02x%02x%02x%02x",
+ ver->hw_platform, ver->hw_variant, ver->hw_revision,
+ ver->fw_variant, ver->fw_revision, ver->fw_build_num,
+ ver->fw_build_ww, ver->fw_build_yy, ver->fw_patch_num);
+
+ if (ver->fw_patch_num) {
+ BT_INFO("fw is already loaded. skip the downloading");
+ return;
+ }
+
+ fw = intel_bl_get_fw(data, ver);
+ if (!fw)
+ return;
+
+ fw_ptr = fw->data;
+
+ /* make sure the first byte is FF */
+ if (fw->data[0] != 0xFF) {
+ BT_ERR("invalid fw image file: %02x", fw->data[0]);
+ release_firmware(fw);
+ return;
+ }
+ fw_ptr++;
+
+ /* enter mfg mode */
+ err = intel_bl_send_cmd_sync(data, mfg_enable, 5, CMD_TIMEOUT);
+ if (err < 0) {
+ BT_ERR("failed to enable mfg mode (%d)", err);
+ release_firmware(fw);
+ return;
+ }
+
+ while (fw->size > fw_ptr - fw->data) {
+ err = intel_bl_downloading(data, fw, &fw_ptr);
+ if (err < 0) {
+ release_firmware(fw);
+ goto exit_mfg_deactivate;
+ }
+ }
+
+ release_firmware(fw);
+
+ /* patching success */
+ err = intel_bl_send_cmd_sync(data, mfg_reset_activate, 5, CMD_TIMEOUT);
+ if (err < 0) {
+ BT_ERR("failed to disable mfg mode: (%d)", err);
+ return;
+ }
+
+ BT_INFO("Intel fw downloading is completed");
+
+ return;
+
+exit_mfg_deactivate:
+ err = intel_bl_send_cmd_sync(data, mfg_reset_deactivate, 5,
+ CMD_TIMEOUT);
+ if (err < 0) {
+ BT_ERR("failed to disable mfg mode(deactivate fw): (%d)", err);
+ return;
+ }
+
+ BT_INFO("device is reset without enabling fw");
+ return;
+
+}
+
+static int intel_bl_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct intel_bl_data *data;
+ int err;
+
+ BT_DBG("intf %p id %p", intf, id);
+
+ /* interface numbers are hardcoded in the spec */
+ if (intf->cur_altsetting->desc.bInterfaceNumber != 0)
+ return -ENODEV;
+
+ data = devm_kzalloc(&intf->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->udev = interface_to_usbdev(intf);
+
+ usb_set_intfdata(intf, data);
+
+ /* There is a bug in the bootloader that interrupt interface is only
+ * enabled after receiving SetInterface(0, AltSetting=0).
+ */
+ err = usb_set_interface(data->udev, 0, 0);
+ if (err < 0) {
+ BT_ERR("failed to set interface 0, alt 0 %d", err);
+ return err;
+ }
+
+ INIT_WORK(&data->work, intel_bl_setup);
+
+ /* use workqueue to have a small delay */
+ schedule_work(&data->work);
+
+ return 0;
+}
+
+static void intel_bl_disconnect(struct usb_interface *intf)
+{
+ struct intel_bl_data *data = usb_get_intfdata(intf);
+
+ BT_DBG("intf %p", intf);
+
+ atomic_inc(&data->shutdown);
+ cancel_work_sync(&data->work);
+
+ BT_DBG("intel_bl_disconnect");
+
+ usb_set_intfdata(intf, NULL);
+}
+
+static struct usb_driver intel_bl_driver = {
+ .name = "intel_bl",
+ .probe = intel_bl_probe,
+ .disconnect = intel_bl_disconnect,
+ .id_table = intel_bl_table,
+ .disable_hub_initiated_lpm = 1,
+};
+
+module_usb_driver(intel_bl_driver);
+
+MODULE_AUTHOR("Tedd Ho-Jeong An <[email protected]>");
+MODULE_DESCRIPTION("Intel Bluetooth USB Bootloader driver ver " VERSION);
+MODULE_VERSION(VERSION);
+MODULE_LICENSE("GPL");
--
1.7.9.5
--
Regards
Tedd Ho-Jeong An
Intel Corporation
Hi Don,
small reminder to not top post on this mailing list please
> What is the status of this patch? Is it something that can go upstream
> or does it need more rework?
I have not had a chance too look at it in detail. I will try to get to it by the end of this week.
Regards
Marcel
Hi Gustavo,
What is the status of this patch? Is it something that can go upstream
or does it need more rework?
Thanks,
Don
On 8/23/13 9:21 AM, "An, Tedd" <[email protected]> wrote:
>From: Tedd Ho-Jeong An <[email protected]>
>
>This patch adds Intel Bluetooth Bootloader driver for Intel BT device.
>
>It downloads the full FW image file from firmware/intel to the device.
>Once downloading is done successfully, the device reset and bootup as
>Bluetooth class device.
>
>Signed-off-by: Tedd Ho-Jeong An <[email protected]>
>Tested-by: Don Fry <[email protected]>
>---
> drivers/bluetooth/Kconfig | 13 ++
> drivers/bluetooth/Makefile | 1 +
> drivers/bluetooth/intel_bl.c | 370
>++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 384 insertions(+)
> create mode 100644 drivers/bluetooth/intel_bl.c
>
>diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig
>index fdfd61a..060dd10 100644
>--- a/drivers/bluetooth/Kconfig
>+++ b/drivers/bluetooth/Kconfig
>@@ -242,4 +242,17 @@ config BT_WILINK
>
> Say Y here to compile support for Texas Instrument's WiLink7 driver
> into the kernel or say M to compile it as module.
>+
>+config BT_INTEL_BL
>+ tristate "Intel firmware download driver"
>+ depends on USB
>+ select FW_LOADER
>+ help
>+ Intel Bluetooth firmware download driver.
>+ This driver loads the full firmware into the Intel Bluetooth
>+ chipset.
>+
>+ Say Y here to compile support for "Intel firmware download driver"
>+ into the kernel or say M to compile it as module.
>+
> endmenu
>diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile
>index 4afae20..edfcdb5 100644
>--- a/drivers/bluetooth/Makefile
>+++ b/drivers/bluetooth/Makefile
>@@ -19,6 +19,7 @@ obj-$(CONFIG_BT_ATH3K) += ath3k.o
> obj-$(CONFIG_BT_MRVL) += btmrvl.o
> obj-$(CONFIG_BT_MRVL_SDIO) += btmrvl_sdio.o
> obj-$(CONFIG_BT_WILINK) += btwilink.o
>+obj-$(CONFIG_BT_INTEL_BL) += intel_bl.o
>
> btmrvl-y := btmrvl_main.o
> btmrvl-$(CONFIG_DEBUG_FS) += btmrvl_debugfs.o
>diff --git a/drivers/bluetooth/intel_bl.c b/drivers/bluetooth/intel_bl.c
>new file mode 100644
>index 0000000..4c8296b
>--- /dev/null
>+++ b/drivers/bluetooth/intel_bl.c
>@@ -0,0 +1,387 @@
>+/*
>+ *
>+ * Intel Bluetooth BootLoader driver
>+ *
>+ * Copyright (C) 2012 Intel Corporation
>+ *
>+ *
>+ * 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/errno.h>
>+#include <linux/usb.h>
>+#include <linux/firmware.h>
>+
>+#include <net/bluetooth/bluetooth.h>
>+#include <net/bluetooth/hci_core.h>
>+
>+#define VERSION "1.0"
>+
>+static struct usb_driver intel_bl_driver;
>+
>+static const struct usb_device_id intel_bl_table[] = {
>+ /* Intel Bluetooth USB BootLoader(RAM module) */
>+ { USB_DEVICE(0x8087, 0x0A5A) },
>+
>+ { } /* Terminating entry */
>+};
>+MODULE_DEVICE_TABLE(usb, intel_bl_table);
>+
>+#define MAX_DATA_SIZE 260
>+
>+#define CMD_TIMEOUT msecs_to_jiffies(2000) /* 2 seconds */
>+
>+struct intel_version {
>+ u8 status;
>+ u8 hw_platform;
>+ u8 hw_variant;
>+ u8 hw_revision;
>+ u8 fw_variant;
>+ u8 fw_revision;
>+ u8 fw_build_num;
>+ u8 fw_build_ww;
>+ u8 fw_build_yy;
>+ u8 fw_patch_num;
>+} __packed;
>+
>+struct intel_bl_data {
>+ struct usb_device *udev;
>+
>+ u8 evt_buff[MAX_DATA_SIZE];
>+ int evt_len;
>+
>+ struct work_struct work;
>+ atomic_t shutdown;
>+};
>+
>+static int intel_bl_send_cmd_sync(struct intel_bl_data *data,
>+ const u8 *cmd_buff, int cmd_len, u32 timeout)
>+{
>+ u8 *buff;
>+ int err;
>+
>+ BT_DBG("send cmd len %d", cmd_len);
>+
>+ buff = kmalloc(cmd_len, GFP_KERNEL);
>+ if (!buff)
>+ return -ENOMEM;
>+
>+ memcpy(buff, cmd_buff, cmd_len);
>+
>+ err = usb_control_msg(data->udev, usb_sndctrlpipe(data->udev, 0x00),
>+ 0, USB_TYPE_CLASS, 0, 0,
>+ buff, cmd_len, USB_CTRL_SET_TIMEOUT);
>+ if (err < 0) {
>+ BT_ERR("failed to send control message (%d)", err);
>+ goto exit_error;
>+ }
>+
>+ err = usb_interrupt_msg(data->udev, usb_rcvintpipe(data->udev, 0x81),
>+ data->evt_buff, MAX_DATA_SIZE, &data->evt_len,
>+ 5000);
>+ if (err < 0) {
>+ BT_ERR("failed to receive interrupt message (%d)", err);
>+ goto exit_error;
>+ }
>+
>+ BT_DBG("received event(%d): %02x %02x %02x %02x %02x %02x",
>+ data->evt_len, data->evt_buff[0], data->evt_buff[1],
>+ data->evt_buff[2], data->evt_buff[3], data->evt_buff[4],
>+ data->evt_buff[5]);
>+
>+exit_error:
>+ kfree(buff);
>+
>+ return err;
>+}
>+
>+static const struct firmware *intel_bl_get_fw(struct intel_bl_data *data,
>+ struct intel_version *ver)
>+{
>+ const struct firmware *fw;
>+ char fwname[64];
>+ int err;
>+
>+ snprintf(fwname, sizeof(fwname),
>+ "intel/ibt-hw-%x.%x.%x-fw-%x.%x.%x.%x.%x.bseq",
>+ ver->hw_platform, ver->hw_variant, ver->hw_revision,
>+ ver->fw_variant, ver->fw_revision, ver->fw_build_num,
>+ ver->fw_build_ww, ver->fw_build_yy);
>+
>+ BT_INFO("Intel Bluetooth firmware file: %s", fwname);
>+
>+ err = request_firmware(&fw, fwname, &data->udev->dev);
>+ if (err < 0) {
>+ BT_ERR("failed to open the Intel firmware file (%d)", err);
>+ return NULL;
>+ }
>+
>+ return fw;
>+}
>+
>+static int intel_bl_downloading(struct intel_bl_data *data,
>+ const struct firmware *fw,
>+ const u8 **fw_ptr)
>+{
>+ struct hci_command_hdr *cmd;
>+ const u8 *cmd_ptr;
>+ struct hci_event_hdr *evt = NULL;
>+ const u8 *evt_ptr = NULL;
>+ int err;
>+
>+ int remain = fw->size - (*fw_ptr - fw->data);
>+
>+ if (atomic_read(&data->shutdown)) {
>+ BT_ERR("firmware download aborted.");
>+ return -EFAULT;
>+ }
>+
>+ BT_DBG("downloading fw data: remain %d", remain);
>+
>+
>+ if (remain <= HCI_COMMAND_HDR_SIZE || *fw_ptr[0] != 0x01) {
>+ BT_ERR("fw file is corrupted: invalid cmd read");
>+ return -EINVAL;
>+ }
>+ (*fw_ptr)++;
>+ remain--;
>+
>+ cmd = (struct hci_command_hdr *)(*fw_ptr);
>+ cmd_ptr = *fw_ptr;
>+
>+ /* push HCI command header */
>+ *fw_ptr += sizeof(*cmd);
>+ remain -= sizeof(*cmd);
>+
>+ if (remain < cmd->plen) {
>+ BT_ERR("fw file is corrupted. invalid cmd len");
>+ return -EFAULT;
>+ }
>+
>+ /* push HCI command parameter */
>+ *fw_ptr += cmd->plen;
>+ remain -= cmd->plen;
>+
>+ /* read event */
>+ if (remain <= HCI_EVENT_HDR_SIZE || *fw_ptr[0] != 0x02) {
>+ BT_ERR("fw file is corrupted: invalid evt read");
>+ return -EINVAL;
>+ }
>+
>+ (*fw_ptr)++;
>+ remain--;
>+
>+ evt = (struct hci_event_hdr *)(*fw_ptr);
>+ evt_ptr = *fw_ptr;
>+
>+ /* push HCI event header */
>+ *fw_ptr += sizeof(*evt);
>+ remain -= sizeof(*evt);
>+
>+ if (remain < evt->plen) {
>+ BT_ERR("fw file is corrupted: invalid evt len");
>+ return -EFAULT;
>+ }
>+
>+ *fw_ptr += evt->plen;
>+ remain -= evt->plen;
>+
>+ err = intel_bl_send_cmd_sync(data, cmd_ptr, cmd->plen + 3, CMD_TIMEOUT);
>+ if (err) {
>+ BT_ERR("failed to send patch command %d", err);
>+ return -EFAULT;
>+ }
>+
>+ if (evt->plen + 2 != data->evt_len) {
>+ BT_ERR("mismatch event length");
>+ return -EFAULT;
>+ }
>+
>+ if (memcmp(data->evt_buff, evt_ptr, evt->plen + 2)) {
>+ BT_ERR("mismatch event parameter");
>+ return -EFAULT;
>+ }
>+
>+ return 0;
>+}
>+
>+static void intel_bl_setup(struct work_struct *work)
>+{
>+ struct intel_bl_data *data =
>+ container_of(work, struct intel_bl_data, work);
>+ const struct firmware *fw;
>+ const u8 *fw_ptr;
>+ struct intel_version *ver;
>+ int err;
>+
>+ const u8 get_version[] = { 0x05, 0xFC, 0x00 };
>+ const u8 mfg_enable[] = { 0x11, 0xFC, 0x02, 0x01, 0x00 };
>+ const u8 mfg_reset_deactivate[] = { 0x11, 0xFC, 0x02, 0x00, 0x01 };
>+ const u8 mfg_reset_activate[] = { 0x11, 0xFC, 0x02, 0x00, 0x02 };
>+
>+ BT_DBG("start Intel fw setup: data %p", data);
>+
>+ /* get version */
>+ err = intel_bl_send_cmd_sync(data, get_version, 3, CMD_TIMEOUT);
>+ if (err < 0) {
>+ BT_ERR("failed to send get_version command (%d)", err);
>+ return;
>+ }
>+
>+ BT_DBG("received get_version event");
>+ if (data->evt_len != 15) {
>+ BT_ERR("received invalid get_version event: (%d)",
>+ data->evt_len);
>+ return;
>+ }
>+
>+ ver = (struct intel_version *)&data->evt_buff[5];
>+ if (ver->status) {
>+ BT_ERR("get_version event failed (%02x)", ver->status);
>+ return;
>+ }
>+
>+ BT_DBG("Intel fw version: %02x%02x%02x%02x%02x%02x%02x%02x%02x",
>+ ver->hw_platform, ver->hw_variant, ver->hw_revision,
>+ ver->fw_variant, ver->fw_revision, ver->fw_build_num,
>+ ver->fw_build_ww, ver->fw_build_yy, ver->fw_patch_num);
>+
>+ if (ver->fw_patch_num) {
>+ BT_INFO("fw is already loaded. skip the downloading");
>+ return;
>+ }
>+
>+ fw = intel_bl_get_fw(data, ver);
>+ if (!fw)
>+ return;
>+
>+ fw_ptr = fw->data;
>+
>+ /* make sure the first byte is FF */
>+ if (fw->data[0] != 0xFF) {
>+ BT_ERR("invalid fw image file: %02x", fw->data[0]);
>+ release_firmware(fw);
>+ return;
>+ }
>+ fw_ptr++;
>+
>+ /* enter mfg mode */
>+ err = intel_bl_send_cmd_sync(data, mfg_enable, 5, CMD_TIMEOUT);
>+ if (err < 0) {
>+ BT_ERR("failed to enable mfg mode (%d)", err);
>+ release_firmware(fw);
>+ return;
>+ }
>+
>+ while (fw->size > fw_ptr - fw->data) {
>+ err = intel_bl_downloading(data, fw, &fw_ptr);
>+ if (err < 0) {
>+ release_firmware(fw);
>+ goto exit_mfg_deactivate;
>+ }
>+ }
>+
>+ release_firmware(fw);
>+
>+ /* patching success */
>+ err = intel_bl_send_cmd_sync(data, mfg_reset_activate, 5, CMD_TIMEOUT);
>+ if (err < 0) {
>+ BT_ERR("failed to disable mfg mode: (%d)", err);
>+ return;
>+ }
>+
>+ BT_INFO("Intel fw downloading is completed");
>+
>+ return;
>+
>+exit_mfg_deactivate:
>+ err = intel_bl_send_cmd_sync(data, mfg_reset_deactivate, 5,
>+ CMD_TIMEOUT);
>+ if (err < 0) {
>+ BT_ERR("failed to disable mfg mode(deactivate fw): (%d)", err);
>+ return;
>+ }
>+
>+ BT_INFO("device is reset without enabling fw");
>+ return;
>+
>+}
>+
>+static int intel_bl_probe(struct usb_interface *intf,
>+ const struct usb_device_id *id)
>+{
>+ struct intel_bl_data *data;
>+ int err;
>+
>+ BT_DBG("intf %p id %p", intf, id);
>+
>+ /* interface numbers are hardcoded in the spec */
>+ if (intf->cur_altsetting->desc.bInterfaceNumber != 0)
>+ return -ENODEV;
>+
>+ data = devm_kzalloc(&intf->dev, sizeof(*data), GFP_KERNEL);
>+ if (!data)
>+ return -ENOMEM;
>+
>+ data->udev = interface_to_usbdev(intf);
>+
>+ usb_set_intfdata(intf, data);
>+
>+ /* There is a bug in the bootloader that interrupt interface is only
>+ * enabled after receiving SetInterface(0, AltSetting=0).
>+ */
>+ err = usb_set_interface(data->udev, 0, 0);
>+ if (err < 0) {
>+ BT_ERR("failed to set interface 0, alt 0 %d", err);
>+ return err;
>+ }
>+
>+ INIT_WORK(&data->work, intel_bl_setup);
>+
>+ /* use workqueue to have a small delay */
>+ schedule_work(&data->work);
>+
>+ return 0;
>+}
>+
>+static void intel_bl_disconnect(struct usb_interface *intf)
>+{
>+ struct intel_bl_data *data = usb_get_intfdata(intf);
>+
>+ BT_DBG("intf %p", intf);
>+
>+ atomic_inc(&data->shutdown);
>+ cancel_work_sync(&data->work);
>+
>+ BT_DBG("intel_bl_disconnect");
>+
>+ usb_set_intfdata(intf, NULL);
>+}
>+
>+static struct usb_driver intel_bl_driver = {
>+ .name = "intel_bl",
>+ .probe = intel_bl_probe,
>+ .disconnect = intel_bl_disconnect,
>+ .id_table = intel_bl_table,
>+ .disable_hub_initiated_lpm = 1,
>+};
>+
>+module_usb_driver(intel_bl_driver);
>+
>+MODULE_AUTHOR("Tedd Ho-Jeong An <[email protected]>");
>+MODULE_DESCRIPTION("Intel Bluetooth USB Bootloader driver ver " VERSION);
>+MODULE_VERSION(VERSION);
>+MODULE_LICENSE("GPL");
>--
>1.7.9.5
>
>
>--
>Regards
>Tedd Ho-Jeong An
>Intel Corporation