/** * Copyright (c) 2018, Nordic Semiconductor ASA * * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * 2. Redistributions in binary form, except as embedded into a Nordic * Semiconductor ASA integrated circuit in a product or a software update for * such product, must reproduce the above copyright notice, this list of * conditions and the following disclaimer in the documentation and/or other * materials provided with the distribution. * * 3. Neither the name of Nordic Semiconductor ASA nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * 4. This software, with or without modification, must only be used with a * Nordic Semiconductor ASA integrated circuit. * * 5. Any software provided in binary form under this license must not be reverse * engineered, decompiled, modified and/or disassembled. * * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * Modified by Jacob Eva (Liberated Embedded Systems) (c) 2025. * Changes licensed under the GPL. * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) * any later version. * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * You should have received a copy of the GNU General Public License along * with this program. If not, see . */ #include #include #include #include #include "dfu_serial.h" #include "hci.h" #include "../../util.h" #include "../../logging/log.h" // SLIP data log buffer size #define MAX_BUFF_SIZE 1024 #define DFU_INIT_PACKET 1 #define DFU_START_PACKET 3 #define DFU_DATA_PACKET 4 #define DFU_STOP_DATA_PACKET 5 #define DFU_PACKET_MAX_SIZE 512 #define FLASH_PAGE_SIZE 4096 #define FLASH_WORD_WRITE_TIME 0.000100 #define FLASH_PAGE_WRITE_TIME (FLASH_PAGE_SIZE / 4) * FLASH_WORD_WRITE_TIME #define FLASH_PAGE_ERASE_TIME 0.0897 #define FLASH_MAX_FW_SIZE 300000 #define DAT_FILE_SIZE 14 #define LOG_BUF_SIZE 2048 static uint8_t send_data[UART_SLIP_SIZE_MAX]; static uint8_t receive_data[UART_SLIP_SIZE_MAX]; char logger_buf[LOG_BUF_SIZE]; void (*progress_cb)(uint8_t) = NULL; static uint32_t get_uint32_le(const uint8_t *p_data) { uint32_t data; data = ((uint32_t)*(p_data + 0) << 0); data += ((uint32_t)*(p_data + 1) << 8); data += ((uint32_t)*(p_data + 2) << 16); data += ((uint32_t)*(p_data + 3) << 24); return data; } static void put_uint32_le(uint8_t *p_data, uint32_t data) { *(p_data + 0) = (uint8_t)(data >> 0); *(p_data + 1) = (uint8_t)(data >> 8); *(p_data + 2) = (uint8_t)(data >> 16); *(p_data + 3) = (uint8_t)(data >> 24); } static void uart_data_to_buff(const uint8_t *pData, uint32_t nSize) { uint32_t n; int len, pos; pos = 0; if (nSize*3 <= sizeof(logger_buf)) { for (n = 0; n < nSize; n++) { sprintf(logger_buf+n*3, "%02x ", *(pData + n)); } } } static int dfu_serial_send(uart_drv_t *p_uart, const uint8_t *pData, uint32_t nSize) { uart_data_to_buff(pData, nSize); log_trace("SLIP: --> [%s]", logger_buf); return uart_drv_send(p_uart, pData, nSize); } //static int dfu_serial_get_rsp(uart_drv_t *p_uart, nrf_dfu_op_t oper, uint32_t *p_data_cnt) //{ // int err_code; // // err_code = uart_slip_receive(p_uart, receive_data, sizeof(receive_data), p_data_cnt); // // if (!err_code) // { // //int info_lvl = //logger_get_info_level(); // // //if (info_lvl >= LOGGER_INFO_LVL_3) // //{ // // uart_data_to_buff(receive_data, *p_data_cnt); // // //logger_info_3("SLIP: <-- [%s]", //logger_buf); // //} // // if (*p_data_cnt >= 3 && // receive_data[0] == NRF_DFU_OP_RESPONSE && // receive_data[1] == oper) // { // if (receive_data[2] != NRF_DFU_RES_CODE_SUCCESS) // { // uint16_t rsp_error = receive_data[2]; // // // get 2-byte error code, if applicable // if (*p_data_cnt >= 4) // rsp_error = (rsp_error << 8) + receive_data[3]; // // //logger_error("Bad result code (0x%X)!", rsp_error); // // err_code = 1; // } // } // else // { // //logger_error("Invalid response!"); // // err_code = 1; // } // } // // return err_code; //} static void dfu_serial_u32_to_bytes(uint32_t val, uint8_t* array, uint16_t offset) { array[offset] = val & 0x000000ff; array[offset+1] = (val & 0x0000ff00) >> 8; array[offset+2] = (val & 0x00ff0000) >> 16; array[offset+3] = (val & 0xff000000) >> 24; } // Waits the time necessary for the MCU to erase its flash before we begin sending the new image int dfu_serial_wait_for_erase(uint32_t total_img_size) { return sleep_ms(MAX(0.5, (total_img_size / FLASH_PAGE_SIZE) * FLASH_PAGE_ERASE_TIME)); } int dfu_serial_send_start_dfu(uart_drv_t *p_uart, uint8_t mode, uint32_t softdevice_size, uint32_t bootloader_size, uint32_t application_size) { int error = 0; uint8_t frame[5*4] = {0}; uint8_t hci_frame[(5*4)*2]; dfu_serial_u32_to_bytes(DFU_START_PACKET, frame, 0); dfu_serial_u32_to_bytes(mode, frame, 4); dfu_serial_u32_to_bytes(softdevice_size, frame, 8); dfu_serial_u32_to_bytes(bootloader_size, frame, 12); dfu_serial_u32_to_bytes(application_size, frame, 16); uint32_t size = encode_hci(frame, 5*4, hci_frame); error = dfu_serial_wait_for_erase(softdevice_size + bootloader_size + application_size); if (!error) { error = dfu_serial_send(p_uart, hci_frame, size); } return error; } int dfu_serial_send_init(uart_drv_t *p_uart, const uint8_t* dat_file, uint32_t dat_size) { if (dat_size > DAT_FILE_SIZE) { log_error("Firmware DAT file is invalid! Is your ZIP file corrupt?"); return -1; } uint8_t frame[6+DAT_FILE_SIZE] = {0}; uint8_t hci_frame[(6+DAT_FILE_SIZE) * 2]; dfu_serial_u32_to_bytes(DFU_INIT_PACKET, frame, 0); memcpy(frame+4, dat_file, DAT_FILE_SIZE); uint32_t size = encode_hci(frame, 6+DAT_FILE_SIZE, hci_frame); return dfu_serial_send(p_uart, hci_frame, size); } // todo, may allow for firmware extraction from board? //static int dfu_serial_try_to_recover_fw(uart_drv_t *p_uart, const uint8_t *p_data, uint32_t data_size, // nrf_dfu_response_select_t *p_rsp_recover, // const nrf_dfu_response_select_t *p_rsp_select) //{ // int err_code = 0; // uint32_t max_size, stp_size; // uint32_t pos_start, len_remain; // uint32_t crc_32 = 0; // int obj_exec = 1; // // *p_rsp_recover = *p_rsp_select; // // pos_start = p_rsp_recover->offset; // // if (pos_start > data_size) // { // //logger_error("Invalid firmware offset reported!"); // // err_code = 1; // } // else if (pos_start > 0) // { // max_size = p_rsp_select->max_size; // //crc_32 = crc32_compute(p_data, pos_start, NULL); // len_remain = pos_start % max_size; // // if (p_rsp_select->crc != crc_32) // { // pos_start -= ((len_remain > 0) ? len_remain : max_size); // p_rsp_recover->offset = pos_start; // // return err_code; // } // // if (len_remain > 0) // { // stp_size = max_size - len_remain; // // err_code = dfu_serial_stream_data_crc(p_uart, p_data + pos_start, stp_size, pos_start, &crc_32); // if (!err_code) // { // pos_start += stp_size; // } // else if (err_code == 2) // { // err_code = 0; // // pos_start -= len_remain; // // obj_exec = 0; // } // // p_rsp_recover->offset = pos_start; // } // // if (!err_code && obj_exec) // { // err_code = dfu_serial_execute_obj(p_uart); // } // } // // return err_code; //} int dfu_serial_send_firmware(uart_drv_t *p_uart, struct RNode* rn, const uint8_t *p_data, uint32_t data_size) { uint8_t frame[DFU_PACKET_MAX_SIZE + 4]; uint8_t hci_frame[(DFU_PACKET_MAX_SIZE + 4) * 2]; int err_code = 0; //logger_info_1("Sending firmware file..."); if (p_data == NULL || !data_size) { log_error("Payload data is invalid! Is your ZIP file corrupt?"); err_code = 1; } if (!err_code) { uint16_t len; uint32_t hci_len; for (int i = 0; i < data_size; i = i + DFU_PACKET_MAX_SIZE) { dfu_serial_u32_to_bytes(DFU_DATA_PACKET, frame, 0); if (data_size - i > DFU_PACKET_MAX_SIZE) { len = DFU_PACKET_MAX_SIZE; } else { len = data_size - i; } memcpy(frame+4, p_data+i, len); hci_len = encode_hci(frame, len+4, hci_frame); err_code = dfu_serial_send(p_uart, hci_frame, hci_len); if (err_code) { log_error("Sending firmware to RNode failed! Is the device still connected?"); break; } // Trigger callback and provide percentage of packets sent if (rn->prog_cb != NULL) { (*rn->prog_cb)(i / data_size * 100); } // nrf52 is erasing and writing to flash if (i % 8 == 0) { err_code = sleep_ms(FLASH_PAGE_WRITE_TIME); if (err_code) { log_error("Timing failed during flash, please try flashing again!"); break; } } } if (!err_code) { err_code = sleep_ms(FLASH_PAGE_WRITE_TIME); if (err_code) { log_error("Timing failed during flash, please try flashing again!"); } dfu_serial_u32_to_bytes(DFU_STOP_DATA_PACKET, frame, 0); hci_len = encode_hci(frame, 4, hci_frame); err_code = dfu_serial_send(p_uart, hci_frame, hci_len); } } return err_code; }