Home | History | Annotate | Download | only in libbt-vendor
      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