librnode/src/libs/flashers/nrf/dfu_serial.c
2025-07-18 18:00:14 +01:00

357 lines
11 KiB
C

/**
* 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 <https://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <sys/param.h>
#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;
}