1/****************************************************************************** 2 * 3 * Copyright (C) 2012 Marvell International Ltd. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at: 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 * 17 ******************************************************************************/ 18 19#define LOG_TAG "hardware_mrvl" 20 21#include <utils/Log.h> 22#include <stdlib.h> 23#include <stdio.h> 24#include <string.h> 25#include <assert.h> 26 27#include "bt_vendor_lib.h" 28#include "bt_hci_bdroid.h" 29 30#define HCI_CMD_MARVELL_WRITE_PCM_SETTINGS 0xFC07 31#define HCI_CMD_MARVELL_WRITE_PCM_SYNC_SETTINGS 0xFC28 32#define HCI_CMD_MARVELL_WRITE_PCM_LINK_SETTINGS 0xFC29 33#define HCI_CMD_MARVELL_SET_SCO_DATA_PATH 0xFC1D 34#define HCI_CMD_MARVELL_WRITE_BD_ADDRESS 0xFC22 35 36#define WRITE_PCM_SETTINGS_SIZE 1 37#define WRITE_PCM_SYNC_SETTINGS_SIZE 3 38#define WRITE_PCM_LINK_SETTINGS_SIZE 2 39#define SET_SCO_DATA_PATH_SIZE 1 40#define WRITE_BD_ADDRESS_SIZE 8 41 42 43#define HCI_CMD_PREAMBLE_SIZE 3 44 45#define HCI_EVT_CMD_CMPL_OPCODE 3 46 47#define STREAM_TO_UINT16(u16, p) \ 48do { \ 49 u16 = ((uint16_t)(*(p)) + (((uint16_t)(*((p) + 1))) << 8)); \ 50 (p) += 2; \ 51} while (0) 52 53#define UINT16_TO_STREAM(p, u16) \ 54do { \ 55 *(p)++ = (uint8_t)(u16); \ 56 *(p)++ = (uint8_t)((u16) >> 8); \ 57} while (0) 58 59struct bt_evt_param_t { 60 uint16_t cmd; 61 uint8_t cmd_ret_param; 62}; 63 64/*********************************************************** 65 * Externs 66 *********************************************************** 67 */ 68extern unsigned char bdaddr[6]; 69extern bt_vendor_callbacks_t *vnd_cb; 70 71/*********************************************************** 72 * Local variables 73 *********************************************************** 74 */ 75static uint8_t write_pcm_settings[WRITE_PCM_SETTINGS_SIZE] = { 76 0x02 77}; 78 79static uint8_t write_pcm_sync_settings[WRITE_PCM_SYNC_SETTINGS_SIZE] = { 80 0x03, 81 0x00, 82 0x03 83}; 84 85static uint8_t write_pcm_link_settings[WRITE_PCM_LINK_SETTINGS_SIZE] = { 86 0x03, 87 0x00 88}; 89 90static uint8_t set_sco_data_path[SET_SCO_DATA_PATH_SIZE] = { 91 0x01 92}; 93 94static uint8_t write_bd_address[WRITE_BD_ADDRESS_SIZE] = { 95 0xFE, /* Parameter ID */ 96 0x06, /* bd_addr length */ 97 0x00, /* 6th byte of bd_addr */ 98 0x00, /* 5th */ 99 0x00, /* 4th */ 100 0x00, /* 3rd */ 101 0x00, /* 2nd */ 102 0x00 /* 1st */ 103}; 104 105/*********************************************************** 106 * Local functions 107 *********************************************************** 108 */ 109static char *cmd_to_str(uint16_t cmd) 110{ 111 switch (cmd) { 112 case HCI_CMD_MARVELL_WRITE_PCM_SETTINGS: 113 return "write_pcm_settings"; 114 case HCI_CMD_MARVELL_WRITE_PCM_SYNC_SETTINGS: 115 return "write_pcm_sync_settings"; 116 case HCI_CMD_MARVELL_WRITE_PCM_LINK_SETTINGS: 117 return "write_pcm_link_settings"; 118 case HCI_CMD_MARVELL_SET_SCO_DATA_PATH: 119 return "set_sco_data_path"; 120 case HCI_CMD_MARVELL_WRITE_BD_ADDRESS: 121 return "write_bd_address"; 122 default: 123 break; 124 } 125 126 return "unknown command"; 127} 128 129static void populate_bd_addr_params(uint8_t *params, uint8_t *addr) 130{ 131 assert(params && addr); 132 133 *params++ = addr[5]; 134 *params++ = addr[4]; 135 *params++ = addr[3]; 136 *params++ = addr[2]; 137 *params++ = addr[1]; 138 *params = addr[0]; 139} 140 141static HC_BT_HDR *build_cmd_buf(uint16_t cmd, uint8_t pl_len, uint8_t *payload) 142{ 143 HC_BT_HDR *p_buf = NULL; 144 uint16_t cmd_len = HCI_CMD_PREAMBLE_SIZE + pl_len; 145 uint8_t *p; 146 147 assert(vnd_cb && payload); 148 149 p_buf = (HC_BT_HDR *) vnd_cb->alloc(BT_HC_HDR_SIZE + cmd_len); 150 151 if (!p_buf) 152 return NULL; 153 154 p_buf->event = MSG_STACK_TO_HC_HCI_CMD; 155 p_buf->offset = 0; 156 p_buf->layer_specific = 0; 157 p_buf->len = cmd_len; 158 159 p = (uint8_t *) (p_buf + 1); 160 161 /* opcode */ 162 UINT16_TO_STREAM(p, cmd); 163 164 /* length of payload */ 165 *p = pl_len; 166 ++p; 167 168 /* payload */ 169 memcpy(p, payload, pl_len); 170 171 return p_buf; 172} 173 174static void parse_evt_buf(HC_BT_HDR *p_evt_buf, 175 struct bt_evt_param_t *evt_params) 176{ 177 uint8_t *p = (uint8_t *) (p_evt_buf + 1) + HCI_EVT_CMD_CMPL_OPCODE; 178 179 assert(p_evt_buf && evt_params); 180 181 /* opcode */ 182 STREAM_TO_UINT16(evt_params->cmd, p); 183 184 /* command return parameter */ 185 evt_params->cmd_ret_param = *p; 186} 187 188static void hw_mrvl_config_start_cb(void *p_mem) 189{ 190 HC_BT_HDR *p_evt_buf = (HC_BT_HDR *) p_mem; 191 struct bt_evt_param_t evt_params = {0, 0}; 192 193 assert(vnd_cb && p_mem); 194 195 parse_evt_buf(p_evt_buf, &evt_params); 196 197 /* free the buffer */ 198 vnd_cb->dealloc(p_evt_buf); 199 200 switch (evt_params.cmd) { 201 case HCI_CMD_MARVELL_WRITE_BD_ADDRESS: 202 /* fw config succeeds */ 203 ALOGI("FW config succeeds!"); 204 vnd_cb->fwcfg_cb(BT_VND_OP_RESULT_SUCCESS); 205 return; 206 207 default: 208 ALOGE("Received event for unexpected cmd (0x%04hX). Fail.", 209 evt_params.cmd); 210 break; 211 } /* end of switch (evt_params.cmd) */ 212 213 ALOGE("Vendor lib fwcfg aborted"); 214 vnd_cb->fwcfg_cb(BT_VND_OP_RESULT_FAIL); 215} 216 217static void hw_mrvl_sco_config_cb(void *p_mem) 218{ 219 HC_BT_HDR *p_evt_buf = (HC_BT_HDR *) p_mem; 220 struct bt_evt_param_t evt_params = {0, 0}; 221 uint16_t cmd; 222 HC_BT_HDR *p_buf; 223 224 assert(vnd_cb && p_mem); 225 226 parse_evt_buf(p_evt_buf, &evt_params); 227 228 /* free the buffer */ 229 vnd_cb->dealloc(p_evt_buf); 230 231 switch (evt_params.cmd) { 232 case HCI_CMD_MARVELL_WRITE_PCM_SETTINGS: 233 /* Send HCI_CMD_MARVELL_WRITE_PCM_SYNC_SETTINGS */ 234 cmd = HCI_CMD_MARVELL_WRITE_PCM_SYNC_SETTINGS; 235 p_buf = build_cmd_buf(cmd, 236 WRITE_PCM_SYNC_SETTINGS_SIZE, 237 write_pcm_sync_settings); 238 break; 239 240 case HCI_CMD_MARVELL_WRITE_PCM_SYNC_SETTINGS: 241 /* Send HCI_CMD_MARVELL_WRITE_PCM_LINK_SETTINGS */ 242 cmd = HCI_CMD_MARVELL_WRITE_PCM_LINK_SETTINGS; 243 p_buf = build_cmd_buf(cmd, 244 WRITE_PCM_LINK_SETTINGS_SIZE, 245 write_pcm_link_settings); 246 break; 247 248 case HCI_CMD_MARVELL_WRITE_PCM_LINK_SETTINGS: 249 /* Send HCI_CMD_MARVELL_SET_SCO_DATA_PATH */ 250 cmd = HCI_CMD_MARVELL_SET_SCO_DATA_PATH; 251 p_buf = build_cmd_buf(cmd, 252 SET_SCO_DATA_PATH_SIZE, 253 set_sco_data_path); 254 break; 255 256 case HCI_CMD_MARVELL_SET_SCO_DATA_PATH: 257 /* sco config succeeds */ 258 ALOGI("SCO PCM config succeeds!"); 259 vnd_cb->scocfg_cb(BT_VND_OP_RESULT_SUCCESS); 260 return; 261 262 default: 263 ALOGE("Received event for unexpected cmd (0x%04hX). Fail.", 264 evt_params.cmd); 265 p_buf = NULL; 266 break; 267 } /* switch (evt_params.cmd) */ 268 269 if (p_buf) { 270 ALOGI("Sending hci command 0x%04hX (%s)", cmd, cmd_to_str(cmd)); 271 if (vnd_cb->xmit_cb(cmd, p_buf, hw_mrvl_sco_config_cb)) 272 return; 273 else 274 vnd_cb->dealloc(p_buf); 275 } 276 277 ALOGE("Vendor lib scocfg aborted"); 278 vnd_cb->scocfg_cb(BT_VND_OP_RESULT_FAIL); 279} 280 281/*********************************************************** 282 * Global functions 283 *********************************************************** 284 */ 285void hw_mrvl_config_start(void) 286{ 287 HC_BT_HDR *p_buf; 288 uint16_t cmd; 289 290 assert(vnd_cb); 291 292 ALOGI("Start HW config ..."); 293 /* Start with HCI_CMD_MARVELL_WRITE_BD_ADDRESS */ 294 ALOGI("Setting bd addr to %02hhX:%02hhX:%02hhX:%02hhX:%02hhX:%02hhX", 295 bdaddr[0], bdaddr[1], bdaddr[2], 296 bdaddr[3], bdaddr[4], bdaddr[5]); 297 populate_bd_addr_params(write_bd_address + 2, bdaddr); 298 299 cmd = HCI_CMD_MARVELL_WRITE_BD_ADDRESS; 300 p_buf = build_cmd_buf(cmd, 301 WRITE_BD_ADDRESS_SIZE, 302 write_bd_address); 303 304 if (p_buf) { 305 ALOGI("Sending hci command 0x%04hX (%s)", cmd, cmd_to_str(cmd)); 306 if (vnd_cb->xmit_cb(cmd, p_buf, hw_mrvl_config_start_cb)) 307 return; 308 else 309 vnd_cb->dealloc(p_buf); 310 } 311 312 ALOGE("Vendor lib fwcfg aborted"); 313 vnd_cb->fwcfg_cb(BT_VND_OP_RESULT_FAIL); 314} 315 316 317void hw_mrvl_sco_config(void) 318{ 319 HC_BT_HDR *p_buf; 320 uint16_t cmd; 321 322 assert(vnd_cb); 323 324 ALOGI("Start SCO config ..."); 325 /* Start with HCI_CMD_MARVELL_WRITE_PCM_SETTINGS */ 326 cmd = HCI_CMD_MARVELL_WRITE_PCM_SETTINGS; 327 p_buf = build_cmd_buf(cmd, 328 WRITE_PCM_SETTINGS_SIZE, 329 write_pcm_settings); 330 331 if (p_buf) { 332 ALOGI("Sending hci command 0x%04hX (%s)", cmd, cmd_to_str(cmd)); 333 if (vnd_cb->xmit_cb(cmd, p_buf, hw_mrvl_sco_config_cb)) 334 return; 335 else 336 vnd_cb->dealloc(p_buf); 337 } 338 339 ALOGE("Vendor lib scocfg aborted"); 340 vnd_cb->scocfg_cb(BT_VND_OP_RESULT_FAIL); 341} 342