Return-Path: Subject: Re: [PATCH v3] hciattach application support for Atheros AR300x Bluetooth Chip From: suraj To: CC: , , In-Reply-To: <1273672059.32536.14.camel@atheros013-desktop> References: <1268629296.21425.23.camel@atheros013-desktop> <1271758832.6585.33.camel@atheros013-desktop> <1271845337.15010.1.camel@atheros013-desktop> <1271927414.1409.3.camel@atheros013-desktop> <1273672059.32536.14.camel@atheros013-desktop> Content-Type: text/plain; charset="UTF-8" Date: Thu, 20 May 2010 19:07:02 +0530 Message-ID: <1274362622.12553.0.camel@atheros013-desktop> MIME-Version: 1.0 Sender: linux-bluetooth-owner@vger.kernel.org List-ID: Hi, On Wed, 2010-05-12 at 19:17 +0530, suraj wrote: > 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 > > --- > 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 > + */ > +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 > + */ > +static int ar3kpmpost(int fd, struct uart_t *u, struct termios *ti) > +{ > + return ath_configure_sleep(fd, 1); > +} > +/* > + * Atheros AR300x specific initialization > + * Suraj Sumangala > + */ > +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 > */ > @@ -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 > +#endif > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#include > +#include > +#include > + > +#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; > +} Please spend some time to verify the patch so that I can take it to the next level. Regards Suraj