From 8d25f9528b1ffb58c7c110b9cd06b1d88af5b697 Mon Sep 17 00:00:00 2001
From: Tedd Ho-Jeong An <tedd.an@intel.com>
Date: Tue, 15 May 2012 10:23:29 -0700
Subject: [PATCH] hciattach: Add support for Intel Bluetooth device

This patch enables the Intel Bluetooth device (UART sku) over the H4
protocol.  It is responsible for bring up the device into known state by
configuring the baudrate and applying the patches, if required.
---
 Makefile.tools          |    3 +-
 tools/hciattach.8       |    3 +
 tools/hciattach.c       |   13 ++
 tools/hciattach.h       |    1 +
 tools/hciattach_intel.c |  596 +++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 615 insertions(+), 1 deletion(-)
 create mode 100644 tools/hciattach_intel.c

diff --git a/Makefile.tools b/Makefile.tools
index cb0d1d0..4df7453 100644
--- a/Makefile.tools
+++ b/Makefile.tools
@@ -27,7 +27,8 @@ tools_hciattach_SOURCES = tools/hciattach.c tools/hciattach.h \
 						tools/hciattach_ti.c \
 						tools/hciattach_tialt.c \
 						tools/hciattach_ath3k.c \
-						tools/hciattach_qualcomm.c
+						tools/hciattach_qualcomm.c \
+						tools/hciattach_intel.c
 tools_hciattach_LDADD = lib/libbluetooth-private.la
 
 tools_hciconfig_SOURCES = tools/hciconfig.c tools/csr.h tools/csr.c \
diff --git a/tools/hciattach.8 b/tools/hciattach.8
index e0e2730..cc97cad 100644
--- a/tools/hciattach.8
+++ b/tools/hciattach.8
@@ -89,6 +89,9 @@ Serial adapters using CSR chips with BCSP serial protocol
 .TP
 .B ath3k
 Atheros AR300x based serial Bluetooth device
+.TP
+.B intel
+Intel Bluetooth device
 .RE
 
 Supported IDs are (manufacturer id, product id)
diff --git a/tools/hciattach.c b/tools/hciattach.c
index e0a9821..3e73956 100644
--- a/tools/hciattach.c
+++ b/tools/hciattach.c
@@ -131,6 +131,10 @@ static int uart_speed(int s)
 	case 3500000:
 		return B3500000;
 #endif
+#ifdef B3710000
+	case 3710000
+		return B3710000;
+#endif
 #ifdef B4000000
 	case 4000000:
 		return B4000000;
@@ -322,6 +326,11 @@ static int qualcomm(int fd, struct uart_t *u, struct termios *ti)
 	return qualcomm_init(fd, u->speed, ti, u->bdaddr);
 }
 
+static int intel(int fd, struct uart_t *u, struct termios *ti)
+{
+	return intel_init(fd, u->init_speed, &u->speed, ti);
+}
+
 static int read_check(int fd, void *buf, int count)
 {
 	int res;
@@ -1137,6 +1146,10 @@ struct uart_t uart[] = {
 	{ "qualcomm",   0x0000, 0x0000, HCI_UART_H4,   115200, 115200,
 			FLOW_CTL, DISABLE_PM, NULL, qualcomm, NULL },
 
+	/* Intel Bluetooth Module */
+	{ "intel",      0x0000, 0x0000, HCI_UART_H4,   115200, 115200,
+			FLOW_CTL, DISABLE_PM, NULL, intel, NULL },
+
 	{ NULL, 0 }
 };
 
diff --git a/tools/hciattach.h b/tools/hciattach.h
index f8093ff..a24dbc4 100644
--- a/tools/hciattach.h
+++ b/tools/hciattach.h
@@ -56,3 +56,4 @@ int ath3k_init(int fd, int speed, int init_speed, char *bdaddr,
 						struct termios *ti);
 int ath3k_post(int fd, int pm);
 int qualcomm_init(int fd, int speed, struct termios *ti, const char *bdaddr);
+int intel_init(int fd, int init_speed, int *speed, struct termios *ti);
diff --git a/tools/hciattach_intel.c b/tools/hciattach_intel.c
new file mode 100644
index 0000000..9129993
--- /dev/null
+++ b/tools/hciattach_intel.c
@@ -0,0 +1,596 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012 Intel Corporation. All rights reserved.
+ *
+ *
+ *  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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <time.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+
+#include "hciattach.h"
+
+#ifdef INTEL_DEBUG
+#define DBGPRINT(fmt, args...)	printf("DBG: " fmt "\n", ## args)
+#define PRINT_PACKET(buf, len, msg)	{	\
+	int i;					\
+	printf("%s\n", msg);			\
+	for (i = 0; i < len; i++)		\
+		printf("%02X ", buf[i]);	\
+	printf("\n");				\
+	}
+#else
+#define DBGPRINT(fmt, args...)
+#define PRINT_PACKET(buf, len, msg)
+#endif
+
+#define PATCH_SEQ_EXT           ".bseq"
+#define PATCH_FILE_PATH         "/lib/firmware/intel/"
+#define PATCH_MAX_LEN           260
+#define PATCH_TYPE_CMD          1
+#define PATCH_TYPE_EVT          2
+
+#define INTEL_VER_PARAM_LEN     9
+#define INTEL_MFG_PARAM_LEN     2
+
+/**
+ * A data structure for a patch entry.
+ */
+struct patch_entry {
+	int type;
+	int len;
+	unsigned char data[PATCH_MAX_LEN];
+};
+
+/**
+ * A structure for patch context
+ */
+struct patch_ctx {
+	int dev;
+	int fd;
+	int patch_error;
+	int reset_enable_patch;
+};
+
+/**
+ * Send HCI command to the controller
+ */
+static int intel_write_cmd(int dev, unsigned char *buf, int len)
+{
+	int ret;
+
+	PRINT_PACKET(buf, len, "<----- SEND CMD: ");
+
+	ret = write(dev, buf, len);
+	if (ret < 0)
+		return -errno;
+
+	if (ret != len)
+		return -1;
+
+	return ret;
+}
+
+/**
+ * Read the event from the controller
+ */
+static int intel_read_evt(int dev, unsigned char *buf, int len)
+{
+	int ret;
+
+	ret = read_hci_event(dev, buf, len);
+	if (ret < 0)
+		return -1;
+
+	PRINT_PACKET(buf, ret, "-----> READ EVT: ");
+
+	return ret;
+}
+
+/**
+ * Validate HCI events
+ */
+static int validate_events(struct patch_entry *event,
+		struct patch_entry *entry)
+{
+	if (event == NULL || entry == NULL) {
+		DBGPRINT("invalid patch entry parameters");
+		return -1;
+	}
+
+	if (event->len != entry->len) {
+		DBGPRINT("lengths are mismatched:[%d|%d]",
+				event->len, entry->len);
+		return -1;
+	}
+
+	if (memcmp(event->data, entry->data, event->len)) {
+		DBGPRINT("data is mismatched");
+		return -1;
+	}
+
+	return 0;
+}
+
+/**
+ * Read the next patch entry one line at a time
+ */
+static int get_next_patch_entry(int fd, struct patch_entry *entry)
+{
+	int len, size;
+	char rb;
+
+	if (read(fd, &rb, 1) <= 0)
+		return 0;
+
+	entry->type = rb;
+	len = 0;
+
+	switch (entry->type) {
+	case PATCH_TYPE_CMD:
+		entry->data[0] = HCI_COMMAND_PKT;
+
+		if (read(fd, &entry->data[1], 3) < 0)
+			return -1;
+
+		size = (int)entry->data[3];
+
+		if (read(fd, &entry->data[4], size) < 0)
+			return -1;
+
+		entry->len = HCI_TYPE_LEN + HCI_COMMAND_HDR_SIZE + size;
+
+		break;
+
+	case PATCH_TYPE_EVT:
+		entry->data[0] = HCI_EVENT_PKT;
+
+		if (read(fd, &entry->data[len], 2) < 0)
+			return -1;
+
+		size = (int)entry->data[2];
+
+		if (read(fd, &entry->data[3], size) < 0)
+			return -1;
+
+		entry->len = HCI_TYPE_LEN + HCI_EVENT_HDR_SIZE + size;
+
+		break;
+
+	default:
+		fprintf(stderr, "invalid patch entry(%d)\n", entry->type);
+		return -1;
+	}
+
+	return len;
+}
+
+/**
+ * Download the patch set to the controller and verify the event
+ */
+static int intel_download_patch(struct patch_ctx *ctx)
+{
+	int ret;
+	struct patch_entry entry;
+	struct patch_entry event;
+
+	DBGPRINT("start patch downloading");
+
+	do {
+		ret = get_next_patch_entry(ctx->fd, &entry);
+		if (ret <= 0) {
+			ctx->patch_error = 1;
+			break;
+		}
+
+		switch (entry.type) {
+		case PATCH_TYPE_CMD:
+			ret = intel_write_cmd(ctx->dev,
+					entry.data,
+					entry.len);
+			if (ret <= 0) {
+				fprintf(stderr, "failed to send cmd(%d)\n",
+						ret);
+				return ret;
+			}
+			break;
+
+		case PATCH_TYPE_EVT:
+			ret = intel_read_evt(ctx->dev, event.data,
+					sizeof(event.data));
+			if (ret <= 0) {
+				fprintf(stderr, "failed to read evt(%d)\n",
+						ret);
+				return ret;
+			}
+			event.len = ret;
+
+			if (validate_events(&event, &entry) < 0) {
+				DBGPRINT("events are mismatched");
+				ctx->patch_error = 1;
+				return -1;
+			}
+			break;
+
+		default:
+			fprintf(stderr, "unknown patch type(%d)\n",
+					entry.type);
+			return -1;
+		}
+	} while (1);
+
+	return ret;
+}
+
+static int open_patch_file(struct patch_ctx *ctx, char *fw_ver)
+{
+	char patch_file[PATH_MAX];
+
+	snprintf(patch_file, PATH_MAX, "%s%s%s", PATCH_FILE_PATH,
+			fw_ver, PATCH_SEQ_EXT);
+	DBGPRINT("PATCH_FILE: %s", patch_file);
+
+	ctx->fd = open(patch_file, O_RDONLY);
+	if (ctx->fd < 0) {
+		DBGPRINT("cannot open patch file. go to post patch");
+		return -1;
+	}
+
+	return 0;
+}
+
+/**
+ * Prepare the controller for patching.
+ */
+static int pre_patch(struct patch_ctx *ctx)
+{
+	int ret, i;
+	struct patch_entry entry;
+	char fw_ver[INTEL_VER_PARAM_LEN * 2];
+
+	DBGPRINT("start pre_patch");
+
+	entry.data[0] = HCI_COMMAND_PKT;
+	entry.data[1] = 0x11;
+	entry.data[2] = 0xFC;
+	entry.data[3] = 0x02;
+	entry.data[4] = 0x01;
+	entry.data[5] = 0x00;
+	entry.len = HCI_TYPE_LEN + HCI_COMMAND_HDR_SIZE + INTEL_MFG_PARAM_LEN;
+
+	ret = intel_write_cmd(ctx->dev, entry.data, entry.len);
+	if (ret < 0) {
+		fprintf(stderr, "failed to send cmd(%d)\n", ret);
+		return ret;
+	}
+
+	ret = intel_read_evt(ctx->dev, entry.data, sizeof(entry.data));
+	if (ret < 0) {
+		fprintf(stderr, "failed to read evt(%d)\n", ret);
+		return ret;
+	}
+	entry.len = ret;
+
+	if (entry.data[6] != 0x00) {
+		DBGPRINT("command failed. status=%02x", entry.data[6]);
+		ctx->patch_error = 1;
+		return -1;
+	}
+
+	entry.data[0] = HCI_COMMAND_PKT;
+	entry.data[1] = 0x05;
+	entry.data[2] = 0xFC;
+	entry.data[3] = 0x00;
+	entry.len = HCI_TYPE_LEN + HCI_COMMAND_HDR_SIZE;
+
+	ret = intel_write_cmd(ctx->dev, entry.data, entry.len);
+	if (ret < 0) {
+		fprintf(stderr, "failed to send cmd(%d)\n", ret);
+		return ret;
+	}
+
+	ret = intel_read_evt(ctx->dev, entry.data, sizeof(entry.data));
+	if (ret < 0) {
+		fprintf(stderr, "failed to read evt(%d)\n", ret);
+		return ret;
+	}
+	entry.len = ret;
+
+	if (entry.data[6] != 0x00) {
+		DBGPRINT("command failed. status=%02x", entry.data[6]);
+		ctx->patch_error = 1;
+		return -1;
+	}
+
+	for (i = 0; i < INTEL_VER_PARAM_LEN; i++)
+		sprintf(&fw_ver[i*2], "%02x", entry.data[7+i]);
+
+	if (open_patch_file(ctx, fw_ver) < 0) {
+		ctx->patch_error = 1;
+		return -1;
+	}
+
+	return ret;
+}
+
+/*
+ * check the event is startup event
+ */
+static int is_startup_evt(unsigned char *buf)
+{
+	if (buf[1] == 0xFF && buf[2] == 0x01 && buf[3] == 0x00)
+		return 1;
+
+	return 0;
+}
+
+/**
+ * Finalize the patch process and reset the controller
+ */
+static int post_patch(struct patch_ctx *ctx)
+{
+	int ret;
+	struct patch_entry entry;
+
+	DBGPRINT("start post_patch");
+
+	entry.data[0] = HCI_COMMAND_PKT;
+	entry.data[1] = 0x11;
+	entry.data[2] = 0xFC;
+	entry.data[3] = 0x02;
+	entry.data[4] = 0x00;
+	if (ctx->reset_enable_patch)
+		entry.data[5] = 0x02;
+	else
+		entry.data[5] = 0x01;
+
+	entry.len = HCI_TYPE_LEN + HCI_COMMAND_HDR_SIZE + INTEL_MFG_PARAM_LEN;
+
+	ret = intel_write_cmd(ctx->dev, entry.data, entry.len);
+	if (ret < 0) {
+		fprintf(stderr, "failed to send cmd(%d)\n", ret);
+		return ret;
+	}
+
+	ret = intel_read_evt(ctx->dev, entry.data, sizeof(entry.data));
+	if (ret < 0) {
+		fprintf(stderr, "failed to read evt(%d)\n", ret);
+		return ret;
+	}
+	entry.len = ret;
+
+	if (entry.data[6] != 0x00) {
+		fprintf(stderr, "cmd failed. st=%02x\n", entry.data[6]);
+		return -1;
+	}
+
+	do {
+		ret = intel_read_evt(ctx->dev, entry.data,
+					sizeof(entry.data));
+		if (ret < 0) {
+			fprintf(stderr, "failed to read cmd(%d)\n", ret);
+			return ret;
+		}
+		entry.len = ret;
+	} while (!is_startup_evt(entry.data));
+
+	return ret;
+}
+
+/**
+ * Main routine that handles the device patching process.
+ */
+static int intel_patch_device(struct patch_ctx *ctx)
+{
+	int ret;
+
+	ret = pre_patch(ctx);
+	if (ret < 0) {
+		if (!ctx->patch_error) {
+			fprintf(stderr, "I/O error: pre_patch failed\n");
+			return ret;
+		}
+
+		DBGPRINT("patch failed. proceed to post patch");
+		goto post_patch;
+	}
+
+	ret = intel_download_patch(ctx);
+	if (ret < 0) {
+		if (!ctx->patch_error) {
+			fprintf(stderr, "I/O error: download_patch failed\n");
+			close(ctx->fd);
+			return ret;
+		}
+	} else {
+		DBGPRINT("patch done");
+		ctx->reset_enable_patch = 1;
+	}
+
+	close(ctx->fd);
+
+post_patch:
+	ret = post_patch(ctx);
+	if (ret < 0) {
+		fprintf(stderr, "post_patch failed(%d)\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int set_rts(int dev, int rtsval)
+{
+	int arg;
+
+	if (ioctl(dev, TIOCMGET, &arg) < 0) {
+		perror("cannot get TIOCMGET");
+		return -errno;
+	}
+	if (rtsval)
+		arg |= TIOCM_RTS;
+	else
+		arg &= ~TIOCM_RTS;
+
+	if (ioctl(dev, TIOCMSET, &arg) == -1) {
+		perror("cannot set TIOCMGET");
+		return -errno;
+	}
+
+	return 0;
+}
+
+static unsigned char get_intel_speed(int speed)
+{
+	switch (speed) {
+	case 9600:
+		return 0x00;
+	case 19200:
+		return 0x01;
+	case 38400:
+		return 0x02;
+	case 57600:
+		return 0x03;
+	case 115200:
+		return 0x04;
+	case 230400:
+		return 0x05;
+	case 460800:
+		return 0x06;
+	case 921600:
+		return 0x07;
+	case 1843200:
+		return 0x08;
+	case 3250000:
+		return 0x09;
+	case 2000000:
+		return 0x0A;
+	case 3000000:
+		return 0x0B;
+	default:
+		return 0xFF;
+	}
+}
+
+/**
+ * if it failed to change to new baudrate, it will rollback
+ * to initial baudrate
+ */
+static int change_baudrate(int dev, int init_speed, int *speed,
+				struct termios *ti)
+{
+	int ret;
+	unsigned char br;
+	unsigned char cmd[5];
+	unsigned char evt[7];
+
+	DBGPRINT("start baudrate change");
+
+	ret = set_rts(dev, 0);
+	if (ret < 0) {
+		fprintf(stderr, "failed to clear RTS\n");
+		return ret;
+	}
+
+	cmd[0] = HCI_COMMAND_PKT;
+	cmd[1] = 0x06;
+	cmd[2] = 0xFC;
+	cmd[3] = 0x01;
+
+	br = get_intel_speed(*speed);
+	if (br == 0xFF) {
+		fprintf(stderr, "speed %d is not supported\n", *speed);
+		return -1;
+	}
+	cmd[4] = br;
+
+	ret = intel_write_cmd(dev, cmd, sizeof(cmd));
+	if (ret < 0) {
+		fprintf(stderr, "failed to send cmd(%d)\n", ret);
+		return ret;
+	}
+
+	/*
+	 *  wait for buffer to be consumed by the controller
+	 */
+	usleep(300000);
+
+	if (set_speed(dev, ti, *speed) < 0) {
+		fprintf(stderr, "can't set to new baud rate\n");
+		return -1;
+	}
+
+	ret = set_rts(dev, 1);
+	if (ret < 0) {
+		fprintf(stderr, "failed to set RTS\n");
+		return ret;
+	}
+
+	ret = intel_read_evt(dev, evt, sizeof(evt));
+	if (ret < 0) {
+		fprintf(stderr, "failed to read evt(%d)\n", ret);
+		return ret;
+	}
+
+	if (evt[4] != 0x00) {
+		fprintf(stderr,
+			"failed to change speed. use default speed %d\n",
+			init_speed);
+		*speed = init_speed;
+	}
+
+	return 0;
+}
+
+/**
+ * An entry point for Intel specific initialization
+ */
+int intel_init(int dev, int init_speed, int *speed, struct termios *ti)
+{
+	int ret = 0;
+	struct patch_ctx ctx;
+
+	if (change_baudrate(dev, init_speed, speed, ti) < 0)
+		return -1;
+
+	ctx.dev = dev;
+	ctx.patch_error = 0;
+	ctx.reset_enable_patch = 0;
+
+	ret = intel_patch_device(&ctx);
+	if (ret < 0)
+		fprintf(stderr, "failed to initialize the device");
+
+	return ret;
+}
-- 
1.7.10.4

