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