Re: [PATCH] Bluetooth: Add Intel Bluetooth Bootloader driver

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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" <tedd.an@xxxxxxxxx> wrote:

>From: Tedd Ho-Jeong An <tedd.an@xxxxxxxxx>
>
>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 <tedd.an@xxxxxxxxx>
>Tested-by: Don Fry <don.fry@xxxxxxxxx>
>---
> 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 <tedd.an@xxxxxxxxx>");
>+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

--
To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Bluez Devel]     [Linux Wireless Networking]     [Linux Wireless Personal Area Networking]     [Linux ATH6KL]     [Linux USB Devel]     [Linux Media Drivers]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Big List of Linux Books]

  Powered by Linux