357 lines
11 KiB
C
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;
|
|
}
|