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