xref: /btstack/chipset/nxp/btstack_chipset_nxp.c (revision d0f26bf0c78bf52a56173adb9e57b021e5ec80cc)
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_FIRMWARE_REQUEST_PACKET	0xa5
53 #define NXP_V1_CHIP_VERION_PACKET	0xaa
54 #define NXP_V3_FIRMWARE_REQUEST_PACKET	0xa7
55 #define NXP_V3_CHIP_VERSION_PACKET	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 // vendor commands
76 #define NXP_OPCODE_SET_SCO_DATA_PATH	0xFC1D
77 #define NXP_OPCODE_SET_BDADDR		    0xFC22
78 
79 // prototypes
80 static void nxp_send_chunk_v1(void);
81 static void nxp_done_with_status(uint8_t status);
82 static void nxp_send_chunk_v3(void);
83 static void nxp_read_uart_handler(void);
84 static void nxp_start(void);
85 
86 // globals
87 static void (*nxp_download_complete)(uint8_t status);
88 static const btstack_uart_t * nxp_uart_driver;
89 static bool                   nxp_have_firmware;
90 
91 static uint16_t nxp_chip_id;
92 
93 static const uint8_t * nxp_fw_data;
94 static uint32_t        nxp_fw_size;
95 static uint32_t        nxp_fw_offset;
96 
97 static uint8_t       nxp_input_buffer[10];
98 static uint16_t      nxp_input_pos;
99 static uint16_t      nxp_input_bytes_requested;
100 
101 static uint8_t       nxp_output_buffer[2048 + 1];
102 
103 static const uint8_t nxp_ack_buffer_v1[] = {NXP_ACK_V1 };
104 static const uint8_t nxp_ack_buffer_v3[] = {NXP_ACK_V3, 0x92 };
105 
106 static uint16_t      nxp_fw_request_len;
107 static uint8_t       nxp_fw_resend_count;
108 
109 static enum {
110     NXP_TX_IDLE,
111     NXP_TX_SEND_CHUNK_V1,
112     NXP_TX_SEND_CHUNK_V3,
113 } nxp_tx_state;
114 
115 #ifdef HAVE_POSIX_FILE_IO
116 static char   nxp_firmware_path[1000];
117 static FILE * nxp_firmware_file;
118 
119 static char *nxp_fw_name_from_chipid(uint16_t chip_id)
120 {
121     switch (chip_id) {
122         case NXP_CHIP_ID_W9098:
123             return NXP_FIRMWARE_W9098;
124         case NXP_CHIP_ID_IW416:
125             return NXP_FIRMWARE_IW416;
126         case NXP_CHIP_ID_IW612:
127             return NXP_FIRMWARE_IW612;
128         default:
129             log_error("Unknown chip id 0x%04x", chip_id);
130             return NULL;
131     }
132 }
133 
134 static void nxp_load_firmware(void) {
135     if (nxp_firmware_file == NULL){
136         log_info("chipset-bcm: open file %s", nxp_firmware_path);
137         nxp_firmware_file = fopen(nxp_firmware_path, "rb");
138         if (nxp_firmware_file != NULL){
139             nxp_have_firmware = true;
140         }
141     }
142 
143 }
144 static uint16_t nxp_read_firmware(uint16_t bytes_to_read, uint8_t * buffer) {
145     size_t bytes_read = fread(buffer, 1, bytes_to_read, nxp_firmware_file);
146     return bytes_read;
147 }
148 
149 static void nxp_unload_firmware(void) {
150     btstack_assert(nxp_firmware_file != NULL);
151     fclose(nxp_firmware_file);
152     nxp_firmware_file = NULL;
153 }
154 #else
155 void nxp_load_firmware(void){
156     nxp_have_firmware = true;
157 }
158 
159 // read bytes from firmware file
160 static uint16_t nxp_read_firmware(uint16_t bytes_to_read, uint8_t * buffer){
161     if (nxp_fw_request_len > nxp_fw_size - nxp_fw_offset){
162         printf("fw_request_len %u > remaining file len %u\n", nxp_fw_request_len, nxp_fw_size - nxp_fw_offset);
163         return nxp_fw_size - nxp_fw_offset;
164     }
165     memcpy(buffer, &nxp_fw_data[nxp_fw_offset], bytes_to_read);
166     nxp_fw_offset += nxp_fw_request_len;
167     return bytes_to_read;
168 }
169 static void nxp_unload_firmware(void) {
170 }
171 #endif
172 
173 static void nxp_dummy(void){
174 }
175 
176 static void nxp_send_ack_v1() {
177     printf("SEND: ack v1\n");
178     nxp_uart_driver->send_block(nxp_ack_buffer_v1, sizeof(nxp_ack_buffer_v1));
179 }
180 
181 static void nxp_send_ack_v3() {
182     printf("SEND: ack v3\n");
183     nxp_uart_driver->send_block(nxp_ack_buffer_v3, sizeof(nxp_ack_buffer_v3));
184 }
185 
186 static bool nxp_valid_packet(void){
187     switch (nxp_input_buffer[0]){
188         case NXP_V1_FIRMWARE_REQUEST_PACKET:
189         case NXP_V1_CHIP_VERION_PACKET:
190             // first two uint16_t should xor to 0xffff
191             return ((nxp_input_buffer[1] ^ nxp_input_buffer[3]) == 0xff) && ((nxp_input_buffer[2] ^ nxp_input_buffer [4]) == 0xff);
192         case NXP_V3_CHIP_VERSION_PACKET:
193         case NXP_V3_FIRMWARE_REQUEST_PACKET:
194             // TODO: check crc-8
195             return true;
196         default:
197             return false;
198     }
199 }
200 
201 static void nxp_handle_chip_version_v1(void){
202     printf("RECV: NXP_V1_CHIP_VER_PKT, id = 0x%x02, revision = 0x%02x\n", nxp_input_buffer[0], nxp_input_buffer[1]);
203     nxp_tx_state = NXP_TX_IDLE;
204     nxp_send_ack_v1();
205 }
206 
207 static void nxp_handle_chip_version_v3(void){
208     nxp_chip_id = little_endian_read_16(nxp_input_buffer, 1);
209     btstack_strcpy(nxp_firmware_path, sizeof(nxp_firmware_path), nxp_fw_name_from_chipid(nxp_chip_id));
210     printf("RECV: NXP_V3_CHIP_VER_PKT, id = 0x%04x, loader 0x%02x -> firmware '%s'\n", nxp_chip_id, nxp_input_buffer[3], nxp_firmware_path);
211     nxp_tx_state = NXP_TX_IDLE;
212     nxp_send_ack_v3();
213 }
214 
215 static void nxp_prepare_firmware(void){
216     // get firmware
217     if (nxp_have_firmware == false){
218         nxp_load_firmware();
219     }
220     if (nxp_have_firmware == false){
221         printf("No firmware found, abort\n");
222     }
223 }
224 
225 static void nxp_send_chunk_v1(void){
226     if (nxp_fw_request_len == 0){
227         printf("last chunk sent!\n");
228         nxp_unload_firmware();
229         nxp_done_with_status(ERROR_CODE_SUCCESS);
230         return;
231     } else if ((nxp_fw_request_len & 1) == 0){
232         // update sttate
233         nxp_fw_offset += nxp_fw_request_len;
234         nxp_fw_resend_count = 0;
235         // read next firmware chunk
236         uint16_t bytes_read = nxp_read_firmware(nxp_fw_request_len, nxp_output_buffer);
237         if (bytes_read < nxp_fw_request_len){
238             printf("only %u of %u bytes available, abort.\n", bytes_read, nxp_fw_request_len);
239             nxp_done_with_status(ERROR_CODE_HARDWARE_FAILURE);
240             return;
241         }
242     } else {
243         // resend last chunk if request len is odd
244         if (nxp_fw_resend_count >= NXP_MAX_RESEND_COUNT){
245             printf("Resent last block %u times, abort.", nxp_fw_resend_count);
246             nxp_done_with_status(ERROR_CODE_HARDWARE_FAILURE);
247             return;
248         }
249         nxp_fw_resend_count++;
250     }
251     printf("SEND: firmware %08x - %u bytes (%u. try)\n", nxp_fw_offset, nxp_fw_request_len, nxp_fw_resend_count + 1);
252     nxp_tx_state = NXP_TX_IDLE;
253     nxp_uart_driver->send_block(nxp_output_buffer, nxp_fw_request_len);
254 }
255 
256 static void nxp_send_chunk_v3(void){
257     // update state
258     nxp_fw_offset += nxp_fw_request_len;
259     nxp_fw_resend_count = 0;
260     if (nxp_fw_request_len == 0){
261         printf("last chunk sent!\n");
262         nxp_unload_firmware();
263         nxp_done_with_status(ERROR_CODE_SUCCESS);
264         return;
265     }
266     // read next firmware chunk
267     uint16_t bytes_read = nxp_read_firmware(nxp_fw_request_len, nxp_output_buffer);
268     if (bytes_read < nxp_fw_request_len){
269         printf("only %u of %u bytes available, abort.\n", bytes_read, nxp_fw_request_len);
270         nxp_done_with_status(ERROR_CODE_HARDWARE_FAILURE);
271         return;
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_tx_state = NXP_TX_IDLE;
275     nxp_uart_driver->send_block(nxp_output_buffer, nxp_fw_request_len);
276 }
277 
278 static void nxp_handle_firmware_request_v1(void){
279     nxp_fw_request_len = little_endian_read_16(nxp_input_buffer, 1);
280     printf("RECV: NXP_V1_FW_REQ_PKT, len %u\n", nxp_fw_request_len);
281 
282     nxp_prepare_firmware();
283     if (nxp_have_firmware == false){
284         return;
285     }
286     nxp_tx_state = NXP_TX_SEND_CHUNK_V1;
287     nxp_send_ack_v1();
288 }
289 
290 static void nxp_handle_firmware_request_v3(void){
291     nxp_fw_request_len = little_endian_read_16(nxp_input_buffer, 1);
292     printf("RECV: NXP_V3_FW_REQ_PKT, len %u\n", nxp_fw_request_len);
293 
294     nxp_prepare_firmware();
295     if (nxp_have_firmware == false){
296         return;
297     }
298     nxp_tx_state = NXP_TX_SEND_CHUNK_V3;
299     nxp_send_ack_v3();
300 }
301 
302 static void nxp_start_read(uint16_t bytes_to_read){
303     nxp_input_bytes_requested = bytes_to_read;
304     nxp_uart_driver->receive_block(&nxp_input_buffer[nxp_input_pos], bytes_to_read);
305 }
306 
307 static void nxp_read_uart_handler(void){
308     uint16_t bytes_to_read;
309     if (nxp_input_pos == 0){
310         switch (nxp_input_buffer[0]){
311             case NXP_V1_CHIP_VERION_PACKET:
312             case NXP_V3_CHIP_VERSION_PACKET:
313             case NXP_V1_FIRMWARE_REQUEST_PACKET:
314                 nxp_input_pos++;
315                 bytes_to_read = 4;
316                 break;
317             case NXP_V3_FIRMWARE_REQUEST_PACKET:
318                 nxp_input_pos++;
319                 bytes_to_read = 9;
320                 break;
321             default:
322                 // invalid packet type, skip and get next byte
323                 bytes_to_read = 1;
324                 break;
325         }
326         nxp_start_read(bytes_to_read);
327     } else {
328         nxp_input_pos += nxp_input_bytes_requested;
329         printf("RECV: ");
330         printf_hexdump(nxp_input_buffer, nxp_input_pos);
331         switch (nxp_input_buffer[0]){
332             case NXP_V1_CHIP_VERION_PACKET:
333                 nxp_handle_chip_version_v1();
334                 break;
335             case NXP_V3_CHIP_VERSION_PACKET:
336                 nxp_handle_chip_version_v3();
337                 break;
338             case NXP_V1_FIRMWARE_REQUEST_PACKET:
339                 nxp_handle_firmware_request_v1();
340                 break;
341             case NXP_V3_FIRMWARE_REQUEST_PACKET:
342                 nxp_handle_firmware_request_v3();
343                 break;
344             default:
345                 btstack_assert(false);
346                 break;
347         }
348         nxp_input_pos = 0;
349         bytes_to_read = 1;
350     }
351     nxp_start_read(bytes_to_read);
352 }
353 
354 static void nxp_write_uart_handler(void){
355     switch (nxp_tx_state){
356         case NXP_TX_IDLE:
357             break;
358         case NXP_TX_SEND_CHUNK_V1:
359             nxp_send_chunk_v1();
360             break;
361         case NXP_TX_SEND_CHUNK_V3:
362             nxp_send_chunk_v3();
363             break;
364         default:
365             break;
366     }
367 }
368 
369 static void nxp_start(void){
370     nxp_tx_state = NXP_TX_IDLE;
371     nxp_fw_resend_count = 0;
372     nxp_fw_offset = 0;
373     nxp_have_firmware = false;
374     nxp_uart_driver->set_block_received(&nxp_read_uart_handler);
375     nxp_uart_driver->set_block_sent(&nxp_write_uart_handler);
376     nxp_uart_driver->receive_block(nxp_input_buffer, 1);
377 }
378 
379 static void nxp_done_with_status(uint8_t status){
380     printf("DONE!\n");
381     (*nxp_download_complete)(status);
382 }
383 
384 static void nxp_done(void){
385     nxp_done_with_status(ERROR_CODE_SUCCESS);
386 }
387 
388 void btstack_chipset_nxp_set_v1_firmware_path(const char * firmware_path){
389     btstack_strcpy(nxp_firmware_path, sizeof(nxp_firmware_path), firmware_path);
390 }
391 
392 void btstack_chipset_nxp_set_firmware(const uint8_t * fw_data, uint32_t fw_size){
393     nxp_fw_data = fw_data;
394     nxp_fw_size = fw_size;
395 }
396 
397 void btstack_chipset_nxp_download_firmware_with_uart(const btstack_uart_t *uart_driver, void (*done)(uint8_t status)) {
398     nxp_uart_driver   = uart_driver;
399     nxp_download_complete = done;
400 
401     int res = nxp_uart_driver->open();
402 
403     if (res) {
404         log_error("uart_block init failed %u", res);
405         nxp_download_complete(res);
406     }
407 
408     nxp_start();
409 }
410 
411 // init script support
412 static enum {
413     NXP_INIT_SEND_SCO_CONFIG,
414     NXP_INIT_DONE,
415 } nxp_init_state;
416 
417 static void nxp_init(const void *transport_config){
418     UNUSED(transport_config);
419     nxp_init_state = NXP_INIT_SEND_SCO_CONFIG;
420 }
421 
422 static btstack_chipset_result_t nxp_next_command(uint8_t * hci_cmd_buffer) {
423     switch (nxp_init_state){
424         case NXP_INIT_SEND_SCO_CONFIG:
425 #if defined(ENABLE_SCO_OVER_HCI) || defined(ENABLE_SCO_OVER_PCM)
426             little_endian_store_16(hci_cmd_buffer, 0, NXP_OPCODE_SET_SCO_DATA_PATH);
427             hci_cmd_buffer[2] = 1;
428 #ifdef ENABLE_SCO_OVER_HCI
429             // Voice Path: Host
430             hci_cmd_buffer[3] = 0;
431 #else
432             // Voice Path: PCM/I2S
433             hci_cmd_buffer[3] = 1;
434 #endif
435             nxp_init_state = NXP_INIT_DONE;
436             return BTSTACK_CHIPSET_VALID_COMMAND;
437 #endif
438             break;
439         case NXP_INIT_DONE:
440             break;
441     }
442     return BTSTACK_CHIPSET_DONE;
443 }
444 
445 static btstack_chipset_t btstack_chipset_nxp = {
446         .name = "NXP",
447         .init = nxp_init,
448         .next_command = nxp_next_command,
449         .set_baudrate_command = NULL,
450         .set_bd_addr_command = NULL
451 };
452 
453 const btstack_chipset_t *btstack_chipset_nxp_instance(void){
454     return &btstack_chipset_nxp;
455 }
456