[PATCH] Added Host level support for Atheros AR3xxx Bluetooth Chip

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

 



Added support for Atheros AR3xxx Bluetooth chip

Implemented feature to download firmware configuration from host.


 Signed-off-by: Suraj <suraj@xxxxxxxxxxx>

---
 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@xxxxxxxxxxx>
+ */
+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@xxxxxxxxxxx>
+ */
+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@xxxxxxxxxxx>
+ */
+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@xxxxxxxxx>
  */
 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



--
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