341 lines
8.1 KiB
C
341 lines
8.1 KiB
C
/******************************************************************************
|
|
*
|
|
* 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);
|
|
}
|