All of lore.kernel.org
 help / color / mirror / Atom feed
From: suraj <suraj@atheros.com>
To: <linux-bluetooth@vger.kernel.org>
Cc: <marcel@holtmann.org>, <Luis.Rodriguez@Atheros.com>,
	<Jothikumar.Mothilal@Atheros.com>
Subject: [PATCH] Added Host level support for Atheros AR3xxx Bluetooth Chip
Date: Wed, 31 Mar 2010 16:29:27 +0530	[thread overview]
Message-ID: <1270033167.1679.86.camel@atheros013-desktop> (raw)
In-Reply-To: <1269408445.23163.0.camel@atheros013-desktop>

Added support for Atheros AR3xxx Bluetooth chip

Implemented feature to download firmware configuration from host.


 Signed-off-by: Suraj <suraj@atheros.com>

---
 Makefile.tools         |    1 +
 tools/hciattach.8      |    6 +
 tools/hciattach.c      |  159 +++++++-
 tools/hciattach.h      |    2 +
 tools/hciattach_ar3k.c | 1228 ++++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 1395 insertions(+), 1 deletions(-)
 create mode 100755 tools/hciattach_ar3k.c

diff --git a/Makefile.tools b/Makefile.tools
index 2735d68..48cf097 100644
--- a/Makefile.tools
+++ b/Makefile.tools
@@ -23,6 +23,7 @@ tools_l2ping_LDADD = lib/libbluetooth.la
 tools_hciattach_SOURCES = tools/hciattach.c tools/hciattach.h \
 						tools/hciattach_st.c \
 						tools/hciattach_ti.c \
+						tools/hciattach_ar3k.c \
 						tools/hciattach_tialt.c
 tools_hciattach_LDADD = lib/libbluetooth.la
 
diff --git a/tools/hciattach.8 b/tools/hciattach.8
index f750222..af648f2 100644
--- a/tools/hciattach.8
+++ b/tools/hciattach.8
@@ -49,6 +49,12 @@ specific identifier. Currently supported types are
 .B any
 Unspecified HCI_UART interface, no vendor specific options
 .TP
+.B ar3kalt
+Atheros AR3xxx based modules with power management disabled
+.TP
+.B ar3k
+Atheros AR3xxx based modules
+.TP
 .B ericsson
 Ericsson based modules
 .TP
diff --git a/tools/hciattach.c b/tools/hciattach.c
index 364c5ff..3557554 100644
--- a/tools/hciattach.c
+++ b/tools/hciattach.c
@@ -653,7 +653,156 @@ static int csr(int fd, struct uart_t *u, struct termios *ti)
 }
 
 /* 
- * Silicon Wave specific initialization 
+ * Atheros AR3xxx specific initialization code with power management disabled.
+ * Suraj Sumangala <Suraj@Atheros.com>
+ */
+static int ar3kpost(int fd, struct uart_t *u, struct termios *ti)
+{
+	int dev_id, dd;
+	struct timespec tm = {0, 50000};
+	int status = 0;
+
+
+	dev_id = ioctl(fd, HCIUARTGETDEVICE, 0);
+	if (dev_id < 0) {
+		perror("cannot get device id");
+		return -1;
+	}
+
+
+	dd = hci_open_dev(dev_id);
+	if (dd < 0) {
+		perror("HCI device open failed");
+		return -1;
+	}
+	sleep(2);
+	/* send command with Sleep feature disabled */
+	hci_send_cmd(dd, OGF_VENDOR_CMD, 0x04, 1, &status);
+
+	nanosleep(&tm, NULL);
+	hci_close_dev(dd);
+
+	return 0;
+
+}
+/*
+ * Atheros AR3xxx specific initialization post callback
+ *  with power management enabled
+ * Suraj Sumangala <Suraj@Atheros.com>
+ */
+static int ar3kpmpost(int fd, struct uart_t *u, struct termios *ti)
+{
+	int dev_id, dd;
+	struct timespec tm = {0, 50000};
+	int status = 1;
+
+
+	dev_id = ioctl(fd, HCIUARTGETDEVICE, 0);
+	if (dev_id < 0) {
+		perror("cannot get device id");
+		return -1;
+	}
+
+
+	dd = hci_open_dev(dev_id);
+	if (dd < 0) {
+		perror("HCI device open failed");
+		return -1;
+	}
+	sleep(2);
+	/* send command with Sleep feature disabled */
+	if (hci_send_cmd(dd, OGF_VENDOR_CMD, 0x04, 1, &status) < 0)
+		perror("sleep enable command not sent");
+
+	nanosleep(&tm, NULL);
+	hci_close_dev(dd);
+
+	return 0;
+}
+/*
+ * Atheros AR3xxx specific initialization
+ * Suraj Sumangala <Suraj@Atheros.com>
+ */
+static int ar3kinit(int fd, struct uart_t *u, struct termios *ti)
+{
+	struct timespec tm = { 0, 500000 };
+	unsigned char cmd[14], rsp[100];
+	int r;
+	int baud;
+
+	/* Download PS and patch */
+	r = ath_ps_download(fd);
+	if (r < 0) {
+		perror("Failed to Download configuration");
+		return -1;
+	}
+	/* Write BDADDR if user has provided any */
+	if (u->bdaddr != NULL) {
+		/* Set BD_ADDR */
+		memset(cmd, 0, sizeof(cmd));
+		memset(rsp, 0, sizeof(rsp));
+		cmd[0] = HCI_COMMAND_PKT;
+		cmd[1] = 0x0B;
+		cmd[2] = 0xfc;
+		cmd[3] = 0x0A;
+		cmd[4] = 0x01;
+		cmd[5] = 0x01;
+		cmd[6] = 0x00;
+		cmd[7] = 0x06;
+		str2ba(u->bdaddr, (bdaddr_t *) (cmd + 8));
+
+		/* Send command */
+		if (write(fd, cmd, 14) != 14) {
+			fprintf(stderr, "Failed to write BD_ADDR command\n");
+			return -1;
+		}
+
+		/* Read reply */
+		if (read_hci_event(fd, rsp, 10) < 0) {
+			fprintf(stderr, "Failed to set BD_ADDR\n");
+			return -1;
+		}
+	}
+
+	/* Send HCI Reset to write the configuration */
+	cmd[0] = HCI_COMMAND_PKT;
+	cmd[1] = 0x03;
+	cmd[2] = 0x0c;
+	cmd[3] = 0x00;
+	/* Send reset command */
+	r = write(fd, cmd, 4);
+
+	if (r != 4)
+		return -1;
+
+	nanosleep(&tm, NULL);
+	if (read_hci_event(fd, rsp, sizeof(rsp)) < 0)
+		return -1;
+
+	/* Set baud rate command,
+	 * set controller baud rate to user specified value */
+	cmd[0] = HCI_COMMAND_PKT;
+	cmd[1] = 0x0C;
+	cmd[2] = 0xfc;
+	cmd[3] = 0x02;
+	baud = u->speed/100;
+	cmd[4] = (char)baud;
+	cmd[5] = (char)(baud >> 8);
+
+	if (write(fd, cmd, 6) != 6) {
+		perror("Failed to write init command");
+		return -1;
+	}
+
+	/* Wait for the command complete event for Baud rate change Command */
+	nanosleep(&tm, NULL);
+	if (read_hci_event(fd, rsp, sizeof(rsp)) < 0)
+		return -1;
+
+	return 0;
+}
+/*
+ * Silicon Wave specific initialization
  * Thomas Moser <thomas.moser@tmoser.ch>
  */
 static int swave(int fd, struct uart_t *u, struct termios *ti)
@@ -1071,6 +1220,14 @@ struct uart_t uart[] = {
 	/* Broadcom BCM2035 */
 	{ "bcm2035",    0x0A5C, 0x2035, HCI_UART_H4,   115200, 460800, FLOW_CTL, NULL, bcm2035  },
 
+	/* ATHEROS AR3xxx */
+	{ "ar3kalt",	 0x0000, 0x0000, HCI_UART_ATH,
+	   115200, 115200, FLOW_CTL, NULL, ar3kinit, ar3kpost  },
+
+	{ "ar3k",    0x0000, 0x0000, HCI_UART_ATH,
+	   115200, 115200, FLOW_CTL, NULL, ar3kinit, ar3kpmpost  },
+
+
 	{ NULL, 0 }
 };
 
diff --git a/tools/hciattach.h b/tools/hciattach.h
index 867563b..5b68668 100644
--- a/tools/hciattach.h
+++ b/tools/hciattach.h
@@ -36,6 +36,7 @@
 #define HCI_UART_3WIRE	2
 #define HCI_UART_H4DS	3
 #define HCI_UART_LL	4
+#define HCI_UART_ATH    5
 
 int read_hci_event(int fd, unsigned char* buf, int size);
 int set_speed(int fd, struct termios *ti, int speed);
@@ -45,3 +46,4 @@ int texas_post(int fd, struct termios *ti);
 int texasalt_init(int fd, int speed, struct termios *ti);
 int stlc2500_init(int fd, bdaddr_t *bdaddr);
 int bgb2xx_init(int dd, bdaddr_t *bdaddr);
+int ath_ps_download(int fd);
diff --git a/tools/hciattach_ar3k.c b/tools/hciattach_ar3k.c
new file mode 100755
index 0000000..ca418d9
--- /dev/null
+++ b/tools/hciattach_ar3k.c
@@ -0,0 +1,1228 @@
+/*
+ * 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
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <termios.h>
+#include <time.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+
+#include "hciattach.h"
+
+/* Helper data type declaration */
+
+#define FALSE				0
+#define TRUE				1
+
+/* The maximum number of bytes possible in a patch entry */
+#define MAX_PATCH_SIZE			20000
+
+/* Maximum HCI packets that will be formed from the Patch file */
+#define MAX_NUM_PATCH_ENTRY		((MAX_PATCH_SIZE/MAX_BYTE_LENGTH) + 1)
+
+#define FPGA_REGISTER			0x4FFC
+
+#define PS_ASIC_FILE			"PS_ASIC.pst"
+#define PS_FPGA_FILE			"PS_FPGA.pst"
+#define FW_PATH				"/lib/firmware/"
+#define PATCH_FILE			"RamPatch.txt"
+#define BDADDR_FILE			"ar3kbdaddr.pst"
+
+
+
+#define PS_RESET			2
+#define PS_WRITE			1
+#define WRITE_PATCH			8
+#define PS_VERIFY_CRC			9
+#define ENABLE_PATCH			11
+
+
+#define NUM_WAKEUP_RETRY		10
+
+#define HCI_CMD_HEADER_LEN		7
+
+#define RAM_PS_REGION			(1<<0)
+#define RAM_PATCH_REGION		(1<<1)
+
+#define RAMPS_MAX_PS_TAGS_PER_FILE	50
+#define PS_MAX_LEN			500
+#define LINE_SIZE_MAX			(PS_MAX_LEN * 2)
+
+/* Constant values used by parser */
+#define BYTES_OF_PS_DATA_PER_LINE	16
+
+#define MAX_BYTE_LENGTH                 244
+
+#define skip_space(str) while (*(str) == (' ')) ((str)++)
+
+#define IS_BETWEEN(x, lower, upper) (((lower) <= (x)) && ((x) <= (upper)))
+
+#define tohexval(c) (isdigit(c) ? ((c) - '0') :	\
+			(IS_BETWEEN((c), 'A', 'Z') ?	\
+			((c) - 'A' + 10) : ((c) - 'a' + 10)))
+
+enum ps_entry_type {
+	hex_type,
+	decimal_type
+};
+
+struct ps_tag_entry {
+	uint32_t tag_id;
+	uint32_t tag_len;
+	uint8_t *tag_data;
+};
+
+struct ps_ram_patch {
+	int16_t Len;
+	uint8_t *Data;
+};
+struct ps_data_format {
+	enum ps_entry_type data_type;
+	unsigned char is_array;
+};
+
+struct ps_cmd_packet {
+	uint8_t *Hcipacket;
+	int packetLen;
+};
+
+struct st_read_status {
+	unsigned section;
+	unsigned line_count;
+	unsigned char_cnt;
+	unsigned byte_count;
+};
+
+/* Stores the number of PS Tags */
+static uint32_t tag_count;
+
+/* Stores the number of patch commands */
+static uint32_t patch_count;
+static uint32_t total_tag_len;
+static uint32_t rom_version;
+static uint32_t build_version;
+
+struct ps_tag_entry ps_tag_entry[RAMPS_MAX_PS_TAGS_PER_FILE];
+struct ps_ram_patch ram_patch[MAX_NUM_PATCH_ENTRY];
+
+
+/* PS parser helper function */
+static void load_hci_header(uint8_t *hci_ps_cmd,
+			    uint8_t opcode, int length, int index)
+{
+	hci_ps_cmd[0] = 0x0B;
+	hci_ps_cmd[1] = 0xFC;
+	hci_ps_cmd[2] = length + 4;
+	hci_ps_cmd[3] = opcode;
+	hci_ps_cmd[4] = (index & 0xFF);
+	hci_ps_cmd[5] = ((index >> 8) & 0xFF);
+	hci_ps_cmd[6] = length;
+}
+
+
+static int ath_create_ps_command(uint8_t opcode, uint32_t param_1,
+				 struct ps_cmd_packet *ps_patch_packet,
+				 uint32_t *index)
+{
+	uint8_t *hci_ps_cmd;
+	uint32_t len;
+	int i;
+
+	hci_ps_cmd = NULL;
+
+	switch (opcode) {
+
+	case WRITE_PATCH:
+
+		for (i = 0; i < param_1; i++) {
+			/* Allocate command buffer */
+			hci_ps_cmd =
+			    (uint8_t *) malloc(ram_patch[i].Len +
+					       HCI_CMD_HEADER_LEN);
+
+			if (hci_ps_cmd == NULL)
+				return -1;
+
+			memset(hci_ps_cmd, 0,
+			       ram_patch[i].Len + HCI_CMD_HEADER_LEN);
+
+			/* Update commands to buffer */
+			load_hci_header(hci_ps_cmd, opcode, ram_patch[i].Len,
+					i);
+			memcpy(ram_patch[i].Data,
+				&hci_ps_cmd[HCI_CMD_HEADER_LEN],
+				ram_patch[i].Len);
+
+			ps_patch_packet[*index].Hcipacket = hci_ps_cmd;
+			ps_patch_packet[*index].packetLen =
+			    ram_patch[i].Len + HCI_CMD_HEADER_LEN;
+
+			(*index)++;
+		}
+		break;
+	case ENABLE_PATCH:
+
+		len = 0;
+		i = 0;
+
+		hci_ps_cmd = (uint8_t *) malloc(len + HCI_CMD_HEADER_LEN);
+		if (hci_ps_cmd == NULL)
+			return -1;
+
+		memset(hci_ps_cmd, 0, len + HCI_CMD_HEADER_LEN);
+
+		load_hci_header(hci_ps_cmd, opcode, len, i);
+
+		ps_patch_packet[*index].Hcipacket = hci_ps_cmd;
+		ps_patch_packet[*index].packetLen
+						= len + HCI_CMD_HEADER_LEN;
+
+		(*index)++;
+
+		break;
+	case PS_RESET:
+
+		len = 0x06;
+		i = 0;
+
+		hci_ps_cmd = (uint8_t *) malloc(len + HCI_CMD_HEADER_LEN);
+		if (hci_ps_cmd == NULL)
+			return -1;
+
+		memset(hci_ps_cmd, 0, len + HCI_CMD_HEADER_LEN);
+
+		load_hci_header(hci_ps_cmd, opcode, len, i);
+
+		hci_ps_cmd[7] = 0x00;
+		hci_ps_cmd[len + HCI_CMD_HEADER_LEN - 2] = (param_1 & 0xFF);
+		hci_ps_cmd[len + HCI_CMD_HEADER_LEN - 1] =
+		    ((param_1 >> 8) & 0xFF);
+
+		ps_patch_packet[*index].Hcipacket = hci_ps_cmd;
+		ps_patch_packet[*index].packetLen
+						= len + HCI_CMD_HEADER_LEN;
+
+		(*index)++;
+
+		break;
+	case PS_WRITE:
+		for (i = 0; i < param_1; i++) {
+			hci_ps_cmd =
+			    (uint8_t *) malloc(ps_tag_entry[i].tag_len +
+					       HCI_CMD_HEADER_LEN);
+			if (hci_ps_cmd == NULL)
+				return -1;
+
+			memset(hci_ps_cmd, 0,
+			       ps_tag_entry[i].tag_len + HCI_CMD_HEADER_LEN);
+
+			load_hci_header(hci_ps_cmd, opcode,
+					ps_tag_entry[i].tag_len,
+					ps_tag_entry[i].tag_id);
+			memcpy(&hci_ps_cmd[HCI_CMD_HEADER_LEN],
+				ps_tag_entry[i].tag_data,
+				ps_tag_entry[i].tag_len);
+
+			ps_patch_packet[*index].Hcipacket = hci_ps_cmd;
+
+			ps_patch_packet[*index].packetLen =
+			ps_tag_entry[i].tag_len + HCI_CMD_HEADER_LEN;
+
+			(*index)++;
+		}
+		break;
+	case PS_VERIFY_CRC:
+
+		len = 0x0;
+
+		hci_ps_cmd = (uint8_t *) malloc(len + HCI_CMD_HEADER_LEN);
+		if (hci_ps_cmd == NULL)
+			return -1;
+
+		memset(hci_ps_cmd, 0, len + HCI_CMD_HEADER_LEN);
+
+		load_hci_header(hci_ps_cmd, opcode, len, param_1);
+
+		ps_patch_packet[*index].Hcipacket = hci_ps_cmd;
+		ps_patch_packet[*index].packetLen
+						= len + HCI_CMD_HEADER_LEN;
+
+		(*index)++;
+
+		break;
+	default:
+		break;
+	}
+	return 0;
+}
+
+unsigned int get_input_data_format(char *line,
+				   struct ps_data_format *pst_format)
+{
+	if (line[0] != '[') {
+		pst_format->data_type = hex_type;
+		pst_format->is_array = TRUE;
+		return 0;
+	}
+
+	switch (line[1]) {
+
+	case 'H':
+	case 'h':
+		if (line[2] == ':') {
+			if ((line[3] == 'a') || (line[3] == 'A')) {
+
+				if (line[4] == ']') {
+					pst_format->data_type = hex_type;
+					pst_format->is_array = TRUE;
+					line += 5;
+					return 0;
+				} else
+					return 1;
+
+			}
+
+			if ((line[3] == 'S') || (line[3] == 's')) {
+
+				if (line[4] == ']') {
+					pst_format->data_type = hex_type;
+					pst_format->is_array = FALSE;
+					line += 5;
+					return 0;
+				} else
+					return 1;
+
+			} else if (line[3] == ']') {
+				pst_format->data_type = hex_type;
+				pst_format->is_array = TRUE;
+				line += 4;
+				return 0;
+			} else
+				return 1;
+
+		} else if (line[2] == ']') {
+			pst_format->data_type = hex_type;
+			pst_format->is_array = TRUE;
+			line += 3;
+
+			return 0;
+		} else
+			return 1;
+
+		break;
+
+	case 'A':
+	case 'a':
+
+		if (line[2] == ':') {
+
+			if ((line[3] == 'h') || (line[3] == 'H')) {
+				if (line[4] == ']') {
+					pst_format->data_type = hex_type;
+					pst_format->is_array = TRUE;
+					line += 5;
+					return 0;
+				} else
+					return 1;
+			} else if (line[3] == ']') {
+				pst_format->data_type = hex_type;
+				pst_format->is_array = TRUE;
+				line += 4;
+				return 0;
+			} else
+				return 1;
+
+		} else if (line[2] == ']') {
+
+			pst_format->data_type = hex_type;
+			pst_format->is_array = TRUE;
+			line += 3;
+			return 0;
+
+		} else
+			return 1;
+
+		break;
+
+	case 'S':
+	case 's':
+		if (line[2] == ':') {
+
+			if ((line[3] == 'h') || (line[3] == 'H')) {
+
+				if (line[4] == ']') {
+					pst_format->data_type = hex_type;
+					pst_format->is_array = TRUE;
+					line += 5;
+					return 0;
+				} else
+					return 1;
+			} else if (line[3] == ']') {
+				pst_format->data_type = hex_type;
+				pst_format->is_array = TRUE;
+				line += 4;
+				return 0;
+			} else
+				return 1;
+
+		} else if (line[2] == ']') {
+			pst_format->data_type = hex_type;
+			pst_format->is_array = TRUE;
+			line += 3;
+			return 0;
+		} else
+			return 1;
+		break;
+	default:
+		return 1;
+	}
+}
+
+static unsigned int read_data_in_section(char *line,
+					struct ps_data_format format_info)
+{
+	char *token_ptr = line;
+
+	if (token_ptr[0] == '[') {
+		while (token_ptr[0] != ']' && token_ptr[0] != '\0')
+			token_ptr++;
+
+		if (token_ptr[0] == '\0')
+			return 0x0FFF;
+
+		token_ptr++;
+	}
+
+	if (format_info.data_type == hex_type) {
+		if (format_info.is_array == TRUE)
+			return 0x0FFF;
+		else
+			return strtol(token_ptr, NULL, 16);
+	} else
+		return 0x0FFF;
+}
+
+static int ath_parse_file(FILE *stream)
+{
+	char *buffer;
+	char *line;
+	uint8_t tag_cnt;
+	int16_t byte_count;
+	uint32_t pos;
+	int read_count;
+	struct ps_data_format stps_data_format;
+
+	int line_read = 0;
+	struct st_read_status read_status = {
+		0, 0, 0, 0
+	};
+
+	pos = 0;
+	buffer = NULL;
+	tag_cnt = 0;
+	byte_count = 0;
+
+	if (stream == NULL) {
+		perror("Could not open config file  .\n");
+		return -1;
+	}
+
+	buffer = malloc(LINE_SIZE_MAX + 1);
+	if (NULL == buffer)
+		return -1;
+
+	while ((line = fgets(buffer, LINE_SIZE_MAX, stream)) != NULL) {
+		skip_space(line);
+
+		if ((line[0] == '/') && (line[1] == '/'))
+			continue;
+
+		if ((line[0] == '#')) {
+			if (read_status.section != 0) {
+				perror("error\n");
+				if (buffer != NULL)
+					free(buffer);
+				return -1;
+			} else {
+				read_status.section = 1;
+				continue;
+			}
+		}
+
+		if ((line[0] == '/') && (line[1] == '*')) {
+
+			line += 2;
+			skip_space(line);
+			line_read = 0;
+			read_status.section = 0;
+
+			continue;
+		}
+
+		if (read_status.section == 1) {
+			skip_space(line);
+
+			if (get_input_data_format(line, &stps_data_format)) {
+				if (buffer != NULL)
+					free(buffer);
+				return -1;
+			}
+
+			ps_tag_entry[tag_cnt].tag_id =
+			    read_data_in_section(line, stps_data_format);
+			read_status.section = 2;
+
+		} else if (read_status.section == 2) {
+
+			if (get_input_data_format(line, &stps_data_format)) {
+				if (buffer != NULL)
+					free(buffer);
+				return -1;
+			}
+
+			byte_count =
+			    read_data_in_section(line, stps_data_format);
+
+			if (byte_count > LINE_SIZE_MAX / 2) {
+				if (buffer != NULL)
+					free(buffer);
+
+				return -1;
+			}
+
+			ps_tag_entry[tag_cnt].tag_len = byte_count;
+			ps_tag_entry[tag_cnt].tag_data = (uint8_t *)
+							malloc(byte_count);
+
+			read_status.section = 3;
+			read_status.line_count = 0;
+
+		} else if (read_status.section == 3) {
+
+			if (read_status.line_count == 0) {
+				if (get_input_data_format(line,
+					&stps_data_format)) {
+					if (buffer != NULL)
+						free(buffer);
+					return -1;
+				}
+			}
+
+			skip_space(line);
+			read_status.char_cnt = 0;
+			if (line[read_status.char_cnt] == '[') {
+
+				while (line[read_status.char_cnt] != ']' &&
+				       line[read_status.char_cnt] != '\0')
+					read_status.char_cnt++;
+
+				if (line[read_status.char_cnt] == ']')
+					read_status.char_cnt++;
+				else
+					read_status.char_cnt = 0;
+
+			}
+
+			read_count = (byte_count > BYTES_OF_PS_DATA_PER_LINE)
+			    ? BYTES_OF_PS_DATA_PER_LINE : byte_count;
+
+			if ((stps_data_format.data_type == hex_type)
+			    && stps_data_format.is_array == TRUE) {
+
+				while (read_count > 0) {
+
+					ps_tag_entry[tag_cnt].tag_data
+					    [read_status.byte_count]
+					    = (uint8_t)(tohexval
+					     (line[read_status.char_cnt])
+					     << 4)
+					    | (uint8_t)(tohexval
+					     (line[read_status.char_cnt + 1]));
+
+					ps_tag_entry[tag_cnt].tag_data
+					    [read_status.byte_count
+					     + 1] = (uint8_t)(tohexval
+					     (line[read_status.char_cnt + 3])
+					     << 4)
+					    | (uint8_t)(tohexval
+					     (line[read_status.char_cnt + 4]));
+
+					read_status.char_cnt += 6;
+					read_status.byte_count += 2;
+					read_count -= 2;
+
+				}
+
+				if (byte_count > BYTES_OF_PS_DATA_PER_LINE)
+					byte_count -= BYTES_OF_PS_DATA_PER_LINE;
+				else
+					byte_count = 0;
+			}
+
+			read_status.line_count++;
+
+			if (byte_count == 0) {
+				read_status.section = 0;
+				read_status.char_cnt = 0;
+				read_status.line_count = 0;
+				read_status.byte_count = 0;
+			} else
+				read_status.char_cnt = 0;
+
+			if ((read_status.section == 0) &&
+			    (++tag_cnt == RAMPS_MAX_PS_TAGS_PER_FILE)) {
+				if (buffer != NULL)
+					free(buffer);
+				return -1;
+			}
+
+		}
+		line_read++;
+	}
+
+	tag_count = tag_cnt;
+
+	if (tag_cnt > RAMPS_MAX_PS_TAGS_PER_FILE) {
+		if (buffer != NULL)
+			free(buffer);
+		return -1;
+	}
+
+	if (buffer != NULL)
+		free(buffer);
+
+	return 0;
+}
+
+static int parse_patch_file(FILE *stream)
+{
+	char byte[3];
+	char line[MAX_BYTE_LENGTH + 1];
+	int byte_cnt, byte_cnt_org;
+	int count;
+	int i, j, k;
+	int data;
+	uint32_t filepos;
+
+	byte[2] = '\0';
+	j = 0;
+	filepos = 0;
+
+	while (NULL != fgets(line, MAX_BYTE_LENGTH, stream)) {
+		if (strlen(line) <= 1 || !isxdigit(line[0]))
+			continue;
+		else
+			break;
+	}
+
+	byte_cnt = strtol(line, NULL, 16);
+	byte_cnt_org = byte_cnt;
+
+	while (byte_cnt > MAX_BYTE_LENGTH) {
+
+		/* Handle case when the number of patch buffer is
+		 * more than the 20K */
+		if (MAX_NUM_PATCH_ENTRY == patch_count) {
+			for (i = 0; i < patch_count; i++)
+				free(ram_patch[i].Data);
+			return -1;
+		}
+		ram_patch[patch_count].Len = MAX_BYTE_LENGTH;
+		ram_patch[patch_count].Data =
+		    (uint8_t *) malloc(MAX_BYTE_LENGTH);
+
+		patch_count++;
+		byte_cnt = byte_cnt - MAX_BYTE_LENGTH;
+	}
+
+	ram_patch[patch_count].Len = (byte_cnt & 0xFF);
+
+	if (byte_cnt != 0) {
+		ram_patch[patch_count].Data = (uint8_t *) malloc(byte_cnt);
+		patch_count++;
+	}
+
+	count = 0;
+
+	while (byte_cnt_org > MAX_BYTE_LENGTH) {
+		for (i = 0, k = 0; i < MAX_BYTE_LENGTH * 2;
+		     i += 2, k++, count += 2) {
+			if (fgets(byte, 2, stream) == NULL)
+				return -1;
+			data = strtoul(&byte[0], NULL, 16);
+			ram_patch[j].Data[k] = (data & 0xFF);
+		}
+
+		j++;
+		byte_cnt_org = byte_cnt_org - MAX_BYTE_LENGTH;
+	}
+
+	if (j == 0)
+		j++;
+
+	for (k = 0; k < byte_cnt_org; i += 2, k++, count += 2) {
+
+		if (fgets(byte, 2, stream) == NULL)
+			return -1;
+
+		data = strtoul(byte, NULL, 16);
+		ram_patch[j].Data[k] = (data & 0xFF);
+	}
+
+	return 0;
+}
+
+static int ath_parse_ps(FILE *stream)
+{
+	int status;
+	int i;
+	unsigned char bdaddr_present = 0;
+	status = -1;
+
+
+	if (NULL != stream)
+		status = ath_parse_file(stream);
+
+	if (tag_count == 0)
+		total_tag_len = 10;
+	else {
+
+		for (i = 0; i < tag_count; i++) {
+
+			if (ps_tag_entry[i].tag_id == 1)
+				bdaddr_present = 1;
+			if (ps_tag_entry[i].tag_len % 2 == 1)
+				total_tag_len = total_tag_len
+					+ ps_tag_entry[i].tag_len + 1;
+			else
+				total_tag_len =
+				total_tag_len + ps_tag_entry[i].tag_len;
+
+		}
+	}
+	if (tag_count > 0 && !bdaddr_present)
+		total_tag_len = total_tag_len + 10;
+
+	total_tag_len = total_tag_len + 10 + (tag_count * 4);
+
+	return status;
+}
+
+static int ath_create_cmd_list(struct ps_cmd_packet **hci_packet_list,
+			uint32_t *num_packets)
+{
+	uint8_t count;
+	uint32_t num_cmd_entry = 0;
+	uint32_t crc = 0;
+
+	*num_packets = 0;
+
+	if (patch_count > 0)
+		crc |= RAM_PATCH_REGION;
+	if (tag_count > 0)
+		crc |= RAM_PS_REGION;
+
+	if (patch_count || tag_count) {
+
+		/* CRC Packet + PS Reset Packet  + Patch List + PS List */
+		num_cmd_entry += (2 + patch_count + tag_count);
+		if (patch_count > 0)
+			num_cmd_entry++;	/* Patch Enable Command */
+
+		(*hci_packet_list) =
+		    malloc(sizeof(struct ps_cmd_packet) * num_cmd_entry);
+		if (NULL == *hci_packet_list)
+			perror("memory allocation failed  \r\n");
+
+		ath_create_ps_command(PS_VERIFY_CRC, crc, *hci_packet_list,
+				      num_packets);
+
+		if (patch_count > 0) {
+
+			ath_create_ps_command(WRITE_PATCH, patch_count,
+					      *hci_packet_list, num_packets);
+			ath_create_ps_command(ENABLE_PATCH, 0,
+					      *hci_packet_list,
+					      num_packets);
+
+		}
+
+		ath_create_ps_command(PS_RESET, total_tag_len,
+				      *hci_packet_list, num_packets);
+
+		if (tag_count > 0)
+			ath_create_ps_command(PS_WRITE, tag_count,
+					      *hci_packet_list, num_packets);
+	}
+
+	for (count = 0; count < patch_count; count++)
+		free(ram_patch[patch_count].Data);
+
+	for (count = 0; count < tag_count; count++)
+		free(ps_tag_entry[count].tag_data);
+
+	return *num_packets;
+}
+
+static int ath_free_command_list(struct ps_cmd_packet **hci_packet_list,
+			  uint32_t num_packets)
+{
+	int i;
+
+	if (*hci_packet_list == NULL)
+		return -1;
+
+	for (i = 0; i < num_packets; i++)
+		free((*hci_packet_list)[i].Hcipacket);
+
+	free(*hci_packet_list);
+
+	return 0;
+}
+
+static int wake_up_ar3k(int fd)
+{
+	struct termios ti;
+	int status;
+	int retrycount = 0;
+	if (tcgetattr(fd, &ti) < 0) {
+		perror("Can't get port settings");
+		return -1;
+	}
+	ioctl(fd, TIOCMGET, &status);
+	if (status & TIOCM_CTS)
+		return 0;
+
+	ti.c_cflag &= ~CRTSCTS;
+	if (tcsetattr(fd, TCSANOW, &ti) < 0) {
+		perror("Can't set port settings");
+		return -1;
+	}
+
+	do {
+		ioctl(fd, TIOCMGET, &status);
+
+		/* deassert */
+		status &= (~TIOCM_RTS);
+		ioctl(fd, TIOCMSET, &status);
+
+		/* read */
+		ioctl(fd, TIOCMGET, &status);
+
+		/* assert */
+		status |= (TIOCM_RTS);
+		ioctl(fd, TIOCMSET, &status);
+		usleep(200);
+
+		/* read */
+		ioctl(fd, TIOCMGET, &status);
+		retrycount++;
+		if (retrycount > NUM_WAKEUP_RETRY)
+			break;
+	} while (!(status & TIOCM_CTS));
+	if (!(status & TIOCM_CTS))
+		return -1;
+
+	ti.c_cflag |= CRTSCTS;
+	if (tcsetattr(fd, TCSANOW, &ti) < 0) {
+		perror("Can't set port settings");
+		return -1;
+	}
+	return 0;
+}
+
+/*
+ *  This API is used to send the HCI command to controller and return
+ *  with a HCI Command Complete event.
+ */
+static int send_hci_cmd_wait_event(int dev,
+			    uint8_t *hci_command,
+			    int cmd_length,
+			    uint8_t **event, uint8_t **buffer_to_free)
+{
+	int r;
+	uint8_t *hci_event;
+	uint8_t pkt_type = 0x01;
+
+	if (cmd_length == 0)
+		return -1;
+
+	/*
+	 *  Try to wake up the board if it is asleep
+	 *  The assumption here is that the board is asleep.
+	 */
+	if (wake_up_ar3k(dev) < 0)
+		return -1;
+
+	if (write(dev, &pkt_type, 1) != 1)
+		return -1;
+
+	if (write(dev, (unsigned char *)hci_command, cmd_length) != cmd_length)
+		return -1;
+
+	hci_event = (uint8_t *) malloc(100);
+	r = read_hci_event(dev, (unsigned char *)hci_event, 100);
+	if (r > 0) {
+		*event = hci_event;
+		*buffer_to_free = hci_event;
+	} else {
+
+		/* Did not get an event from controller. return error */
+		free(hci_event);
+		*buffer_to_free = NULL;
+		return -1;
+	}
+	return 0;
+}
+
+static int read_ps_event(uint8_t *data)
+{
+
+	if (data[5] == 0xFC && data[6] == 0x00) {
+		switch (data[4]) {
+		case 0x0B:
+			return 0;
+			break;
+		case 0x0C:
+
+			/* Change Baudrate */
+			return 0;
+			break;
+		case 0x04:
+			return 0;
+			break;
+		case 0x1E:
+			rom_version = data[10];
+			rom_version = ((rom_version << 8) | data[9]);
+			rom_version = ((rom_version << 8) | data[8]);
+			rom_version = ((rom_version << 8) | data[7]);
+			build_version = data[14];
+			build_version = ((build_version << 8) | data[13]);
+			build_version = ((build_version << 8) | data[12]);
+			build_version = ((build_version << 8) | data[11]);
+			return 0;
+			break;
+		}
+	}
+	return -1;
+}
+
+static int get_ps_file_name(int devtype, char *path)
+{
+	char *filename;
+	int status = 0;
+
+	if (devtype == 0xdeadc0de) {
+		filename = PS_ASIC_FILE;
+		status = 1;
+	} else {
+		filename = PS_FPGA_FILE;
+		status = 0;
+	}
+
+	sprintf(path, "%s%s", FW_PATH, filename);
+	return status;
+}
+
+static int get_patch_file_name(int dev_type, int rom_version,
+			       int build_version, char *path)
+{
+	if ((dev_type != 0) && (dev_type != 0xdeadc0de)
+	    && (rom_version == 0x99999999) && (build_version == 1)) {
+		path[0] = '\0';
+	} else
+		sprintf(path, "%s%s", FW_PATH, PATCH_FILE);
+
+	return 0;
+}
+
+static int get_device_type(int dev, uint32_t *code)
+{
+	uint8_t hciCommand[] = {
+		0x05, 0xfc, 0x05, 0x00, 0x00, 0x00, 0x00, 0x04
+	};
+	uint8_t *event;
+	uint8_t *buffer_to_free = NULL;
+	uint32_t reg;
+
+	int result = -1;
+	*code = 0;
+
+	hciCommand[3] = (uint8_t) (FPGA_REGISTER & 0xFF);
+	hciCommand[4] = (uint8_t) ((FPGA_REGISTER >> 8) & 0xFF);
+	hciCommand[5] = (uint8_t) ((FPGA_REGISTER >> 16) & 0xFF);
+	hciCommand[6] = (uint8_t) ((FPGA_REGISTER >> 24) & 0xFF);
+
+	if (send_hci_cmd_wait_event(dev, hciCommand,
+				    sizeof(hciCommand), &event,
+				    &buffer_to_free) == 0) {
+		if (event[5] == 0xFC && event[6] == 0x00) {
+
+			switch (event[4]) {
+
+			case 0x05:
+				reg = event[10];
+				reg = ((reg << 8) | event[9]);
+				reg = ((reg << 8) | event[8]);
+				reg = ((reg << 8) | event[7]);
+				*code = reg;
+				result = 0;
+				break;
+
+			case 0x06:
+				break;
+			}
+		}
+	}
+
+	if (buffer_to_free != NULL)
+		free(buffer_to_free);
+
+	return result;
+}
+
+static int read_ar3k_version(int pConfig)
+{
+	uint8_t hciCommand[] = {
+		0x1E, 0xfc, 0x00
+	};
+	uint8_t *event;
+	uint8_t *buffer_to_free = NULL;
+	int result = -1;
+
+	if (0 ==
+	    send_hci_cmd_wait_event(pConfig, hciCommand,
+				    sizeof(hciCommand), &event,
+				    &buffer_to_free)) {
+		result = read_ps_event(event);
+	}
+	if (buffer_to_free != NULL)
+		free(buffer_to_free);
+
+	return result;
+}
+
+static int str2bdaddr(char *str_bdaddr, char *bdaddr)
+{
+	char bdbyte[3];
+	char *str_byte = str_bdaddr;
+	int i, j;
+	unsigned char colon_present = 0;
+
+	if (strstr(str_bdaddr, ":") != NULL)
+		colon_present = 1;
+
+	bdbyte[2] = '\0';
+
+	bdbyte[0] = str_byte[0];
+	bdbyte[1] = str_byte[1];
+
+	for (i = 0, j = 5; i < 6; i++, j--) {
+		bdaddr[j] = strtol(bdbyte, NULL, 16);
+
+		if (colon_present == 1)
+			str_byte += 3;
+		else
+			str_byte += 2;
+	}
+	return 0;
+}
+
+static int write_bdaddr(int pConfig, char *bdaddr)
+{
+	uint8_t bdaddr_cmd[] = { 0x0B, 0xFC, 0x0A, 0x01, 0x01,
+		0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+	};
+	uint8_t *event;
+	uint8_t *buffer_to_free = NULL;
+	int result = -1;
+
+	str2bdaddr(bdaddr, (char *)&bdaddr_cmd[7]);
+
+	if (0 == send_hci_cmd_wait_event(pConfig, bdaddr_cmd,
+					 sizeof(bdaddr_cmd),
+					 &event, &buffer_to_free)) {
+
+		if (event[5] == 0xFC && event[6] == 0x00) {
+			if (event[4] == 0x0B)
+				result = 0;
+		}
+
+	} else
+		perror(" Write failed \n");
+
+	if (buffer_to_free != NULL)
+		free(buffer_to_free);
+
+	return result;
+}
+
+int ath_ps_download(int hdev)
+{
+	int i;
+	int status;
+	struct ps_cmd_packet *hci_cmd_list;	/* List storing the commands */
+	uint32_t numCmds;
+	uint8_t *event;
+	uint8_t *buffer_to_free;
+	uint32_t DevType;
+	char patchFileName[PATH_MAX];
+	char PsFileName[PATH_MAX];
+	char bdaddr_file_name[PATH_MAX];
+	FILE *stream;
+	char bdaddr[21];
+
+	status = 0;
+	hci_cmd_list = NULL;
+
+	/* First verify if the controller is an FPGA or ASIC,
+	 *so depending on the device type the PS file to be written
+	 * will be different.
+	 */
+	do {
+		if (get_device_type(hdev, &DevType) == -1) {
+			status = -1;
+			break;
+		}
+		if (read_ar3k_version(hdev) == -1) {
+			status = -1;
+			break;
+		}
+
+		get_ps_file_name(DevType, PsFileName);
+		get_patch_file_name(DevType, rom_version, build_version,
+				    patchFileName);
+
+		/* Read the PS file to a dynamically allocated buffer */
+		stream = fopen(PsFileName, "r");
+		if (stream == NULL) {
+			perror("firmware file open error\n");
+			status = -1;
+			break;
+		}
+		status = ath_parse_ps(stream);
+		fclose(stream);
+
+		/*
+		 *  It is not necessary that Patch file be available,
+		 *  continue with PS Operations if.
+		 *  failed.
+		 */
+		if (patchFileName[0] == '\0')
+			status = 0;
+		stream = fopen(patchFileName, "r");
+		if (stream  == NULL)
+			status = 0;
+		else {
+			/* parse and store the Patch file contents to
+			 * a global variables
+			 */
+			status = parse_patch_file(stream);
+			fclose(stream);
+		}
+
+		/* Create an HCI command list
+		 * from the parsed PS and patch information */
+		ath_create_cmd_list(&hci_cmd_list, &numCmds);
+
+		/*
+		 * First Send the CRC packet,
+		 * We have to continue with the PS operations
+		 * only if the CRC packet has been replied with
+		 * a Command complete event with status Error.
+		 */
+		if (send_hci_cmd_wait_event
+		    (hdev, hci_cmd_list[0].Hcipacket, hci_cmd_list[0].packetLen,
+		     &event, &buffer_to_free) == 0) {
+
+			if (read_ps_event(event) == 0) {
+
+				/* Exit if the status is success */
+				if (buffer_to_free != NULL)
+					free(buffer_to_free);
+
+				status = 0;
+				break;
+			}
+			if (buffer_to_free != NULL)
+				free(buffer_to_free);
+		} else {
+			status = -1;
+			break;
+		}
+		for (i = 1; i < numCmds; i++) {
+
+			if (send_hci_cmd_wait_event
+			    (hdev, hci_cmd_list[i].Hcipacket,
+			     hci_cmd_list[i].packetLen, &event,
+			     &buffer_to_free) == 0) {
+
+				if (read_ps_event(event) < 0) {
+
+					/* Exit if the status is not success */
+					if (buffer_to_free != NULL)
+						free(buffer_to_free);
+
+					status = -1;
+					break;
+				}
+				if (buffer_to_free != NULL)
+					free(buffer_to_free);
+			} else {
+				status = 0;
+				break;
+			}
+		}
+		/* Read the PS file to a dynamically allocated buffer */
+		sprintf(bdaddr_file_name, "%s%s", FW_PATH, BDADDR_FILE);
+		stream = fopen(bdaddr_file_name, "r");
+
+		if (stream == NULL) {
+			status = 0;
+			break;
+		}
+
+		if (fgets(bdaddr, 20, stream) != NULL)
+			status = write_bdaddr(hdev, bdaddr);
+
+		fclose(stream);
+
+	} while (FALSE);
+
+	if (hci_cmd_list != NULL)
+		ath_free_command_list(&hci_cmd_list, numCmds);
+
+	return status;
+}
-- 
1.7.0

  parent reply	other threads:[~2010-03-31 10:59 UTC|newest]

Thread overview: 33+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2010-03-15  5:01 [PATCH] Added support for Atheros AR300x Bluetooth Chip suraj
2010-03-24  5:27 ` suraj
2010-03-29  9:01   ` suraj
     [not found]     ` <1271673889.19858.4.camel@atheros013-desktop>
2010-04-19 18:11       ` Support " Luis R. Rodriguez
2010-03-31 10:59   ` suraj [this message]
2010-04-19 23:53 ` [PATCH] Added support " Gustavo F. Padovan
2010-04-20 10:20 ` [PATCH v3] " suraj
2010-04-20 15:36   ` Gustavo F. Padovan
2010-04-20 17:34   ` Luis R. Rodriguez
2010-04-21  4:21     ` Suraj Sumangala
2010-04-21 10:22   ` [PATCH v4] Add support for the " suraj
2010-04-21 17:30     ` Luis R. Rodriguez
2010-04-22  6:10     ` Gustavo F. Padovan
2010-04-22  6:54       ` Suraj Sumangala
2010-04-22  8:59         ` Gustavo F. Padovan
2010-04-22  9:10     ` [PATCH v5] " suraj
2010-04-26 11:00       ` suraj
2010-04-27  6:19         ` [PATCH] New Firmware for Atheros bluetooth chipset AR3011 suraj
2010-04-27  8:28           ` [PATCH] patch to request new firmware for AR3011 Chip suraj
2010-04-27 15:55             ` Luis R. Rodriguez
2010-05-11  9:04             ` [PATCH v2] ath3k: add support for new firmware suraj
2010-05-05 12:33         ` [PATCH v5] Add support for the Atheros AR300x Bluetooth Chip suraj
2010-05-06  7:45           ` buffer starvation with multiple ACL link suraj
2010-05-20 16:02             ` Marcel Holtmann
2010-05-10 20:12           ` [PATCH v5] Add support for the Atheros AR300x Bluetooth Chip Luis R. Rodriguez
2010-05-11  8:29           ` suraj
2010-05-18 11:39             ` suraj
2010-05-12 13:47       ` [PATCH v3] hciattach application support for " suraj
2010-05-20 13:37         ` suraj
2010-05-20 16:00         ` Marcel Holtmann
2010-05-21  5:01           ` Suraj Sumangala
2010-05-21  7:34             ` Marcel Holtmann
2010-05-20 16:09       ` [PATCH v5] Add support for the " Marcel Holtmann

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=1270033167.1679.86.camel@atheros013-desktop \
    --to=suraj@atheros.com \
    --cc=Jothikumar.Mothilal@Atheros.com \
    --cc=Luis.Rodriguez@Atheros.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.