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  */
cmd_to_str(uint16_t cmd)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 
populate_bd_addr_params(uint8_t * params,uint8_t * addr)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 
build_cmd_buf(uint16_t cmd,uint8_t pl_len,uint8_t * payload)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 
parse_evt_buf(HC_BT_HDR * p_evt_buf,struct bt_evt_param_t * evt_params)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 
hw_mrvl_config_start_cb(void * p_mem)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 
hw_mrvl_sco_config_cb(void * p_mem)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  */
hw_mrvl_config_start(void)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 
hw_mrvl_sco_config(void)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