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