Signed-off-by: Suraj <suraj@xxxxxxxxxxx> --- Makefile.tools | 2 + tools/ar3kpsconfig.c | 455 ++++++++++++++++++++++++++++ tools/ar3kpsparser.c | 822 ++++++++++++++++++++++++++++++++++++++++++++++++++ tools/ar3kpsparser.h | 127 ++++++++ tools/hciattach.8 | 6 + tools/hciattach.c | 158 ++++++++++- tools/hciattach.h | 5 +- 7 files changed, 1572 insertions(+), 3 deletions(-) create mode 100755 tools/ar3kpsconfig.c create mode 100755 tools/ar3kpsparser.c create mode 100755 tools/ar3kpsparser.h diff --git a/Makefile.tools b/Makefile.tools index 2735d68..72277f9 100644 --- a/Makefile.tools +++ b/Makefile.tools @@ -21,6 +21,8 @@ tools_rfcomm_LDADD = lib/libbluetooth.la tools_l2ping_LDADD = lib/libbluetooth.la tools_hciattach_SOURCES = tools/hciattach.c tools/hciattach.h \ + tools/ar3kpsparser.c \ + tools/ar3kpsconfig.c \ tools/hciattach_st.c \ tools/hciattach_ti.c \ tools/hciattach_tialt.c diff --git a/tools/ar3kpsconfig.c b/tools/ar3kpsconfig.c new file mode 100755 index 0000000..e94e4b5 --- /dev/null +++ b/tools/ar3kpsconfig.c @@ -0,0 +1,455 @@ +/* + * 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 + * + */ + +#include "ar3kpsparser.h" +#include "hciattach.h" + +#define FW_PATH "/lib/firmware/" +#define NUM_WAKEUP_RETRY 10 + +uint32_t rom_version; +uint32_t build_version; + +int wake_up_ar3001(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. + */ +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 pktType = 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_ar3001(dev) < 0) + return -1; + + if (write(dev, &pktType, 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; +} + +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; +} + +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; +} + +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; +} + +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 (0 == + send_hci_cmd_wait_event(dev, hciCommand, + sizeof(hciCommand), &event, + &buffer_to_free)) { + 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; +} + +int read_ar3001_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; +} + +int str2bdaddr(char *str_bdaddr, char *bdaddr) +{ + char bdbyte[3]; + char *str_byte = str_bdaddr; + int i, j; + unsigned char colon_present = 0; + + if (NULL != 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; +} + +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 *HciCmdList; /* 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; + HciCmdList = 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 (-1 == get_device_type(hdev, &DevType)) { + status = -1; + break; + } + if (-1 == read_ar3001_version(hdev)) { + 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(&HciCmdList, &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, HciCmdList[0].Hcipacket, + HciCmdList[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, HciCmdList[i].Hcipacket, + HciCmdList[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 (NULL != HciCmdList) + ath_free_command_list(&HciCmdList, numCmds); + + return status; +} + +int configure_sleep(int fd, unsigned char status) +{ + unsigned char cmd[6]; + int r; + + /* Send command to enable/disable Advanced power management featuresa*/ + cmd[0] = HCI_COMMAND_PKT; + cmd[1] = 0x04; + cmd[2] = 0xFC; + cmd[3] = 0x01; + cmd[4] = status; + + /* Send Sleep enable/disable command */ + r = write(fd, cmd, 5); + if (r != 5) { + perror("Can't write Sleep Enable Cmd."); + return -1; + } + return 0; +} + +/* Parser Section Begin */ +int ar3001_post(int fd) +{ + return configure_sleep(fd, 0x00); +} + +int ar3001pm_post(int fd) +{ + return configure_sleep(fd, 0x01); +} diff --git a/tools/ar3kpsparser.c b/tools/ar3kpsparser.c new file mode 100755 index 0000000..cb62bee --- /dev/null +++ b/tools/ar3kpsparser.c @@ -0,0 +1,822 @@ +/* + * 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 + * + */ + +#include "ar3kpsparser.h" + +#define WRITE_PATCH 8 +#define ENABLE_PATCH 11 +#define PS_RESET 2 +#define PS_WRITE 1 +#define PS_VERIFY_CRC 9 +#define CHANGE_BDADDR 15 + +#define HCI_CMD_HEADER_LEN 7 +#define HCI_EVENT_SIZE 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 INVALID_THRESHOLD 0xFF + +#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 st_read_status { + unsigned section; + unsigned line_count; + unsigned char_cnt; + unsigned byte_count; +}; +struct brm_rxtx_pkt_sel_cfg { + unsigned int force_up_rate_threshold; + unsigned char edr_up_threshold[PKT_SEL_BUFFER_SIZE]; + unsigned char edr_down_threshold[PKT_SEL_BUFFER_SIZE]; + unsigned char rss_min_delta; + unsigned char per_min_delta; + unsigned char max_long_term_errors[PKT_SEL_BUFFER_SIZE]; + unsigned char long_term_error_per_threshold; +}; + +uint8_t hci_rfcomm_down_cmd[] = { + 0x01, 0x32, 0xFC, 0x21, 0x00, 0x00 /* Param begin */ , + 0x19, 0x00, 0x00, 0x00, /* force_up_rate_threshold */ + /* EDR_PER_UP_THRESHOLDS_DEFAULT */ + INVALID_THRESHOLD, /* ePktSel_EDR_Init */ + 0x05, /* ePktSel_EDR_DM1 */ + 0x05, /* ePktSel_EDR_2DH1 */ + 0x05, /* ePktSel_EDR_2DH3 */ + 0x05, /* ePktSel_EDR_2DH5 */ + 0x05, /* ePktSel_EDR_3DH1 */ + 0x05, /* ePktSel_EDR_3DH3 */ + INVALID_THRESHOLD /* ePktSel_EDR_3DH5 */ + , + /* EDR_PER_DOWN_THRESHOLDS_DEFAULT */ + INVALID_THRESHOLD, /* ePktSel_EDR_Init */ + INVALID_THRESHOLD, /* ePktSel_EDR_DM1 */ + 0x40, /* ePktSel_EDR_2DH1 */ + 0x20, /* ePktSel_EDR_2DH3 */ + 0x20, /* ePktSel_EDR_2DH5 */ + 0x20, /* ePktSel_EDR_3DH1 */ + 0x20, /* ePktSel_EDR_3DH3 */ + 0x20 /* ePktSel_EDR_3DH5 */ + , + + 0x04, /* rss_min_delta: */ + 0x05, /* per_min_delta: */ + + /* EDR_MAX_LONG_TERM_ERRORS_DEFAULT */ + INVALID_THRESHOLD, /* ePktSel_EDR_Init */ + INVALID_THRESHOLD, /* ePktSel_EDR_DM1 */ + 0x11, /* ePktSel_EDR_2DH1 */ + 0x11, /* ePktSel_EDR_2DH3 */ + 0x11, /* ePktSel_EDR_2DH5 */ + 0x11, /* ePktSel_EDR_3DH1 */ + 0x11, /* ePktSel_EDR_3DH3 */ + 0x11 /* ePktSel_EDR_3DH5 */ + , + 0x01 /* long_term_error_per_threshold */ +}; + +uint8_t hci_rfcomm_up_cmd[] = { + 0x01, 0x32, 0xFC, 0x21, 0x00, 0x00 /* Param begin */ , + 0x32, 0x00, 0x00, 0x00, /* force_up_rate_threshold */ + /* EDR_PER_UP_THRESHOLDS_CONSV */ + INVALID_THRESHOLD, /* ePktSel_EDR_Init */ + 0x05, /* ePktSel_EDR_DM1 */ + 0x10, /* ePktSel_EDR_2DH1 */ + 0x02, /* ePktSel_EDR_2DH3 */ + 0x01, /* ePktSel_EDR_2DH5 */ + 0x02, /* ePktSel_EDR_3DH1 */ + 0x01, /* ePktSel_EDR_3DH3 */ + INVALID_THRESHOLD /* ePktSel_EDR_3DH5 */ + , + /* EDR_PER_DOWN_THRESHOLDS_CONSV */ + INVALID_THRESHOLD, /* ePktSel_EDR_Init */ + INVALID_THRESHOLD, /* ePktSel_EDR_DM1 */ + 0x50, /* ePktSel_EDR_2DH1 */ + 0x50, /* ePktSel_EDR_2DH3 */ + 0x02, /* ePktSel_EDR_2DH5 */ + 0x03, /* ePktSel_EDR_3DH1 */ + 0x03, /* ePktSel_EDR_3DH3 */ + 0x02, /* ePktSel_EDR_3DH5 */ + + 0x04, /* rss_min_delta */ + 0x05, /* per_min_delta */ + + /* EDR_MAX_LONG_TERM_ERRORS_CONSV */ + INVALID_THRESHOLD, /* ePktSel_EDR_Init */ + INVALID_THRESHOLD, /* ePktSel_EDR_DM1 */ + 0x11, /* ePktSel_EDR_2DH1 */ + 0x11, /* ePktSel_EDR_2DH3 */ + 0x03, /* ePktSel_EDR_2DH5 */ + 0x03, /* ePktSel_EDR_3DH1 */ + 0x03, /* ePktSel_EDR_3DH3 */ + 0x03, /* ePktSel_EDR_3DH5 */ + + 0x01 /* long_term_error_per_threshold */ +}; + +/* 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; + +struct ps_tag_entry ps_tag_entry[RAMPS_MAX_PS_TAGS_PER_FILE]; +struct ps_ram_patch ram_patch[MAX_NUM_PATCH_ENTRY]; + +struct brm_rxtx_pkt_sel_cfg *packet_config_conserv; +struct brm_rxtx_pkt_sel_cfg *packet_config_default; + +/* 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; +} + +void parse_rx_tx_pkt_config(struct ps_tag_entry ps_tag) +{ + switch (ps_tag.tag_id) { + case PSTAG_EdrPerUpThresholds: + memcpy(packet_config_default->edr_up_threshold, + ps_tag.tag_data, 8); + break; + case PSTAG_EdrPerDownThresholds: + memcpy(packet_config_default->edr_down_threshold, + ps_tag.tag_data, 8); + break; + case PSTAG_RSSIMinDelta: + packet_config_default->rss_min_delta = ps_tag.tag_data[0]; + break; + case PSTAG_PERMinDelta: + packet_config_default->per_min_delta = ps_tag.tag_data[0]; + break; + case PSTAG_MaxLongTermError: + memcpy(packet_config_default->max_long_term_errors, + ps_tag.tag_data, 8); + break; + case PSTAG_LongTermPERThreshold: + packet_config_default->long_term_error_per_threshold = + ps_tag.tag_data[0]; + break; + case PSTAG_ForceUpRateThreshold: + memcpy(&packet_config_default->force_up_rate_threshold, + ps_tag.tag_data, 4); + break; + + case PSTAG_EdrPerUpThresholds_Consv: + memcpy(packet_config_conserv->edr_up_threshold, + ps_tag.tag_data, 8); + break; + case PSTAG_EdrPerDownThresholds_Consv: + memcpy(packet_config_conserv->edr_down_threshold, + ps_tag.tag_data, 8); + break; + case PSTAG_RSSIMinDelta_Consv: + packet_config_conserv->rss_min_delta = ps_tag.tag_data[0]; + break; + case PSTAG_PERMinDelta_Consv: + packet_config_conserv->per_min_delta = ps_tag.tag_data[0]; + break; + case PSTAG_MaxLongTermError_Consv: + memcpy(packet_config_conserv->max_long_term_errors, + ps_tag.tag_data, 8); + break; + case PSTAG_LongTermPERThreshold_Consv: + packet_config_conserv->long_term_error_per_threshold = + ps_tag.tag_data[0]; + break; + case PSTAG_ForceUpRateThreshold_Consv: + memcpy(&packet_config_conserv->force_up_rate_threshold, + ps_tag.tag_data, 4); + break; + + } +} + +static int ath_create_ps_command(uint8_t opcode, uint32_t Param1, + struct ps_cmd_packet *ps_patch_packet, + uint32_t *index) +{ + uint8_t *hci_ps_cmd; + uint32_t Length; + int i, j; + + hci_ps_cmd = NULL; + switch (opcode) { + case WRITE_PATCH: + for (i = 0; i < Param1; i++) { + 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); + load_hci_header(hci_ps_cmd, opcode, ram_patch[i].Len, + i); + for (j = 0; j < ram_patch[i].Len; j++) + hci_ps_cmd[HCI_CMD_HEADER_LEN + j] = + ram_patch[i].Data[j]; + 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: + Length = 0; + i = 0; + hci_ps_cmd = (uint8_t *) malloc(Length + HCI_CMD_HEADER_LEN); + if (hci_ps_cmd == NULL) + return -1; + + memset(hci_ps_cmd, 0, Length + HCI_CMD_HEADER_LEN); + load_hci_header(hci_ps_cmd, opcode, Length, i); + ps_patch_packet[*index].Hcipacket = hci_ps_cmd; + ps_patch_packet[*index].packetLen + = Length + HCI_CMD_HEADER_LEN; + (*index)++; + break; + case PS_RESET: + Length = 0x06; + i = 0; + hci_ps_cmd = (uint8_t *) malloc(Length + HCI_CMD_HEADER_LEN); + if (hci_ps_cmd == NULL) + return -1; + + memset(hci_ps_cmd, 0, Length + HCI_CMD_HEADER_LEN); + load_hci_header(hci_ps_cmd, opcode, Length, i); + hci_ps_cmd[7] = 0x00; + hci_ps_cmd[Length + HCI_CMD_HEADER_LEN - 2] = (Param1 & 0xFF); + hci_ps_cmd[Length + HCI_CMD_HEADER_LEN - 1] = + ((Param1 >> 8) & 0xFF); + ps_patch_packet[*index].Hcipacket = hci_ps_cmd; + ps_patch_packet[*index].packetLen + = Length + HCI_CMD_HEADER_LEN; + (*index)++; + break; + case PS_WRITE: + for (i = 0; i < Param1; 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); + for (j = 0; j < ps_tag_entry[i].tag_len; j++) + hci_ps_cmd[HCI_CMD_HEADER_LEN + j] = + ps_tag_entry[i].tag_data[j]; + + 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: + Length = 0x0; + hci_ps_cmd = (uint8_t *) malloc(Length + HCI_CMD_HEADER_LEN); + if (hci_ps_cmd == NULL) + return -1; + memset(hci_ps_cmd, 0, Length + HCI_CMD_HEADER_LEN); + load_hci_header(hci_ps_cmd, opcode, Length, Param1); + ps_patch_packet[*index].Hcipacket = hci_ps_cmd; + ps_patch_packet[*index].packetLen + = Length + 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; + } +} + +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; +} + +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; + if (stream == NULL) { + perror("Could not open config file .\n"); + return -1; + } + tag_cnt = 0; + byte_count = 0; + 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; +} + +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; +} + +int ath_parse_ps(FILE *stream) +{ + int status; + int i; + unsigned char bdaddr_Present = 0; + status = -1; + + packet_config_default = + (struct brm_rxtx_pkt_sel_cfg *)&hci_rfcomm_up_cmd[6]; + packet_config_conserv = + (struct brm_rxtx_pkt_sel_cfg *)&hci_rfcomm_down_cmd[6]; + + 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; + parse_rx_tx_pkt_config(ps_tag_entry[i]); + } + } + 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; +} + +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; +} + +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; +} diff --git a/tools/ar3kpsparser.h b/tools/ar3kpsparser.h new file mode 100755 index 0000000..351ae18 --- /dev/null +++ b/tools/ar3kpsparser.h @@ -0,0 +1,127 @@ +/* + * 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 + * + */ + +#ifndef __AR3KPSPARSER_H +#define __AR3KPSPARSER_H + +#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) + +/* This block is reserved for Bluetooth system's + * common componet, for example, PCM, LMP etc. + */ +#define PSTAG_SYSTEM_BLOCK_START (uint16_t)(1) + +#define PSTAG_EdrPerUpThresholds (uint16_t)(PSTAG_SYSTEM_BLOCK_START+38) +#define PSTAG_EdrPerDownThresholds (uint16_t)(PSTAG_SYSTEM_BLOCK_START+39) +#define PSTAG_RSSIMinDelta (uint16_t)(PSTAG_SYSTEM_BLOCK_START+40) +#define PSTAG_PERMinDelta (uint16_t)(PSTAG_SYSTEM_BLOCK_START+41) +#define PSTAG_MaxLongTermError (uint16_t)(PSTAG_SYSTEM_BLOCK_START+42) +#define PSTAG_LongTermPERThreshold (uint16_t)(PSTAG_SYSTEM_BLOCK_START+43) +#define PSTAG_ForceUpRateThreshold (uint16_t)(PSTAG_SYSTEM_BLOCK_START+44) + +#define PSTAG_EdrPerUpThresholds_Consv (uint16_t)\ + (PSTAG_SYSTEM_BLOCK_START+45) +#define PSTAG_EdrPerDownThresholds_Consv (uint16_t)\ + (PSTAG_SYSTEM_BLOCK_START+46) +#define PSTAG_RSSIMinDelta_Consv (uint16_t)\ + (PSTAG_SYSTEM_BLOCK_START+47) +#define PSTAG_PERMinDelta_Consv (uint16_t)\ + (PSTAG_SYSTEM_BLOCK_START+48) +#define PSTAG_MaxLongTermError_Consv (uint16_t)\ + (PSTAG_SYSTEM_BLOCK_START+49) +#define PSTAG_LongTermPERThreshold_Consv (uint16_t)\ + (PSTAG_SYSTEM_BLOCK_START+50) +#define PSTAG_ForceUpRateThreshold_Consv (uint16_t)\ + (PSTAG_SYSTEM_BLOCK_START+51) + +#define PKT_SEL_BUFFER_SIZE 8 + +#define PSTAG_RF_TEST_BLOCK_END (uint16_t)(349) + +#define FPGA_REGISTER 0x4FFC + +#define PS_ASIC_FILE "PS_ASIC.pst" +#define PS_FPGA_FILE "PS_FPGA.pst" + +#define PATCH_FILE "RamPatch.txt" +#define BDADDR_FILE "ar3kbdaddr.pst" + +struct ps_cmd_packet { + uint8_t *Hcipacket; + int packetLen; +}; + +/* Parses a Patch information buffer and store it in global structure */ +int parse_patch_file(FILE *); + +/* parses a PS information buffer and stores it in a global structure */ +int ath_parse_ps(FILE *); + +/* + * Uses the output of Both ath_parse_ps and AthDoParsePatch + * APIs to form HCI command array with + * all the PS and patch commands. + * The list will have the below mentioned commands in order. + * CRC command packet + * Download patch command(s) + * Enable patch Command + * PS Reset Command + * PS Tag Command(s) + * + */ +int ath_create_cmd_list(struct ps_cmd_packet **, uint32_t *); + +/* Cleanup the dynamically allicated HCI command list */ +int ath_free_command_list(struct ps_cmd_packet **HciPacketList, + uint32_t numPackets); + +#endif /* __AR3KPSPARSER_H */ diff --git a/tools/hciattach.8 b/tools/hciattach.8 index f750222..74ad0d0 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 ar3001 +Atheros AR3001 based modules +.TP +.B ar3001pm +Atheros AR3001 based modules with advanced power save feature enabled +.TP .B ericsson Ericsson based modules .TP diff --git a/tools/hciattach.c b/tools/hciattach.c index 364c5ff..82f0e69 100644 --- a/tools/hciattach.c +++ b/tools/hciattach.c @@ -146,7 +146,7 @@ int set_speed(int fd, struct termios *ti, int speed) return tcsetattr(fd, TCSANOW, ti); } -/* +/* * Read an HCI event from the given file descriptor. */ int read_hci_event(int fd, unsigned char* buf, int size) @@ -653,7 +653,153 @@ static int csr(int fd, struct uart_t *u, struct termios *ti) } /* - * Silicon Wave specific initialization + * Atheros AR3001 specific initialization code with power management disabled. + * Suraj Sumangala <Suraj@xxxxxxxxxxx> + */ +static int ar3001post(int fd, struct uart_t *u, struct termios *ti) +{ + 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(1); + /* send command with Sleep feature disabled */ + ar3001_post(dd); + + nanosleep(&tm, NULL); + hci_close_dev(dd); + + return 0; + +} +/* + * Atheros AR3001 specific initialization post callback + * with power management enabled + * Suraj Sumangala <Suraj@xxxxxxxxxxx> + */ +static int ar3001pmpost(int fd, struct uart_t *u, struct termios *ti) +{ + int dev_id, dd; + struct timespec tm = {0, 50000}; + + sleep(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; + } + + /* send command with Sleep feature enabled */ + ar3001pm_post(dd); + nanosleep(&tm, NULL); + hci_close_dev(dd); + + return 0; +} +/* + * Atheros AR3001 specific initialization + * Suraj Sumangala <Suraj@xxxxxxxxxxx> + */ +static int ar3001(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 +1217,14 @@ struct uart_t uart[] = { /* Broadcom BCM2035 */ { "bcm2035", 0x0A5C, 0x2035, HCI_UART_H4, 115200, 460800, FLOW_CTL, NULL, bcm2035 }, + /* ATHEROS AR3001 */ + { "ar3001", 0x0000, 0x0000, HCI_UART_ATH, + 115200, 115200, FLOW_CTL, NULL, ar3001, ar3001post }, + + { "ar3001pm", 0x0000, 0x0000, HCI_UART_ATH, + 115200, 115200, FLOW_CTL, NULL, ar3001, ar3001pmpost }, + + { NULL, 0 } }; diff --git a/tools/hciattach.h b/tools/hciattach.h index 867563b..6f6469f 100644 --- a/tools/hciattach.h +++ b/tools/hciattach.h @@ -36,7 +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 +45,6 @@ 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); +int ar3001_post(int fd); +int ar3001pm_post(int fd); -- 1.6.3.3 -- 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