blob: 45008efb7f8f75d7f69506cb23f18d91fa5be959 [file] [log] [blame]
/******************************************************************************
*
* Copyright (C) 2009-2012 Broadcom Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************/
/******************************************************************************
*
* Filename: hardware.c
*
* Description: Contains controller-specific functions, like
* firmware patch download
* low power mode operations
*
******************************************************************************/
#define LOG_TAG "bt_hwcfg"
#include <utils/Log.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <signal.h>
#include <time.h>
#include <errno.h>
#include <fcntl.h>
#include <dirent.h>
#include <ctype.h>
#include <cutils/properties.h>
#include <stdlib.h>
#include "bt_hci_bdroid.h"
#include "bt_vendor_brcm.h"
#include "userial.h"
#include "userial_vendor.h"
#include "upio.h"
/******************************************************************************
** Constants & Macros
******************************************************************************/
#ifndef BTHW_DBG
#define BTHW_DBG FALSE
#endif
#if (BTHW_DBG == TRUE)
#define BTHWDBG(param, ...) {ALOGD(param, ## __VA_ARGS__);}
#else
#define BTHWDBG(param, ...) {}
#endif
#define FW_PATCHFILE_EXTENSION ".hcd"
#define FW_PATCHFILE_EXTENSION_LEN 4
#define FW_PATCHFILE_PATH_MAXLEN 248 /* Local_Name length of return of
HCI_Read_Local_Name */
#define HCI_CMD_MAX_LEN 258
#define HCI_RESET 0x0C03
#define HCI_VSC_WRITE_UART_CLOCK_SETTING 0xFC45
#define HCI_VSC_UPDATE_BAUDRATE 0xFC18
#define HCI_READ_LOCAL_NAME 0x0C14
#define HCI_VSC_DOWNLOAD_MINIDRV 0xFC2E
#define HCI_VSC_WRITE_BD_ADDR 0xFC01
#define HCI_VSC_WRITE_SLEEP_MODE 0xFC27
#define HCI_VSC_WRITE_SCO_PCM_INT_PARAM 0xFC1C
#define HCI_VSC_WRITE_PCM_DATA_FORMAT_PARAM 0xFC1E
#define HCI_VSC_WRITE_I2SPCM_INTERFACE_PARAM 0xFC6D
#define HCI_VSC_LAUNCH_RAM 0xFC4E
#define HCI_READ_LOCAL_BDADDR 0x1009
#define HCI_EVT_CMD_CMPL_STATUS_RET_BYTE 5
#define HCI_EVT_CMD_CMPL_LOCAL_NAME_STRING 6
#define HCI_EVT_CMD_CMPL_LOCAL_BDADDR_ARRAY 6
#define HCI_EVT_CMD_CMPL_OPCODE 3
#define LPM_CMD_PARAM_SIZE 12
#define UPDATE_BAUDRATE_CMD_PARAM_SIZE 6
#define HCI_CMD_PREAMBLE_SIZE 3
#define HCD_REC_PAYLOAD_LEN_BYTE 2
#define BD_ADDR_LEN 6
#define LOCAL_NAME_BUFFER_LEN 32
#define LOCAL_BDADDR_PATH_BUFFER_LEN 256
#define STREAM_TO_UINT16(u16, p) {u16 = ((uint16_t)(*(p)) + (((uint16_t)(*((p) + 1))) << 8)); (p) += 2;}
#define UINT16_TO_STREAM(p, u16) {*(p)++ = (uint8_t)(u16); *(p)++ = (uint8_t)((u16) >> 8);}
#define UINT32_TO_STREAM(p, u32) {*(p)++ = (uint8_t)(u32); *(p)++ = (uint8_t)((u32) >> 8); *(p)++ = (uint8_t)((u32) >> 16); *(p)++ = (uint8_t)((u32) >> 24);}
/******************************************************************************
** Local type definitions
******************************************************************************/
/* Hardware Configuration State */
enum {
HW_CFG_START = 1,
HW_CFG_SET_UART_CLOCK,
HW_CFG_SET_UART_BAUD_1,
HW_CFG_READ_LOCAL_NAME,
HW_CFG_DL_MINIDRIVER,
HW_CFG_DL_FW_PATCH,
HW_CFG_SET_UART_BAUD_2,
HW_CFG_SET_BD_ADDR
#if (USE_CONTROLLER_BDADDR == TRUE)
, HW_CFG_READ_BD_ADDR
#endif
};
/* h/w config control block */
typedef struct
{
uint8_t state; /* Hardware configuration state */
int fw_fd; /* FW patch file fd */
uint8_t f_set_baud_2; /* Baud rate switch state */
char local_chip_name[LOCAL_NAME_BUFFER_LEN];
} bt_hw_cfg_cb_t;
/* low power mode parameters */
typedef struct
{
uint8_t sleep_mode; /* 0(disable),1(UART),9(H5) */
uint8_t host_stack_idle_threshold; /* Unit scale 300ms/25ms */
uint8_t host_controller_idle_threshold; /* Unit scale 300ms/25ms */
uint8_t bt_wake_polarity; /* 0=Active Low, 1= Active High */
uint8_t host_wake_polarity; /* 0=Active Low, 1= Active High */
uint8_t allow_host_sleep_during_sco;
uint8_t combine_sleep_mode_and_lpm;
uint8_t enable_uart_txd_tri_state; /* UART_TXD Tri-State */
uint8_t sleep_guard_time; /* sleep guard time in 12.5ms */
uint8_t wakeup_guard_time; /* wakeup guard time in 12.5ms */
uint8_t txd_config; /* TXD is high in sleep state */
uint8_t pulsed_host_wake; /* pulsed host wake if mode = 1 */
} bt_lpm_param_t;
/* Firmware re-launch settlement time */
typedef struct {
const char *chipset_name;
const uint32_t delay_time;
} fw_settlement_entry_t;
/******************************************************************************
** Externs
******************************************************************************/
void hw_config_cback(void *p_evt_buf);
extern uint8_t vnd_local_bd_addr[BD_ADDR_LEN];
/******************************************************************************
** Static variables
******************************************************************************/
static char fw_patchfile_path[256] = FW_PATCHFILE_LOCATION;
static char fw_patchfile_name[128] = { 0 };
#if (VENDOR_LIB_RUNTIME_TUNING_ENABLED == TRUE)
static int fw_patch_settlement_delay = -1;
#endif
static bt_hw_cfg_cb_t hw_cfg_cb;
static bt_lpm_param_t lpm_param =
{
LPM_SLEEP_MODE,
LPM_IDLE_THRESHOLD,
LPM_HC_IDLE_THRESHOLD,
LPM_BT_WAKE_POLARITY,
LPM_HOST_WAKE_POLARITY,
LPM_ALLOW_HOST_SLEEP_DURING_SCO,
LPM_COMBINE_SLEEP_MODE_AND_LPM,
LPM_ENABLE_UART_TXD_TRI_STATE,
0, /* not applicable */
0, /* not applicable */
0, /* not applicable */
LPM_PULSED_HOST_WAKE
};
#if (!defined(SCO_USE_I2S_INTERFACE) || (SCO_USE_I2S_INTERFACE == FALSE))
static uint8_t bt_sco_param[SCO_PCM_PARAM_SIZE] =
{
SCO_PCM_ROUTING,
SCO_PCM_IF_CLOCK_RATE,
SCO_PCM_IF_FRAME_TYPE,
SCO_PCM_IF_SYNC_MODE,
SCO_PCM_IF_CLOCK_MODE
};
static uint8_t bt_pcm_data_fmt_param[PCM_DATA_FORMAT_PARAM_SIZE] =
{
PCM_DATA_FMT_SHIFT_MODE,
PCM_DATA_FMT_FILL_BITS,
PCM_DATA_FMT_FILL_METHOD,
PCM_DATA_FMT_FILL_NUM,
PCM_DATA_FMT_JUSTIFY_MODE
};
#else
static uint8_t bt_sco_param[SCO_I2SPCM_PARAM_SIZE] =
{
SCO_I2SPCM_IF_MODE,
SCO_I2SPCM_IF_ROLE,
SCO_I2SPCM_IF_SAMPLE_RATE,
SCO_I2SPCM_IF_CLOCK_RATE
};
#endif
/*
* The look-up table of recommended firmware settlement delay (milliseconds) on
* known chipsets.
*/
static const fw_settlement_entry_t fw_settlement_table[] = {
{"BCM43241", 200},
{(const char *) NULL, 100} // Giving the generic fw settlement delay setting.
};
/******************************************************************************
** Static functions
******************************************************************************/
/******************************************************************************
** Controller Initialization Static Functions
******************************************************************************/
/*******************************************************************************
**
** Function look_up_fw_settlement_delay
**
** Description If FW_PATCH_SETTLEMENT_DELAY_MS has not been explicitly
** re-defined in the platform specific build-time configuration
** file, we will search into the look-up table for a
** recommended firmware settlement delay value.
**
** Although the settlement time might be also related to board
** configurations such as the crystal clocking speed.
**
** Returns Firmware settlement delay
**
*******************************************************************************/
uint32_t look_up_fw_settlement_delay (void)
{
uint32_t ret_value;
fw_settlement_entry_t *p_entry;
if (FW_PATCH_SETTLEMENT_DELAY_MS > 0)
{
ret_value = FW_PATCH_SETTLEMENT_DELAY_MS;
}
#if (VENDOR_LIB_RUNTIME_TUNING_ENABLED == TRUE)
else if (fw_patch_settlement_delay >= 0)
{
ret_value = fw_patch_settlement_delay;
}
#endif
else
{
p_entry = (fw_settlement_entry_t *)fw_settlement_table;
while (p_entry->chipset_name != NULL)
{
if (strstr(hw_cfg_cb.local_chip_name, p_entry->chipset_name)!=NULL)
{
break;
}
p_entry++;
}
ret_value = p_entry->delay_time;
}
BTHWDBG( "Settlement delay -- %d ms", ret_value);
return (ret_value);
}
/*******************************************************************************
**
** Function ms_delay
**
** Description sleep unconditionally for timeout milliseconds
**
** Returns None
**
*******************************************************************************/
void ms_delay (uint32_t timeout)
{
struct timespec delay;
int err;
if (timeout == 0)
return;
delay.tv_sec = timeout / 1000;
delay.tv_nsec = 1000 * 1000 * (timeout%1000);
/* [u]sleep can't be used because it uses SIGALRM */
do {
err = nanosleep(&delay, &delay);
} while (err < 0 && errno ==EINTR);
}
/*******************************************************************************
**
** Function line_speed_to_userial_baud
**
** Description helper function converts line speed number into USERIAL baud
** rate symbol
**
** Returns unit8_t (USERIAL baud symbol)
**
*******************************************************************************/
uint8_t line_speed_to_userial_baud(uint32_t line_speed)
{
uint8_t baud;
if (line_speed == 4000000)
baud = USERIAL_BAUD_4M;
else if (line_speed == 3000000)
baud = USERIAL_BAUD_3M;
else if (line_speed == 2000000)
baud = USERIAL_BAUD_2M;
else if (line_speed == 1000000)
baud = USERIAL_BAUD_1M;
else if (line_speed == 921600)
baud = USERIAL_BAUD_921600;
else if (line_speed == 460800)
baud = USERIAL_BAUD_460800;
else if (line_speed == 230400)
baud = USERIAL_BAUD_230400;
else if (line_speed == 115200)
baud = USERIAL_BAUD_115200;
else if (line_speed == 57600)
baud = USERIAL_BAUD_57600;
else if (line_speed == 19200)
baud = USERIAL_BAUD_19200;
else if (line_speed == 9600)
baud = USERIAL_BAUD_9600;
else if (line_speed == 1200)
baud = USERIAL_BAUD_1200;
else if (line_speed == 600)
baud = USERIAL_BAUD_600;
else
{
ALOGE( "userial vendor: unsupported baud speed %d", line_speed);
baud = USERIAL_BAUD_115200;
}
return baud;
}
/*******************************************************************************
**
** Function hw_strncmp
**
** Description Used to compare two strings in caseless
**
** Returns 0: match, otherwise: not match
**
*******************************************************************************/
static int hw_strncmp (const char *p_str1, const char *p_str2, const int len)
{
int i;
if (!p_str1 || !p_str2)
return (1);
for (i = 0; i < len; i++)
{
if (toupper(p_str1[i]) != toupper(p_str2[i]))
return (i+1);
}
return 0;
}
/*******************************************************************************
**
** Function hw_config_findpatch
**
** Description Search for a proper firmware patch file
** The selected firmware patch file name with full path
** will be stored in the input string parameter, i.e.
** p_chip_id_str, when returns.
**
** Returns TRUE when found the target patch file, otherwise FALSE
**
*******************************************************************************/
static uint8_t hw_config_findpatch(char *p_chip_id_str)
{
DIR *dirp;
struct dirent *dp;
int filenamelen;
uint8_t retval = FALSE;
BTHWDBG("Target name = [%s]", p_chip_id_str);
if (strlen(fw_patchfile_name)> 0)
{
/* If specific filepath and filename have been given in run-time
* configuration /etc/bluetooth/bt_vendor.conf file, we will use them
* to concatenate the filename to open rather than searching a file
* matching to chipset name in the fw_patchfile_path folder.
*/
sprintf(p_chip_id_str, "%s", fw_patchfile_path);
if (fw_patchfile_path[strlen(fw_patchfile_path)- 1] != '/')
{
strcat(p_chip_id_str, "/");
}
strcat(p_chip_id_str, fw_patchfile_name);
ALOGI("FW patchfile: %s", p_chip_id_str);
return TRUE;
}
if ((dirp = opendir(fw_patchfile_path)) != NULL)
{
/* Fetch next filename in patchfile directory */
while ((dp = readdir(dirp)) != NULL)
{
/* Check if filename starts with chip-id name */
if ((hw_strncmp(dp->d_name, p_chip_id_str, strlen(p_chip_id_str)) \
) == 0)
{
/* Check if it has .hcd extenstion */
filenamelen = strlen(dp->d_name);
if ((filenamelen >= FW_PATCHFILE_EXTENSION_LEN) &&
((hw_strncmp(
&dp->d_name[filenamelen-FW_PATCHFILE_EXTENSION_LEN], \
FW_PATCHFILE_EXTENSION, \
FW_PATCHFILE_EXTENSION_LEN) \
) == 0))
{
ALOGI("Found patchfile: %s/%s", \
fw_patchfile_path, dp->d_name);
/* Make sure length does not exceed maximum */
if ((filenamelen + strlen(fw_patchfile_path)) > \
FW_PATCHFILE_PATH_MAXLEN)
{
ALOGE("Invalid patchfile name (too long)");
}
else
{
memset(p_chip_id_str, 0, FW_PATCHFILE_PATH_MAXLEN);
/* Found patchfile. Store location and name */
strcpy(p_chip_id_str, fw_patchfile_path);
if (fw_patchfile_path[ \
strlen(fw_patchfile_path)- 1 \
] != '/')
{
strcat(p_chip_id_str, "/");
}
strcat(p_chip_id_str, dp->d_name);
retval = TRUE;
}
break;
}
}
}
closedir(dirp);
if (retval == FALSE)
{
/* Try again chip name without revision info */
int len = strlen(p_chip_id_str);
char *p = p_chip_id_str + len - 1;
/* Scan backward and look for the first alphabet
which is not M or m
*/
while (len > 3) // BCM****
{
if ((isdigit(*p)==0) && (*p != 'M') && (*p != 'm'))
break;
p--;
len--;
}
if (len > 3)
{
*p = 0;
retval = hw_config_findpatch(p_chip_id_str);
}
}
}
else
{
ALOGE("Could not open %s", fw_patchfile_path);
}
return (retval);
}
/*******************************************************************************
**
** Function hw_config_set_bdaddr
**
** Description Program controller's Bluetooth Device Address
**
** Returns TRUE, if valid address is sent
** FALSE, otherwise
**
*******************************************************************************/
static uint8_t hw_config_set_bdaddr(HC_BT_HDR *p_buf)
{
uint8_t retval = FALSE;
uint8_t *p = (uint8_t *) (p_buf + 1);
ALOGI("Setting local bd addr to %02X:%02X:%02X:%02X:%02X:%02X",
vnd_local_bd_addr[0], vnd_local_bd_addr[1], vnd_local_bd_addr[2],
vnd_local_bd_addr[3], vnd_local_bd_addr[4], vnd_local_bd_addr[5]);
UINT16_TO_STREAM(p, HCI_VSC_WRITE_BD_ADDR);
*p++ = BD_ADDR_LEN; /* parameter length */
*p++ = vnd_local_bd_addr[5];
*p++ = vnd_local_bd_addr[4];
*p++ = vnd_local_bd_addr[3];
*p++ = vnd_local_bd_addr[2];
*p++ = vnd_local_bd_addr[1];
*p = vnd_local_bd_addr[0];
p_buf->len = HCI_CMD_PREAMBLE_SIZE + BD_ADDR_LEN;
hw_cfg_cb.state = HW_CFG_SET_BD_ADDR;
retval = bt_vendor_cbacks->xmit_cb(HCI_VSC_WRITE_BD_ADDR, p_buf, \
hw_config_cback);
return (retval);
}
#if (USE_CONTROLLER_BDADDR == TRUE)
/*******************************************************************************
**
** Function hw_config_read_bdaddr
**
** Description Read controller's Bluetooth Device Address
**
** Returns TRUE, if valid address is sent
** FALSE, otherwise
**
*******************************************************************************/
static uint8_t hw_config_read_bdaddr(HC_BT_HDR *p_buf)
{
uint8_t retval = FALSE;
uint8_t *p = (uint8_t *) (p_buf + 1);
UINT16_TO_STREAM(p, HCI_READ_LOCAL_BDADDR);
*p = 0; /* parameter length */
p_buf->len = HCI_CMD_PREAMBLE_SIZE;
hw_cfg_cb.state = HW_CFG_READ_BD_ADDR;
retval = bt_vendor_cbacks->xmit_cb(HCI_READ_LOCAL_BDADDR, p_buf, \
hw_config_cback);
return (retval);
}
#endif // (USE_CONTROLLER_BDADDR == TRUE)
/*******************************************************************************
**
** Function hw_config_cback
**
** Description Callback function for controller configuration
**
** Returns None
**
*******************************************************************************/
void hw_config_cback(void *p_mem)
{
HC_BT_HDR *p_evt_buf = (HC_BT_HDR *) p_mem;
char *p_name, *p_tmp;
uint8_t *p, status;
uint16_t opcode;
HC_BT_HDR *p_buf=NULL;
uint8_t is_proceeding = FALSE;
int i;
#if (USE_CONTROLLER_BDADDR == TRUE)
const uint8_t null_bdaddr[BD_ADDR_LEN] = {0,0,0,0,0,0};
#endif
status = *((uint8_t *)(p_evt_buf + 1) + HCI_EVT_CMD_CMPL_STATUS_RET_BYTE);
p = (uint8_t *)(p_evt_buf + 1) + HCI_EVT_CMD_CMPL_OPCODE;
STREAM_TO_UINT16(opcode,p);
/* Ask a new buffer big enough to hold any HCI commands sent in here */
if ((status == 0) && bt_vendor_cbacks)
p_buf = (HC_BT_HDR *) bt_vendor_cbacks->alloc(BT_HC_HDR_SIZE + \
HCI_CMD_MAX_LEN);
if (p_buf != NULL)
{
p_buf->event = MSG_STACK_TO_HC_HCI_CMD;
p_buf->offset = 0;
p_buf->len = 0;
p_buf->layer_specific = 0;
p = (uint8_t *) (p_buf + 1);
switch (hw_cfg_cb.state)
{
case HW_CFG_SET_UART_BAUD_1:
/* update baud rate of host's UART port */
ALOGI("bt vendor lib: set UART baud %i", UART_TARGET_BAUD_RATE);
userial_vendor_set_baud( \
line_speed_to_userial_baud(UART_TARGET_BAUD_RATE) \
);
/* read local name */
UINT16_TO_STREAM(p, HCI_READ_LOCAL_NAME);
*p = 0; /* parameter length */
p_buf->len = HCI_CMD_PREAMBLE_SIZE;
hw_cfg_cb.state = HW_CFG_READ_LOCAL_NAME;
is_proceeding = bt_vendor_cbacks->xmit_cb(HCI_READ_LOCAL_NAME, \
p_buf, hw_config_cback);
break;
case HW_CFG_READ_LOCAL_NAME:
p_tmp = p_name = (char *) (p_evt_buf + 1) + \
HCI_EVT_CMD_CMPL_LOCAL_NAME_STRING;
for (i=0; (i < LOCAL_NAME_BUFFER_LEN)||(*(p_name+i) != 0); i++)
*(p_name+i) = toupper(*(p_name+i));
if ((p_name = strstr(p_name, "BCM")) != NULL)
{
strncpy(hw_cfg_cb.local_chip_name, p_name, \
LOCAL_NAME_BUFFER_LEN-1);
}
else
{
strncpy(hw_cfg_cb.local_chip_name, "UNKNOWN", \
LOCAL_NAME_BUFFER_LEN-1);
p_name = p_tmp;
}
hw_cfg_cb.local_chip_name[LOCAL_NAME_BUFFER_LEN-1] = 0;
BTHWDBG("Chipset %s", hw_cfg_cb.local_chip_name);
if ((status = hw_config_findpatch(p_name)) == TRUE)
{
if ((hw_cfg_cb.fw_fd = open(p_name, O_RDONLY)) == -1)
{
ALOGE("vendor lib preload failed to open [%s]", p_name);
}
else
{
/* vsc_download_minidriver */
UINT16_TO_STREAM(p, HCI_VSC_DOWNLOAD_MINIDRV);
*p = 0; /* parameter length */
p_buf->len = HCI_CMD_PREAMBLE_SIZE;
hw_cfg_cb.state = HW_CFG_DL_MINIDRIVER;
is_proceeding = bt_vendor_cbacks->xmit_cb( \
HCI_VSC_DOWNLOAD_MINIDRV, p_buf, \
hw_config_cback);
}
}
else
{
ALOGE( \
"vendor lib preload failed to locate firmware patch file" \
);
}
if (is_proceeding == FALSE)
{
is_proceeding = hw_config_set_bdaddr(p_buf);
}
break;
case HW_CFG_DL_MINIDRIVER:
/* give time for placing firmware in download mode */
ms_delay(50);
hw_cfg_cb.state = HW_CFG_DL_FW_PATCH;
/* fall through intentionally */
case HW_CFG_DL_FW_PATCH:
p_buf->len = read(hw_cfg_cb.fw_fd, p, HCI_CMD_PREAMBLE_SIZE);
if (p_buf->len > 0)
{
if ((p_buf->len < HCI_CMD_PREAMBLE_SIZE) || \
(opcode == HCI_VSC_LAUNCH_RAM))
{
ALOGW("firmware patch file might be altered!");
}
else
{
p_buf->len += read(hw_cfg_cb.fw_fd, \
p+HCI_CMD_PREAMBLE_SIZE,\
*(p+HCD_REC_PAYLOAD_LEN_BYTE));
STREAM_TO_UINT16(opcode,p);
is_proceeding = bt_vendor_cbacks->xmit_cb(opcode, \
p_buf, hw_config_cback);
break;
}
}
close(hw_cfg_cb.fw_fd);
hw_cfg_cb.fw_fd = -1;
/* Normally the firmware patch configuration file
* sets the new starting baud rate at 115200.
* So, we need update host's baud rate accordingly.
*/
ALOGI("bt vendor lib: set UART baud 115200");
userial_vendor_set_baud(USERIAL_BAUD_115200);
/* Next, we would like to boost baud rate up again
* to desired working speed.
*/
hw_cfg_cb.f_set_baud_2 = TRUE;
/* Check if we need to pause a few hundred milliseconds
* before sending down any HCI command.
*/
ms_delay(look_up_fw_settlement_delay());
/* fall through intentionally */
case HW_CFG_START:
if (UART_TARGET_BAUD_RATE > 3000000)
{
/* set UART clock to 48MHz */
UINT16_TO_STREAM(p, HCI_VSC_WRITE_UART_CLOCK_SETTING);
*p++ = 1; /* parameter length */
*p = 1; /* (1,"UART CLOCK 48 MHz")(2,"UART CLOCK 24 MHz") */
p_buf->len = HCI_CMD_PREAMBLE_SIZE + 1;
hw_cfg_cb.state = HW_CFG_SET_UART_CLOCK;
is_proceeding = bt_vendor_cbacks->xmit_cb( \
HCI_VSC_WRITE_UART_CLOCK_SETTING, \
p_buf, hw_config_cback);
break;
}
/* fall through intentionally */
case HW_CFG_SET_UART_CLOCK:
/* set controller's UART baud rate to 3M */
UINT16_TO_STREAM(p, HCI_VSC_UPDATE_BAUDRATE);
*p++ = UPDATE_BAUDRATE_CMD_PARAM_SIZE; /* parameter length */
*p++ = 0; /* encoded baud rate */
*p++ = 0; /* use encoded form */
UINT32_TO_STREAM(p, UART_TARGET_BAUD_RATE);
p_buf->len = HCI_CMD_PREAMBLE_SIZE + \
UPDATE_BAUDRATE_CMD_PARAM_SIZE;
hw_cfg_cb.state = (hw_cfg_cb.f_set_baud_2) ? \
HW_CFG_SET_UART_BAUD_2 : HW_CFG_SET_UART_BAUD_1;
is_proceeding = bt_vendor_cbacks->xmit_cb(HCI_VSC_UPDATE_BAUDRATE, \
p_buf, hw_config_cback);
break;
case HW_CFG_SET_UART_BAUD_2:
/* update baud rate of host's UART port */
ALOGI("bt vendor lib: set UART baud %i", UART_TARGET_BAUD_RATE);
userial_vendor_set_baud( \
line_speed_to_userial_baud(UART_TARGET_BAUD_RATE) \
);
#if (USE_CONTROLLER_BDADDR == TRUE)
if ((is_proceeding = hw_config_read_bdaddr(p_buf)) == TRUE)
break;
#else
if ((is_proceeding = hw_config_set_bdaddr(p_buf)) == TRUE)
break;
#endif
/* fall through intentionally */
case HW_CFG_SET_BD_ADDR:
ALOGI("vendor lib fwcfg completed");
bt_vendor_cbacks->dealloc(p_buf);
bt_vendor_cbacks->fwcfg_cb(BT_VND_OP_RESULT_SUCCESS);
hw_cfg_cb.state = 0;
if (hw_cfg_cb.fw_fd != -1)
{
close(hw_cfg_cb.fw_fd);
hw_cfg_cb.fw_fd = -1;
}
is_proceeding = TRUE;
break;
#if (USE_CONTROLLER_BDADDR == TRUE)
case HW_CFG_READ_BD_ADDR:
p_tmp = (char *) (p_evt_buf + 1) + \
HCI_EVT_CMD_CMPL_LOCAL_BDADDR_ARRAY;
if (memcmp(p_tmp, null_bdaddr, BD_ADDR_LEN) == 0)
{
// Controller does not have a valid OTP BDADDR!
// Set the BTIF initial BDADDR instead.
if ((is_proceeding = hw_config_set_bdaddr(p_buf)) == TRUE)
break;
}
else
{
ALOGI("Controller OTP bdaddr %02X:%02X:%02X:%02X:%02X:%02X",
*(p_tmp+5), *(p_tmp+4), *(p_tmp+3),
*(p_tmp+2), *(p_tmp+1), *p_tmp);
}
ALOGI("vendor lib fwcfg completed");
bt_vendor_cbacks->dealloc(p_buf);
bt_vendor_cbacks->fwcfg_cb(BT_VND_OP_RESULT_SUCCESS);
hw_cfg_cb.state = 0;
if (hw_cfg_cb.fw_fd != -1)
{
close(hw_cfg_cb.fw_fd);
hw_cfg_cb.fw_fd = -1;
}
is_proceeding = TRUE;
break;
#endif // (USE_CONTROLLER_BDADDR == TRUE)
} // switch(hw_cfg_cb.state)
} // if (p_buf != NULL)
/* Free the RX event buffer */
if (bt_vendor_cbacks)
bt_vendor_cbacks->dealloc(p_evt_buf);
if (is_proceeding == FALSE)
{
ALOGE("vendor lib fwcfg aborted!!!");
if (bt_vendor_cbacks)
{
if (p_buf != NULL)
bt_vendor_cbacks->dealloc(p_buf);
bt_vendor_cbacks->fwcfg_cb(BT_VND_OP_RESULT_FAIL);
}
if (hw_cfg_cb.fw_fd != -1)
{
close(hw_cfg_cb.fw_fd);
hw_cfg_cb.fw_fd = -1;
}
hw_cfg_cb.state = 0;
}
}
/******************************************************************************
** LPM Static Functions
******************************************************************************/
/*******************************************************************************
**
** Function hw_lpm_ctrl_cback
**
** Description Callback function for lpm enable/disable rquest
**
** Returns None
**
*******************************************************************************/
void hw_lpm_ctrl_cback(void *p_mem)
{
HC_BT_HDR *p_evt_buf = (HC_BT_HDR *) p_mem;
bt_vendor_op_result_t status = BT_VND_OP_RESULT_FAIL;
if (*((uint8_t *)(p_evt_buf + 1) + HCI_EVT_CMD_CMPL_STATUS_RET_BYTE) == 0)
{
status = BT_VND_OP_RESULT_SUCCESS;
}
if (bt_vendor_cbacks)
{
bt_vendor_cbacks->lpm_cb(status);
bt_vendor_cbacks->dealloc(p_evt_buf);
}
}
#if (SCO_CFG_INCLUDED == TRUE)
/*****************************************************************************
** SCO Configuration Static Functions
*****************************************************************************/
/*******************************************************************************
**
** Function hw_sco_cfg_cback
**
** Description Callback function for SCO configuration rquest
**
** Returns None
**
*******************************************************************************/
void hw_sco_cfg_cback(void *p_mem)
{
HC_BT_HDR *p_evt_buf = (HC_BT_HDR *) p_mem;
uint8_t *p;
uint16_t opcode;
HC_BT_HDR *p_buf=NULL;
p = (uint8_t *)(p_evt_buf + 1) + HCI_EVT_CMD_CMPL_OPCODE;
STREAM_TO_UINT16(opcode,p);
/* Free the RX event buffer */
if (bt_vendor_cbacks)
bt_vendor_cbacks->dealloc(p_evt_buf);
#if (!defined(SCO_USE_I2S_INTERFACE) || (SCO_USE_I2S_INTERFACE == FALSE))
if (opcode == HCI_VSC_WRITE_SCO_PCM_INT_PARAM)
{
uint8_t ret = FALSE;
/* Ask a new buffer to hold WRITE_PCM_DATA_FORMAT_PARAM command */
if (bt_vendor_cbacks)
p_buf = (HC_BT_HDR *) bt_vendor_cbacks->alloc(BT_HC_HDR_SIZE + \
HCI_CMD_PREAMBLE_SIZE + \
PCM_DATA_FORMAT_PARAM_SIZE);
if (p_buf)
{
p_buf->event = MSG_STACK_TO_HC_HCI_CMD;
p_buf->offset = 0;
p_buf->layer_specific = 0;
p_buf->len = HCI_CMD_PREAMBLE_SIZE + PCM_DATA_FORMAT_PARAM_SIZE;
p = (uint8_t *) (p_buf + 1);
UINT16_TO_STREAM(p, HCI_VSC_WRITE_PCM_DATA_FORMAT_PARAM);
*p++ = PCM_DATA_FORMAT_PARAM_SIZE;
memcpy(p, &bt_pcm_data_fmt_param, PCM_DATA_FORMAT_PARAM_SIZE);
if ((ret = bt_vendor_cbacks->xmit_cb(HCI_VSC_WRITE_PCM_DATA_FORMAT_PARAM,\
p_buf, hw_sco_cfg_cback)) == FALSE)
{
bt_vendor_cbacks->dealloc(p_buf);
}
else
return;
}
}
#endif // !SCO_USE_I2S_INTERFACE
if (bt_vendor_cbacks)
bt_vendor_cbacks->scocfg_cb(BT_VND_OP_RESULT_SUCCESS);
}
#endif // SCO_CFG_INCLUDED
/*****************************************************************************
** Hardware Configuration Interface Functions
*****************************************************************************/
/*******************************************************************************
**
** Function hw_config_start
**
** Description Kick off controller initialization process
**
** Returns None
**
*******************************************************************************/
void hw_config_start(void)
{
HC_BT_HDR *p_buf = NULL;
uint8_t *p;
hw_cfg_cb.state = 0;
hw_cfg_cb.fw_fd = -1;
hw_cfg_cb.f_set_baud_2 = FALSE;
/* Start from sending HCI_RESET */
if (bt_vendor_cbacks)
{
p_buf = (HC_BT_HDR *) bt_vendor_cbacks->alloc(BT_HC_HDR_SIZE + \
HCI_CMD_PREAMBLE_SIZE);
}
if (p_buf)
{
p_buf->event = MSG_STACK_TO_HC_HCI_CMD;
p_buf->offset = 0;
p_buf->layer_specific = 0;
p_buf->len = HCI_CMD_PREAMBLE_SIZE;
p = (uint8_t *) (p_buf + 1);
UINT16_TO_STREAM(p, HCI_RESET);
*p = 0; /* parameter length */
hw_cfg_cb.state = HW_CFG_START;
bt_vendor_cbacks->xmit_cb(HCI_RESET, p_buf, hw_config_cback);
}
else
{
if (bt_vendor_cbacks)
{
ALOGE("vendor lib fw conf aborted [no buffer]");
bt_vendor_cbacks->fwcfg_cb(BT_VND_OP_RESULT_FAIL);
}
}
}
/*******************************************************************************
**
** Function hw_lpm_enable
**
** Description Enalbe/Disable LPM
**
** Returns TRUE/FALSE
**
*******************************************************************************/
uint8_t hw_lpm_enable(uint8_t turn_on)
{
HC_BT_HDR *p_buf = NULL;
uint8_t *p;
uint8_t ret = FALSE;
if (bt_vendor_cbacks)
p_buf = (HC_BT_HDR *) bt_vendor_cbacks->alloc(BT_HC_HDR_SIZE + \
HCI_CMD_PREAMBLE_SIZE + \
LPM_CMD_PARAM_SIZE);
if (p_buf)
{
p_buf->event = MSG_STACK_TO_HC_HCI_CMD;
p_buf->offset = 0;
p_buf->layer_specific = 0;
p_buf->len = HCI_CMD_PREAMBLE_SIZE + LPM_CMD_PARAM_SIZE;
p = (uint8_t *) (p_buf + 1);
UINT16_TO_STREAM(p, HCI_VSC_WRITE_SLEEP_MODE);
*p++ = LPM_CMD_PARAM_SIZE; /* parameter length */
if (turn_on)
{
memcpy(p, &lpm_param, LPM_CMD_PARAM_SIZE);
upio_set(UPIO_LPM_MODE, UPIO_ASSERT, 0);
}
else
{
memset(p, 0, LPM_CMD_PARAM_SIZE);
upio_set(UPIO_LPM_MODE, UPIO_DEASSERT, 0);
}
if ((ret = bt_vendor_cbacks->xmit_cb(HCI_VSC_WRITE_SLEEP_MODE, p_buf, \
hw_lpm_ctrl_cback)) == FALSE)
{
bt_vendor_cbacks->dealloc(p_buf);
}
}
if ((ret == FALSE) && bt_vendor_cbacks)
bt_vendor_cbacks->lpm_cb(BT_VND_OP_RESULT_FAIL);
return ret;
}
/*******************************************************************************
**
** Function hw_lpm_get_idle_timeout
**
** Description Calculate idle time based on host stack idle threshold
**
** Returns idle timeout value
**
*******************************************************************************/
uint32_t hw_lpm_get_idle_timeout(void)
{
uint32_t timeout_ms;
/* set idle time to be LPM_IDLE_TIMEOUT_MULTIPLE times of
* host stack idle threshold (in 300ms/25ms)
*/
timeout_ms = (uint32_t)lpm_param.host_stack_idle_threshold \
* LPM_IDLE_TIMEOUT_MULTIPLE;
if (strstr(hw_cfg_cb.local_chip_name, "BCM4325") != NULL)
timeout_ms *= 25; // 12.5 or 25 ?
else
timeout_ms *= 300;
return timeout_ms;
}
/*******************************************************************************
**
** Function hw_lpm_set_wake_state
**
** Description Assert/Deassert BT_WAKE
**
** Returns None
**
*******************************************************************************/
void hw_lpm_set_wake_state(uint8_t wake_assert)
{
uint8_t state = (wake_assert) ? UPIO_ASSERT : UPIO_DEASSERT;
upio_set(UPIO_BT_WAKE, state, lpm_param.bt_wake_polarity);
}
#if (SCO_CFG_INCLUDED == TRUE)
/*******************************************************************************
**
** Function hw_sco_config
**
** Description Configure SCO related hardware settings
**
** Returns None
**
*******************************************************************************/
void hw_sco_config(void)
{
HC_BT_HDR *p_buf = NULL;
uint8_t *p, ret;
#if (!defined(SCO_USE_I2S_INTERFACE) || (SCO_USE_I2S_INTERFACE == FALSE))
uint16_t cmd_u16 = HCI_CMD_PREAMBLE_SIZE + SCO_PCM_PARAM_SIZE;
#else
uint16_t cmd_u16 = HCI_CMD_PREAMBLE_SIZE + SCO_I2SPCM_PARAM_SIZE;
#endif
if (bt_vendor_cbacks)
p_buf = (HC_BT_HDR *) bt_vendor_cbacks->alloc(BT_HC_HDR_SIZE+cmd_u16);
if (p_buf)
{
p_buf->event = MSG_STACK_TO_HC_HCI_CMD;
p_buf->offset = 0;
p_buf->layer_specific = 0;
p_buf->len = cmd_u16;
p = (uint8_t *) (p_buf + 1);
#if (!defined(SCO_USE_I2S_INTERFACE) || (SCO_USE_I2S_INTERFACE == FALSE))
UINT16_TO_STREAM(p, HCI_VSC_WRITE_SCO_PCM_INT_PARAM);
*p++ = SCO_PCM_PARAM_SIZE;
memcpy(p, &bt_sco_param, SCO_PCM_PARAM_SIZE);
cmd_u16 = HCI_VSC_WRITE_SCO_PCM_INT_PARAM;
ALOGI("SCO PCM configure {%d, %d, %d, %d, %d}",
bt_sco_param[0], bt_sco_param[1], bt_sco_param[2], bt_sco_param[3], \
bt_sco_param[4]);
#else
UINT16_TO_STREAM(p, HCI_VSC_WRITE_I2SPCM_INTERFACE_PARAM);
*p++ = SCO_I2SPCM_PARAM_SIZE;
memcpy(p, &bt_sco_param, SCO_I2SPCM_PARAM_SIZE);
cmd_u16 = HCI_VSC_WRITE_I2SPCM_INTERFACE_PARAM;
ALOGI("SCO over I2SPCM interface {%d, %d, %d, %d}",
bt_sco_param[0], bt_sco_param[1], bt_sco_param[2], bt_sco_param[3]);
#endif
if ((ret=bt_vendor_cbacks->xmit_cb(cmd_u16, p_buf, hw_sco_cfg_cback)) \
== FALSE)
{
bt_vendor_cbacks->dealloc(p_buf);
}
else
return;
}
if (bt_vendor_cbacks)
{
ALOGE("vendor lib scocfg aborted");
bt_vendor_cbacks->scocfg_cb(BT_VND_OP_RESULT_FAIL);
}
}
#endif // SCO_CFG_INCLUDED
/*******************************************************************************
**
** Function hw_set_patch_file_path
**
** Description Set the location of firmware patch file
**
** Returns 0 : Success
** Otherwise : Fail
**
*******************************************************************************/
int hw_set_patch_file_path(char *p_conf_name, char *p_conf_value, int param)
{
strcpy(fw_patchfile_path, p_conf_value);
return 0;
}
/*******************************************************************************
**
** Function hw_set_patch_file_name
**
** Description Give the specific firmware patch filename
**
** Returns 0 : Success
** Otherwise : Fail
**
*******************************************************************************/
int hw_set_patch_file_name(char *p_conf_name, char *p_conf_value, int param)
{
strcpy(fw_patchfile_name, p_conf_value);
return 0;
}
#if (VENDOR_LIB_RUNTIME_TUNING_ENABLED == TRUE)
/*******************************************************************************
**
** Function hw_set_patch_settlement_delay
**
** Description Give the specific firmware patch settlement time in milliseconds
**
** Returns 0 : Success
** Otherwise : Fail
**
*******************************************************************************/
int hw_set_patch_settlement_delay(char *p_conf_name, char *p_conf_value, int param)
{
fw_patch_settlement_delay = atoi(p_conf_value);
return 0;
}
#endif //VENDOR_LIB_RUNTIME_TUNING_ENABLED