xref: /btstack/chipset/nxp/btstack_chipset_nxp.c (revision f2b175aa135facd4888d1226dd2f99fb09ded54d)
1 /*
2  * Copyright (C) 2023 BlueKitchen GmbH
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the copyright holders nor the names of
14  *    contributors may be used to endorse or promote products derived
15  *    from this software without specific prior written permission.
16  * 4. Any redistribution, use, or modification is done solely for
17  *    personal benefit and not for any commercial purpose or for
18  *    monetary gain.
19  *
20  * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN
24  * GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
27  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
30  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  * Please inquire about commercial licensing options at
34  * [email protected]
35  *
36  */
37 
38 #define BTSTACK_FILE__ "btstack_chipset_nxp.c"
39 
40 #include "btstack_chipset_nxp.h"
41 #include "btstack_debug.h"
42 #include "btstack_event.h"
43 
44 #include <stdio.h>
45 
46 #ifdef _MSC_VER
47 // ignore deprecated warning for fopen
48 #pragma warning(disable : 4996)
49 #endif
50 
51 // Firmware download protocol constants
52 #define NXP_V1_FW_REQ_PKT	0xa5
53 #define NXP_V1_CHIP_VER_PKT	0xaa
54 #define NXP_V3_FW_REQ_PKT	0xa7
55 #define NXP_V3_CHIP_VER_PKT	0xab
56 
57 #define NXP_ACK_V1		0x5a
58 #define NXP_NAK_V1		0xbf
59 #define NXP_ACK_V3		0x7a
60 #define NXP_NAK_V3		0x7b
61 #define NXP_CRC_ERROR_V3	0x7c
62 
63 // chip ids
64 #define NXP_CHIP_ID_W9098		0x5c03
65 #define NXP_CHIP_ID_IW416		0x7201
66 #define NXP_CHIP_ID_IW612		0x7601
67 
68 // firmwares
69 #define NXP_FIRMWARE_W9098	"uartuart9098_bt_v1.bin"
70 #define NXP_FIRMWARE_IW416	"uartiw416_bt_v0.bin"
71 #define NXP_FIRMWARE_IW612	"uartspi_n61x_v1.bin.se"
72 
73 #define NXP_MAX_RESEND_COUNT 5
74 
75 // prototypes
76 static void nxp_w4_fw_req(void);
77 static void nxp_done(void);
78 static void nxp_prepare_chunk(void);
79 static void nxp_send_chunk(btstack_timer_source_t * ts);
80 static void nxp_done_with_status(uint8_t status);
81 
82 // globals
83 static void (*nxp_download_complete)(uint8_t status);
84 static const btstack_uart_t * nxp_uart_driver;
85 static btstack_timer_source_t nxp_timer;
86 static bool                   nxp_have_firmware;
87 
88 static uint16_t nxp_chip_id;
89 
90 static const uint8_t * nxp_fw_data;
91 static uint32_t        nxp_fw_size;
92 static uint32_t        nxp_fw_offset;
93 
94 static uint8_t       nxp_response_buffer[5];
95 static const uint8_t nxp_ack_buffer_v1[] = {NXP_ACK_V1 };
96 static const uint8_t nxp_ack_buffer_v3[] = {NXP_ACK_V3, 0x92 };
97 static uint16_t      nxp_fw_request_len;
98 static uint8_t       nxp_fw_resend_count;
99 static uint8_t       nxp_output_buffer[2048 + 1];
100 
101 #ifdef HAVE_POSIX_FILE_IO
102 static char   nxp_firmware_path[1000];
103 static FILE * nxp_firmware_file;
104 
105 static char *nxp_fw_name_from_chipid(uint16_t chip_id)
106 {
107     switch (chip_id) {
108         case NXP_CHIP_ID_W9098:
109             return NXP_FIRMWARE_W9098;
110             break;
111         case NXP_CHIP_ID_IW416:
112             return NXP_FIRMWARE_IW416;
113         case NXP_CHIP_ID_IW612:
114             return NXP_FIRMWARE_IW612;
115         default:
116             log_error("Unknown chip id 0x%04x", chip_id);
117             return NULL;
118     }
119 }
120 
121 static void nxp_load_firmware(void) {
122     if (nxp_firmware_file == NULL){
123         log_info("chipset-bcm: open file %s", nxp_firmware_path);
124         nxp_firmware_file = fopen(nxp_firmware_path, "rb");
125         if (nxp_firmware_file != NULL){
126             nxp_have_firmware = true;
127         }
128     }
129 
130 }
131 static uint16_t nxp_read_firmware(uint16_t bytes_to_read, uint8_t * buffer) {
132     size_t bytes_read = fread(buffer, 1, bytes_to_read, nxp_firmware_file);
133     return bytes_read;
134 }
135 
136 static void nxp_unload_firmware(void) {
137     btstack_assert(nxp_firmware_file != NULL);
138     fclose(nxp_firmware_file);
139     nxp_firmware_file = NULL;
140 }
141 #else
142 void nxp_load_firmware(void){
143     nxp_have_firmware = true;
144 }
145 
146 // read bytes from firmware file
147 static uint16_t nxp_read_firmware(uint16_t bytes_to_read, uint8_t * buffer){
148     if (nxp_fw_request_len > nxp_fw_size - nxp_fw_offset){
149         printf("fw_request_len %u > remaining file len %u\n", nxp_fw_request_len, nxp_fw_size - nxp_fw_offset);
150         return nxp_fw_size - nxp_fw_offset;
151     }
152     memcpy(buffer, &nxp_fw_data[nxp_fw_offset], bytes_to_read);
153     nxp_fw_offset += nxp_fw_request_len;
154     return bytes_to_read;
155 }
156 static void nxp_unload_firmware(void) {
157 }
158 #endif
159 
160 // first two uint16_t should xor to 0xffff
161 static bool nxp_valid_packet(void){
162     switch (nxp_response_buffer[0]){
163         case NXP_V1_FW_REQ_PKT:
164         case NXP_V1_CHIP_VER_PKT:
165             return ((nxp_response_buffer[1] ^ nxp_response_buffer[3]) == 0xff) && ((nxp_response_buffer[2] ^ nxp_response_buffer [4]) == 0xff);
166         case NXP_V3_FW_REQ_PKT:
167         case NXP_V3_CHIP_VER_PKT:
168             // TODO: check crc-7
169             return true;
170         default:
171             return false;
172     }
173 }
174 
175 static void nxp_start(){
176     // start to read
177     nxp_fw_resend_count = 0;
178     nxp_fw_offset = 0;
179     nxp_have_firmware = false;
180     nxp_uart_driver->set_block_received(&nxp_w4_fw_req);
181     nxp_uart_driver->receive_block(nxp_response_buffer, 5);
182     log_info("nxp_start: wait for 0x%02x", NXP_V1_FW_REQ_PKT);
183 }
184 
185 static void nxp_send_ack_v1(void (*done_handler)(void)){
186     nxp_uart_driver->set_block_sent(done_handler);
187     nxp_uart_driver->send_block(nxp_ack_buffer_v1, sizeof(nxp_ack_buffer_v1));
188 }
189 
190 static void nxp_send_ack_v3(void (*done_handler)(void)){
191     nxp_uart_driver->set_block_sent(done_handler);
192     nxp_uart_driver->send_block(nxp_ack_buffer_v3, sizeof(nxp_ack_buffer_v3));
193 }
194 
195 static void nxp_prepare_chunk(void){
196     // delay chunk send by 5 ms
197     btstack_run_loop_set_timer_handler(&nxp_timer, nxp_send_chunk);
198     btstack_run_loop_set_timer(&nxp_timer, 5);
199     btstack_run_loop_add_timer(&nxp_timer);
200 }
201 
202 static void nxp_dummy(void){
203 }
204 
205 static void nxp_w4_fw_req(void){
206     // validate checksum
207     if (nxp_valid_packet()){
208         printf("RECV: ");
209         printf_hexdump(nxp_response_buffer, sizeof(nxp_response_buffer));
210         switch (nxp_response_buffer[0]){
211             case NXP_V1_FW_REQ_PKT:
212                 // get firmware
213                 if (nxp_have_firmware == false){
214                     nxp_load_firmware();
215                 }
216                 if (nxp_have_firmware == false){
217                     printf("No firmware found, abort\n");
218                     break;
219                 }
220                 nxp_fw_request_len = little_endian_read_16(nxp_response_buffer, 1);
221                 printf("RECV: NXP_V1_FW_REQ_PKT, len %u\n", nxp_fw_request_len);
222                 if (nxp_fw_request_len == 0){
223                     printf("last chunk sent!\n");
224                     nxp_unload_firmware();
225                     nxp_send_ack_v1(nxp_done);
226                 } else {
227                     nxp_send_ack_v1(nxp_prepare_chunk);
228                 }
229                 return;
230             case NXP_V1_CHIP_VER_PKT:
231                 printf("RECV: NXP_V1_CHIP_VER_PKT, id = 0x%x02, revision = 0x%02x\n", nxp_response_buffer[0], nxp_response_buffer[1]);
232                 nxp_send_ack_v1(nxp_dummy);
233                 break;
234             case NXP_V3_CHIP_VER_PKT:
235                 nxp_chip_id = little_endian_read_16(nxp_response_buffer, 1);
236                 btstack_strcpy(nxp_firmware_path, sizeof(nxp_firmware_path), nxp_fw_name_from_chipid(nxp_chip_id));
237                 printf("RECV: NXP_V3_CHIP_VER_PKT, id = 0x%04x, loader 0x%02x -> firmware '%s'\n", nxp_chip_id, nxp_response_buffer[3], nxp_firmware_path);
238                 nxp_send_ack_v3(nxp_dummy);
239                 break;
240             default:
241                 printf("RECV: unknown packet type 0x%02x\n", nxp_response_buffer[0]);
242                 break;
243         }
244         nxp_start();
245     }
246     // drop byte and read another byte
247     memmove(&nxp_response_buffer[0], &nxp_response_buffer[1], 4);
248     nxp_uart_driver->receive_block(&nxp_response_buffer[4], 1);
249 }
250 
251 static void nxp_send_chunk(btstack_timer_source_t * ts){
252     if ((nxp_fw_request_len & 1) == 0){
253         // update sttate
254         nxp_fw_offset += nxp_fw_request_len;
255         nxp_fw_resend_count = 0;
256         // read next firmware chunk
257         uint16_t bytes_read = nxp_read_firmware(nxp_fw_request_len, nxp_output_buffer);
258         if (bytes_read < nxp_fw_request_len){
259             printf("only %u of %u bytes available, abort.\n", bytes_read, nxp_fw_request_len);
260             nxp_done_with_status(ERROR_CODE_HARDWARE_FAILURE);
261             return;
262         }
263     } else {
264         // resend last chunk if request len is odd
265         if (nxp_fw_resend_count >= NXP_MAX_RESEND_COUNT){
266             printf("Resent last block %u times, abort.", nxp_fw_resend_count);
267             nxp_done_with_status(ERROR_CODE_HARDWARE_FAILURE);
268             return;
269         }
270         nxp_fw_resend_count++;
271     }
272 
273     printf("SEND: firmware %08x - %u bytes (%u. try)\n", nxp_fw_offset, nxp_fw_request_len, nxp_fw_resend_count + 1);
274     nxp_uart_driver->set_block_received(&nxp_w4_fw_req);
275     nxp_uart_driver->receive_block(nxp_response_buffer, 5);
276     nxp_uart_driver->set_block_sent(nxp_dummy);
277     nxp_uart_driver->send_block(nxp_output_buffer, nxp_fw_request_len);
278 }
279 
280 static void nxp_done_with_status(uint8_t status){
281     printf("DONE!\n");
282     (*nxp_download_complete)(status);
283 }
284 
285 static void nxp_done(void){
286     nxp_done_with_status(ERROR_CODE_SUCCESS);
287 }
288 
289 void btstack_chipset_nxp_set_v1_firmware_path(const char * firmware_path){
290     btstack_strcpy(nxp_firmware_path, sizeof(nxp_firmware_path), firmware_path);
291 }
292 
293 void btstack_chipset_nxp_set_firmware(const uint8_t * fw_data, uint32_t fw_size){
294     nxp_fw_data = fw_data;
295     nxp_fw_size = fw_size;
296 }
297 
298 void btstack_chipset_nxp_download_firmware_with_uart(const btstack_uart_t *uart_driver, void (*done)(uint8_t status)) {
299     nxp_uart_driver   = uart_driver;
300     nxp_download_complete = done;
301 
302     int res = nxp_uart_driver->open();
303 
304     if (res) {
305         log_error("uart_block init failed %u", res);
306         nxp_download_complete(res);
307     }
308 
309     nxp_start();
310 }
311 
312 
313 static void chipset_init(const void *transport_config){
314     UNUSED(transport_config);
315 }
316 
317 static btstack_chipset_t btstack_chipset_nxp = {
318         .name = "NXP",
319         .init = chipset_init,
320         .next_command = NULL,
321         .set_baudrate_command = NULL,
322         .set_bd_addr_command = NULL
323 };
324 
325 const btstack_chipset_t *btstack_chipset_nxp_instance(void){
326     return &btstack_chipset_nxp;
327 }
328