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