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