*
* 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 <stdbool.h>
#include <sys/stat.h>
#include <signal.h>
#include <time.h>
#include <errno.h>
#include <fcntl.h>
#include <dirent.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include "bt_hci_bdroid.h"
#include "bt_vendor_brcm.h"
#include "esco_parameters.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, ...) \
{ \
HILOGD(param, ##__VA_ARGS__); \
}
#else
#define BTHWDBG(param, ...) \
{ \
HILOGD(param, ##__VA_ARGS__); \
}
#endif
#define FW_PATCHFILE_EXTENSION ".hcd"
#define FW_PATCHFILE_EXTENSION_LEN 4
#define FW_PATCHFILE_PATH_MAXLEN 248
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_FIRMWARE 0xFC4C
#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_ENABLE_WBS 0xFC7E
#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 LOCAL_NAME_BUFFER_LEN 32
#define LOCAL_BDADDR_PATH_BUFFER_LEN 256
#define STREAM_TO_UINT16(u16, p) \
do \
{ \
u16 = ((uint16_t)(*(p)) + (((uint16_t)(*((p) + 1))) << 8)); \
(p) += 2; \
} while (0)
#define UINT8_TO_STREAM(p, u8) \
do \
{ \
*(p)++ = (uint8_t)(u8); \
} while (0)
#define UINT16_TO_STREAM(p, u16) \
do \
{ \
*(p)++ = (uint8_t)(u16); \
*(p)++ = (uint8_t)((u16) >> 8); \
} while (0)
#define UINT32_TO_STREAM(p, u32) \
do \
{ \
*(p)++ = (uint8_t)(u32); \
*(p)++ = (uint8_t)((u32) >> 8); \
*(p)++ = (uint8_t)((u32) >> 16); \
*(p)++ = (uint8_t)((u32) >> 24); \
} while (0)
#define SCO_INTERFACE_PCM 0
#define SCO_INTERFACE_I2S 1
next 2 bytes are for codec type */
#define SCO_CODEC_PARAM_SIZE 3
#define BT_VENDOR_CFG_TIMEDELAY_ 40
#define BT_VENDOR_LDM_DEFAULT_IDLE 300
** Local type definitions
******************************************************************************/
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,
HW_CFG_READ_BD_ADDR
};
typedef struct {
uint8_t state;
int fw_fd;
uint8_t f_set_baud_2;
char local_chip_name[LOCAL_NAME_BUFFER_LEN];
} bt_hw_cfg_cb_t;
typedef struct {
uint8_t host_stack_idle_threshold;
uint8_t bt_wake_polarity;
} bt_lpm_param_t;
typedef struct {
const char *chipset_name;
const uint32_t delay_time;
} fw_settlement_entry_t;
** Externs
******************************************************************************/
void hw_config_cback(void *p_mem);
** 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 int wbs_sample_rate = SCO_WBS_SAMPLE_RATE;
static bt_hw_cfg_cb_t hw_cfg_cb;
static bt_lpm_param_t lpm_param = {
LPM_IDLE_THRESHOLD,
LPM_BT_WAKE_POLARITY,
};
bt_sco_i2spcm_param will be used for WBS setting
update the bt_sco_param and bt_sco_i2spcm_param */
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
};
static uint8_t bt_sco_i2spcm_param[SCO_I2SPCM_PARAM_SIZE] = {
SCO_I2SPCM_IF_MODE,
SCO_I2SPCM_IF_ROLE,
SCO_I2SPCM_IF_SAMPLE_RATE,
SCO_I2SPCM_IF_CLOCK_RATE
};
* The look-up table of recommended firmware settlement delay (milliseconds) on
* known chipsets.
*/
static const fw_settlement_entry_t fw_settlement_table[] = {
{"BCM43241", 200},
{"BCM43341", 100},
{(const char *)NULL, 200}
};
* NOTICE:
* If the platform plans to run I2S interface bus over I2S/PCM port of the
* BT Controller with the Host AP, explicitly set "SCO_USE_I2S_INTERFACE = TRUE"
* in the correspodning include/vnd_<target>.txt file.
* Otherwise, leave SCO_USE_I2S_INTERFACE undefined in the vnd_<target>.txt file.
* And, PCM interface will be set as the default bus format running over I2S/PCM
* port.
*/
#if (defined(SCO_USE_I2S_INTERFACE) && SCO_USE_I2S_INTERFACE == TRUE)
static uint8_t sco_bus_interface = SCO_INTERFACE_I2S;
#else
static uint8_t sco_bus_interface = SCO_INTERFACE_PCM;
#endif
#define INVALID_SCO_CLOCK_RATE 0xFF
static uint8_t sco_bus_clock_rate = INVALID_SCO_CLOCK_RATE;
static uint8_t sco_bus_wbs_clock_rate = INVALID_SCO_CLOCK_RATE;
** Static functions
******************************************************************************/
static void hw_sco_i2spcm_config(uint16_t codec);
static void hw_sco_i2spcm_config_from_command(void *p_mem, uint16_t codec);
** 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 / BT_VENDOR_TIME_RAIDX;
delay.tv_nsec = BT_VENDOR_TIME_RAIDX * BT_VENDOR_TIME_RAIDX * (timeout % BT_VENDOR_TIME_RAIDX);
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 == USERIAL_LINESPEED_4M)
baud = USERIAL_BAUD_4M;
else if (line_speed == USERIAL_LINESPEED_3M)
baud = USERIAL_BAUD_3M;
else if (line_speed == USERIAL_LINESPEED_2M)
baud = USERIAL_BAUD_2M;
else if (line_speed == USERIAL_LINESPEED_1_5M)
baud = USERIAL_BAUD_1_5M;
else if (line_speed == USERIAL_LINESPEED_1M)
baud = USERIAL_BAUD_1M;
else if (line_speed == USERIAL_LINESPEED_921600)
baud = USERIAL_BAUD_921600;
else if (line_speed == USERIAL_LINESPEED_460800)
baud = USERIAL_BAUD_460800;
else if (line_speed == USERIAL_LINESPEED_230400)
baud = USERIAL_BAUD_230400;
else if (line_speed == USERIAL_LINESPEED_115200)
baud = USERIAL_BAUD_115200;
else if (line_speed == USERIAL_LINESPEED_57600)
baud = USERIAL_BAUD_57600;
else if (line_speed == USERIAL_LINESPEED_19200)
baud = USERIAL_BAUD_19200;
else if (line_speed == USERIAL_LINESPEED_9600)
baud = USERIAL_BAUD_9600;
else if (line_speed == USERIAL_LINESPEED_1200)
baud = USERIAL_BAUD_1200;
else if (line_speed == USERIAL_LINESPEED_600)
baud = USERIAL_BAUD_600;
else {
HILOGE("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_set_bdaddr
**
** Description Program controller's Bluetooth Device Address
**
** Returns xmit bytes
**
*******************************************************************************/
static ssize_t hw_config_set_bdaddr(HC_BT_HDR *p_buf)
{
uint8_t retval = FALSE;
uint8_t *p = (uint8_t *)(p_buf + 1);
int i = BD_ADDR_LEN;
UINT16_TO_STREAM(p, HCI_VSC_WRITE_BD_ADDR);
*p++ = BD_ADDR_LEN;
*p++ = vnd_local_bd_addr[--i];
*p++ = vnd_local_bd_addr[--i];
*p++ = vnd_local_bd_addr[--i];
*p++ = vnd_local_bd_addr[--i];
*p++ = vnd_local_bd_addr[--i];
*p = vnd_local_bd_addr[--i];
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);
return (retval);
}
#if (USE_CONTROLLER_BDADDR == TRUE)
**
** Function hw_config_read_bdaddr
**
** Description Read controller's Bluetooth Device Address
**
** Returns xmit bytes
**
*******************************************************************************/
static ssize_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;
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);
return (retval);
}
#endif
typedef void (*tTIMER_HANDLE_CBACK)(union sigval sigval_value);
static timer_t OsAllocateTimer(tTIMER_HANDLE_CBACK timer_callback)
{
struct sigevent sigev;
timer_t timerid;
(void)memset_s(&sigev, sizeof(struct sigevent), 0, sizeof(struct sigevent));
sigev.sigev_notify = SIGEV_THREAD;
sigev.sigev_notify_function = timer_callback;
sigev.sigev_value.sival_ptr = &timerid;
if (timer_create(CLOCK_REALTIME, &sigev, &timerid) == 0) {
return timerid;
} else {
HILOGE("timer_create error!");
return (timer_t)-1;
}
}
int OsFreeTimer(timer_t timerid)
{
int ret = 0;
ret = timer_delete(timerid);
if (ret != 0) {
HILOGE("timer_delete fail with errno(%d)", errno);
}
return ret;
}
static int OsStartTimer(timer_t timerid, int msec, int mode)
{
struct itimerspec itval;
itval.it_value.tv_sec = msec / BT_VENDOR_TIME_RAIDX;
itval.it_value.tv_nsec = (long)(msec % BT_VENDOR_TIME_RAIDX) * (BT_VENDOR_TIME_RAIDX * BT_VENDOR_TIME_RAIDX);
if (mode == 1) {
itval.it_interval.tv_sec = itval.it_value.tv_sec;
itval.it_interval.tv_nsec = itval.it_value.tv_nsec;
} else {
itval.it_interval.tv_sec = 0;
itval.it_interval.tv_nsec = 0;
}
if (timer_settime(timerid, 0, &itval, NULL) != 0) {
HILOGE("time_settime error!");
return -1;
}
return 0;
}
static timer_t localtimer = 0;
static void local_timer_handler(union sigval sigev_value)
{
bt_vendor_cbacks->init_cb(BTC_OP_RESULT_SUCCESS);
OsFreeTimer(localtimer);
}
static void start_fwcfg_cbtimer(void)
{
if (localtimer == 0) {
localtimer = OsAllocateTimer(local_timer_handler);
}
OsStartTimer(localtimer, BT_VENDOR_CFG_TIMEDELAY_, 0);
}
void hw_sco_config(void);
**
** 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;
uint8_t *p, status;
uint16_t opcode;
HC_BT_HDR *p_buf = NULL;
ssize_t xmit_bytes = 0;
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);
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:
HILOGI("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 0
UINT16_TO_STREAM(p, HCI_READ_LOCAL_NAME);
*p = 0;
p_buf->len = HCI_CMD_PREAMBLE_SIZE;
hw_cfg_cb.state = HW_CFG_READ_LOCAL_NAME;
xmit_bytes = bt_vendor_cbacks->xmit_cb(HCI_READ_LOCAL_NAME, p_buf);
break;
#endif
case HW_CFG_READ_LOCAL_NAME:
#if 0
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);
#ifdef USE_BLUETOOTH_BCM4343
} else if ((p_name = strstr(p_tmp, "4343")) != NULL) {
snprintf(hw_cfg_cb.local_chip_name,
LOCAL_NAME_BUFFER_LEN - 1, "BCM%s", p_name);
strncpy(p_name, hw_cfg_cb.local_chip_name,
LOCAL_NAME_BUFFER_LEN - 1);
}
#endif
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);
#endif
{
char *p_name;
p_name = FW_PATCHFILE_LOCATION "BCM4362A2.hcd";
if ((hw_cfg_cb.fw_fd = open(p_name, O_RDONLY)) == -1) {
HILOGE("vendor lib preload failed to open [%s]", p_name);
} else {
UINT16_TO_STREAM(p, HCI_VSC_DOWNLOAD_MINIDRV);
*p = 0;
p_buf->len = HCI_CMD_PREAMBLE_SIZE;
hw_cfg_cb.state = HW_CFG_DL_MINIDRIVER;
xmit_bytes = bt_vendor_cbacks->xmit_cb(HCI_VSC_DOWNLOAD_MINIDRV, p_buf);
}
}
if (xmit_bytes <= 0) {
HILOGE("vendor lib preload failed to locate firmware patch file and set bdaddr");
xmit_bytes = hw_config_set_bdaddr(p_buf);
}
break;
case HW_CFG_DL_MINIDRIVER:
ms_delay(50);
hw_cfg_cb.state = HW_CFG_DL_FW_PATCH;
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)) {
HILOGW("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);
xmit_bytes = bt_vendor_cbacks->xmit_cb(opcode, p_buf);
break;
}
}
close(hw_cfg_cb.fw_fd);
hw_cfg_cb.fw_fd = -1;
* sets the new starting baud rate at 115200.
* So, we need update host's baud rate accordingly.
*/
HILOGI("bt vendor lib: set UART baud 115200");
userial_vendor_set_baud(USERIAL_BAUD_115200);
* to desired working speed.
*/
hw_cfg_cb.f_set_baud_2 = TRUE;
* before sending down any HCI command.
*/
int delay = 0;
delay = look_up_fw_settlement_delay();
HILOGI("Setting fw settlement delay to %d ", delay);
ms_delay(delay);
p_buf->len = HCI_CMD_PREAMBLE_SIZE;
UINT16_TO_STREAM(p, HCI_RESET);
*p = 0;
hw_cfg_cb.state = HW_CFG_START;
xmit_bytes = bt_vendor_cbacks->xmit_cb(HCI_RESET, p_buf);
break;
case HW_CFG_START:
if (UART_TARGET_BAUD_RATE > 3000000) {
UINT16_TO_STREAM(p, HCI_VSC_WRITE_UART_CLOCK_SETTING);
*p++ = 1;
*p = 1;
p_buf->len = HCI_CMD_PREAMBLE_SIZE + 1;
hw_cfg_cb.state = HW_CFG_SET_UART_CLOCK;
xmit_bytes = bt_vendor_cbacks->xmit_cb(HCI_VSC_WRITE_UART_CLOCK_SETTING, p_buf);
break;
}
case HW_CFG_SET_UART_CLOCK:
UINT16_TO_STREAM(p, HCI_VSC_UPDATE_BAUDRATE);
*p++ = UPDATE_BAUDRATE_CMD_PARAM_SIZE;
*p++ = 0;
*p++ = 0;
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;
xmit_bytes = bt_vendor_cbacks->xmit_cb(HCI_VSC_UPDATE_BAUDRATE, p_buf);
break;
case HW_CFG_SET_UART_BAUD_2:
HILOGI("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 ((xmit_bytes = hw_config_read_bdaddr(p_buf)) > 0)
break;
#else
if ((xmit_bytes = hw_config_set_bdaddr(p_buf)) > 0)
break;
#endif
case HW_CFG_SET_BD_ADDR:
HILOGI("vendor lib fwcfg completed");
bt_vendor_cbacks->dealloc(p_buf);
hw_sco_config();
start_fwcfg_cbtimer();
hw_cfg_cb.state = 0;
if (hw_cfg_cb.fw_fd != -1) {
close(hw_cfg_cb.fw_fd);
hw_cfg_cb.fw_fd = -1;
}
xmit_bytes = 1;
break;
#if (USE_CONTROLLER_BDADDR == TRUE)
case HW_CFG_READ_BD_ADDR:
{
char *p_tmp;
p_tmp = (char *)(p_evt_buf + 1) +
HCI_EVT_CMD_CMPL_LOCAL_BDADDR_ARRAY;
HILOGI("entering HW_CFG_READ_BD_ADDR");
if (memcmp(p_tmp, null_bdaddr, BD_ADDR_LEN) == 0) {
HILOGI("entering HW_CFG_READ_BD_ADDR");
if ((xmit_bytes = hw_config_set_bdaddr(p_buf)) > 0)
break;
} else {
HILOGI("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);
}
}
HILOGI("vendor lib fwcfg completed");
bt_vendor_cbacks->dealloc(p_buf);
hw_sco_config();
start_fwcfg_cbtimer();
hw_cfg_cb.state = 0;
if (hw_cfg_cb.fw_fd != -1) {
close(hw_cfg_cb.fw_fd);
hw_cfg_cb.fw_fd = -1;
}
xmit_bytes = 1;
break;
#endif
}
}
if (xmit_bytes <= 0) {
HILOGE("vendor lib fwcfg aborted!!!");
if (bt_vendor_cbacks) {
if (p_buf != NULL)
bt_vendor_cbacks->dealloc(p_buf);
bt_vendor_cbacks->init_cb(BTC_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 request
**
** Returns None
**
*******************************************************************************/
void hw_lpm_ctrl_cback(void *p_mem)
{
HC_BT_HDR *p_evt_buf = (HC_BT_HDR *)p_mem;
bt_op_result_t status = BTC_OP_RESULT_FAIL;
if (*((uint8_t *)(p_evt_buf + 1) + HCI_EVT_CMD_CMPL_STATUS_RET_BYTE) == 0) {
status = BTC_OP_RESULT_SUCCESS;
HILOGI("%d", status);
}
if (bt_vendor_cbacks) {
}
}
#if (SCO_CFG_INCLUDED == TRUE)
** SCO Configuration Static Functions
*****************************************************************************/
static void hw_sco_i2spcm_proc_interface_param(void)
{
bt_op_result_t status = BTC_OP_RESULT_FAIL;
uint8_t ret = FALSE;
HC_BT_HDR *p_buf = NULL;
if (bt_vendor_cbacks) {
p_buf = (HC_BT_HDR *)bt_vendor_cbacks->alloc(BT_HC_HDR_SIZE
+ HCI_CMD_PREAMBLE_SIZE + SCO_PCM_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 + SCO_PCM_PARAM_SIZE;
uint8_t *p = (uint8_t *)(p_buf + 1);
UINT16_TO_STREAM(p, HCI_VSC_WRITE_SCO_PCM_INT_PARAM);
*p++ = SCO_PCM_PARAM_SIZE;
memcpy_s(p, &bt_sco_param, SCO_PCM_PARAM_SIZE);
if ((ret = bt_vendor_cbacks->xmit_cb(HCI_VSC_WRITE_SCO_PCM_INT_PARAM, p_buf)) == FALSE) {
bt_vendor_cbacks->dealloc(p_buf);
} else
return;
}
status = BTC_OP_RESULT_FAIL;
HILOGI("sco I2S/PCM config interface result %d [0-Success, 1-Fail]", status);
}
static void hw_sco_i2spcm_proc_int_param(void)
{
bt_op_result_t status = BTC_OP_RESULT_FAIL;
uint8_t ret = FALSE;
HC_BT_HDR *p_buf = NULL;
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;
uint8_t *p = (uint8_t *)(p_buf + 1);
UINT16_TO_STREAM(p, HCI_VSC_WRITE_PCM_DATA_FORMAT_PARAM);
*p++ = PCM_DATA_FORMAT_PARAM_SIZE;
memcpy_s(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)) == FALSE) {
bt_vendor_cbacks->dealloc(p_buf);
} else
return;
}
status = BTC_OP_RESULT_FAIL;
HILOGI("sco I2S/PCM config int result %d [0-Success, 1-Fail]", status);
}
**
** Function hw_sco_i2spcm_cfg_cback
**
** Description Callback function for SCO I2S/PCM configuration request
**
** Returns None
**
*******************************************************************************/
static void hw_sco_i2spcm_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;
bt_op_result_t status = BTC_OP_RESULT_FAIL;
p = (uint8_t *)(p_evt_buf + 1) + HCI_EVT_CMD_CMPL_OPCODE;
STREAM_TO_UINT16(opcode, p);
if (*((uint8_t *)(p_evt_buf + 1) + HCI_EVT_CMD_CMPL_STATUS_RET_BYTE) == 0) {
status = BTC_OP_RESULT_SUCCESS;
}
if (bt_vendor_cbacks) {
}
if (status != BTC_OP_RESULT_SUCCESS) {
return;
}
if ((opcode == HCI_VSC_WRITE_I2SPCM_INTERFACE_PARAM) &&
(sco_bus_interface == SCO_INTERFACE_PCM)) {
hw_sco_i2spcm_proc_interface_param();
} else if ((opcode == HCI_VSC_WRITE_SCO_PCM_INT_PARAM) &&
(sco_bus_interface == SCO_INTERFACE_PCM)) {
hw_sco_i2spcm_proc_int_param();
}
}
**
** Function hw_set_MSBC_codec_cback
**
** Description Callback function for setting WBS codec
**
** Returns None
**
*******************************************************************************/
static void hw_set_MSBC_codec_cback(void *p_mem)
{
HILOGI("SCO I2S interface change the sample rate to 16K");
hw_sco_i2spcm_config_from_command(p_mem, SCO_CODEC_MSBC);
}
**
** Function hw_set_CVSD_codec_cback
**
** Description Callback function for setting NBS codec
**
** Returns None
**
*******************************************************************************/
static void hw_set_CVSD_codec_cback(void *p_mem)
{
HILOGI("SCO I2S interface change the sample rate to 8K");
hw_sco_i2spcm_config_from_command(p_mem, SCO_CODEC_CVSD);
}
#endif
** 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;
hw_cfg_cb.state = 0;
hw_cfg_cb.fw_fd = -1;
hw_cfg_cb.f_set_baud_2 = FALSE;
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;
uint8_t *p = (uint8_t *)(p_buf + 1);
UINT16_TO_STREAM(p, HCI_RESET);
*p = 0;
hw_cfg_cb.state = HW_CFG_START;
bt_vendor_cbacks->xmit_cb(HCI_RESET, p_buf);
} else {
if (bt_vendor_cbacks) {
HILOGE("vendor lib fw conf aborted [no buffer]");
bt_vendor_cbacks->init_cb(BTC_OP_RESULT_FAIL);
}
}
}
**
** Function hw_lpm_enable
**
** Description Enalbe/Disable LPM
**
** Returns TRUE/FALSE
**
*******************************************************************************/
uint8_t hw_lpm_enable(uint8_t turn_on)
{
HILOGD("entering hw_lpm_enable11");
HC_BT_HDR *p_buf = NULL;
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;
uint8_t *p = (uint8_t *)(p_buf + 1);
UINT16_TO_STREAM(p, HCI_VSC_WRITE_SLEEP_MODE);
*p++ = LPM_CMD_PARAM_SIZE;
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)) == FALSE) {
bt_vendor_cbacks->dealloc(p_buf);
}
}
if ((ret == 0) && bt_vendor_cbacks) {
}
HILOGD("hw_lpm_enable ret:%d", ret);
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;
* host stack idle threshold (in 300ms/25ms)
*/
timeout_ms = (uint32_t)lpm_param.host_stack_idle_threshold * LPM_IDLE_TIMEOUT_MULTIPLE;
timeout_ms *= BT_VENDOR_LDM_DEFAULT_IDLE;
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)
{
if (sco_bus_interface == SCO_INTERFACE_I2S) {
bt_sco_i2spcm_param[SCO_I2SPCM_PARAM_IF_MODE] = 1;
sco_bus_clock_rate = bt_sco_i2spcm_param[SCO_I2SPCM_PARAM_IF_CLOCK_RATE];
} else {
bt_sco_i2spcm_param[SCO_I2SPCM_PARAM_IF_MODE] = 0;
sco_bus_clock_rate = bt_sco_param[SCO_PCM_PARAM_IF_CLOCK_RATE];
bt_sco_i2spcm_param[SCO_I2SPCM_PARAM_IF_MODE] = bt_sco_param[SCO_PCM_PARAM_IF_CLOCK_MODE];
}
if (sco_bus_wbs_clock_rate == INVALID_SCO_CLOCK_RATE) {
sco_bus_wbs_clock_rate = SCO_I2SPCM_IF_CLOCK_RATE4WBS;
if (sco_bus_wbs_clock_rate < sco_bus_clock_rate)
sco_bus_wbs_clock_rate = sco_bus_clock_rate;
}
* To support I2S/PCM port multiplexing signals for sharing Bluetooth audio
* and FM on the same PCM pins, we defer Bluetooth audio (SCO/eSCO)
* configuration till SCO/eSCO is being established;
* i.e. in hw_set_audio_state() call.
* When configured as I2S only, Bluetooth audio configuration is executed
* immediately with SCO_CODEC_CVSD by default.
*/
if (sco_bus_interface == SCO_INTERFACE_I2S) {
hw_sco_i2spcm_config(SCO_CODEC_CVSD);
} else {
hw_sco_i2spcm_config(SCO_CODEC_NONE);
}
if (bt_vendor_cbacks) {
}
}
static void hw_sco_i2spcm_config_from_command(void *p_mem, uint16_t codec)
{
HC_BT_HDR *p_evt_buf = (HC_BT_HDR *)p_mem;
bool command_success = *((uint8_t *)(p_evt_buf + 1) + HCI_EVT_CMD_CMPL_STATUS_RET_BYTE) == 0;
if (bt_vendor_cbacks) {
}
if (command_success) {
hw_sco_i2spcm_config(codec);
} else if (bt_vendor_cbacks) {
}
}
**
** Function hw_sco_i2spcm_config
**
** Description Configure SCO over I2S or PCM
**
** Returns None
**
*******************************************************************************/
static void hw_sco_i2spcm_config(uint16_t codec)
{
HC_BT_HDR *p_buf = NULL;
uint16_t cmd_u16 = HCI_CMD_PREAMBLE_SIZE + SCO_I2SPCM_PARAM_SIZE;
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;
uint8_t ret;
uint8_t *p = (uint8_t *)(p_buf + 1);
UINT16_TO_STREAM(p, HCI_VSC_WRITE_I2SPCM_INTERFACE_PARAM);
*p++ = SCO_I2SPCM_PARAM_SIZE;
if (codec == SCO_CODEC_CVSD) {
bt_sco_i2spcm_param[SCO_I2SPCM_PARAM_IF_SAMPLE_RATE] = 0;
bt_sco_i2spcm_param[SCO_I2SPCM_PARAM_IF_CLOCK_RATE] =
bt_sco_param[SCO_PCM_PARAM_IF_CLOCK_RATE] = sco_bus_clock_rate;
} else if (codec == SCO_CODEC_MSBC) {
bt_sco_i2spcm_param[SCO_I2SPCM_PARAM_IF_SAMPLE_RATE] = wbs_sample_rate;
bt_sco_i2spcm_param[SCO_I2SPCM_PARAM_IF_CLOCK_RATE] =
bt_sco_param[SCO_PCM_PARAM_IF_CLOCK_RATE] = sco_bus_wbs_clock_rate;
} else {
bt_sco_i2spcm_param[SCO_I2SPCM_PARAM_IF_SAMPLE_RATE] = 0;
bt_sco_i2spcm_param[SCO_I2SPCM_PARAM_IF_CLOCK_RATE] =
bt_sco_param[SCO_PCM_PARAM_IF_CLOCK_RATE] = sco_bus_clock_rate;
HILOGE("wrong codec is use in hw_sco_i2spcm_config, goes default NBS");
}
memcpy_s(p, &bt_sco_i2spcm_param, SCO_I2SPCM_PARAM_SIZE);
cmd_u16 = HCI_VSC_WRITE_I2SPCM_INTERFACE_PARAM;
HILOGI("I2SPCM config {0x%x, 0x%x, 0x%x, 0x%x}",
bt_sco_i2spcm_param[SCO_I2SPCM_PARAM_IF_MODE], bt_sco_i2spcm_param[SCO_I2SPCM_PARAM_IF_ROLE],
bt_sco_i2spcm_param[SCO_I2SPCM_PARAM_IF_SAMPLE_RATE],
bt_sco_i2spcm_param[SCO_I2SPCM_PARAM_IF_CLOCK_RATE]);
if ((ret = bt_vendor_cbacks->xmit_cb(cmd_u16, p_buf)) == FALSE) {
bt_vendor_cbacks->dealloc(p_buf);
} else {
return;
}
}
}
**
** Function hw_set_SCO_codec
**
** Description This functgion sends command to the controller to setup
** WBS/NBS codec for the upcoming eSCO connection.
**
** Returns -1 : Failed to send VSC
** 0 : Success
**
*******************************************************************************/
static int hw_set_SCO_codec(uint16_t codec)
{
HC_BT_HDR *p_buf = NULL;
int ret_val = 0;
return ret_val;
}
**
** Function hw_set_audio_state
**
** Description This function configures audio base on provided audio state
**
** Paramters pointer to audio state structure
**
** Returns 0: ok, -1: error
**
*******************************************************************************/
int hw_set_audio_state(bt_vendor_op_audio_state_t *p_state)
{
int ret_val = -1;
if (!bt_vendor_cbacks) {
return ret_val;
}
ret_val = hw_set_SCO_codec(p_state->peer_codec);
return ret_val;
}
#endif
**
** 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)
{
if (strcpy_s(fw_patchfile_path, sizeof(fw_patchfile_path), p_conf_value) != 0) {
return -1;
}
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)
{
if (strcpy_s(fw_patchfile_name, sizeof(fw_patchfile_name), p_conf_value) != 0) {
return -1;
}
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
** Sample Codes Section
*****************************************************************************/
#if (HW_END_WITH_HCI_RESET == TRUE)
**
** Function hw_epilog_cback
**
** Description Callback function for Command Complete Events from HCI
** commands sent in epilog process.
**
** Returns None
**
*******************************************************************************/
void hw_epilog_cback(void *p_mem)
{
HC_BT_HDR *p_evt_buf = (HC_BT_HDR *)p_mem;
uint8_t *p, status;
uint16_t opcode;
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);
BTHWDBG("%s Opcode:0x%04X Status: %d", __FUNCTION__, opcode, status);
if (bt_vendor_cbacks) {
to notify caller */
}
}
**
** Function hw_epilog_process
**
** Description Sample implementation of epilog process
**
** Returns None
**
*******************************************************************************/
void hw_epilog_process(void)
{
HC_BT_HDR *p_buf = NULL;
BTHWDBG("hw_epilog_process");
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;
uint8_t *p = (uint8_t *)(p_buf + 1);
UINT16_TO_STREAM(p, HCI_RESET);
*p = 0;
bt_vendor_cbacks->xmit_cb(HCI_RESET, p_buf);
} else {
if (bt_vendor_cbacks) {
HILOGE("vendor lib epilog process aborted [no buffer]");
}
}
}
#endif
void hw_process_event(HC_BT_HDR *p_buf)
{
uint16_t opcode;
uint8_t *p = (uint8_t *)(p_buf + 1) + HCI_EVT_CMD_CMPL_OPCODE;
STREAM_TO_UINT16(opcode, p);
HILOGI("%s, opcode:0x%04x", __FUNCTION__, opcode);
switch (opcode) {
case HCI_VSC_WRITE_BD_ADDR:
#if (USE_CONTROLLER_BDADDR == TRUE)
case HCI_READ_LOCAL_BDADDR:
#endif
case HCI_READ_LOCAL_NAME:
case HCI_VSC_DOWNLOAD_MINIDRV:
case HCI_VSC_WRITE_FIRMWARE:
case HCI_VSC_LAUNCH_RAM:
case HCI_RESET:
case HCI_VSC_WRITE_UART_CLOCK_SETTING:
case HCI_VSC_UPDATE_BAUDRATE:
hw_config_cback(p_buf);
break;
case HCI_VSC_WRITE_SCO_PCM_INT_PARAM:
case HCI_VSC_WRITE_PCM_DATA_FORMAT_PARAM:
case HCI_VSC_WRITE_I2SPCM_INTERFACE_PARAM:
hw_sco_i2spcm_cfg_cback(p_buf);
break;
case HCI_VSC_WRITE_SLEEP_MODE:
hw_lpm_ctrl_cback(p_buf);
break;
case HCI_VSC_ENABLE_WBS:
break;
}
HILOGI("%s, Complete", __FUNCTION__);
}