1 /*******************************************************************************
2 * \file cybt_prm.c
3 *
4 * \brief
5 * Provides utilities for BT firmware download.
6 *
7 ********************************************************************************
8 * \copyright
9 * Copyright 2018-2019 Cypress Semiconductor Corporation
10 * SPDX-License-Identifier: Apache-2.0
11 *
12 * Licensed under the Apache License, Version 2.0 (the "License");
13 * you may not use this file except in compliance with the License.
14 * You may obtain a copy of the License at
15 *
16 * http://www.apache.org/licenses/LICENSE-2.0
17 *
18 * Unless required by applicable law or agreed to in writing, software
19 * distributed under the License is distributed on an "AS IS" BASIS,
20 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21 * See the License for the specific language governing permissions and
22 * limitations under the License.
23 *******************************************************************************/
24
25 #include <string.h>
26 #include <stdbool.h>
27
28 #include "wiced_bt_dev.h"
29 #include "cyabs_rtos.h"
30
31 #include "cybt_prm.h"
32 #include "cybt_platform_trace.h"
33
34
35 /*****************************************************************************
36 * Constants
37 *****************************************************************************/
38 #define HCI_VSC_DOWNLOAD_MINI_DRV (0xFC2E)
39 #define HCI_VSC_WRITE_RAM (0xFC4C)
40 #define HCI_VSC_LAUNCH_RAM (0xFC4E)
41
42 /* dest ram location */
43 #define CYBT_DEST_RAM_LOCATION (0x00085D00)
44 #define CYBT_END_DELAY (250) /* delay before sending any new command (ms) */
45
46 #define CYBT_MAX_PRM_LEN (250)
47
48
49 /*****************************************************************************
50 * Type Definitions
51 *****************************************************************************/
52 enum
53 {
54 CYBT_PRM_ST_IDLE = 0,
55 CYBT_PRM_ST_INITIALIZING,
56 CYBT_PRM_ST_INITIALIZING_DONE,
57 CYBT_PRM_ST_LOADING_DATA,
58 CYBT_PRM_ST_LOADING_DATA_DONE,
59 CYBT_PRM_ST_LAUNCHING_RAM
60 };
61 typedef uint8_t cybt_prm_state_t;
62
63 /* Define a structure for CYBT PRM Control Block
64 */
65 typedef struct
66 {
67 cybt_prm_state_t state; /* download state */
68 uint8_t internal_patch; /* internal patch or not */
69 uint16_t opcode; /* current opcode being used for the download */
70 uint32_t patch_length; /* total patch length need to download*/
71 const uint8_t* p_patch_data; /* pointer to orginal patch data */
72 uint32_t tx_patch_length; /* patch length send to controller */
73 cybt_prm_cback_t *p_cb; /* call back for patch ram status */
74 uint32_t dest_ram;
75 uint8_t format_type; /* patch format bin/hcd/... */
76 } cybt_prm_cb_t;
77
78
79 /******************************************************************************
80 * Variables Definitions
81 ******************************************************************************/
82 /* patch ram control block */
83 static cybt_prm_cb_t cybt_prm_cb;
84
85
86 /******************************************************************************
87 * Function Declarations
88 ******************************************************************************/
89 /* Vendor specified command complete callback */
90 static void cybt_prm_command_complete_cback(wiced_bt_dev_vendor_specific_command_complete_params_t *p_param);
91
92 /* Send next patch of data */
93 static void cybt_prm_send_next_patch(void);
94
95 /* Launching RAM */
96 static void cybt_prm_launch_ram_internal(void);
97
98
99 /******************************************************************************
100 * Function Definitions
101 ******************************************************************************/
102
103 /*******************************************************************************
104 **
105 ** Function cybt_prm_download
106 **
107 ** Description Register patch ram callback, and start the patch ram
108 ** download process.
109 ** Input Param p_cb - callback for download status
110 ** p_patch_buf - address of patch ram buffer
111 ** patch_buf_len - length of patch ram buffer
112 ** address - address of patch ram to be written,
113 ** format_type - patch format type ( bin, hcd ...)
114 ** download_mini_drv - enable to download minidriver
115 **
116 ** Returns true if successful, otherwise false
117 **
118 *******************************************************************************/
cybt_prm_download(cybt_prm_cback_t * p_cb,const uint8_t * p_patch_buf,uint32_t patch_buf_len,uint32_t address,uint8_t format_type,bool download_mini_drv)119 bool cybt_prm_download (cybt_prm_cback_t *p_cb,
120 const uint8_t *p_patch_buf,
121 uint32_t patch_buf_len,
122 uint32_t address,
123 uint8_t format_type,
124 bool download_mini_drv
125 )
126 {
127 PRM_TRACE_DEBUG("cybt_prm_init()");
128
129 memset(&cybt_prm_cb, 0, sizeof(cybt_prm_cb_t));
130
131 cybt_prm_cb.opcode = HCI_VSC_DOWNLOAD_MINI_DRV;
132
133 if (p_patch_buf)
134 {
135 cybt_prm_cb.p_patch_data = p_patch_buf;
136 cybt_prm_cb.patch_length = patch_buf_len;
137 if (cybt_prm_cb.patch_length == 0)
138 {
139 return false;
140 }
141
142 cybt_prm_cb.internal_patch = TRUE;
143 }
144 else
145 {
146 cybt_prm_cb.internal_patch = FALSE;
147 }
148 cybt_prm_cb.format_type = format_type;
149 cybt_prm_cb.p_cb = p_cb;
150
151 if( address )
152 {
153 cybt_prm_cb.dest_ram = address;
154 }
155 else
156 {
157 cybt_prm_cb.dest_ram = CYBT_DEST_RAM_LOCATION;
158 }
159
160 cybt_prm_cb.state = CYBT_PRM_ST_INITIALIZING;
161 if(true == download_mini_drv)
162 {
163 wiced_bt_dev_vendor_specific_command(cybt_prm_cb.opcode,
164 0,
165 NULL,
166 cybt_prm_command_complete_cback
167 );
168 }
169 else
170 {
171 uint8_t p_param_buf = 0;
172 wiced_bt_dev_vendor_specific_command_complete_params_t dummy_resp = {
173 .opcode = HCI_VSC_DOWNLOAD_MINI_DRV,
174 .p_param_buf = &p_param_buf
175 };
176 cybt_prm_command_complete_cback(&dummy_resp);
177 }
178
179 return true;
180 }
181
182 /*******************************************************************************
183 **
184 ** Function cybt_prm_command_complete_cback
185 **
186 ** Description This is the vendor specific comand complete event call back.
187 **
188 ** Returns void
189 **
190 *******************************************************************************/
cybt_prm_command_complete_cback(wiced_bt_dev_vendor_specific_command_complete_params_t * p_param)191 static void cybt_prm_command_complete_cback(wiced_bt_dev_vendor_specific_command_complete_params_t *p_param)
192 {
193 /* if it is not completed */
194 if (p_param->p_param_buf[0] != 0)
195 {
196 if (cybt_prm_cb.p_cb)
197 (cybt_prm_cb.p_cb)(CYBT_PRM_STS_ABORT);
198 return;
199 }
200
201 switch (cybt_prm_cb.state)
202 {
203 case CYBT_PRM_ST_INITIALIZING:
204 if (!(p_param->opcode == cybt_prm_cb.opcode))
205 {
206 if (cybt_prm_cb.p_cb)
207 (cybt_prm_cb.p_cb)(CYBT_PRM_STS_ABORT);
208 return;
209 }
210 cybt_prm_cb.state = CYBT_PRM_ST_INITIALIZING_DONE;
211 PRM_TRACE_DEBUG("CYBT_PRM_ST_INITIALIZING_DONE");
212
213 if (!cybt_prm_cb.internal_patch)
214 {
215 if (cybt_prm_cb.p_cb)
216 (cybt_prm_cb.p_cb)(CYBT_PRM_STS_CONTINUE);
217 }
218 else
219 {
220 cybt_prm_cb.state = CYBT_PRM_ST_LOADING_DATA;
221 cybt_prm_send_next_patch();
222 }
223 break;
224 case CYBT_PRM_ST_LOADING_DATA:
225 if (!(p_param->opcode == HCI_VSC_WRITE_RAM))
226 {
227 if (cybt_prm_cb.p_cb)
228 {
229 (cybt_prm_cb.p_cb)(CYBT_PRM_STS_ABORT);
230 }
231 return;
232 }
233 if (cybt_prm_cb.tx_patch_length >= cybt_prm_cb.patch_length)
234 {
235 cybt_prm_cb.state = CYBT_PRM_ST_LOADING_DATA_DONE;
236 /* For internal patch, if all patches are sent */
237 if (cybt_prm_cb.internal_patch)
238 {
239 PRM_TRACE_DEBUG("Internal patch download completed, starting launching ram");
240 cybt_prm_cb.state = CYBT_PRM_ST_LAUNCHING_RAM;
241 cybt_prm_launch_ram_internal();
242 }
243 /* For external patch, send contiune to application back */
244 else
245 {
246 PRM_TRACE_DEBUG("External patch piece down, wait for next piece");
247 cybt_prm_cb.dest_ram += cybt_prm_cb.patch_length;
248 if (cybt_prm_cb.p_cb)
249 (cybt_prm_cb.p_cb)(CYBT_PRM_STS_CONTINUE);
250 }
251 }
252 else
253 {
254 /* Have not complete this piece yet */
255 cybt_prm_send_next_patch();
256 }
257 break;
258 case CYBT_PRM_ST_LAUNCHING_RAM:
259 if (!(p_param->opcode == HCI_VSC_LAUNCH_RAM))
260 {
261 if (cybt_prm_cb.p_cb)
262 (cybt_prm_cb.p_cb)(CYBT_PRM_STS_ABORT);
263 return;
264 }
265
266 PRM_TRACE_DEBUG("Launch RAM successful");
267
268 if (cybt_prm_cb.p_cb)
269 (cybt_prm_cb.p_cb)(CYBT_PRM_STS_COMPLETE);
270
271 memset(&cybt_prm_cb, 0, sizeof(cybt_prm_cb_t));
272
273 break;
274 default:
275 break;
276 }
277 }
278
279 /*******************************************************************************
280 **
281 ** Function cybt_prm_send_next_patch
282 **
283 ** Description Send next patch of data: 250 bytes max
284 **
285 ** Returns void
286 **
287 *******************************************************************************/
cybt_prm_send_next_patch(void)288 static void cybt_prm_send_next_patch(void)
289 {
290 uint8_t write_ram_vsc[260], *p;
291 uint32_t len, offset = cybt_prm_cb.tx_patch_length;
292 uint16_t vsc_command;
293
294 if (cybt_prm_cb.format_type == CYBT_PRM_FORMAT_HCD)
295 {
296 p = (uint8_t *)(cybt_prm_cb.p_patch_data + offset);
297
298 STREAM_TO_UINT16 (vsc_command, p);
299 STREAM_TO_UINT8 (len, p);
300
301 wiced_bt_dev_vendor_specific_command(vsc_command,
302 (uint8_t)len,
303 p,
304 cybt_prm_command_complete_cback
305 );
306
307 /* including 3 bytes header */
308 cybt_prm_cb.tx_patch_length += (len + 3);
309
310 if ( vsc_command == HCI_VSC_LAUNCH_RAM )
311 {
312 cybt_prm_cb.state = CYBT_PRM_ST_LAUNCHING_RAM;
313 }
314 }
315 else
316 {
317 len = (cybt_prm_cb.patch_length - cybt_prm_cb.tx_patch_length);
318
319 if (len > CYBT_MAX_PRM_LEN)
320 len = CYBT_MAX_PRM_LEN;
321
322 /* Address in little endian order */
323 write_ram_vsc[0] = (cybt_prm_cb.dest_ram + offset) & 0xFF;
324 write_ram_vsc[1] = ((cybt_prm_cb.dest_ram + offset) >> 8) & 0xFF;
325 write_ram_vsc[2] = ((cybt_prm_cb.dest_ram + offset) >> 16) & 0xFF;
326 write_ram_vsc[3] = ((cybt_prm_cb.dest_ram + offset) >> 24) & 0xFF;
327
328 memcpy(write_ram_vsc + 4, cybt_prm_cb.p_patch_data + offset, len);
329
330 wiced_bt_dev_vendor_specific_command (HCI_VSC_WRITE_RAM,
331 (uint8_t) (len+4),
332 write_ram_vsc,
333 cybt_prm_command_complete_cback
334 );
335
336 cybt_prm_cb.tx_patch_length += len;
337 }
338 }
339
340
341 /*******************************************************************************
342 **
343 ** Function cybt_prm_load_data
344 **
345 ** Description Download data to controller if application need to provide patch data.
346 **
347 ** Input Param p_patch_data - pointer to patch data
348 ** patch_data_len - patch data len
349 **
350 ** Returns TRUE if successful, otherwise FALSE
351 **
352 *******************************************************************************/
cybt_prm_load_data(uint8_t * p_patch_data,uint16_t patch_data_len)353 bool cybt_prm_load_data (uint8_t *p_patch_data,
354 uint16_t patch_data_len
355 )
356 {
357 /* Only load data if loading minidri or data piece is done */
358 if (cybt_prm_cb.state != CYBT_PRM_ST_INITIALIZING_DONE
359 && cybt_prm_cb.state != CYBT_PRM_ST_LOADING_DATA_DONE
360 )
361 {
362 return false;
363 }
364
365 if (patch_data_len == 0)
366 {
367 return false;
368 }
369
370 cybt_prm_cb.tx_patch_length = 0;
371 cybt_prm_cb.p_patch_data = p_patch_data;
372 cybt_prm_cb.patch_length = patch_data_len;
373
374 cybt_prm_cb.state = CYBT_PRM_ST_LOADING_DATA;
375 /* state could be changed inside cybt_prm_send_next_patch() if file is HCD format */
376 cybt_prm_send_next_patch();
377
378 PRM_TRACE_DEBUG("cybt_prm_load_data(): send next patch");
379
380 return true;
381 }
382
383 /*******************************************************************************
384 **
385 ** Function cybt_prm_launch_ram
386 **
387 ** Description After all patches are download, lauch ram
388 ** Input Param
389 **
390 **
391 ** Returns TRUE if successful, otherwise FALSE
392 **
393 *******************************************************************************/
cybt_prm_launch_ram(void)394 bool cybt_prm_launch_ram(void)
395 {
396 /* Only launch ram if data piece is done */
397 if (cybt_prm_cb.state != CYBT_PRM_ST_LOADING_DATA_DONE)
398 {
399 return false;
400 }
401
402 PRM_TRACE_DEBUG("Externl patch download completed, starting launching ram");
403
404 cybt_prm_cb.state = CYBT_PRM_ST_LAUNCHING_RAM;
405 cybt_prm_launch_ram_internal();
406
407 return true;
408 }
409
410 /*******************************************************************************
411 **
412 ** Function cybt_prm_launch_ram
413 **
414 ** Description Launch RAM
415 **
416 ** Returns void
417 **
418 *******************************************************************************/
cybt_prm_launch_ram_internal(void)419 static void cybt_prm_launch_ram_internal(void)
420 {
421 uint8_t write_launch_ram_vsc[4] = {0xFF, 0xFF, 0xFF, 0xFF};
422
423 wiced_bt_dev_vendor_specific_command(HCI_VSC_LAUNCH_RAM,
424 4,
425 write_launch_ram_vsc,
426 cybt_prm_command_complete_cback
427 );
428 }
429
430
431 /*******************************************************************************
432 **
433 ** Function cybt_prm_get_state
434 **
435 ** Description get patch ram state info
436 ** Input Param
437 **
438 **
439 ** Returns patch ram state
440 **
441 *******************************************************************************/
cybt_prm_get_state(void)442 uint8_t cybt_prm_get_state(void)
443 {
444 return (cybt_prm_cb.state);
445 }
446
447