Files
kunlun/driver/extern/virtualchannel/vc_upgrade/vc_upgrade_driver.c
2024-09-28 14:24:04 +08:00

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;
}