You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
341 lines
8.1 KiB
341 lines
8.1 KiB
/****************************************************************************** |
|
* |
|
* Copyright (C) 2012 Marvell International Ltd. |
|
* |
|
* 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. |
|
* |
|
******************************************************************************/ |
|
|
|
#define LOG_TAG "hardware_mrvl" |
|
|
|
#include <utils/Log.h> |
|
#include <stdlib.h> |
|
#include <stdio.h> |
|
#include <string.h> |
|
#include <assert.h> |
|
|
|
#include "bt_vendor_lib.h" |
|
#include "bt_hci_bdroid.h" |
|
|
|
#define HCI_CMD_MARVELL_WRITE_PCM_SETTINGS 0xFC07 |
|
#define HCI_CMD_MARVELL_WRITE_PCM_SYNC_SETTINGS 0xFC28 |
|
#define HCI_CMD_MARVELL_WRITE_PCM_LINK_SETTINGS 0xFC29 |
|
#define HCI_CMD_MARVELL_SET_SCO_DATA_PATH 0xFC1D |
|
#define HCI_CMD_MARVELL_WRITE_BD_ADDRESS 0xFC22 |
|
|
|
#define WRITE_PCM_SETTINGS_SIZE 1 |
|
#define WRITE_PCM_SYNC_SETTINGS_SIZE 3 |
|
#define WRITE_PCM_LINK_SETTINGS_SIZE 2 |
|
#define SET_SCO_DATA_PATH_SIZE 1 |
|
#define WRITE_BD_ADDRESS_SIZE 8 |
|
|
|
|
|
#define HCI_CMD_PREAMBLE_SIZE 3 |
|
|
|
#define HCI_EVT_CMD_CMPL_OPCODE 3 |
|
|
|
#define STREAM_TO_UINT16(u16, p) \ |
|
do { \ |
|
u16 = ((uint16_t)(*(p)) + (((uint16_t)(*((p) + 1))) << 8)); \ |
|
(p) += 2; \ |
|
} while (0) |
|
|
|
#define UINT16_TO_STREAM(p, u16) \ |
|
do { \ |
|
*(p)++ = (uint8_t)(u16); \ |
|
*(p)++ = (uint8_t)((u16) >> 8); \ |
|
} while (0) |
|
|
|
struct bt_evt_param_t { |
|
uint16_t cmd; |
|
uint8_t cmd_ret_param; |
|
}; |
|
|
|
/*********************************************************** |
|
* Externs |
|
*********************************************************** |
|
*/ |
|
extern unsigned char bdaddr[6]; |
|
extern bt_vendor_callbacks_t *vnd_cb; |
|
|
|
/*********************************************************** |
|
* Local variables |
|
*********************************************************** |
|
*/ |
|
static uint8_t write_pcm_settings[WRITE_PCM_SETTINGS_SIZE] = { |
|
0x02 |
|
}; |
|
|
|
static uint8_t write_pcm_sync_settings[WRITE_PCM_SYNC_SETTINGS_SIZE] = { |
|
0x03, |
|
0x00, |
|
0x03 |
|
}; |
|
|
|
static uint8_t write_pcm_link_settings[WRITE_PCM_LINK_SETTINGS_SIZE] = { |
|
0x03, |
|
0x00 |
|
}; |
|
|
|
static uint8_t set_sco_data_path[SET_SCO_DATA_PATH_SIZE] = { |
|
0x01 |
|
}; |
|
|
|
static uint8_t write_bd_address[WRITE_BD_ADDRESS_SIZE] = { |
|
0xFE, /* Parameter ID */ |
|
0x06, /* bd_addr length */ |
|
0x00, /* 6th byte of bd_addr */ |
|
0x00, /* 5th */ |
|
0x00, /* 4th */ |
|
0x00, /* 3rd */ |
|
0x00, /* 2nd */ |
|
0x00 /* 1st */ |
|
}; |
|
|
|
/*********************************************************** |
|
* Local functions |
|
*********************************************************** |
|
*/ |
|
static char *cmd_to_str(uint16_t cmd) |
|
{ |
|
switch (cmd) { |
|
case HCI_CMD_MARVELL_WRITE_PCM_SETTINGS: |
|
return "write_pcm_settings"; |
|
case HCI_CMD_MARVELL_WRITE_PCM_SYNC_SETTINGS: |
|
return "write_pcm_sync_settings"; |
|
case HCI_CMD_MARVELL_WRITE_PCM_LINK_SETTINGS: |
|
return "write_pcm_link_settings"; |
|
case HCI_CMD_MARVELL_SET_SCO_DATA_PATH: |
|
return "set_sco_data_path"; |
|
case HCI_CMD_MARVELL_WRITE_BD_ADDRESS: |
|
return "write_bd_address"; |
|
default: |
|
break; |
|
} |
|
|
|
return "unknown command"; |
|
} |
|
|
|
static void populate_bd_addr_params(uint8_t *params, uint8_t *addr) |
|
{ |
|
assert(params && addr); |
|
|
|
*params++ = addr[5]; |
|
*params++ = addr[4]; |
|
*params++ = addr[3]; |
|
*params++ = addr[2]; |
|
*params++ = addr[1]; |
|
*params = addr[0]; |
|
} |
|
|
|
static HC_BT_HDR *build_cmd_buf(uint16_t cmd, uint8_t pl_len, uint8_t *payload) |
|
{ |
|
HC_BT_HDR *p_buf = NULL; |
|
uint16_t cmd_len = HCI_CMD_PREAMBLE_SIZE + pl_len; |
|
uint8_t *p; |
|
|
|
assert(vnd_cb && payload); |
|
|
|
p_buf = (HC_BT_HDR *) vnd_cb->alloc(BT_HC_HDR_SIZE + cmd_len); |
|
|
|
if (!p_buf) |
|
return NULL; |
|
|
|
p_buf->event = MSG_STACK_TO_HC_HCI_CMD; |
|
p_buf->offset = 0; |
|
p_buf->layer_specific = 0; |
|
p_buf->len = cmd_len; |
|
|
|
p = (uint8_t *) (p_buf + 1); |
|
|
|
/* opcode */ |
|
UINT16_TO_STREAM(p, cmd); |
|
|
|
/* length of payload */ |
|
*p = pl_len; |
|
++p; |
|
|
|
/* payload */ |
|
memcpy(p, payload, pl_len); |
|
|
|
return p_buf; |
|
} |
|
|
|
static void parse_evt_buf(HC_BT_HDR *p_evt_buf, |
|
struct bt_evt_param_t *evt_params) |
|
{ |
|
uint8_t *p = (uint8_t *) (p_evt_buf + 1) + HCI_EVT_CMD_CMPL_OPCODE; |
|
|
|
assert(p_evt_buf && evt_params); |
|
|
|
/* opcode */ |
|
STREAM_TO_UINT16(evt_params->cmd, p); |
|
|
|
/* command return parameter */ |
|
evt_params->cmd_ret_param = *p; |
|
} |
|
|
|
static void hw_mrvl_config_start_cb(void *p_mem) |
|
{ |
|
HC_BT_HDR *p_evt_buf = (HC_BT_HDR *) p_mem; |
|
struct bt_evt_param_t evt_params = {0, 0}; |
|
|
|
assert(vnd_cb && p_mem); |
|
|
|
parse_evt_buf(p_evt_buf, &evt_params); |
|
|
|
/* free the buffer */ |
|
vnd_cb->dealloc(p_evt_buf); |
|
|
|
switch (evt_params.cmd) { |
|
case HCI_CMD_MARVELL_WRITE_BD_ADDRESS: |
|
/* fw config succeeds */ |
|
ALOGI("FW config succeeds!"); |
|
vnd_cb->fwcfg_cb(BT_VND_OP_RESULT_SUCCESS); |
|
return; |
|
|
|
default: |
|
ALOGE("Received event for unexpected cmd (0x%04hX). Fail.", |
|
evt_params.cmd); |
|
break; |
|
} /* end of switch (evt_params.cmd) */ |
|
|
|
ALOGE("Vendor lib fwcfg aborted"); |
|
vnd_cb->fwcfg_cb(BT_VND_OP_RESULT_FAIL); |
|
} |
|
|
|
static void hw_mrvl_sco_config_cb(void *p_mem) |
|
{ |
|
HC_BT_HDR *p_evt_buf = (HC_BT_HDR *) p_mem; |
|
struct bt_evt_param_t evt_params = {0, 0}; |
|
uint16_t cmd; |
|
HC_BT_HDR *p_buf; |
|
|
|
assert(vnd_cb && p_mem); |
|
|
|
parse_evt_buf(p_evt_buf, &evt_params); |
|
|
|
/* free the buffer */ |
|
vnd_cb->dealloc(p_evt_buf); |
|
|
|
switch (evt_params.cmd) { |
|
case HCI_CMD_MARVELL_WRITE_PCM_SETTINGS: |
|
/* Send HCI_CMD_MARVELL_WRITE_PCM_SYNC_SETTINGS */ |
|
cmd = HCI_CMD_MARVELL_WRITE_PCM_SYNC_SETTINGS; |
|
p_buf = build_cmd_buf(cmd, |
|
WRITE_PCM_SYNC_SETTINGS_SIZE, |
|
write_pcm_sync_settings); |
|
break; |
|
|
|
case HCI_CMD_MARVELL_WRITE_PCM_SYNC_SETTINGS: |
|
/* Send HCI_CMD_MARVELL_WRITE_PCM_LINK_SETTINGS */ |
|
cmd = HCI_CMD_MARVELL_WRITE_PCM_LINK_SETTINGS; |
|
p_buf = build_cmd_buf(cmd, |
|
WRITE_PCM_LINK_SETTINGS_SIZE, |
|
write_pcm_link_settings); |
|
break; |
|
|
|
case HCI_CMD_MARVELL_WRITE_PCM_LINK_SETTINGS: |
|
/* Send HCI_CMD_MARVELL_SET_SCO_DATA_PATH */ |
|
cmd = HCI_CMD_MARVELL_SET_SCO_DATA_PATH; |
|
p_buf = build_cmd_buf(cmd, |
|
SET_SCO_DATA_PATH_SIZE, |
|
set_sco_data_path); |
|
break; |
|
|
|
case HCI_CMD_MARVELL_SET_SCO_DATA_PATH: |
|
/* sco config succeeds */ |
|
ALOGI("SCO PCM config succeeds!"); |
|
vnd_cb->scocfg_cb(BT_VND_OP_RESULT_SUCCESS); |
|
return; |
|
|
|
default: |
|
ALOGE("Received event for unexpected cmd (0x%04hX). Fail.", |
|
evt_params.cmd); |
|
p_buf = NULL; |
|
break; |
|
} /* switch (evt_params.cmd) */ |
|
|
|
if (p_buf) { |
|
ALOGI("Sending hci command 0x%04hX (%s)", cmd, cmd_to_str(cmd)); |
|
if (vnd_cb->xmit_cb(cmd, p_buf, hw_mrvl_sco_config_cb)) |
|
return; |
|
else |
|
vnd_cb->dealloc(p_buf); |
|
} |
|
|
|
ALOGE("Vendor lib scocfg aborted"); |
|
vnd_cb->scocfg_cb(BT_VND_OP_RESULT_FAIL); |
|
} |
|
|
|
/*********************************************************** |
|
* Global functions |
|
*********************************************************** |
|
*/ |
|
void hw_mrvl_config_start(void) |
|
{ |
|
HC_BT_HDR *p_buf; |
|
uint16_t cmd; |
|
|
|
assert(vnd_cb); |
|
|
|
ALOGI("Start HW config ..."); |
|
/* Start with HCI_CMD_MARVELL_WRITE_BD_ADDRESS */ |
|
ALOGI("Setting bd addr to %02hhX:%02hhX:%02hhX:%02hhX:%02hhX:%02hhX", |
|
bdaddr[0], bdaddr[1], bdaddr[2], |
|
bdaddr[3], bdaddr[4], bdaddr[5]); |
|
populate_bd_addr_params(write_bd_address + 2, bdaddr); |
|
|
|
cmd = HCI_CMD_MARVELL_WRITE_BD_ADDRESS; |
|
p_buf = build_cmd_buf(cmd, |
|
WRITE_BD_ADDRESS_SIZE, |
|
write_bd_address); |
|
|
|
if (p_buf) { |
|
ALOGI("Sending hci command 0x%04hX (%s)", cmd, cmd_to_str(cmd)); |
|
if (vnd_cb->xmit_cb(cmd, p_buf, hw_mrvl_config_start_cb)) |
|
return; |
|
else |
|
vnd_cb->dealloc(p_buf); |
|
} |
|
|
|
ALOGE("Vendor lib fwcfg aborted"); |
|
vnd_cb->fwcfg_cb(BT_VND_OP_RESULT_FAIL); |
|
} |
|
|
|
|
|
void hw_mrvl_sco_config(void) |
|
{ |
|
HC_BT_HDR *p_buf; |
|
uint16_t cmd; |
|
|
|
assert(vnd_cb); |
|
|
|
ALOGI("Start SCO config ..."); |
|
/* Start with HCI_CMD_MARVELL_WRITE_PCM_SETTINGS */ |
|
cmd = HCI_CMD_MARVELL_WRITE_PCM_SETTINGS; |
|
p_buf = build_cmd_buf(cmd, |
|
WRITE_PCM_SETTINGS_SIZE, |
|
write_pcm_settings); |
|
|
|
if (p_buf) { |
|
ALOGI("Sending hci command 0x%04hX (%s)", cmd, cmd_to_str(cmd)); |
|
if (vnd_cb->xmit_cb(cmd, p_buf, hw_mrvl_sco_config_cb)) |
|
return; |
|
else |
|
vnd_cb->dealloc(p_buf); |
|
} |
|
|
|
ALOGE("Vendor lib scocfg aborted"); |
|
vnd_cb->scocfg_cb(BT_VND_OP_RESULT_FAIL); |
|
}
|
|
|