ArduinoCore-samd/bootloaders/zero/sam_ba_monitor.c
2018-08-30 14:07:08 -04:00

472 lines
13 KiB
C

/*
Copyright (c) 2015 Arduino LLC. All right reserved.
Copyright (c) 2015 Atmel Corporation/Thibaut VIARD. All right reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "sam.h"
#include <string.h>
#include "sam_ba_monitor.h"
#include "sam_ba_serial.h"
#include "board_driver_serial.h"
#include "board_driver_usb.h"
#include "sam_ba_usb.h"
#include "sam_ba_cdc.h"
const char RomBOOT_Version[] = SAM_BA_VERSION;
const char RomBOOT_ExtendedCapabilities[] = "[Arduino:XYZ]";
/* Provides one common interface to handle both USART and USB-CDC */
typedef struct
{
/* send one byte of data */
int (*put_c)(int value);
/* Get one byte */
int (*get_c)(void);
/* Receive buffer not empty */
bool (*is_rx_ready)(void);
/* Send given data (polling) */
uint32_t (*putdata)(void const* data, uint32_t length);
/* Get data from comm. device */
uint32_t (*getdata)(void* data, uint32_t length);
/* Send given data (polling) using xmodem (if necessary) */
uint32_t (*putdata_xmd)(void const* data, uint32_t length);
/* Get data from comm. device using xmodem (if necessary) */
uint32_t (*getdata_xmd)(void* data, uint32_t length);
} t_monitor_if;
#if SAM_BA_INTERFACE == SAM_BA_UART_ONLY || SAM_BA_INTERFACE == SAM_BA_BOTH_INTERFACES
/* Initialize structures with function pointers from supported interfaces */
const t_monitor_if uart_if =
{
.put_c = serial_putc,
.get_c = serial_getc,
.is_rx_ready = serial_is_rx_ready,
.putdata = serial_putdata,
.getdata = serial_getdata,
.putdata_xmd = serial_putdata_xmd,
.getdata_xmd = serial_getdata_xmd
};
#endif
#if SAM_BA_INTERFACE == SAM_BA_USBCDC_ONLY || SAM_BA_INTERFACE == SAM_BA_BOTH_INTERFACES
//Please note that USB doesn't use Xmodem protocol, since USB already includes flow control and data verification
//Data are simply forwarded without further coding.
const t_monitor_if usbcdc_if =
{
.put_c = cdc_putc,
.get_c = cdc_getc,
.is_rx_ready = cdc_is_rx_ready,
.putdata = cdc_write_buf,
.getdata = cdc_read_buf,
.putdata_xmd = cdc_write_buf,
.getdata_xmd = cdc_read_buf_xmd
};
#endif
/* The pointer to the interface object use by the monitor */
t_monitor_if * ptr_monitor_if;
/* b_terminal_mode mode (ascii) or hex mode */
volatile bool b_terminal_mode = false;
volatile bool b_sam_ba_interface_usart = false;
void sam_ba_monitor_init(uint8_t com_interface)
{
#if SAM_BA_INTERFACE == SAM_BA_UART_ONLY || SAM_BA_INTERFACE == SAM_BA_BOTH_INTERFACES
//Selects the requested interface for future actions
if (com_interface == SAM_BA_INTERFACE_USART)
{
ptr_monitor_if = (t_monitor_if*) &uart_if;
b_sam_ba_interface_usart = true;
}
#endif
#if SAM_BA_INTERFACE == SAM_BA_USBCDC_ONLY || SAM_BA_INTERFACE == SAM_BA_BOTH_INTERFACES
if (com_interface == SAM_BA_INTERFACE_USBCDC)
{
ptr_monitor_if = (t_monitor_if*) &usbcdc_if;
}
#endif
}
/**
* \brief This function allows data rx by USART
*
* \param *data Data pointer
* \param length Length of the data
*/
void sam_ba_putdata_term(uint8_t* data, uint32_t length)
{
uint8_t temp, buf[12], *data_ascii;
uint32_t i, int_value;
if (b_terminal_mode)
{
if (length == 4)
int_value = *(uint32_t *) data;
else if (length == 2)
int_value = *(uint16_t *) data;
else
int_value = *(uint8_t *) data;
data_ascii = buf + 2;
data_ascii += length * 2 - 1;
for (i = 0; i < length * 2; i++)
{
temp = (uint8_t) (int_value & 0xf);
if (temp <= 0x9)
*data_ascii = temp | 0x30;
else
*data_ascii = temp + 0x37;
int_value >>= 4;
data_ascii--;
}
buf[0] = '0';
buf[1] = 'x';
buf[length * 2 + 2] = '\n';
buf[length * 2 + 3] = '\r';
ptr_monitor_if->putdata(buf, length * 2 + 4);
}
else
ptr_monitor_if->putdata(data, length);
return;
}
volatile uint32_t sp;
void call_applet(uint32_t address)
{
uint32_t app_start_address;
__disable_irq();
sp = __get_MSP();
/* Rebase the Stack Pointer */
__set_MSP(*(uint32_t *) address);
/* Load the Reset Handler address of the application */
app_start_address = *(uint32_t *)(address + 4);
/* Jump to application Reset Handler in the application */
asm("bx %0"::"r"(app_start_address));
}
uint32_t current_number;
uint32_t i, length;
uint8_t command, *ptr_data, *ptr, data[SIZEBUFMAX];
uint8_t j;
uint32_t u32tmp;
uint32_t PAGE_SIZE, PAGES, MAX_FLASH;
// Prints a 32-bit integer in hex.
static void put_uint32(uint32_t n)
{
char buff[8];
int i;
for (i=0; i<8; i++)
{
int d = n & 0XF;
n = (n >> 4);
buff[7-i] = d > 9 ? 'A' + d - 10 : '0' + d;
}
ptr_monitor_if->putdata(buff, 8);
}
static void sam_ba_monitor_loop(void)
{
length = ptr_monitor_if->getdata(data, SIZEBUFMAX);
ptr = data;
for (i = 0; i < length; i++, ptr++)
{
if (*ptr == 0xff) continue;
if (*ptr == '#')
{
if (b_terminal_mode)
{
ptr_monitor_if->putdata("\n\r", 2);
}
if (command == 'S')
{
//Check if some data are remaining in the "data" buffer
if(length>i)
{
//Move current indexes to next avail data (currently ptr points to "#")
ptr++;
i++;
//We need to add first the remaining data of the current buffer already read from usb
//read a maximum of "current_number" bytes
if ((length-i) < current_number)
{
u32tmp=(length-i);
}
else
{
u32tmp=current_number;
}
memcpy(ptr_data, ptr, u32tmp);
i += u32tmp;
ptr += u32tmp;
j = u32tmp;
}
//update i with the data read from the buffer
i--;
ptr--;
//Do we expect more data ?
if(j<current_number)
ptr_monitor_if->getdata_xmd(ptr_data, current_number-j);
__asm("nop");
}
else if (command == 'R')
{
ptr_monitor_if->putdata_xmd(ptr_data, current_number);
}
else if (command == 'O')
{
*ptr_data = (char) current_number;
}
else if (command == 'H')
{
*((uint16_t *) ptr_data) = (uint16_t) current_number;
}
else if (command == 'W')
{
*((int *) ptr_data) = current_number;
}
else if (command == 'o')
{
sam_ba_putdata_term(ptr_data, 1);
}
else if (command == 'h')
{
current_number = *((uint16_t *) ptr_data);
sam_ba_putdata_term((uint8_t*) &current_number, 2);
}
else if (command == 'w')
{
current_number = *((uint32_t *) ptr_data);
sam_ba_putdata_term((uint8_t*) &current_number, 4);
}
else if (command == 'G')
{
call_applet(current_number);
/* Rebase the Stack Pointer */
__set_MSP(sp);
__enable_irq();
if (b_sam_ba_interface_usart) {
ptr_monitor_if->put_c(0x6);
}
}
else if (command == 'T')
{
b_terminal_mode = 1;
ptr_monitor_if->putdata("\n\r", 2);
}
else if (command == 'N')
{
if (b_terminal_mode == 0)
{
ptr_monitor_if->putdata("\n\r", 2);
}
b_terminal_mode = 0;
}
else if (command == 'V')
{
ptr_monitor_if->putdata("v", 1);
ptr_monitor_if->putdata((uint8_t *) RomBOOT_Version, strlen(RomBOOT_Version));
ptr_monitor_if->putdata(" ", 1);
ptr_monitor_if->putdata((uint8_t *) RomBOOT_ExtendedCapabilities, strlen(RomBOOT_ExtendedCapabilities));
ptr_monitor_if->putdata(" ", 1);
ptr = (uint8_t*) &(__DATE__);
i = 0;
while (*ptr++ != '\0')
i++;
ptr_monitor_if->putdata((uint8_t *) &(__DATE__), i);
ptr_monitor_if->putdata(" ", 1);
i = 0;
ptr = (uint8_t*) &(__TIME__);
while (*ptr++ != '\0')
i++;
ptr_monitor_if->putdata((uint8_t *) &(__TIME__), i);
ptr_monitor_if->putdata("\n\r", 2);
}
else if (command == 'X')
{
// Syntax: X[ADDR]#
// Erase the flash memory starting from ADDR to the end of flash.
// Note: the flash memory is erased in ROWS, that is in block of 4 pages.
// Even if the starting address is the last byte of a ROW the entire
// ROW is erased anyway.
uint32_t dst_addr = current_number; // starting address
while (dst_addr < MAX_FLASH)
{
// Execute "ER" Erase Row
NVMCTRL->ADDR.reg = dst_addr / 2;
NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_ER;
while (NVMCTRL->INTFLAG.bit.READY == 0)
;
dst_addr += PAGE_SIZE * 4; // Skip a ROW
}
// Notify command completed
ptr_monitor_if->putdata("X\n\r", 3);
}
else if (command == 'Y')
{
// This command writes the content of a buffer in SRAM into flash memory.
// Syntax: Y[ADDR],0#
// Set the starting address of the SRAM buffer.
// Syntax: Y[ROM_ADDR],[SIZE]#
// Write the first SIZE bytes from the SRAM buffer (previously set) into
// flash memory starting from address ROM_ADDR
static uint32_t *src_buff_addr = NULL;
if (current_number == 0)
{
// Set buffer address
src_buff_addr = (uint32_t*)ptr_data;
}
else
{
// Write to flash
uint32_t size = current_number/4;
uint32_t *src_addr = src_buff_addr;
uint32_t *dst_addr = (uint32_t*)ptr_data;
// Set automatic page write
NVMCTRL->CTRLB.bit.MANW = 0;
// Do writes in pages
while (size)
{
// Execute "PBC" Page Buffer Clear
NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_PBC;
while (NVMCTRL->INTFLAG.bit.READY == 0)
;
// Fill page buffer
uint32_t i;
for (i=0; i<(PAGE_SIZE/4) && i<size; i++)
{
dst_addr[i] = src_addr[i];
}
// Execute "WP" Write Page
//NVMCTRL->ADDR.reg = ((uint32_t)dst_addr) / 2;
NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_WP;
while (NVMCTRL->INTFLAG.bit.READY == 0)
;
// Advance to next page
dst_addr += i;
src_addr += i;
size -= i;
}
}
// Notify command completed
ptr_monitor_if->putdata("Y\n\r", 3);
}
else if (command == 'Z')
{
// This command calculate CRC for a given area of memory.
// It's useful to quickly check if a transfer has been done
// successfully.
// Syntax: Z[START_ADDR],[SIZE]#
// Returns: Z[CRC]#
uint8_t *data = (uint8_t *)ptr_data;
uint32_t size = current_number;
uint16_t crc = 0;
uint32_t i = 0;
for (i=0; i<size; i++)
crc = serial_add_crc(*data++, crc);
// Send response
ptr_monitor_if->putdata("Z", 1);
put_uint32(crc);
ptr_monitor_if->putdata("#\n\r", 3);
}
command = 'z';
current_number = 0;
if (b_terminal_mode)
{
ptr_monitor_if->putdata(">", 1);
}
}
else
{
if (('0' <= *ptr) && (*ptr <= '9'))
{
current_number = (current_number << 4) | (*ptr - '0');
}
else if (('A' <= *ptr) && (*ptr <= 'F'))
{
current_number = (current_number << 4) | (*ptr - 'A' + 0xa);
}
else if (('a' <= *ptr) && (*ptr <= 'f'))
{
current_number = (current_number << 4) | (*ptr - 'a' + 0xa);
}
else if (*ptr == ',')
{
ptr_data = (uint8_t *) current_number;
current_number = 0;
}
else
{
command = *ptr;
current_number = 0;
}
}
}
}
/**
* \brief This function starts the SAM-BA monitor.
*/
void sam_ba_monitor_run(void)
{
uint32_t pageSizes[] = { 8, 16, 32, 64, 128, 256, 512, 1024 };
PAGE_SIZE = pageSizes[NVMCTRL->PARAM.bit.PSZ];
PAGES = NVMCTRL->PARAM.bit.NVMP;
MAX_FLASH = PAGE_SIZE * PAGES;
ptr_data = NULL;
command = 'z';
while (1)
{
sam_ba_monitor_loop();
}
}