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) \ 48 do { \ 49 u16 = ((uint16_t)(*(p)) + (((uint16_t)(*((p) + 1))) << 8)); \ 50 (p) += 2; \ 51 } while (0) 52 53 #define UINT16_TO_STREAM(p, u16) \ 54 do { \ 55 *(p)++ = (uint8_t)(u16); \ 56 *(p)++ = (uint8_t)((u16) >> 8); \ 57 } while (0) 58 59 struct bt_evt_param_t { 60 uint16_t cmd; 61 uint8_t cmd_ret_param; 62 }; 63 64 /*********************************************************** 65 * Externs 66 *********************************************************** 67 */ 68 extern unsigned char bdaddr[6]; 69 extern bt_vendor_callbacks_t *vnd_cb; 70 71 /*********************************************************** 72 * Local variables 73 *********************************************************** 74 */ 75 static uint8_t write_pcm_settings[WRITE_PCM_SETTINGS_SIZE] = { 76 0x02 77 }; 78 79 static uint8_t write_pcm_sync_settings[WRITE_PCM_SYNC_SETTINGS_SIZE] = { 80 0x03, 81 0x00, 82 0x03 83 }; 84 85 static uint8_t write_pcm_link_settings[WRITE_PCM_LINK_SETTINGS_SIZE] = { 86 0x03, 87 0x00 88 }; 89 90 static uint8_t set_sco_data_path[SET_SCO_DATA_PATH_SIZE] = { 91 0x01 92 }; 93 94 static 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 */ 109 static 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 129 static 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 141 static 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 174 static 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 188 static 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 217 static 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 */ 285 void 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 317 void 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