Implements support for Atheros AR300x Bluetooth chip in hciattach. Adds feature to bring up AR300x Bluetooth chip with and without enhanced power management enabled. Signed-off-by: Suraj <suraj@xxxxxxxxxxx> --- Makefile.tools | 1 + tools/hciattach.8 | 6 + tools/hciattach.c | 111 +++++ tools/hciattach.h | 3 + tools/hciattach_ar3k.c | 1223 ++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 1344 insertions(+), 0 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..ef943ea 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 AR300x based serial bluetooth device with power management disabled +.TP +.B ar3k +Atheros AR300x based serial bluetooth device +.TP .B ericsson Ericsson based modules .TP diff --git a/tools/hciattach.c b/tools/hciattach.c index b13db1b..768a3f1 100644 --- a/tools/hciattach.c +++ b/tools/hciattach.c @@ -653,6 +653,110 @@ static int csr(int fd, struct uart_t *u, struct termios *ti) } /* + * Atheros AR300x specific initialization post callback + * with power management dsiabled + * Suraj Sumangala <Suraj@xxxxxxxxxxx> + */ +static int ar3kpost(int fd, struct uart_t *u, struct termios *ti) +{ + return ath_configure_sleep(fd, 0); +} + +/* + * Atheros AR300x specific initialization post callback + * with power management enabled + * Suraj Sumangala <Suraj@xxxxxxxxxxx> + */ +static int ar3kpmpost(int fd, struct uart_t *u, struct termios *ti) +{ + return ath_configure_sleep(fd, 1); +} +/* + * Atheros AR300x 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; + + 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> */ @@ -1071,6 +1175,13 @@ struct uart_t uart[] = { /* Broadcom BCM2035 */ { "bcm2035", 0x0A5C, 0x2035, HCI_UART_H4, 115200, 460800, FLOW_CTL, NULL, bcm2035 }, + /* ATHEROS AR300x */ + { "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..7719e04 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,5 @@ 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_configure_sleep(int fd, int sleep_stat); +int ath_ps_download(int fd); diff --git a/tools/hciattach_ar3k.c b/tools/hciattach_ar3k.c new file mode 100755 index 0000000..2542161 --- /dev/null +++ b/tools/hciattach_ar3k.c @@ -0,0 +1,1223 @@ +/* + * 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 <time.h> +#include <sys/time.h> +#include <sys/types.h> +#include <sys/param.h> +#include <sys/ioctl.h> + +#include <bluetooth/bluetooth.h> +#include <bluetooth/hci.h> +#include <bluetooth/hci_lib.h> + +#include "hciattach.h" + +/* The maximum number of bytes possible in a patch entry */ +#define MAX_PATCH_SIZE 20000 + +#define MAX_BYTE_LENGTH 244 + +/* Maximum HCI packets that will be formed from the Patch file */ +#define MAX_NUM_PATCH_ENTRY ((MAX_PATCH_SIZE/MAX_BYTE_LENGTH) + 1) + +#define DEV_REGISTER 0x4FFC + +#define FW_PATH "/lib/firmware/ar3k/" + +#define PS_ASIC_FILE "PS_ASIC.pst" +#define PS_FPGA_FILE "PS_FPGA.pst" +#define PATCH_FILE "RamPatch.txt" +#define BDADDR_FILE "ar3kbdaddr.pst" + +#define HCI_CMD_HEADER_LEN 7 + +/* PS command types */ +#define PS_RESET 2 +#define PS_WRITE 1 +#define WRITE_PATCH 8 +#define PS_VERIFY_CRC 9 +#define ENABLE_PATCH 11 + +#define EXTRA_PATCH_SIZE 500 + +/* PS configuration entry time */ +#define PS_TYPE_HEX 0 +#define PS_TYPE_DEC 1 + +#define PS_RESET_PARAM_LEN 6 +#define PS_RESET_CMD_LEN (PS_RESET_PARAM_LEN +\ + HCI_CMD_HEADER_LEN) + +#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) + +#define BYTES_OF_PS_DATA_PER_LINE 16 + +#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))) + +#define stringtohex(str) (((uint8_t)(tohexval((str)[0]) << 4)) |\ + ((uint8_t)tohexval((str)[1]))) + +#define set_pst_format(pst, type, array_val) ((pst)->data_type = (type),\ + (pst)->is_array = (array_val)) + +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 { + unsigned char 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; +}; + +struct ps_tag_entry ps_tag_entry[RAMPS_MAX_PS_TAGS_PER_FILE]; +struct ps_ram_patch ram_patch[MAX_NUM_PATCH_ENTRY]; + +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; + +} + +/* + *Create PS download commands from parsed data + */ +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; + int i; + + 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) + return -ENOMEM; + + /* Update commands to buffer */ + load_hci_header(hci_ps_cmd, + opcode, + ram_patch[i].Len, + i); + memcpy(&hci_ps_cmd[HCI_CMD_HEADER_LEN], + ram_patch[i].Data, + 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: + + hci_ps_cmd = (uint8_t *) malloc(HCI_CMD_HEADER_LEN); + + if (!hci_ps_cmd) + return -ENOMEM; + + load_hci_header(hci_ps_cmd, opcode, 0, 0x00); + ps_patch_packet[*index].Hcipacket = hci_ps_cmd; + ps_patch_packet[*index].packetLen = HCI_CMD_HEADER_LEN; + + (*index)++; + + break; + case PS_RESET: + + hci_ps_cmd = (uint8_t *) malloc(PS_RESET_CMD_LEN); + + if (!hci_ps_cmd) + return -ENOMEM; + + load_hci_header(hci_ps_cmd, opcode, PS_RESET_PARAM_LEN, 0x00); + + hci_ps_cmd[7] = 0x00; + hci_ps_cmd[PS_RESET_CMD_LEN - 2] = (param_1 & 0xFF); + hci_ps_cmd[PS_RESET_CMD_LEN - 1] = ((param_1 >> 8) & 0xFF); + + ps_patch_packet[*index].Hcipacket = hci_ps_cmd; + ps_patch_packet[*index].packetLen = PS_RESET_CMD_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) + return -ENOMEM; + + 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; + default: + break; + } + + return 0; +} + +static int get_ps_type(char *line, + int eol_index, + unsigned char *type, + unsigned char *sub_type) +{ + + switch (eol_index) { + case 1: + return 0; + case 2: + (*type) = toupper(line[1]); + break; + case 3: + if (line[2] == ':') + (*type) = toupper(line[1]); + else if (line[1] == ':') + (*sub_type) = toupper(line[2]); + else + return -1; + + break; + case 4: + if (line[2] != ':') + return -1; + + (*type) = toupper(line[1]); + (*sub_type) = toupper(line[3]); + + break; + case -1: + return -1; + } + + return 0; +} + +static int get_input_data_format(char *line, struct ps_data_format *pst_format) +{ + unsigned char type = '\0'; + unsigned char sub_type = '\0'; + int eol_index = -1; + int i; + + /* The default values */ + set_pst_format(pst_format, PS_TYPE_HEX, 1); + + if (line[0] != '[') { + set_pst_format(pst_format, PS_TYPE_HEX, 1); + return 0; + } + + for (i = 1; i < 5; i++) { + if (line[i] == ']') { + eol_index = i; + break; + } + } + + if (get_ps_type(line, eol_index, &type, &sub_type) < 0) + return -1; + + /* By default Hex array type is assumed */ + if (type == '\0' && sub_type == '\0') + set_pst_format(pst_format, PS_TYPE_HEX, 1); + + /* Check is data type is of array */ + if (type == 'A' || sub_type == 'A') + pst_format->is_array = 1; + + if (type == 'S' || sub_type == 'S') + pst_format->is_array = 0; + + switch (type) { + + case 'D': + case 'B': + + pst_format->data_type = PS_TYPE_DEC; + break; + default: + + pst_format->data_type = PS_TYPE_HEX; + break; + + } + + line += (eol_index + 1); + + return 0; + +} + +static unsigned int read_data_in_section(char *line, + struct ps_data_format format_info) +{ + char *token_ptr = line; + + if (!token_ptr) + return 0x0FFF; + + 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 == PS_TYPE_HEX) { + + if (format_info.is_array == 1) + return 0x0FFF; + else + return strtol(token_ptr, NULL, 16); + + } else + return 0x0FFF; + + return 0x0FFF; + +} +static int ath_parse_file(FILE *stream) +{ + char *buffer; + char *line; + uint8_t tag_cnt = 0; + int16_t byte_count = 0; + int read_count; + int num_ps_entry; + struct ps_data_format stps_data_format; + struct st_read_status read_status = { 0, 0, 0, 0 }; + + if (!stream) { + perror("Could not open config file .\n"); + return -1; + } + + buffer = malloc(LINE_SIZE_MAX + 1); + + if (!buffer) + return -ENOMEM; + + do { + line = fgets(buffer, LINE_SIZE_MAX, stream); + + if (!line) + break; + + skip_space(line); + + if ((line[0] == '/') && (line[1] == '/')) + continue; + + if ((line[0] == '#')) { + + if (read_status.section != 0) { + + if (buffer) + free(buffer); + return -1; + + } else { + read_status.section = 1; + continue; + } + } + + if ((line[0] == '/') && (line[1] == '*')) { + + read_status.section = 0; + continue; + } + + if (read_status.section == 1) { + skip_space(line); + + if (get_input_data_format( + line, &stps_data_format) < 0) { + + if (buffer) + 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) < 0) { + + if (buffer) + free(buffer); + return -1; + } + + byte_count = + read_data_in_section(line, stps_data_format); + + read_status.section = 2; + if (byte_count > LINE_SIZE_MAX / 2) { + if (buffer) + 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) < 0) { + if (buffer) + 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 == PS_TYPE_HEX && + stps_data_format.is_array == 1) { + + while (read_count > 0) { + + ps_tag_entry[tag_cnt].tag_data + [read_status.byte_count] = + stringtohex( + &line[read_status.char_cnt]); + + ps_tag_entry[tag_cnt].tag_data + [read_status.byte_count + 1] = + stringtohex( + &line[read_status.char_cnt + 3]); + + 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++; + + if (tag_cnt == RAMPS_MAX_PS_TAGS_PER_FILE) { + if (buffer) + free(buffer); + return -1; + } + } + + } + + } while (line); + + num_ps_entry = tag_cnt; + + if (tag_cnt > RAMPS_MAX_PS_TAGS_PER_FILE) { + if (buffer) + free(buffer); + return -1; + } + + if (buffer) + free(buffer); + + return num_ps_entry; +} + +static int parse_patch_file(FILE *stream) +{ + char byte[3]; + char line[MAX_BYTE_LENGTH + 1]; + int byte_cnt, byte_cnt_org; + int patch_index; + int i, k; + int data; + int patch_count = 0; + + byte[2] = '\0'; + + while (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 -ENOMEM; + } + + ram_patch[patch_count].Len = MAX_BYTE_LENGTH; + ram_patch[patch_count].Data = + (uint8_t *) malloc(MAX_BYTE_LENGTH); + + if (!ram_patch[patch_count].Data) + return -ENOMEM; + + 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); + + if (!ram_patch[patch_count].Data) + return -ENOMEM; + patch_count++; + } + + while (byte_cnt_org > MAX_BYTE_LENGTH) { + + k = 0; + for (i = 0; i < MAX_BYTE_LENGTH * 2; i += 2) { + if (!fgets(byte, 3, stream)) + return -1; + data = strtoul(&byte[0], NULL, 16); + ram_patch[patch_index].Data[k] = (data & 0xFF); + + k++; + } + + patch_index++; + + byte_cnt_org = byte_cnt_org - MAX_BYTE_LENGTH; + } + + if (patch_index == 0) + patch_index++; + + for (k = 0; k < byte_cnt_org; k++) { + + if (!fgets(byte, 3, stream)) + return -1; + + data = strtoul(byte, NULL, 16); + ram_patch[patch_index].Data[k] = (data & 0xFF); + } + + return patch_count; +} + +static int ath_parse_ps(FILE *stream, int *total_tag_len) +{ + int num_ps_tags; + int i; + unsigned char bdaddr_present = 0; + + if (stream) + num_ps_tags = ath_parse_file(stream); + + if (num_ps_tags < 0) + return -1; + + if (num_ps_tags == 0) + *total_tag_len = 10; + else { + + for (i = 0; i < num_ps_tags; 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 (num_ps_tags > 0 && !bdaddr_present) + *total_tag_len = *total_tag_len + 10; + + *total_tag_len = *total_tag_len + 10 + (num_ps_tags * 4); + + return num_ps_tags; +} + +static int ath_create_cmd_list(struct ps_cmd_packet **hci_packet_list, + uint32_t *num_packets, + int tag_count, + int patch_count, + int total_tag_len) +{ + uint8_t count; + uint32_t num_cmd_entry = 0; + + *num_packets = 0; + + if (patch_count || tag_count) { + + /* PS Reset Packet + Patch List + PS List */ + num_cmd_entry += (1 + 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 (!(*hci_packet_list)) + return -ENOMEM; + + if (patch_count > 0) { + + if (ath_create_ps_command(WRITE_PATCH, patch_count, + *hci_packet_list, num_packets) < 0) + return -1; + if (ath_create_ps_command(ENABLE_PATCH, 0, + *hci_packet_list, num_packets) < 0) + return -1; + if (ath_create_ps_command(PS_RESET, + total_tag_len + EXTRA_PATCH_SIZE, + *hci_packet_list, num_packets) < 0) + return -1; + + } else { + + if (ath_create_ps_command(PS_RESET, total_tag_len, + *hci_packet_list, num_packets) < 0) + return -1; + } + + 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)) + return -1; + + for (i = 0; i < num_packets; i++) + free((*hci_packet_list)[i].Hcipacket); + + free(*hci_packet_list); + + 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; + + 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); + + if (!hci_event) + return -ENOMEM; + + 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:/* CRC Check */ + case 0x0C:/* Change Baudrate */ + case 0x04:/* Enable sleep */ + return 0; + break; + default: + return -1; + break; + + } + } + + return -1; +} + +static int get_ps_file_name(int devtype, int rom_version, 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%x/%s", FW_PATH, rom_version, 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%x/%s", FW_PATH, rom_version, PATCH_FILE); + + return 0; +} +static int get_ar3k_crc(int dev, int tag_count, int patch_count) +{ + uint8_t hci_cmd[7]; + uint8_t *event; + uint8_t *buffer_to_free = NULL; + int retval = 1; + int crc; + + if (patch_count > 0) + crc |= RAM_PATCH_REGION; + if (tag_count > 0) + crc |= RAM_PS_REGION; + + load_hci_header(hci_cmd, PS_VERIFY_CRC, 0, crc); + + if (send_hci_cmd_wait_event(dev, + hci_cmd, + sizeof(hci_cmd), + &event, + &buffer_to_free) == 0) { + + if (read_ps_event(event) == 0) + retval = -1; + + if (buffer_to_free != NULL) + free(buffer_to_free); + } + + return retval; +} +static int get_device_type(int dev, uint32_t *code) +{ + uint8_t hci_cmd[] = { 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; + + hci_cmd[3] = (uint8_t) (DEV_REGISTER & 0xFF); + hci_cmd[4] = (uint8_t) ((DEV_REGISTER >> 8) & 0xFF); + hci_cmd[5] = (uint8_t) ((DEV_REGISTER >> 16) & 0xFF); + hci_cmd[6] = (uint8_t) ((DEV_REGISTER >> 24) & 0xFF); + + if (send_hci_cmd_wait_event(dev, + hci_cmd, + sizeof(hci_cmd), + &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) + free(buffer_to_free); + + return result; +} + +static int read_ar3k_version(int pConfig, int *rom_version, int *build_version) +{ + uint8_t hci_cmd[] = { 0x1E, 0xfc, 0x00 }; + uint8_t *event; + uint8_t *buffer_to_free = NULL; + int result = -1; + + if (send_hci_cmd_wait_event(pConfig, + hci_cmd, + sizeof(hci_cmd), + &event, + &buffer_to_free) == 0) { + + if (event[5] == 0xFC && + event[6] == 0x00 && + event[4] == 0x1E) { + + (*rom_version) = event[10]; + (*rom_version) = (((*rom_version) << 8) | event[9]); + (*rom_version) = (((*rom_version) << 8) | event[8]); + (*rom_version) = (((*rom_version) << 8) | event[7]); + + (*build_version) = event[14]; + (*build_version) = (((*build_version) << 8) | + event[13]); + (*build_version) = (((*build_version) << 8) | + event[12]); + (*build_version) = (((*build_version) << 8) | + event[11]); + + result = 1; + + } + + if (buffer_to_free) + 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, ":")) + colon_present = 1; + + bdbyte[2] = '\0'; + + for (i = 0, j = 5; i < 6; i++, j--) { + + bdbyte[0] = str_byte[0]; + bdbyte[1] = str_byte[1]; + 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 (send_hci_cmd_wait_event(pConfig, + bdaddr_cmd, + sizeof(bdaddr_cmd), + &event, + &buffer_to_free) == 0) { + + if (event[5] == 0xFC && event[6] == 0x00) { + if (event[4] == 0x0B) + result = 0; + } + + } + + if (buffer_to_free) + free(buffer_to_free); + + return result; +} + +int ath_configure_sleep(int fd, int sleep_stat) +{ + int dev_id, dd; + struct timespec tm = {0, 50000}; + + 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 vendor specific command with Sleep feature Enabled */ + if (hci_send_cmd(dd, OGF_VENDOR_CMD, 0x04, 1, &sleep_stat) < 0) + perror("Power management Disabled"); + + nanosleep(&tm, NULL); + hci_close_dev(dd); + + return 0; +} + +int ath_ps_download(int hdev) +{ + int i; + int status = 0; + int tag_count; + int patch_count; + int total_tag_len = 0; + int rom_version = 0, build_version = 0; + + struct ps_cmd_packet *hci_cmd_list; /* List storing the commands */ + uint32_t num_cmds; + uint8_t *event; + uint8_t *buffer_to_free; + uint32_t dev_type; + + char patch_file[PATH_MAX]; + char ps_file[PATH_MAX]; + char bdaddr_file[PATH_MAX]; + + FILE *stream; + char bdaddr[21]; + + hci_cmd_list = NULL; + + /* + * Verfiy firmware version. depending on it select the PS + * config file to download. + */ + if (get_device_type(hdev, &dev_type) == -1) { + status = -1; + goto download_cmplete; + } + if (read_ar3k_version(hdev, &rom_version, &build_version) == -1) { + status = -1; + goto download_cmplete; + } + + get_ps_file_name(dev_type, rom_version, ps_file); + + get_patch_file_name(dev_type, rom_version, build_version, patch_file); + + /* Read the PS file to a dynamically allocated buffer */ + stream = fopen(ps_file, "r"); + + if (!stream) { + perror("firmware file open error\n"); + status = -1; + goto download_cmplete; + } + + tag_count = ath_parse_ps(stream, &total_tag_len); + + fclose(stream); + + if (tag_count == -1) { + status = -1; + goto download_cmplete; + } + + /* + * It is not necessary that Patch file be available, + * continue with PS Operations if. + * failed. + */ + if (patch_file[0] == '\0') + status = 0; + + stream = fopen(patch_file, "r"); + + if (!stream) { + patch_count = 0; + status = 0; + } else { + /* parse and store the Patch file contents to + * a global variables + */ + patch_count = parse_patch_file(stream); + + fclose(stream); + + if (patch_count < 0) { + status = -1; + goto download_cmplete; + } + } + + /* + * Send the CRC packet, + * Continue with the PS operations + * only if the CRC check failed + */ + if (get_ar3k_crc(hdev, tag_count, patch_count) < 0) { + status = 0; + goto download_cmplete; + } + + /* Create an HCI command list + * from the parsed PS and patch information + */ + ath_create_cmd_list(&hci_cmd_list, + &num_cmds, + tag_count, + patch_count, + total_tag_len); + + for (i = 0; i < num_cmds; 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) + free(buffer_to_free); + + status = -1; + goto download_cmplete; + } + if (buffer_to_free) + free(buffer_to_free); + } else { + status = 0; + goto download_cmplete; + } + } + /* Read the PS file to a dynamically allocated buffer */ + sprintf(bdaddr_file, "%s%x/%s", FW_PATH, rom_version, BDADDR_FILE); + + stream = fopen(bdaddr_file, "r"); + + if (!stream) { + status = 0; + goto download_cmplete; + } + + if (fgets(bdaddr, 20, stream)) + status = write_bdaddr(hdev, bdaddr); + + fclose(stream); + +download_cmplete: + + if (hci_cmd_list) + ath_free_command_list(&hci_cmd_list, num_cmds); + + 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