587 lines
16 KiB
C
587 lines
16 KiB
C
/****************************************************************************
|
|
|
|
Copyright(c) 2019 by Aerospace C.Power (Chongqing) Microelectronics. ALL RIGHTS RESERVED.
|
|
|
|
This Information is proprietary to Aerospace C.Power (Chongqing) Microelectronics and MAY NOT
|
|
be copied by any method or incorporated into another program without
|
|
the express written consent of Aerospace C.Power. This Information or any portion
|
|
thereof remains the property of Aerospace C.Power. The Information contained herein
|
|
is believed to be accurate and Aerospace C.Power assumes no responsibility or
|
|
liability for its use in any way and conveys no license or title under
|
|
any patent or copyright and makes no representation or warranty that this
|
|
Information is free from patent or copyright infringement.
|
|
|
|
****************************************************************************/
|
|
|
|
#include "vc_upgrade_driver.h"
|
|
#include "iot_errno_api.h"
|
|
#include "os_utils_api.h"
|
|
#include "os_mem_api.h"
|
|
|
|
/* define stm32 reply data */
|
|
#define STM32_ACK 0x79
|
|
#define STM32_NACK 0x1F
|
|
#define STM32_BUSY 0x76
|
|
|
|
/* define the instructions supported by stm32 */
|
|
/* init cmd */
|
|
#define STM32_CMD_INIT 0x7F
|
|
/* get the version and command supported */
|
|
#define STM32_CMD_GET 0x00
|
|
/* get version and read protection status */
|
|
#define STM32_CMD_GVR 0x01
|
|
/* get ID */
|
|
#define STM32_CMD_GID 0x02
|
|
/* read memory */
|
|
#define STM32_CMD_RM 0x11
|
|
/* go */
|
|
#define STM32_CMD_GO 0x21
|
|
/* write memory */
|
|
#define STM32_CMD_WM 0x31
|
|
/* no-stretch write memory */
|
|
#define STM32_CMD_WM_NS 0x32
|
|
/* erase */
|
|
#define STM32_CMD_ER 0x43
|
|
/* extended erase */
|
|
#define STM32_CMD_EE 0x44
|
|
/* extended erase no-stretch */
|
|
#define STM32_CMD_EE_NS 0x45
|
|
/* write protect */
|
|
#define STM32_CMD_WP 0x63
|
|
/* write protect no-stretch */
|
|
#define STM32_CMD_WP_NS 0x64
|
|
/* write unprotect */
|
|
#define STM32_CMD_UW 0x73
|
|
/* write unprotect no-stretch */
|
|
#define STM32_CMD_UW_NS 0x74
|
|
/* readout protect */
|
|
#define STM32_CMD_RP 0x82
|
|
/* readout protect no-stretch */
|
|
#define STM32_CMD_RP_NS 0x83
|
|
/* readout unprotect */
|
|
#define STM32_CMD_UR 0x92
|
|
/* readout unprotect no-stretch */
|
|
#define STM32_CMD_UR_NS 0x93
|
|
/* compute CRC */
|
|
#define STM32_CMD_CRC 0xA1
|
|
/* not a valid command */
|
|
#define STM32_CMD_ERR 0xFF
|
|
|
|
extern const stm32_dev_t devices[];
|
|
|
|
int is_addr_in_ram(const stm32_t *stm, uint32_t addr)
|
|
{
|
|
return addr >= stm->dev->ram_start && addr < stm->dev->ram_end;
|
|
}
|
|
|
|
int is_addr_in_flash(const stm32_t *stm, uint32_t addr)
|
|
{
|
|
return addr >= stm->dev->fl_start && addr < stm->dev->fl_end;
|
|
}
|
|
|
|
int is_addr_in_opt_bytes(const stm32_t *stm, uint32_t addr)
|
|
{
|
|
/* option bytes upper range is inclusive in our device table */
|
|
return addr >= stm->dev->opt_start && addr <= stm->dev->opt_end;
|
|
}
|
|
|
|
int is_addr_in_sysmem(const stm32_t *stm, uint32_t addr)
|
|
{
|
|
return addr >= stm->dev->mem_start && addr < stm->dev->mem_end;
|
|
}
|
|
|
|
/* returns the page that contains address "addr" */
|
|
int flash_addr_to_page_floor(const stm32_t *stm, uint32_t addr)
|
|
{
|
|
int page;
|
|
uint32_t *psize;
|
|
|
|
if (!is_addr_in_flash(stm, addr))
|
|
return 0;
|
|
|
|
page = 0;
|
|
addr -= stm->dev->fl_start;
|
|
psize = stm->dev->fl_ps;
|
|
|
|
while (addr >= psize[0]) {
|
|
addr -= psize[0];
|
|
page++;
|
|
if (psize[1])
|
|
psize++;
|
|
}
|
|
|
|
return page;
|
|
}
|
|
|
|
/* returns the first page whose start addr is >= "addr" */
|
|
int flash_addr_to_page_ceil(const stm32_t *stm, uint32_t addr)
|
|
{
|
|
int page;
|
|
uint32_t *psize;
|
|
|
|
if (!(addr >= stm->dev->fl_start && addr <= stm->dev->fl_end))
|
|
return 0;
|
|
|
|
page = 0;
|
|
addr -= stm->dev->fl_start;
|
|
psize = stm->dev->fl_ps;
|
|
while (addr >= psize[0]) {
|
|
addr -= psize[0];
|
|
page++;
|
|
if (psize[1])
|
|
psize++;
|
|
}
|
|
return addr ? page + 1 : page;
|
|
}
|
|
|
|
/* returns the lower address of flash page "page" */
|
|
uint32_t flash_page_to_addr(const stm32_t *stm, int page)
|
|
{
|
|
int i;
|
|
uint32_t addr, *psize;
|
|
|
|
addr = stm->dev->fl_start;
|
|
psize = stm->dev->fl_ps;
|
|
|
|
for (i = 0; i < page; i++) {
|
|
addr += psize[0];
|
|
if (psize[1])
|
|
psize++;
|
|
}
|
|
|
|
return addr;
|
|
}
|
|
|
|
extern uint32_t os_delay(uint32_t millisec);
|
|
|
|
static stm32_err_t stm32_get_ack_timeout(const stm32_t *stm,
|
|
uint32_t timeout)
|
|
{
|
|
port_interface_t *port = stm->port;
|
|
uint8_t byte;
|
|
uint32_t p_err;
|
|
uint32_t t0, t1;
|
|
t0 = os_boot_time32(); //ms
|
|
do {
|
|
p_err = port->read(&byte, 1);
|
|
if (p_err == ERR_TIMEOVER && timeout) {
|
|
t1 = os_boot_time32();
|
|
if (t1 < t0 + timeout * 1000)
|
|
continue;
|
|
}
|
|
|
|
if (p_err != ERR_OK) {
|
|
stm32_dbg_printf("Failed to read ACK byte\n");
|
|
return STM32_ERR_UNKNOWN;
|
|
}
|
|
|
|
if (byte == STM32_ACK)
|
|
return STM32_ERR_OK;
|
|
if (byte == STM32_NACK)
|
|
return STM32_ERR_NACK;
|
|
if (byte != STM32_BUSY) {
|
|
stm32_dbg_printf("Got byte 0x%02x instead of ACK\n",
|
|
byte);
|
|
return STM32_ERR_UNKNOWN;
|
|
}
|
|
} while (1);
|
|
}
|
|
|
|
stm32_err_t stm32_get_ack(const stm32_t *stm)
|
|
{
|
|
return stm32_get_ack_timeout(stm, 0);
|
|
}
|
|
|
|
static stm32_err_t stm32_send_command_timeout(const stm32_t *stm,
|
|
const uint8_t cmd, uint32_t timeout)
|
|
{
|
|
port_interface_t *port = stm->port;
|
|
uint32_t p_err;
|
|
uint8_t buf[2];
|
|
|
|
buf[0] = cmd;
|
|
buf[1] = cmd^0xFF;
|
|
p_err = port->write(buf, 2);
|
|
if (p_err != ERR_OK) {
|
|
stm32_dbg_printf("Failed to send command\n");
|
|
return STM32_ERR_UNKNOWN;
|
|
}
|
|
return STM32_ERR_OK;
|
|
}
|
|
|
|
static stm32_err_t stm32_send_command(const stm32_t *stm, const uint8_t cmd)
|
|
{
|
|
return stm32_send_command_timeout(stm, cmd, 0);
|
|
}
|
|
|
|
stm32_err_t stm32_send_init_cmd(const stm32_t *stm)
|
|
{
|
|
port_interface_t *port = stm->port;
|
|
uint32_t result = ERR_FAIL;
|
|
uint8_t cmd = STM32_CMD_INIT;
|
|
|
|
result = port->write(&cmd, 1);
|
|
if (result != ERR_OK) {
|
|
stm32_dbg_printf("Failed to send init to device\n");
|
|
return STM32_ERR_UNKNOWN;
|
|
}
|
|
|
|
return STM32_ERR_OK;
|
|
}
|
|
|
|
stm32_err_t stm32_send_init_handle(const stm32_t *stm)
|
|
{
|
|
port_interface_t *port = stm->port;
|
|
uint32_t result = ERR_FAIL;
|
|
uint8_t byte = 0;
|
|
|
|
result = port->read(&byte, 1);
|
|
if (result == ERR_OK && byte == STM32_ACK)
|
|
return STM32_ERR_OK;
|
|
if (result == ERR_OK && byte == STM32_NACK) {
|
|
/* We could get error later, but let's continue, for now. */
|
|
stm32_dbg_printf("Warning: the interface was not closed properly.\n");
|
|
return STM32_ERR_OK;
|
|
}
|
|
if (result != ERR_TIMEOVER) {
|
|
stm32_dbg_printf("Failed to init device.\n");
|
|
return STM32_ERR_UNKNOWN;
|
|
}
|
|
return STM32_ERR_OK;
|
|
}
|
|
|
|
stm32_err_t stm32_send_get_version_cmd(const stm32_t *stm)
|
|
{
|
|
return stm32_send_command(stm, STM32_CMD_GVR);
|
|
}
|
|
|
|
stm32_err_t stm32_send_get_version_handle(stm32_t *stm)
|
|
{
|
|
/* From AN, only UART bootloader returns 5 bytes */
|
|
port_interface_t *port = stm->port;
|
|
uint8_t len = 5;
|
|
uint8_t buf[5 + 1] = {0};
|
|
if (port->read(buf, len) != ERR_OK)
|
|
return STM32_ERR_UNKNOWN;
|
|
if (buf[0] != STM32_ACK) {
|
|
return STM32_ERR_UNKNOWN;
|
|
}
|
|
stm->version = buf[1];
|
|
stm->option1 = buf[2];
|
|
stm->option2 = buf[3];
|
|
if (buf[4] != STM32_ACK) {
|
|
return STM32_ERR_UNKNOWN;
|
|
}
|
|
return STM32_ERR_OK;
|
|
}
|
|
|
|
stm32_err_t stm32_send_get_cmd(const stm32_t *stm)
|
|
{
|
|
return stm32_send_command(stm, STM32_CMD_GET);
|
|
}
|
|
|
|
/* find newer command by higher code */
|
|
#define newer(prev, a) (((prev) == STM32_CMD_ERR) \
|
|
? (a) \
|
|
: (((prev) > (a)) ? (prev) : (a)))
|
|
|
|
stm32_err_t stm32_send_get_handle(stm32_t *stm)
|
|
{
|
|
/* From AN, only UART bootloader returns 15 bytes */
|
|
port_interface_t *port = stm->port;
|
|
uint8_t len = 15;
|
|
uint8_t buf[15 + 1] = {0};
|
|
uint8_t val = 0, i = 0, new_cmds = 0;
|
|
|
|
if (port->read(buf, len) != ERR_OK)
|
|
return STM32_ERR_UNKNOWN;
|
|
if (buf[0] != STM32_ACK) {
|
|
return STM32_ERR_UNKNOWN;
|
|
}
|
|
len = buf[1] + 1;
|
|
stm->bl_version = buf[2];
|
|
for (i = 1; i < len; i++) {
|
|
val = buf[i + 2];
|
|
switch (val) {
|
|
case STM32_CMD_GET:
|
|
stm->cmd->get = val; break;
|
|
case STM32_CMD_GVR:
|
|
stm->cmd->gvr = val; break;
|
|
case STM32_CMD_GID:
|
|
stm->cmd->gid = val; break;
|
|
case STM32_CMD_RM:
|
|
stm->cmd->rm = val; break;
|
|
case STM32_CMD_GO:
|
|
stm->cmd->go = val; break;
|
|
case STM32_CMD_WM:
|
|
case STM32_CMD_WM_NS:
|
|
stm->cmd->wm = newer(stm->cmd->wm, val);
|
|
break;
|
|
case STM32_CMD_ER:
|
|
case STM32_CMD_EE:
|
|
case STM32_CMD_EE_NS:
|
|
stm->cmd->er = newer(stm->cmd->er, val);
|
|
break;
|
|
case STM32_CMD_WP:
|
|
case STM32_CMD_WP_NS:
|
|
stm->cmd->wp = newer(stm->cmd->wp, val);
|
|
break;
|
|
case STM32_CMD_UW:
|
|
case STM32_CMD_UW_NS:
|
|
stm->cmd->uw = newer(stm->cmd->uw, val);
|
|
break;
|
|
case STM32_CMD_RP:
|
|
case STM32_CMD_RP_NS:
|
|
stm->cmd->rp = newer(stm->cmd->rp, val);
|
|
break;
|
|
case STM32_CMD_UR:
|
|
case STM32_CMD_UR_NS:
|
|
stm->cmd->ur = newer(stm->cmd->ur, val);
|
|
break;
|
|
case STM32_CMD_CRC:
|
|
stm->cmd->crc = newer(stm->cmd->crc, val);
|
|
break;
|
|
default:
|
|
if (new_cmds++ == 0)
|
|
stm32_dbg_printf("GET returns unknown commands (0x%2x", val);
|
|
else
|
|
stm32_dbg_printf(", 0x%2x", val);
|
|
}
|
|
}
|
|
if (new_cmds)
|
|
stm32_dbg_printf(")\n");
|
|
|
|
if (buf[14] != STM32_ACK) {
|
|
return STM32_ERR_UNKNOWN;
|
|
}
|
|
if (stm->cmd->get == STM32_CMD_ERR
|
|
|| stm->cmd->gvr == STM32_CMD_ERR
|
|
|| stm->cmd->gid == STM32_CMD_ERR) {
|
|
stm32_dbg_printf("Error: bootloader did not returned correct"
|
|
" information from GET command\n");
|
|
return STM32_ERR_UNKNOWN;
|
|
}
|
|
return STM32_ERR_OK;
|
|
}
|
|
|
|
stm32_err_t stm32_send_get_id_cmd(const stm32_t *stm)
|
|
{
|
|
return stm32_send_command(stm, stm->cmd->gid);
|
|
}
|
|
|
|
stm32_err_t stm32_send_get_id_handle(stm32_t *stm)
|
|
{
|
|
/* From AN, only UART bootloader returns 5 bytes */
|
|
port_interface_t *port = stm->port;
|
|
uint8_t len = 5;
|
|
uint8_t buf[5 + 1] = {0};
|
|
uint8_t i = 0;
|
|
if (port->read(buf, len) != ERR_OK)
|
|
return STM32_ERR_UNKNOWN;
|
|
if (buf[0] != STM32_ACK) {
|
|
return STM32_ERR_UNKNOWN;
|
|
}
|
|
len = buf[1] + 1;
|
|
if (len < 2) {
|
|
stm32_dbg_printf("Only %d bytes sent in the PID, "
|
|
"unknown/unsupported device\n", len);
|
|
return STM32_ERR_UNKNOWN;
|
|
}
|
|
stm->pid = (buf[2] << 8) | buf[3];
|
|
if (len > 2) {
|
|
stm32_dbg_printf("This bootloader returns %d extra bytes in PID:", len);
|
|
for (i = 1; i <= len ; i++)
|
|
stm32_dbg_printf(" %02x", buf[i]);
|
|
stm32_dbg_printf("\n");
|
|
}
|
|
|
|
if (buf[4] != STM32_ACK) {
|
|
return STM32_ERR_UNKNOWN;
|
|
}
|
|
|
|
stm->dev = devices;
|
|
while (stm->dev->id != 0x00 && stm->dev->id != stm->pid)
|
|
++stm->dev;
|
|
|
|
if (!stm->dev->id) {
|
|
stm32_dbg_printf("Unknown/unsupported device (Device ID: 0x%03x)\n",
|
|
stm->pid);
|
|
return STM32_ERR_UNKNOWN;
|
|
}
|
|
|
|
stm->is_connect = 1;
|
|
return STM32_ERR_OK;
|
|
}
|
|
|
|
stm32_err_t stm32_send_erase_cmd(const stm32_t *stm)
|
|
{
|
|
return stm32_send_command(stm, stm->cmd->er);
|
|
}
|
|
|
|
stm32_err_t stm32_send_erase_page_info(const stm32_t *stm, uint32_t spage,
|
|
uint32_t pages)
|
|
{
|
|
port_interface_t *port = stm->port;
|
|
uint32_t p_err;
|
|
uint32_t pg_num;
|
|
uint8_t pg_byte;
|
|
uint8_t cs = 0;
|
|
uint8_t *buf;
|
|
int i = 0;
|
|
|
|
/* regular erase (0x43) */
|
|
if (stm->cmd->er == STM32_CMD_ER) {
|
|
buf = os_mem_malloc(IOT_STM32_MODULE_ID,1 + pages + 1);
|
|
if (!buf)
|
|
return STM32_ERR_UNKNOWN;
|
|
|
|
buf[i++] = pages - 1;
|
|
cs ^= (pages-1);
|
|
for (pg_num = spage; pg_num < (pages + spage); pg_num++) {
|
|
buf[i++] = pg_num;
|
|
cs ^= pg_num;
|
|
}
|
|
buf[i++] = cs;
|
|
p_err = port->write(buf, i);
|
|
os_mem_free(buf);
|
|
if (p_err != ERR_OK) {
|
|
stm32_dbg_printf("Erase failed.\n");
|
|
return STM32_ERR_UNKNOWN;
|
|
}
|
|
return STM32_ERR_OK;
|
|
}
|
|
|
|
/* extended erase */
|
|
buf = os_mem_malloc(IOT_STM32_MODULE_ID, (2 + 2 * pages + 1));
|
|
if (!buf)
|
|
return STM32_ERR_UNKNOWN;
|
|
|
|
/* Number of pages to be erased - 1, two bytes, MSB first */
|
|
pg_byte = (pages - 1) >> 8;
|
|
buf[i++] = pg_byte;
|
|
cs ^= pg_byte;
|
|
pg_byte = (pages - 1) & 0xFF;
|
|
buf[i++] = pg_byte;
|
|
cs ^= pg_byte;
|
|
|
|
for (pg_num = spage; pg_num < spage + pages; pg_num++) {
|
|
pg_byte = pg_num >> 8;
|
|
cs ^= pg_byte;
|
|
buf[i++] = pg_byte;
|
|
pg_byte = pg_num & 0xFF;
|
|
cs ^= pg_byte;
|
|
buf[i++] = pg_byte;
|
|
}
|
|
buf[i++] = cs;
|
|
p_err = port->write(buf, i);
|
|
os_mem_free(buf);
|
|
if (p_err != ERR_OK) {
|
|
stm32_dbg_printf("Page-by-page erase error.\n");
|
|
return STM32_ERR_UNKNOWN;
|
|
}
|
|
|
|
return STM32_ERR_OK;
|
|
}
|
|
|
|
stm32_err_t stm32_send_write_cmd(const stm32_t *stm)
|
|
{
|
|
if (stm->cmd->wm == STM32_CMD_ERR) {
|
|
stm32_dbg_printf("Error: WRITE command not implemented in bootloader.\n");
|
|
return STM32_ERR_NO_CMD;
|
|
}
|
|
|
|
/* send the address and checksum */
|
|
if (stm32_send_command(stm, stm->cmd->wm) != STM32_ERR_OK) {
|
|
return STM32_ERR_UNKNOWN;
|
|
}
|
|
return STM32_ERR_OK;
|
|
}
|
|
|
|
stm32_err_t stm32_send_write_addr(const stm32_t *stm, uint32_t address)
|
|
{
|
|
uint8_t buf[5] = {0};
|
|
buf[0] = address >> 24;
|
|
buf[1] = (address >> 16) & 0xFF;
|
|
buf[2] = (address >> 8) & 0xFF;
|
|
buf[3] = address & 0xFF;
|
|
buf[4] = buf[0] ^ buf[1] ^ buf[2] ^ buf[3];
|
|
if (stm->port->write(buf, 5) != ERR_OK) {
|
|
return STM32_ERR_UNKNOWN;
|
|
}
|
|
return STM32_ERR_OK;
|
|
}
|
|
|
|
stm32_err_t stm32_send_write_data(const stm32_t *stm,
|
|
const uint8_t data[], unsigned int len)
|
|
{
|
|
port_interface_t *port = stm->port;
|
|
uint8_t cs, buf[256 + 2];
|
|
unsigned int i, aligned_len;
|
|
|
|
aligned_len = (len + 3) & ~3;
|
|
cs = aligned_len - 1;
|
|
buf[0] = aligned_len - 1;
|
|
for (i = 0; i < len; i++) {
|
|
cs ^= data[i];
|
|
buf[i + 1] = data[i];
|
|
}
|
|
/* padding data */
|
|
for (i = len; i < aligned_len; i++) {
|
|
cs ^= 0xFF;
|
|
buf[i + 1] = 0xFF;
|
|
}
|
|
buf[aligned_len + 1] = cs;
|
|
|
|
if (port->write(buf, aligned_len + 2) != ERR_OK) {
|
|
return STM32_ERR_UNKNOWN;
|
|
}
|
|
return STM32_ERR_OK;
|
|
}
|
|
|
|
stm32_err_t stm32_send_read_cmd(const stm32_t *stm)
|
|
{
|
|
if (stm->cmd->rm == STM32_CMD_ERR) {
|
|
stm32_dbg_printf("Error: READ command not implemented in bootloader.\n");
|
|
return STM32_ERR_NO_CMD;
|
|
}
|
|
|
|
/* send the address and checksum */
|
|
if (stm32_send_command(stm, stm->cmd->rm) != STM32_ERR_OK) {
|
|
return STM32_ERR_UNKNOWN;
|
|
}
|
|
return STM32_ERR_OK;
|
|
}
|
|
|
|
stm32_err_t stm32_send_read_addr(const stm32_t *stm, uint32_t address)
|
|
{
|
|
uint8_t buf[5] = {0};
|
|
buf[0] = address >> 24;
|
|
buf[1] = (address >> 16) & 0xFF;
|
|
buf[2] = (address >> 8) & 0xFF;
|
|
buf[3] = address & 0xFF;
|
|
buf[4] = buf[0] ^ buf[1] ^ buf[2] ^ buf[3];
|
|
if (stm->port->write(buf, 5) != ERR_OK) {
|
|
return STM32_ERR_UNKNOWN;
|
|
}
|
|
return STM32_ERR_OK;
|
|
}
|
|
|
|
stm32_err_t stm32_send_read_length(const stm32_t *stm, uint32_t len)
|
|
{
|
|
if (stm32_send_command(stm, len - 1) != STM32_ERR_OK)
|
|
return STM32_ERR_UNKNOWN;
|
|
return STM32_ERR_OK;
|
|
}
|
|
|
|
stm32_err_t stm32_send_read_rdata(const stm32_t *stm,
|
|
uint8_t data[], uint32_t len)
|
|
{
|
|
port_interface_t *port = stm->port;
|
|
|
|
if (port->read(data, len) != ERR_OK)
|
|
return STM32_ERR_UNKNOWN;
|
|
|
|
return STM32_ERR_OK;
|
|
}
|
|
|