Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.
Path: blob/next/external/cache/sources/hcitools/hciattach_ath3k.c
Views: 3959
/*1* Copyright (c) 2009-2010 Atheros Communications Inc.2*3* This program is free software; you can redistribute it and/or modify4* it under the terms of the GNU General Public License as published by5* the Free Software Foundation; either version 2 of the License, or6* (at your option) any later version.7*8* This program is distributed in the hope that it will be useful,9* but WITHOUT ANY WARRANTY; without even the implied warranty of10* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the11* GNU General Public License for more details.12*13* You should have received a copy of the GNU General Public License14* along with this program; if not, write to the Free Software15* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA16*17*/1819#ifdef HAVE_CONFIG_H20#include <config.h>21#endif2223#include <stdio.h>24#include <errno.h>25#include <unistd.h>26#include <stdlib.h>27#include <string.h>28#include <ctype.h>29#include <time.h>30#include <sys/time.h>31#include <sys/types.h>32#include <sys/param.h>33#include <sys/ioctl.h>3435#include <bluetooth/bluetooth.h>36#include <bluetooth/hci.h>37#include <bluetooth/hci_lib.h>3839#include "hciattach.h"4041#define TRUE 142#define FALSE 04344#define FW_PATH "/lib/firmware/ar3k/"4546struct ps_cfg_entry {47uint32_t id;48uint32_t len;49uint8_t *data;50};5152struct ps_entry_type {53unsigned char type;54unsigned char array;55};5657#define MAX_TAGS 5058#define PS_HDR_LEN 459#define HCI_VENDOR_CMD_OGF 0x3F60#define HCI_PS_CMD_OCF 0x0B6162struct ps_cfg_entry ps_list[MAX_TAGS];6364static void load_hci_ps_hdr(uint8_t *cmd, uint8_t ps_op, int len, int index)65{66hci_command_hdr *ch = (void *)cmd;6768ch->opcode = htobs(cmd_opcode_pack(HCI_VENDOR_CMD_OGF,69HCI_PS_CMD_OCF));70ch->plen = len + PS_HDR_LEN;71cmd += HCI_COMMAND_HDR_SIZE;7273cmd[0] = ps_op;74cmd[1] = index;75cmd[2] = index >> 8;76cmd[3] = len;77}7879#define PS_EVENT_LEN 1008081/*82* Send HCI command and wait for command complete event.83* The event buffer has to be freed by the caller.84*/85static int send_hci_cmd_sync(int dev, uint8_t *cmd, int len, uint8_t **event)86{87int err;88uint8_t *hci_event;89uint8_t pkt_type = HCI_COMMAND_PKT;9091if (len == 0)92return len;9394if (write(dev, &pkt_type, 1) != 1)95return -EILSEQ;96if (write(dev, (unsigned char *)cmd, len) != len)97return -EILSEQ;9899hci_event = (uint8_t *)malloc(PS_EVENT_LEN);100if (!hci_event)101return -ENOMEM;102103err = read_hci_event(dev, (unsigned char *)hci_event, PS_EVENT_LEN);104if (err > 0) {105*event = hci_event;106} else {107free(hci_event);108return -EILSEQ;109}110111return len;112}113114#define HCI_EV_SUCCESS 0x00115116static int read_ps_event(uint8_t *event, uint16_t ocf)117{118hci_event_hdr *eh;119uint16_t opcode = htobs(cmd_opcode_pack(HCI_VENDOR_CMD_OGF, ocf));120121event++;122123eh = (void *)event;124event += HCI_EVENT_HDR_SIZE;125126if (eh->evt == EVT_CMD_COMPLETE) {127evt_cmd_complete *cc = (void *)event;128129event += EVT_CMD_COMPLETE_SIZE;130131if (cc->opcode == opcode && event[0] == HCI_EV_SUCCESS)132return 0;133else134return -EILSEQ;135}136137return -EILSEQ;138}139140static int write_cmd(int fd, uint8_t *buffer, int len)141{142uint8_t *event;143int err;144145err = send_hci_cmd_sync(fd, buffer, len, &event);146if (err < 0)147return err;148149err = read_ps_event(event, HCI_PS_CMD_OCF);150151free(event);152153return err;154}155156#define PS_WRITE 1157#define PS_RESET 2158#define WRITE_PATCH 8159#define ENABLE_PATCH 11160161#define HCI_PS_CMD_HDR_LEN 7162163#define PS_RESET_PARAM_LEN 6164#define HCI_MAX_CMD_SIZE 260165#define PS_RESET_CMD_LEN (HCI_PS_CMD_HDR_LEN + PS_RESET_PARAM_LEN)166167#define PS_ID_MASK 0xFF168169/* Sends PS commands using vendor specficic HCI commands */170static int write_ps_cmd(int fd, uint8_t opcode, uint32_t ps_param)171{172uint8_t cmd[HCI_MAX_CMD_SIZE];173uint32_t i;174175switch (opcode) {176case ENABLE_PATCH:177load_hci_ps_hdr(cmd, opcode, 0, 0x00);178179if (write_cmd(fd, cmd, HCI_PS_CMD_HDR_LEN) < 0)180return -EILSEQ;181break;182183case PS_RESET:184load_hci_ps_hdr(cmd, opcode, PS_RESET_PARAM_LEN, 0x00);185186cmd[7] = 0x00;187cmd[PS_RESET_CMD_LEN - 2] = ps_param & PS_ID_MASK;188cmd[PS_RESET_CMD_LEN - 1] = (ps_param >> 8) & PS_ID_MASK;189190if (write_cmd(fd, cmd, PS_RESET_CMD_LEN) < 0)191return -EILSEQ;192break;193194case PS_WRITE:195for (i = 0; i < ps_param; i++) {196load_hci_ps_hdr(cmd, opcode, ps_list[i].len,197ps_list[i].id);198199memcpy(&cmd[HCI_PS_CMD_HDR_LEN], ps_list[i].data,200ps_list[i].len);201202if (write_cmd(fd, cmd, ps_list[i].len +203HCI_PS_CMD_HDR_LEN) < 0)204return -EILSEQ;205}206break;207}208209return 0;210}211212#define __is_delim(ch) ((ch) == ':')213#define MAX_PREAMBLE_LEN 4214215/* Parse PS entry preamble of format [X:X] for main type and subtype */216static int get_ps_type(char *ptr, int index, char *type, char *sub_type)217{218int i;219int delim = FALSE;220221if (index > MAX_PREAMBLE_LEN)222return -EILSEQ;223224for (i = 1; i < index; i++) {225if (__is_delim(ptr[i])) {226delim = TRUE;227continue;228}229230if (isalpha(ptr[i])) {231if (delim == FALSE)232(*type) = toupper(ptr[i]);233else234(*sub_type) = toupper(ptr[i]);235}236}237238return 0;239}240241#define ARRAY 'A'242#define STRING 'S'243#define DECIMAL 'D'244#define BINARY 'B'245246#define PS_HEX 0247#define PS_DEC 1248249static int get_input_format(char *buf, struct ps_entry_type *format)250{251char *ptr = NULL;252char type = '\0';253char sub_type = '\0';254255format->type = PS_HEX;256format->array = TRUE;257258if (strstr(buf, "[") != buf)259return 0;260261ptr = strstr(buf, "]");262if (!ptr)263return -EILSEQ;264265if (get_ps_type(buf, ptr - buf, &type, &sub_type) < 0)266return -EILSEQ;267268/* Check is data type is of array */269if (type == ARRAY || sub_type == ARRAY)270format->array = TRUE;271272if (type == STRING || sub_type == STRING)273format->array = FALSE;274275if (type == DECIMAL || type == BINARY)276format->type = PS_DEC;277else278format->type = PS_HEX;279280return 0;281}282283#define UNDEFINED 0xFFFF284285static unsigned int read_data_in_section(char *buf, struct ps_entry_type type)286{287char *ptr = buf;288289if (!buf)290return UNDEFINED;291292if (buf == strstr(buf, "[")) {293ptr = strstr(buf, "]");294if (!ptr)295return UNDEFINED;296297ptr++;298}299300if (type.type == PS_HEX && type.array != TRUE)301return strtol(ptr, NULL, 16);302303return UNDEFINED;304}305306struct tag_info {307unsigned section;308unsigned line_count;309unsigned char_cnt;310unsigned byte_count;311};312313static inline int update_char_count(const char *buf)314{315char *end_ptr;316317if (strstr(buf, "[") == buf) {318end_ptr = strstr(buf, "]");319if (!end_ptr)320return 0;321else322return (end_ptr - buf) + 1;323}324325return 0;326}327328/* Read PS entries as string, convert and add to Hex array */329static void update_tag_data(struct ps_cfg_entry *tag,330struct tag_info *info, const char *ptr)331{332char buf[3];333334buf[2] = '\0';335336strncpy(buf, &ptr[info->char_cnt], 2);337tag->data[info->byte_count] = strtol(buf, NULL, 16);338info->char_cnt += 3;339info->byte_count++;340341strncpy(buf, &ptr[info->char_cnt], 2);342tag->data[info->byte_count] = strtol(buf, NULL, 16);343info->char_cnt += 3;344info->byte_count++;345}346347#define PS_UNDEF 0348#define PS_ID 1349#define PS_LEN 2350#define PS_DATA 3351352#define PS_MAX_LEN 500353#define LINE_SIZE_MAX (PS_MAX_LEN * 2)354#define ENTRY_PER_LINE 16355356#define __check_comment(buf) (((buf)[0] == '/') && ((buf)[1] == '/'))357#define __skip_space(str) while (*(str) == ' ') ((str)++)358359static int ath_parse_ps(FILE *stream)360{361char buf[LINE_SIZE_MAX + 1];362char *ptr;363uint8_t tag_cnt = 0;364int16_t byte_count = 0;365struct ps_entry_type format;366struct tag_info status = { 0, 0, 0, 0 };367368do {369int read_count;370struct ps_cfg_entry *tag;371372ptr = fgets(buf, LINE_SIZE_MAX, stream);373if (!ptr)374break;375376__skip_space(ptr);377if (__check_comment(ptr))378continue;379380/* Lines with a '#' will be followed by new PS entry */381if (ptr == strstr(ptr, "#")) {382if (status.section != PS_UNDEF) {383return -EILSEQ;384} else {385status.section = PS_ID;386continue;387}388}389390tag = &ps_list[tag_cnt];391392switch (status.section) {393case PS_ID:394if (get_input_format(ptr, &format) < 0)395return -EILSEQ;396397tag->id = read_data_in_section(ptr, format);398status.section = PS_LEN;399break;400401case PS_LEN:402if (get_input_format(ptr, &format) < 0)403return -EILSEQ;404405byte_count = read_data_in_section(ptr, format);406if (byte_count > PS_MAX_LEN)407return -EILSEQ;408409tag->len = byte_count;410tag->data = (uint8_t *)malloc(byte_count);411412status.section = PS_DATA;413status.line_count = 0;414break;415416case PS_DATA:417if (status.line_count == 0)418if (get_input_format(ptr, &format) < 0)419return -EILSEQ;420421__skip_space(ptr);422423status.char_cnt = update_char_count(ptr);424425read_count = (byte_count > ENTRY_PER_LINE) ?426ENTRY_PER_LINE : byte_count;427428if (format.type == PS_HEX && format.array == TRUE) {429while (read_count > 0) {430update_tag_data(tag, &status, ptr);431read_count -= 2;432}433434if (byte_count > ENTRY_PER_LINE)435byte_count -= ENTRY_PER_LINE;436else437byte_count = 0;438}439440status.line_count++;441442if (byte_count == 0)443memset(&status, 0x00, sizeof(struct tag_info));444445if (status.section == PS_UNDEF)446tag_cnt++;447448if (tag_cnt == MAX_TAGS)449return -EILSEQ;450break;451}452} while (ptr);453454return tag_cnt;455}456457#define MAX_PATCH_CMD 244458struct patch_entry {459int16_t len;460uint8_t data[MAX_PATCH_CMD];461};462463#define SET_PATCH_RAM_ID 0x0D464#define SET_PATCH_RAM_CMD_SIZE 11465#define ADDRESS_LEN 4466static int set_patch_ram(int dev, char *patch_loc, int len)467{468int err;469uint8_t cmd[20];470int i, j;471char loc_byte[3];472uint8_t *event;473uint8_t *loc_ptr = &cmd[7];474475if (!patch_loc)476return -1;477478loc_byte[2] = '\0';479480load_hci_ps_hdr(cmd, SET_PATCH_RAM_ID, ADDRESS_LEN, 0);481482for (i = 0, j = 3; i < 4; i++, j--) {483loc_byte[0] = patch_loc[0];484loc_byte[1] = patch_loc[1];485loc_ptr[j] = strtol(loc_byte, NULL, 16);486patch_loc += 2;487}488489err = send_hci_cmd_sync(dev, cmd, SET_PATCH_RAM_CMD_SIZE, &event);490if (err < 0)491return err;492493err = read_ps_event(event, HCI_PS_CMD_OCF);494495free(event);496497return err;498}499500#define PATCH_LOC_KEY "DA:"501#define PATCH_LOC_STRING_LEN 8502static int ps_patch_download(int fd, FILE *stream)503{504char byte[3];505char ptr[MAX_PATCH_CMD + 1];506int byte_cnt;507int patch_count = 0;508char patch_loc[PATCH_LOC_STRING_LEN + 1];509510byte[2] = '\0';511512while (fgets(ptr, MAX_PATCH_CMD, stream)) {513if (strlen(ptr) <= 1)514continue;515else if (strstr(ptr, PATCH_LOC_KEY) == ptr) {516strncpy(patch_loc, &ptr[sizeof(PATCH_LOC_KEY) - 1],517PATCH_LOC_STRING_LEN);518if (set_patch_ram(fd, patch_loc, sizeof(patch_loc)) < 0)519return -1;520} else if (isxdigit(ptr[0]))521break;522else523return -1;524}525526byte_cnt = strtol(ptr, NULL, 16);527528while (byte_cnt > 0) {529int i;530uint8_t cmd[HCI_MAX_CMD_SIZE];531struct patch_entry patch;532533if (byte_cnt > MAX_PATCH_CMD)534patch.len = MAX_PATCH_CMD;535else536patch.len = byte_cnt;537538for (i = 0; i < patch.len; i++) {539if (!fgets(byte, 3, stream))540return -1;541542patch.data[i] = strtoul(byte, NULL, 16);543}544545load_hci_ps_hdr(cmd, WRITE_PATCH, patch.len, patch_count);546memcpy(&cmd[HCI_PS_CMD_HDR_LEN], patch.data, patch.len);547548if (write_cmd(fd, cmd, patch.len + HCI_PS_CMD_HDR_LEN) < 0)549return -1;550551patch_count++;552byte_cnt = byte_cnt - MAX_PATCH_CMD;553}554555if (write_ps_cmd(fd, ENABLE_PATCH, 0) < 0)556return -1;557558return patch_count;559}560561#define PS_RAM_SIZE 2048562563static int ps_config_download(int fd, int tag_count)564{565if (write_ps_cmd(fd, PS_RESET, PS_RAM_SIZE) < 0)566return -1;567568if (tag_count > 0)569if (write_ps_cmd(fd, PS_WRITE, tag_count) < 0)570return -1;571return 0;572}573574#define PS_ASIC_FILE "PS_ASIC.pst"575#define PS_FPGA_FILE "PS_FPGA.pst"576577static void get_ps_file_name(uint32_t devtype, uint32_t rom_version,578char *path)579{580char *filename;581582if (devtype == 0xdeadc0de)583filename = PS_ASIC_FILE;584else585filename = PS_FPGA_FILE;586587snprintf(path, MAXPATHLEN, "%s%x/%s", FW_PATH, rom_version, filename);588}589590#define PATCH_FILE "RamPatch.txt"591#define FPGA_ROM_VERSION 0x99999999592#define ROM_DEV_TYPE 0xdeadc0de593594static void get_patch_file_name(uint32_t dev_type, uint32_t rom_version,595uint32_t build_version, char *path)596{597if (rom_version == FPGA_ROM_VERSION && dev_type != ROM_DEV_TYPE &&598dev_type != 0 && build_version == 1)599path[0] = '\0';600else601snprintf(path, MAXPATHLEN, "%s%x/%s",602FW_PATH, rom_version, PATCH_FILE);603}604605#define VERIFY_CRC 9606#define PS_REGION 1607#define PATCH_REGION 2608609static int get_ath3k_crc(int dev)610{611uint8_t cmd[7];612uint8_t *event;613int err;614615load_hci_ps_hdr(cmd, VERIFY_CRC, 0, PS_REGION | PATCH_REGION);616617err = send_hci_cmd_sync(dev, cmd, sizeof(cmd), &event);618if (err < 0)619return err;620/* Send error code if CRC check patched */621if (read_ps_event(event, HCI_PS_CMD_OCF) >= 0)622err = -EILSEQ;623624free(event);625626return err;627}628629#define DEV_REGISTER 0x4FFC630#define GET_DEV_TYPE_OCF 0x05631632static int get_device_type(int dev, uint32_t *code)633{634uint8_t cmd[8];635uint8_t *event;636uint32_t reg;637int err;638uint8_t *ptr = cmd;639hci_command_hdr *ch = (void *)cmd;640641ch->opcode = htobs(cmd_opcode_pack(HCI_VENDOR_CMD_OGF,642GET_DEV_TYPE_OCF));643ch->plen = 5;644ptr += HCI_COMMAND_HDR_SIZE;645646ptr[0] = (uint8_t)DEV_REGISTER;647ptr[1] = (uint8_t)DEV_REGISTER >> 8;648ptr[2] = (uint8_t)DEV_REGISTER >> 16;649ptr[3] = (uint8_t)DEV_REGISTER >> 24;650ptr[4] = 0x04;651652err = send_hci_cmd_sync(dev, cmd, sizeof(cmd), &event);653if (err < 0)654return err;655656err = read_ps_event(event, GET_DEV_TYPE_OCF);657if (err < 0)658goto cleanup;659660reg = event[10];661reg = (reg << 8) | event[9];662reg = (reg << 8) | event[8];663reg = (reg << 8) | event[7];664*code = reg;665666cleanup:667free(event);668669return err;670}671672#define GET_VERSION_OCF 0x1E673674static int read_ath3k_version(int pConfig, uint32_t *rom_version,675uint32_t *build_version)676{677uint8_t cmd[3];678uint8_t *event;679int err;680int status;681hci_command_hdr *ch = (void *)cmd;682683ch->opcode = htobs(cmd_opcode_pack(HCI_VENDOR_CMD_OGF,684GET_VERSION_OCF));685ch->plen = 0;686687err = send_hci_cmd_sync(pConfig, cmd, sizeof(cmd), &event);688if (err < 0)689return err;690691err = read_ps_event(event, GET_VERSION_OCF);692if (err < 0)693goto cleanup;694695status = event[10];696status = (status << 8) | event[9];697status = (status << 8) | event[8];698status = (status << 8) | event[7];699*rom_version = status;700701status = event[14];702status = (status << 8) | event[13];703status = (status << 8) | event[12];704status = (status << 8) | event[11];705*build_version = status;706707cleanup:708free(event);709710return err;711}712713static void convert_bdaddr(char *str_bdaddr, char *bdaddr)714{715char bdbyte[3];716char *str_byte = str_bdaddr;717int i, j;718int colon_present = 0;719720if (strstr(str_bdaddr, ":"))721colon_present = 1;722723bdbyte[2] = '\0';724725/* Reverse the BDADDR to LSB first */726for (i = 0, j = 5; i < 6; i++, j--) {727bdbyte[0] = str_byte[0];728bdbyte[1] = str_byte[1];729bdaddr[j] = strtol(bdbyte, NULL, 16);730731if (colon_present == 1)732str_byte += 3;733else734str_byte += 2;735}736}737738static int write_bdaddr(int pConfig, char *bdaddr)739{740uint8_t *event;741int err;742uint8_t cmd[13];743uint8_t *ptr = cmd;744hci_command_hdr *ch = (void *)cmd;745746memset(cmd, 0, sizeof(cmd));747748ch->opcode = htobs(cmd_opcode_pack(HCI_VENDOR_CMD_OGF,749HCI_PS_CMD_OCF));750ch->plen = 10;751ptr += HCI_COMMAND_HDR_SIZE;752753ptr[0] = 0x01;754ptr[1] = 0x01;755ptr[2] = 0x00;756ptr[3] = 0x06;757758convert_bdaddr(bdaddr, (char *)&ptr[4]);759760err = send_hci_cmd_sync(pConfig, cmd, sizeof(cmd), &event);761if (err < 0)762return err;763764err = read_ps_event(event, HCI_PS_CMD_OCF);765766free(event);767768return err;769}770771#define BDADDR_FILE "ar3kbdaddr.pst"772773static void write_bdaddr_from_file(int rom_version, int fd)774{775FILE *stream;776char bdaddr[PATH_MAX];777char bdaddr_file[PATH_MAX];778779snprintf(bdaddr_file, MAXPATHLEN, "%s%x/%s",780FW_PATH, rom_version, BDADDR_FILE);781782stream = fopen(bdaddr_file, "r");783if (!stream)784return;785786if (fgets(bdaddr, PATH_MAX - 1, stream))787write_bdaddr(fd, bdaddr);788789fclose(stream);790}791792static int ath_ps_download(int fd)793{794int err = 0;795int tag_count;796int patch_count = 0;797uint32_t rom_version = 0;798uint32_t build_version = 0;799uint32_t dev_type = 0;800char patch_file[PATH_MAX];801char ps_file[PATH_MAX];802FILE *stream;803804/*805* Verfiy firmware version. depending on it select the PS806* config file to download.807*/808if (get_device_type(fd, &dev_type) < 0) {809err = -EILSEQ;810goto download_cmplete;811}812813if (read_ath3k_version(fd, &rom_version, &build_version) < 0) {814err = -EILSEQ;815goto download_cmplete;816}817818/* Do not download configuration if CRC passes */819if (get_ath3k_crc(fd) < 0) {820err = 0;821goto download_cmplete;822}823824get_ps_file_name(dev_type, rom_version, ps_file);825get_patch_file_name(dev_type, rom_version, build_version, patch_file);826827stream = fopen(ps_file, "r");828if (!stream) {829perror("firmware file open error\n");830err = -EILSEQ;831goto download_cmplete;832}833tag_count = ath_parse_ps(stream);834835fclose(stream);836837if (tag_count < 0) {838err = -EILSEQ;839goto download_cmplete;840}841842/*843* It is not necessary that Patch file be available,844* continue with PS Operations if patch file is not available.845*/846if (patch_file[0] == '\0')847err = 0;848849stream = fopen(patch_file, "r");850if (!stream)851err = 0;852else {853patch_count = ps_patch_download(fd, stream);854fclose(stream);855856if (patch_count < 0) {857err = -EILSEQ;858goto download_cmplete;859}860}861862err = ps_config_download(fd, tag_count);863864download_cmplete:865if (!err)866write_bdaddr_from_file(rom_version, fd);867868return err;869}870871#define HCI_SLEEP_CMD_OCF 0x04872873/*874* Atheros AR300x specific initialization post callback875*/876int ath3k_post(int fd, int pm)877{878int dev_id, dd;879struct timespec tm = { 0, 50000 };880881sleep(1);882883dev_id = ioctl(fd, HCIUARTGETDEVICE, 0);884if (dev_id < 0) {885perror("cannot get device id");886return dev_id;887}888889dd = hci_open_dev(dev_id);890if (dd < 0) {891perror("HCI device open failed");892return dd;893}894895if (ioctl(dd, HCIDEVUP, dev_id) < 0 && errno != EALREADY) {896perror("hci down:Power management Disabled");897hci_close_dev(dd);898return -1;899}900901/* send vendor specific command with Sleep feature Enabled */902if (hci_send_cmd(dd, OGF_VENDOR_CMD, HCI_SLEEP_CMD_OCF, 1, &pm) < 0)903perror("PM command failed, power management Disabled");904905nanosleep(&tm, NULL);906hci_close_dev(dd);907908return 0;909}910911#define HCI_VENDOR_CMD_OGF 0x3F912#define HCI_PS_CMD_OCF 0x0B913#define HCI_CHG_BAUD_CMD_OCF 0x0C914915#define WRITE_BDADDR_CMD_LEN 14916#define WRITE_BAUD_CMD_LEN 6917#define MAX_CMD_LEN WRITE_BDADDR_CMD_LEN918919static int set_cntrlr_baud(int fd, int speed)920{921int baud;922struct timespec tm = { 0, 500000 };923unsigned char cmd[MAX_CMD_LEN], rsp[HCI_MAX_EVENT_SIZE];924unsigned char *ptr = cmd + 1;925hci_command_hdr *ch = (void *)ptr;926927cmd[0] = HCI_COMMAND_PKT;928929/* set controller baud rate to user specified value */930ptr = cmd + 1;931ch->opcode = htobs(cmd_opcode_pack(HCI_VENDOR_CMD_OGF,932HCI_CHG_BAUD_CMD_OCF));933ch->plen = 2;934ptr += HCI_COMMAND_HDR_SIZE;935936baud = speed/100;937ptr[0] = (char)baud;938ptr[1] = (char)(baud >> 8);939940if (write(fd, cmd, WRITE_BAUD_CMD_LEN) != WRITE_BAUD_CMD_LEN) {941perror("Failed to write change baud rate command");942return -ETIMEDOUT;943}944945nanosleep(&tm, NULL);946947if (read_hci_event(fd, rsp, sizeof(rsp)) < 0)948return -ETIMEDOUT;949950return 0;951}952953/*954* Atheros AR300x specific initialization and configuration file955* download956*/957int ath3k_init(int fd, int speed, int init_speed, char *bdaddr,958struct termios *ti)959{960int r;961int err = 0;962struct timespec tm = { 0, 500000 };963unsigned char cmd[MAX_CMD_LEN], rsp[HCI_MAX_EVENT_SIZE];964unsigned char *ptr = cmd + 1;965hci_command_hdr *ch = (void *)ptr;966967cmd[0] = HCI_COMMAND_PKT;968969/* set both controller and host baud rate to maximum possible value */970err = set_cntrlr_baud(fd, speed);971if (err < 0)972return err;973974err = set_speed(fd, ti, speed);975if (err < 0) {976perror("Can't set required baud rate");977return err;978}979980/* Download PS and patch */981r = ath_ps_download(fd);982if (r < 0) {983perror("Failed to Download configuration");984err = -ETIMEDOUT;985goto failed;986}987988/* Write BDADDR */989if (bdaddr) {990ch->opcode = htobs(cmd_opcode_pack(HCI_VENDOR_CMD_OGF,991HCI_PS_CMD_OCF));992ch->plen = 10;993ptr += HCI_COMMAND_HDR_SIZE;994995ptr[0] = 0x01;996ptr[1] = 0x01;997ptr[2] = 0x00;998ptr[3] = 0x06;999str2ba(bdaddr, (bdaddr_t *)(ptr + 4));10001001if (write(fd, cmd, WRITE_BDADDR_CMD_LEN) !=1002WRITE_BDADDR_CMD_LEN) {1003perror("Failed to write BD_ADDR command\n");1004err = -ETIMEDOUT;1005goto failed;1006}10071008if (read_hci_event(fd, rsp, sizeof(rsp)) < 0) {1009perror("Failed to set BD_ADDR\n");1010err = -ETIMEDOUT;1011goto failed;1012}1013}10141015/* Send HCI Reset */1016cmd[1] = 0x03;1017cmd[2] = 0x0C;1018cmd[3] = 0x00;10191020r = write(fd, cmd, 4);1021if (r != 4) {1022err = -ETIMEDOUT;1023goto failed;1024}10251026nanosleep(&tm, NULL);1027if (read_hci_event(fd, rsp, sizeof(rsp)) < 0) {1028err = -ETIMEDOUT;1029goto failed;1030}10311032err = set_cntrlr_baud(fd, speed);1033if (err < 0)1034return err;10351036failed:1037if (err < 0) {1038set_cntrlr_baud(fd, init_speed);1039set_speed(fd, ti, init_speed);1040}10411042return err;1043}104410451046