/****************************************************************************** * * 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); }