All of lore.kernel.org
 help / color / mirror / Atom feed
From: Tedd Ho-Jeong An <tedd.an@intel.com>
To: linux-bluetooth <linux-bluetooth@vger.kernel.org>
Cc: marcel <marcel@holtmann.org>, "tedd.an" <tedd.an@intel.com>,
	don.fry@intel.com, johan.hedberg@intel.com
Subject: [RFC] Bluetooth: Add support for Intel Bluetooth device [8087:07dc]
Date: Tue, 09 Apr 2013 19:44:27 -0700	[thread overview]
Message-ID: <1640417.GJq4DRhhap@tedd-ubuntu> (raw)

From: Tedd Ho-Jeong An <tedd.an@intel.com>

This patch adds support for Intel Bluetooth device by adding
btusb_setup_intel() routine that updates the device with ROM patch
during HCI_SETUP.

T:  Bus=02 Lev=02 Prnt=02 Port=00 Cnt=01 Dev#=  4 Spd=12   MxCh= 0
D:  Ver= 2.00 Cls=e0(wlcon) Sub=01 Prot=01 MxPS=64 #Cfgs=  1
P:  Vendor=8087 ProdID=07dc Rev= 0.01
C:* #Ifs= 2 Cfg#= 1 Atr=e0 MxPwr=100mA
I:* If#= 0 Alt= 0 #EPs= 3 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb
E:  Ad=81(I) Atr=03(Int.) MxPS=  64 Ivl=1ms
E:  Ad=02(O) Atr=02(Bulk) MxPS=  64 Ivl=0ms
E:  Ad=82(I) Atr=02(Bulk) MxPS=  64 Ivl=0ms
I:* If#= 1 Alt= 0 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb
E:  Ad=03(O) Atr=01(Isoc) MxPS=   0 Ivl=1ms
E:  Ad=83(I) Atr=01(Isoc) MxPS=   0 Ivl=1ms
I:  If#= 1 Alt= 1 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb
E:  Ad=03(O) Atr=01(Isoc) MxPS=   9 Ivl=1ms
E:  Ad=83(I) Atr=01(Isoc) MxPS=   9 Ivl=1ms
I:  If#= 1 Alt= 2 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb
E:  Ad=03(O) Atr=01(Isoc) MxPS=  17 Ivl=1ms
E:  Ad=83(I) Atr=01(Isoc) MxPS=  17 Ivl=1ms
I:  If#= 1 Alt= 3 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb
E:  Ad=03(O) Atr=01(Isoc) MxPS=  25 Ivl=1ms
E:  Ad=83(I) Atr=01(Isoc) MxPS=  25 Ivl=1ms
I:  If#= 1 Alt= 4 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb
E:  Ad=03(O) Atr=01(Isoc) MxPS=  33 Ivl=1ms
E:  Ad=83(I) Atr=01(Isoc) MxPS=  33 Ivl=1ms
I:  If#= 1 Alt= 5 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb
E:  Ad=03(O) Atr=01(Isoc) MxPS=  49 Ivl=1ms
E:  Ad=83(I) Atr=01(Isoc) MxPS=  49 Ivl=1ms

Signed-off-by: Tedd Ho-Jeong An <tedd.an@intel.com>
---
 drivers/bluetooth/btusb.c |  198 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 198 insertions(+)

diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
index 35c967f..fafa95d 100644
--- a/drivers/bluetooth/btusb.c
+++ b/drivers/bluetooth/btusb.c
@@ -23,6 +23,7 @@
 
 #include <linux/module.h>
 #include <linux/usb.h>
+#include <linux/firmware.h>
 
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
@@ -47,6 +48,7 @@ static struct usb_driver btusb_driver;
 #define BTUSB_BROKEN_ISOC	0x20
 #define BTUSB_WRONG_SCO_MTU	0x40
 #define BTUSB_ATH3012		0x80
+#define BTUSB_INTEL		0x100
 
 static struct usb_device_id btusb_table[] = {
 	/* Generic Bluetooth USB device */
@@ -207,6 +209,9 @@ static struct usb_device_id blacklist_table[] = {
 	/* Frontline ComProbe Bluetooth Sniffer */
 	{ USB_DEVICE(0x16d3, 0x0002), .driver_info = BTUSB_SNIFFER },
 
+	/* Intel Bluetooth device */
+	{ USB_DEVICE(0x8087, 0x07dc), .driver_info = BTUSB_INTEL },
+
 	{ }	/* Terminating entry */
 };
 
@@ -700,12 +705,205 @@ static int btusb_flush(struct hci_dev *hdev)
 	return 0;
 }
 
+int btusb_setup_intel(struct hci_dev *hdev)
+{
+	struct sk_buff		*skb;
+	const struct firmware	*fw = NULL;
+	const u8		*patch_curr;
+	char			pfile[32];
+	u8			*m_off_code;
+
+	u8 m_on[] = { 0x01, 0x00 };
+	u8 m_off_1[] = { 0x00, 0x01 };
+	u8 m_off_2[] = { 0x00, 0x02 };
+
+	BT_DBG("%s", hdev->name);
+
+	m_off_code = m_off_2;
+
+	/* HCI_RESET - this is a workaround due to ncmd is 0 for the first
+	 * event after booting up the device */
+	skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT);
+	if (IS_ERR(skb)) {
+		BT_ERR("__hci_cmd_sync(reset): %ld", PTR_ERR(skb));
+		goto exit_error;
+	}
+	BT_DBG("%s hci reset succeeded", hdev->name);
+	kfree_skb(skb);
+
+	/* Read Version */
+	skb = __hci_cmd_sync(hdev, 0xfc05, 0, NULL, HCI_INIT_TIMEOUT);
+	if (IS_ERR(skb)) {
+		BT_ERR("__hci_cmd_sync(version): %ld", PTR_ERR(skb));
+		goto exit_error;
+	}
+	BT_DBG("%s version succeeded", hdev->name);
+
+	/* Get bseq file name */
+	snprintf(pfile, 32, "intel/%02x%02x%02x%02x%02x%02x%02x%02x%02x.bseq",
+				skb->data[1], skb->data[2], skb->data[3],
+				skb->data[4], skb->data[5], skb->data[6],
+				skb->data[7], skb->data[8], skb->data[9]);
+	kfree_skb(skb);
+	BT_DBG("%s patch file: %s", hdev->name, pfile);
+
+	/* Open patch file */
+	if (request_firmware(&fw, pfile, &hdev->dev) < 0) {
+		BT_ERR("failed to open patch file: %s", pfile);
+		goto exit_done;
+	}
+	BT_DBG("%s open patch file succeeded: size: %d", hdev->name, fw->size);
+
+	patch_curr = fw->data;
+
+	/* Enter mfg mode */
+	skb = __hci_cmd_sync(hdev, 0xfc11, 2, m_on, HCI_INIT_TIMEOUT);
+	if (IS_ERR(skb)) {
+		BT_ERR("__hci_cmd_sync(mfg on): %ld", PTR_ERR(skb));
+		goto exit_error;
+	}
+
+	/* Checking mfg_on event */
+	if (skb->data[0]) {
+		BT_ERR("%s mfg_on failed(%02x)", hdev->name, skb->data[0]);
+		kfree_skb(skb);
+		goto exit_error;
+	}
+	BT_DBG("%s mfg_on succeeded", hdev->name);
+	kfree_skb(skb);
+
+	BT_DBG("%s start patching!!", hdev->name);
+	/* Patching */
+	while (1) {
+		struct hci_command_hdr *cmd;
+		const u8 *param;
+		struct hci_event_hdr *evt = NULL;
+		const u8 *evt_param = NULL;
+
+		/* Read cmd */
+		if (patch_curr[0] != 0x01) {
+			BT_ERR("%s invalid patch data(cmd)", hdev->name);
+			m_off_code = m_off_1;
+			goto exit_mfg;
+		}
+		patch_curr++;
+
+		cmd = (struct hci_command_hdr *)patch_curr;
+		patch_curr += sizeof(*cmd);
+
+		param = patch_curr;
+		patch_curr += cmd->plen;
+
+		/* Read evt - read the last event if there is more than 1 */
+		while (patch_curr[0] == 0x02) {
+			patch_curr++;
+
+			evt = (struct hci_event_hdr *)patch_curr;
+			patch_curr += sizeof(*evt);
+
+			evt_param = patch_curr;
+			patch_curr += evt->plen;
+		}
+
+		if (!evt || !evt_param) {
+			BT_ERR("%s invalid evt or evt_param data", hdev->name);
+			m_off_code = m_off_1;
+			goto exit_mfg;
+		}
+
+		/* Send command based on the evt */
+		if (evt->evt == HCI_EV_CMD_COMPLETE) {
+			/* Command Complete Event */
+			skb = __hci_cmd_sync(hdev, cmd->opcode, cmd->plen,
+					(void *)param,
+					HCI_INIT_TIMEOUT);
+			if (IS_ERR(skb)) {
+				BT_ERR("__hci_cmd_sync(patch): %ld",
+						PTR_ERR(skb));
+				m_off_code = m_off_1;
+				goto exit_mfg;
+			}
+
+			/* Check the event status */
+			if (skb->data[0]) {
+				BT_ERR("%s patch failed(%02x)", hdev->name,
+						skb->data[0]);
+				m_off_code = m_off_1;
+				kfree_skb(skb);
+				goto exit_mfg;
+			}
+		} else {
+			/* Non Command Complete Event */
+			skb = __hci_cmd_sync_ev(hdev, cmd->opcode, cmd->plen,
+					(void *)param, evt->evt,
+					HCI_INIT_TIMEOUT);
+			if (IS_ERR(skb)) {
+				BT_ERR("__hci_cmd_sync_ev(patch): %ld",
+						PTR_ERR(skb));
+				m_off_code = m_off_1;
+				goto exit_mfg;
+			}
+
+			/* Checking the returned event */
+			if (memcmp(skb->data, evt_param, evt->plen)) {
+				BT_ERR("%s patch event doesn't match!!",
+						hdev->name);
+				m_off_code = m_off_1;
+				kfree_skb(skb);
+				goto exit_mfg;
+			}
+		}
+		BT_DBG("%s patch cmd succeeded %d of %d",
+				hdev->name, patch_curr - fw->data, fw->size);
+		kfree_skb(skb);
+
+		/* Checking if EOF */
+		if (fw->size == patch_curr - fw->data) {
+			BT_DBG("%s patch completed - EOF", hdev->name);
+			m_off_code = m_off_2;
+			break;
+		} else if (fw->size < patch_curr - fw->data) {
+			BT_ERR("%s inconsistent patch read size", hdev->name);
+			m_off_code = m_off_1;
+			break;
+		}
+	}
+
+exit_mfg:
+	/* Exit mfg mode */
+	BT_DBG("%s mfg_off with %s", hdev->name,
+			m_off_code == m_off_1 ? "m_off_1" : "m_off_2");
+	skb = __hci_cmd_sync(hdev, 0xfc11, 2, m_off_code, HCI_INIT_TIMEOUT);
+	if (IS_ERR(skb)) {
+		BT_ERR("__hci_cmd_sync(mfg off): %ld", PTR_ERR(skb));
+		goto exit_error;
+	}
+	BT_DBG("%s mfg_off succeeded", hdev->name);
+	kfree_skb(skb);
+
+exit_done:
+	if (fw)
+		release_firmware(fw);
+	BT_DBG("%s btusb_setup_intel() completed", hdev->name);
+	return 0;
+
+exit_error:
+	if (fw)
+		release_firmware(fw);
+	BT_ERR("%s btusb_setup_intel() failed", hdev->name);
+	return -1;
+}
+
 static int btusb_setup(struct hci_dev *hdev)
 {
 	struct btusb_data *data = hci_get_drvdata(hdev);
 
 	BT_DBG("%s", hdev->name);
 
+	if (data->driver_info & BTUSB_INTEL) {
+		return btusb_setup_intel(hdev);
+	}
+
 	if (data->driver_info & BTUSB_BCM92035) {
 		struct sk_buff *skb;
 		__u8 val = 0x00;
-- 
1.7.9.5

             reply	other threads:[~2013-04-10  2:44 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2013-04-10  2:44 Tedd Ho-Jeong An [this message]
2013-04-10 15:40 ` [RFC] Bluetooth: Add support for Intel Bluetooth device [8087:07dc] Fry, Don
2013-04-10 16:23   ` Tedd Ho-Jeong An
2013-04-10 16:11 ` Marcel Holtmann
2013-04-10 16:34   ` Tedd Ho-Jeong An
2013-04-10 22:04   ` Johan Hedberg
2013-04-10 22:09     ` Marcel Holtmann
2013-04-11 19:35       ` Gustavo Padovan
2013-04-10 23:57   ` Johan Hedberg

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1640417.GJq4DRhhap@tedd-ubuntu \
    --to=tedd.an@intel.com \
    --cc=don.fry@intel.com \
    --cc=johan.hedberg@intel.com \
    --cc=linux-bluetooth@vger.kernel.org \
    --cc=marcel@holtmann.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.