mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
Merge pull request #15493 from benpicco/riotboot-serial
riotboot: implement serial flasher
This commit is contained in:
commit
4f905bfa8c
4
.github/workflows/tools-buildtest.yml
vendored
4
.github/workflows/tools-buildtest.yml
vendored
@ -71,3 +71,7 @@ jobs:
|
||||
uses: aabadie/riot-action@v1
|
||||
with:
|
||||
cmd: make -C dist/tools/lpc2k_pgm
|
||||
- name: Build riotboot_serial tool
|
||||
uses: aabadie/riot-action@v1
|
||||
with:
|
||||
cmd: make -C dist/tools/riotboot_serial
|
||||
|
@ -21,13 +21,34 @@
|
||||
#include "board.h"
|
||||
#include "periph/gpio.h"
|
||||
|
||||
void led_init(void);
|
||||
static void led_init(void)
|
||||
{
|
||||
/* initialize and turn off LEDs */
|
||||
#ifdef LED0_PIN
|
||||
gpio_init(LED0_PIN, GPIO_OUT);
|
||||
LED0_OFF;
|
||||
#endif
|
||||
#ifdef LED1_PIN
|
||||
gpio_init(LED1_PIN, GPIO_OUT);
|
||||
LED1_OFF;
|
||||
#endif
|
||||
#ifdef LED2_PIN
|
||||
gpio_init(LED2_PIN, GPIO_OUT);
|
||||
LED2_OFF;
|
||||
#endif
|
||||
#ifdef LED3_PIN
|
||||
gpio_init(LED3_PIN, GPIO_OUT);
|
||||
LED3_OFF;
|
||||
#endif
|
||||
}
|
||||
|
||||
void board_init(void)
|
||||
{
|
||||
/* initialize the CPU core */
|
||||
cpu_init();
|
||||
|
||||
/* initialize LEDs */
|
||||
led_init();
|
||||
/* initialize LEDs, skip for riotboot */
|
||||
if (!IS_ACTIVE(RIOTBOOT)) {
|
||||
led_init();
|
||||
}
|
||||
}
|
||||
|
@ -1,44 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Inria
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup boards_common_kw41z
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Common led initialization for KW41Z based boards
|
||||
*
|
||||
* @author Alexandre Abadie <alexandre.abadie@inria.fr>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include "board.h"
|
||||
#include "cpu.h"
|
||||
#include "periph/gpio.h"
|
||||
|
||||
void led_init(void)
|
||||
{
|
||||
/* initialize and turn off LEDs */
|
||||
#ifdef LED0_PIN
|
||||
gpio_init(LED0_PIN, GPIO_OUT);
|
||||
gpio_set(LED0_PIN);
|
||||
#endif
|
||||
#ifdef LED1_PIN
|
||||
gpio_init(LED1_PIN, GPIO_OUT);
|
||||
gpio_set(LED1_PIN);
|
||||
#endif
|
||||
#ifdef LED2_PIN
|
||||
gpio_init(LED2_PIN, GPIO_OUT);
|
||||
gpio_set(LED2_PIN);
|
||||
#endif
|
||||
#ifdef LED3_PIN
|
||||
gpio_init(LED3_PIN, GPIO_OUT);
|
||||
gpio_set(LED3_PIN);
|
||||
#endif
|
||||
}
|
@ -1,35 +1,7 @@
|
||||
# Default RIOT bootloader
|
||||
APPLICATION = riotboot
|
||||
|
||||
# Default testing board
|
||||
BOARD ?= samr21-xpro
|
||||
|
||||
# Select the boards with riotboot feature
|
||||
FEATURES_REQUIRED += riotboot
|
||||
|
||||
# Set RIOTBOOT_BUILD to indicate a riotboot application build
|
||||
RIOTBOOT_BUILD = 1
|
||||
# Provide a define to detect if building the bootloader
|
||||
CFLAGS += -DRIOTBOOT
|
||||
|
||||
# Disable unused modules
|
||||
CFLAGS += -DNDEBUG -DLOG_LEVEL=LOG_NONE
|
||||
DISABLE_MODULE += core_init core_msg core_panic
|
||||
DISABLE_MODULE += auto_init auto_init_%
|
||||
DISABLE_MODULE += pm_layered
|
||||
|
||||
# avoid using stdio
|
||||
USEMODULE += stdio_null
|
||||
|
||||
# Include riotboot flash partition functionality
|
||||
USEMODULE += riotboot_slot
|
||||
|
||||
# RIOT codebase
|
||||
RIOTBASE ?= $(CURDIR)/../../
|
||||
|
||||
include $(RIOTBASE)/Makefile.include
|
||||
|
||||
# limit riotboot bootloader size
|
||||
# TODO: Manage to set this variable for boards which already embed a
|
||||
# bootloader, currently it will be overwritten
|
||||
ROM_LEN := $(RIOTBOOT_LEN)
|
||||
include ../riotboot_common.mk
|
||||
|
29
bootloaders/riotboot_common.mk
Normal file
29
bootloaders/riotboot_common.mk
Normal file
@ -0,0 +1,29 @@
|
||||
# Default testing board
|
||||
BOARD ?= samr21-xpro
|
||||
|
||||
# Select the boards with riotboot feature
|
||||
FEATURES_REQUIRED += riotboot
|
||||
|
||||
# Set RIOTBOOT_BUILD to indicate a riotboot application build
|
||||
RIOTBOOT_BUILD = 1
|
||||
# Provide a define to detect if building the bootloader
|
||||
CFLAGS += -DRIOTBOOT
|
||||
|
||||
# Disable unused modules
|
||||
CFLAGS += -DNDEBUG -DLOG_LEVEL=LOG_NONE
|
||||
DISABLE_MODULE += core_init core_msg core_panic
|
||||
DISABLE_MODULE += auto_init auto_init_%
|
||||
DISABLE_MODULE += pm_layered
|
||||
|
||||
# avoid using stdio
|
||||
USEMODULE += stdio_null
|
||||
|
||||
# RIOT codebase
|
||||
RIOTBASE ?= $(CURDIR)/../..
|
||||
|
||||
include $(RIOTBASE)/Makefile.include
|
||||
|
||||
# limit riotboot bootloader size
|
||||
# TODO: Manage to set this variable for boards which already embed a
|
||||
# bootloader, currently it will be overwritten
|
||||
ROM_LEN := $(RIOTBOOT_LEN)
|
@ -1,33 +1,12 @@
|
||||
# Default RIOT bootloader
|
||||
APPLICATION = riotboot_dfu
|
||||
|
||||
# Default testing board
|
||||
BOARD ?= samr21-xpro
|
||||
|
||||
# Select the boards with riotboot feature
|
||||
FEATURES_REQUIRED += riotboot
|
||||
|
||||
# Set RIOTBOOT_BUILD to indicate a riotboot application build
|
||||
RIOTBOOT_BUILD = 1
|
||||
# Provide a define to detect if building the bootloader
|
||||
CFLAGS += -DRIOTBOOT
|
||||
|
||||
# Disable unused modules
|
||||
CFLAGS += -DNDEBUG -DLOG_LEVEL=LOG_NONE
|
||||
DISABLE_MODULE += core_init core_msg core_panic
|
||||
DISABLE_MODULE += auto_init auto_init_%
|
||||
|
||||
# avoid using stdio
|
||||
USEMODULE += stdio_null
|
||||
# Add RIOTBOOT USB DFU integration
|
||||
USEMODULE += riotboot_usb_dfu
|
||||
|
||||
# Use xtimer for scheduled reboot
|
||||
USEMODULE += xtimer
|
||||
|
||||
# RIOT codebase
|
||||
RIOTBASE ?= $(CURDIR)/../../
|
||||
|
||||
# USB device vendor and product ID
|
||||
# pid.codes test VID/PID, not globally unique
|
||||
|
||||
@ -36,9 +15,4 @@ RIOTBASE ?= $(CURDIR)/../../
|
||||
USB_VID ?= 1209
|
||||
USB_PID ?= 7D02
|
||||
|
||||
include $(RIOTBASE)/Makefile.include
|
||||
|
||||
# limit riotboot bootloader size
|
||||
# TODO: Manage to set this variable for boards which already embed a
|
||||
# bootloader, currently it will be overwritten
|
||||
ROM_LEN := $(RIOTBOOT_LEN)
|
||||
include ../riotboot_common.mk
|
||||
|
@ -14,7 +14,7 @@ At startup, the DFU mode is entered when either
|
||||
|
||||
- the first button was pressed when the board started (configurable at board level using @ref BTN_BOOTLOADER_PIN), or
|
||||
|
||||
- the last running firmware asked the bootloader to go to DFU mode by using a magic number (see @ref RIOTBOOT_DFU_ADDR).
|
||||
- the last running firmware asked the bootloader to go to DFU mode by using a magic number (see @ref RIOTBOOT_MAGIC_ADDR).
|
||||
|
||||
# Prerequisites
|
||||
|
||||
|
10
bootloaders/riotboot_serial/Makefile
Normal file
10
bootloaders/riotboot_serial/Makefile
Normal file
@ -0,0 +1,10 @@
|
||||
# Default RIOT bootloader
|
||||
APPLICATION = riotboot_serial
|
||||
|
||||
# Include riotboot flash partition functionality
|
||||
# USEMODULE += riotboot_slot
|
||||
|
||||
# Include serial bootloader functionality
|
||||
USEMODULE += riotboot_serial
|
||||
|
||||
include ../riotboot_common.mk
|
3
bootloaders/riotboot_serial/Makefile.ci
Normal file
3
bootloaders/riotboot_serial/Makefile.ci
Normal file
@ -0,0 +1,3 @@
|
||||
BOARD_INSUFFICIENT_MEMORY := \
|
||||
phynode-kw41z \
|
||||
#
|
24
bootloaders/riotboot_serial/doc.txt
Normal file
24
bootloaders/riotboot_serial/doc.txt
Normal file
@ -0,0 +1,24 @@
|
||||
/**
|
||||
@defgroup bootloader_riotboot_serial riotboot serial bootloader
|
||||
@ingroup bootloaders
|
||||
|
||||
# Overview
|
||||
`riotboot_dfu` is a variation on @ref bootloader_riotboot that adds the capability to flash
|
||||
a new firmware using a serial (UART) connection.
|
||||
|
||||
After reset, riotboot will wait for `RIOTBOOT_DELAY_MS` ms, if within that time no command is
|
||||
received it will automatically start the application.
|
||||
|
||||
If it reads the `B` character, it will enter bootloader mode where flash sectors can be erased
|
||||
and written.
|
||||
|
||||
The application in `dist/tools/riotboot_serial` is used to talk to the bootloader and to flash
|
||||
`.hex` files using the bootloader's serial protocol.
|
||||
|
||||
# Flashing a device
|
||||
To flash a device that is equipped with the riotboot serial bootloader, set the `PROGRAMMER` variable
|
||||
to `riotboot_serial` and chose the `PORT` accordingly.
|
||||
|
||||
e.g.
|
||||
|
||||
make BOARD=same54-xpro PORT=/dev/ttyACM0 PROGRAMMER=riotboot_serial flash
|
93
bootloaders/riotboot_serial/main.c
Normal file
93
bootloaders/riotboot_serial/main.c
Normal file
@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Kaspar Schleiser <kaspar@schleiser.de>
|
||||
* Inria
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup bootloaders
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Serial UART riot-based bootloader
|
||||
*
|
||||
* @author Kaspar Schleiser <kaspar@schleiser.de>
|
||||
* @author Francisco Acosta <francisco.acosta@inria.fr>
|
||||
* @author Benjamin Valentin <benjamin.valentin@ml-pa.com>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include "cpu.h"
|
||||
#include "panic.h"
|
||||
#include "riotboot/slot.h"
|
||||
#include "riotboot/serial.h"
|
||||
|
||||
static int _get_slot(void)
|
||||
{
|
||||
uint32_t version = 0;
|
||||
int slot = -1;
|
||||
|
||||
if (!IS_USED(MODULE_RIOTBOOT_SLOT)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < riotboot_slot_numof; i++) {
|
||||
const riotboot_hdr_t *riot_hdr = riotboot_slot_get_hdr(i);
|
||||
if (riotboot_slot_validate(i)) {
|
||||
/* skip slot if metadata broken */
|
||||
continue;
|
||||
}
|
||||
if (riot_hdr->start_addr != riotboot_slot_get_image_startaddr(i)) {
|
||||
continue;
|
||||
}
|
||||
if (slot == -1 || riot_hdr->version > version) {
|
||||
version = riot_hdr->version;
|
||||
slot = i;
|
||||
}
|
||||
}
|
||||
|
||||
return slot;
|
||||
}
|
||||
|
||||
static void _boot_default(int slot)
|
||||
{
|
||||
if (!IS_USED(MODULE_RIOTBOOT_SLOT)) {
|
||||
/* boot 'raw' firmware image */
|
||||
cpu_jump_to_image(SLOT0_OFFSET);
|
||||
return;
|
||||
}
|
||||
|
||||
if (slot == -1) {
|
||||
slot = _get_slot();
|
||||
}
|
||||
|
||||
if (slot != -1) {
|
||||
riotboot_slot_jump(slot);
|
||||
}
|
||||
}
|
||||
|
||||
void kernel_init(void)
|
||||
{
|
||||
int slot = -1;
|
||||
|
||||
if (IS_USED(MODULE_RIOTBOOT_SERIAL)) {
|
||||
slot = riotboot_serial_loader();
|
||||
}
|
||||
|
||||
irq_disable();
|
||||
_boot_default(slot);
|
||||
|
||||
/* serious trouble! nothing to boot */
|
||||
while (1) {}
|
||||
}
|
||||
|
||||
NORETURN void core_panic(core_panic_t crash_code, const char *message)
|
||||
{
|
||||
(void)crash_code;
|
||||
(void)message;
|
||||
while (1) {}
|
||||
}
|
@ -30,6 +30,7 @@
|
||||
#include "periph/pm.h"
|
||||
#include "periph/gpio.h"
|
||||
#include "periph/usbdev.h"
|
||||
#include "pm_layered.h"
|
||||
#include "usbdev_stm32.h"
|
||||
|
||||
/**
|
||||
@ -922,7 +923,6 @@ static int _usbdev_ep_ready(usbdev_ep_t *ep, size_t len)
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
if (_uses_dma(conf)) {
|
||||
_in_regs(conf, ep->num)->DIEPDMA = (uint32_t)ep->buf;
|
||||
}
|
||||
|
14
dist/tools/riotboot_serial/Makefile
vendored
Normal file
14
dist/tools/riotboot_serial/Makefile
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
CFLAGS += -Wall -Wextra
|
||||
CFLAGS += -I../../../sys/include
|
||||
|
||||
OBJS := main.o serial.o
|
||||
|
||||
%.o: %.c
|
||||
@cc $(CFLAGS) -c -o $@ $<
|
||||
|
||||
riotboot_serial: $(OBJS)
|
||||
@cc -o $@ $(OBJS)
|
||||
|
||||
clean:
|
||||
@rm -fr $(OBJS)
|
||||
@rm -fr riotboot_serial
|
343
dist/tools/riotboot_serial/main.c
vendored
Normal file
343
dist/tools/riotboot_serial/main.c
vendored
Normal file
@ -0,0 +1,343 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Benjamin Valentin
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU General Public
|
||||
* License v2. See the file LICENSE for more details.
|
||||
*
|
||||
* @author Benjamin Valentin <benjamin.valentin@ml-pa.com>
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <termios.h>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include "riotboot/serial.h"
|
||||
|
||||
extern int serial_open(int argc, char **argv);
|
||||
|
||||
typedef int (*line_cb_t)(void *ctx, uint32_t addr, uint8_t len, const char *s);
|
||||
|
||||
enum {
|
||||
IHEX_DATA,
|
||||
IHEX_EOF,
|
||||
IHEX_EXT_SEG_ADDR,
|
||||
IHEX_START_ADDR,
|
||||
};
|
||||
|
||||
static uint8_t crc8(const uint8_t *data, size_t len,
|
||||
uint8_t g_polynom, uint8_t crc)
|
||||
{
|
||||
/* iterate over all bytes */
|
||||
for (size_t i=0; i < len; i++)
|
||||
{
|
||||
crc ^= data[i];
|
||||
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
bool xor = crc & 0x80;
|
||||
crc = crc << 1;
|
||||
crc = xor ? crc ^ g_polynom : crc;
|
||||
}
|
||||
}
|
||||
|
||||
return crc;
|
||||
}
|
||||
|
||||
static char _get_char(int fd)
|
||||
{
|
||||
char in;
|
||||
if (read(fd, &in, 1) == 1) {
|
||||
return in;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _put_char(int fd, char c)
|
||||
{
|
||||
return write(fd, &c, 1);
|
||||
}
|
||||
|
||||
static int _write_crc(int fd, const void *buf, size_t count, uint8_t *crc)
|
||||
{
|
||||
*crc = crc8(buf, count, RIOTBOOT_CRC8_POLY, *crc);
|
||||
return write(fd, buf, count);
|
||||
}
|
||||
|
||||
static int _write_crc_byte(int fd, uint8_t byte, uint8_t *crc)
|
||||
{
|
||||
return _write_crc(fd, &byte, 1, crc);
|
||||
}
|
||||
|
||||
static int _get_result(int fd)
|
||||
{
|
||||
while (1) {
|
||||
char c = _get_char(fd);
|
||||
|
||||
switch (c) {
|
||||
case 0:
|
||||
continue;
|
||||
case RIOTBOOT_STAT_OK:
|
||||
printf("\r");
|
||||
return 0;
|
||||
case RIOTBOOT_STAT_BAD_CRC:
|
||||
puts("CRC Error");
|
||||
return 1;
|
||||
case RIOTBOOT_STAT_ILLEGAL:
|
||||
puts("Illegal Address");
|
||||
return -1;
|
||||
default:
|
||||
putchar(c);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static int cmd_erase(int fd, uint32_t sector)
|
||||
{
|
||||
uint8_t crc = 0xFF;
|
||||
_write_crc_byte(fd, RIOTBOOT_CMD_ERASE, &crc);
|
||||
_write_crc_byte(fd, sizeof(sector), &crc);
|
||||
_write_crc(fd, §or, sizeof(sector), &crc);
|
||||
write(fd, &crc, 1);
|
||||
|
||||
return _get_result(fd);
|
||||
}
|
||||
|
||||
__attribute__((unused))
|
||||
static int cmd_write(int fd, uint32_t addr, const void *data, size_t len)
|
||||
{
|
||||
uint8_t crc = 0xFF;
|
||||
_write_crc_byte(fd, RIOTBOOT_CMD_WRITE, &crc);
|
||||
_write_crc_byte(fd, sizeof(addr) + len, &crc);
|
||||
_write_crc(fd, &addr, sizeof(addr), &crc);
|
||||
_write_crc(fd, data, len, &crc);
|
||||
write(fd, &crc, 1);
|
||||
|
||||
return _get_result(fd);
|
||||
}
|
||||
|
||||
static int cmd_boot(int fd)
|
||||
{
|
||||
_put_char(fd, RIOTBOOT_CMD_BOOT);
|
||||
_put_char(fd, '\n');
|
||||
return _get_result(fd);
|
||||
}
|
||||
|
||||
static uint32_t _get_page(int fd, uint32_t addr)
|
||||
{
|
||||
do {
|
||||
uint8_t crc = 0xFF;
|
||||
_write_crc_byte(fd, RIOTBOOT_CMD_GET_PAGE, &crc);
|
||||
_write_crc_byte(fd, sizeof(addr), &crc);
|
||||
_write_crc(fd, &addr, sizeof(addr), &crc);
|
||||
write(fd, &crc, 1);
|
||||
|
||||
} while (_get_result(fd) > 1);
|
||||
|
||||
uint32_t page = 0;
|
||||
read(fd, &page, sizeof(page));
|
||||
return page;
|
||||
}
|
||||
|
||||
static uint32_t cmd_get_page(int fd, uint32_t addr)
|
||||
{
|
||||
uint32_t page[2];
|
||||
|
||||
do {
|
||||
page[0] = _get_page(fd, addr);
|
||||
page[1] = _get_page(fd, addr);
|
||||
} while (page[0] != page[1]);
|
||||
|
||||
return page[0];
|
||||
}
|
||||
|
||||
static uint8_t _int(char c)
|
||||
{
|
||||
if (c >= '0' && c <= '9') {
|
||||
return c - '0';
|
||||
}
|
||||
|
||||
if (c >= 'A' && c <= 'F') {
|
||||
return c - 'A' + 0xA;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned _hex_to_int(const char *s, size_t len)
|
||||
{
|
||||
unsigned ret = 0;
|
||||
while (len--) {
|
||||
ret <<= 4;
|
||||
ret += _int(*s++);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int _write_out(void *ctx, uint32_t addr, uint8_t len, const char *s)
|
||||
{
|
||||
int fd = *(int*)ctx;
|
||||
|
||||
printf("0x%x: %u bytes ", addr, len);
|
||||
|
||||
uint8_t crc = 0xFF;
|
||||
_write_crc_byte(fd, RIOTBOOT_CMD_WRITE, &crc);
|
||||
_write_crc_byte(fd, sizeof(addr) + len, &crc);
|
||||
_write_crc(fd, &addr, sizeof(addr), &crc);
|
||||
|
||||
while (len--) {
|
||||
uint8_t byte = _hex_to_int(s, 2);
|
||||
_write_crc_byte(fd, byte, &crc);
|
||||
s += 2;
|
||||
}
|
||||
|
||||
write(fd, &crc, 1);
|
||||
|
||||
return _get_result(fd);
|
||||
}
|
||||
|
||||
static void _parse_ihex(FILE *hex, line_cb_t cb, void *ctx)
|
||||
{
|
||||
char line[48];
|
||||
char* pos;
|
||||
uint32_t offset = 0;
|
||||
unsigned line_count = 0;
|
||||
|
||||
rewind(hex);
|
||||
while ((pos = fgets(line, sizeof(line), hex))) {
|
||||
|
||||
++line_count;
|
||||
if (*pos != ':') {
|
||||
printf("%u: skipping line", line_count);
|
||||
continue;
|
||||
}
|
||||
|
||||
pos += 1;
|
||||
uint8_t len = _hex_to_int(pos, 2);
|
||||
pos += 2;
|
||||
uint32_t addr = _hex_to_int(pos, 4) + offset;
|
||||
pos += 4;
|
||||
uint8_t type = _hex_to_int(pos, 2);
|
||||
pos += 2;
|
||||
|
||||
switch (type) {
|
||||
case IHEX_DATA:
|
||||
while (cb(ctx, addr, len, pos) > 0) {}
|
||||
break;
|
||||
case IHEX_EXT_SEG_ADDR:
|
||||
offset = _hex_to_int(pos, len) << 12;
|
||||
printf("%u: adding offset: %x\n", line_count, offset);
|
||||
break;
|
||||
case IHEX_EOF:
|
||||
return;
|
||||
case IHEX_START_ADDR:
|
||||
break;
|
||||
default:
|
||||
printf("%u: unknown record type %x\n", line_count, type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int _min_max(void *ctx, uint32_t addr, uint8_t len, const char *s)
|
||||
{
|
||||
(void)s;
|
||||
|
||||
uint32_t *min_max = ctx;
|
||||
|
||||
if (addr < min_max[0]) {
|
||||
min_max[0] = addr;
|
||||
}
|
||||
|
||||
if (addr + len > min_max[1]) {
|
||||
min_max[1] = addr + len;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void _connect_bootloader(int fd)
|
||||
{
|
||||
const char spinner[] = "|/-\\";
|
||||
unsigned tries = 0;
|
||||
while (_get_char(fd) != RIOTBOOT_STAT_WAITING) {
|
||||
printf("connecting [%c]", spinner[tries % (sizeof(spinner) - 1)]);
|
||||
_put_char(fd, RIOTBOOT_PROBE);
|
||||
++tries;
|
||||
|
||||
if (tcdrain(fd)) {
|
||||
puts("\rSerial adapter disconnected");
|
||||
close(fd);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
printf("\r");
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
do {
|
||||
write(fd, "B\n", 2);
|
||||
} while (_get_char(fd) != RIOTBOOT_STAT_READY);
|
||||
|
||||
puts("\rconnected.");
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
if (argc < 2) {
|
||||
fprintf(stderr, "usage: %s <file> <tty> [baudrate]\n", argv[0]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
const char *file = argv[1];
|
||||
FILE *hex = fopen(file, "r");
|
||||
|
||||
if (hex == NULL) {
|
||||
fprintf(stderr, "can't open '%s'\n", file);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* parse hex file to get firmware address range */
|
||||
uint32_t min_max[2] = {UINT32_MAX, 0};
|
||||
_parse_ihex(hex, _min_max, min_max);
|
||||
|
||||
printf("%u byte firmware (0x%x - 0x%x)\n",
|
||||
min_max[1] - min_max[0], min_max[0], min_max[1]);
|
||||
|
||||
int fd = serial_open(argc - 2, &argv[2]);
|
||||
|
||||
if (fd < 0) {
|
||||
fprintf(stderr, "can't open serial interface\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* sync with the boorloader */
|
||||
_connect_bootloader(fd);
|
||||
|
||||
/* find out how many pages we have to erase */
|
||||
uint32_t first_page = cmd_get_page(fd, min_max[0]);
|
||||
uint32_t last_page = cmd_get_page(fd, min_max[1]);
|
||||
|
||||
printf("erasing pages %u to %u\n", first_page, last_page);
|
||||
|
||||
for (unsigned i = first_page; i <= last_page; ++i) {
|
||||
printf("erase %u ", i);
|
||||
cmd_erase(fd, i);
|
||||
}
|
||||
|
||||
/* flash the firmware */
|
||||
_parse_ihex(hex, _write_out, &fd);
|
||||
|
||||
cmd_boot(fd);
|
||||
|
||||
close(fd);
|
||||
|
||||
error:
|
||||
fclose(hex);
|
||||
|
||||
return 0;
|
||||
}
|
166
dist/tools/riotboot_serial/serial.c
vendored
Normal file
166
dist/tools/riotboot_serial/serial.c
vendored
Normal file
@ -0,0 +1,166 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Kaspar Schleiser <kaspar@schleiser.de>
|
||||
* Copyright (C) 2018 Freie Universität Berlin
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU General Public
|
||||
* License v2. See the file LICENSE for more details.
|
||||
*
|
||||
* @author Kaspar Schleiser <kaspar@schleiser.de>
|
||||
* @author Martine Lenders <m.lenders@fu-berlin.de>
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <termios.h>
|
||||
|
||||
#define BAUDRATE_DEFAULT (B115200)
|
||||
#define TTY_TIMEOUT_MS (500U)
|
||||
|
||||
static int _set_serial_attribs(int fd, int speed, int parity)
|
||||
{
|
||||
struct termios tty;
|
||||
|
||||
memset(&tty, 0, sizeof(tty));
|
||||
if (tcgetattr(fd, &tty) != 0) {
|
||||
perror("error in tcgetattr");
|
||||
return -1;
|
||||
}
|
||||
|
||||
cfsetospeed(&tty, speed);
|
||||
cfsetispeed(&tty, speed);
|
||||
|
||||
tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8; /* 8-bit chars */
|
||||
/* disable IGNBRK for mismatched speed
|
||||
* tests; otherwise receive break */
|
||||
/* as \000 chars */
|
||||
tty.c_iflag &= ~IGNBRK; /* disable break processing */
|
||||
tty.c_lflag = 0; /* no signaling chars, no echo, */
|
||||
/* no canonical processing */
|
||||
tty.c_oflag = 0; /* no remapping, no delays */
|
||||
tty.c_cc[VMIN] = 0; /* read doesn't block */
|
||||
tty.c_cc[VTIME] = TTY_TIMEOUT_MS / 100; /* 0.5 seconds read timeout */
|
||||
/* in tenths of a second */
|
||||
|
||||
tty.c_iflag &= ~(IXON | IXOFF | IXANY); /* shut off xon/xoff ctrl */
|
||||
|
||||
tty.c_cflag |= (CLOCAL | CREAD); /* ignore modem controls, */
|
||||
/* enable reading */
|
||||
tty.c_cflag &= ~(PARENB | PARODD); /* shut off parity */
|
||||
tty.c_cflag |= parity;
|
||||
tty.c_cflag &= ~CSTOPB;
|
||||
tty.c_cflag &= ~CRTSCTS;
|
||||
cfmakeraw(&tty);
|
||||
|
||||
if (tcsetattr(fd, TCSANOW, &tty) != 0) {
|
||||
perror("error from tcsetattr");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void _set_blocking(int fd, int should_block)
|
||||
{
|
||||
struct termios tty;
|
||||
|
||||
memset(&tty, 0, sizeof tty);
|
||||
if (tcgetattr(fd, &tty) != 0) {
|
||||
perror("error from tggetattr");
|
||||
return;
|
||||
}
|
||||
|
||||
tty.c_cc[VMIN] = should_block ? 1 : 0;
|
||||
tty.c_cc[VTIME] = TTY_TIMEOUT_MS / 100; /* 0.5 seconds read timeout */
|
||||
/* in tenths of a second */
|
||||
|
||||
if (tcsetattr(fd, TCSANOW, &tty) != 0) {
|
||||
perror("error setting term attributes");
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned _parse_baudrate(char *arg)
|
||||
{
|
||||
#define case_baudrate(val) \
|
||||
case val: \
|
||||
return B ## val
|
||||
|
||||
if (arg == NULL) {
|
||||
return BAUDRATE_DEFAULT;
|
||||
}
|
||||
switch (strtol(arg, NULL, 10)) {
|
||||
case_baudrate(9600);
|
||||
case_baudrate(19200);
|
||||
case_baudrate(38400);
|
||||
case_baudrate(57600);
|
||||
case_baudrate(115200);
|
||||
/* the following baudrates might not be available on all platforms */
|
||||
#ifdef B230400
|
||||
case_baudrate(230400);
|
||||
#endif
|
||||
#ifdef B460800
|
||||
case_baudrate(460800);
|
||||
#endif
|
||||
#ifdef B500000
|
||||
case_baudrate(500000);
|
||||
#endif
|
||||
#ifdef B576000
|
||||
case_baudrate(576000);
|
||||
#endif
|
||||
#ifdef B921600
|
||||
case_baudrate(921600);
|
||||
#endif
|
||||
#ifdef B1000000
|
||||
case_baudrate(1000000);
|
||||
#endif
|
||||
#ifdef B1152000
|
||||
case_baudrate(1152000);
|
||||
#endif
|
||||
#ifdef B1500000
|
||||
case_baudrate(1500000);
|
||||
#endif
|
||||
#ifdef B2000000
|
||||
case_baudrate(2000000);
|
||||
#endif
|
||||
#ifdef B2500000
|
||||
case_baudrate(2500000);
|
||||
#endif
|
||||
#ifdef B3000000
|
||||
case_baudrate(3000000);
|
||||
#endif
|
||||
#ifdef B3500000
|
||||
case_baudrate(3500000);
|
||||
#endif
|
||||
#ifdef B4000000
|
||||
case_baudrate(4000000);
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
#undef case_baudrate
|
||||
return 0;
|
||||
}
|
||||
|
||||
int serial_open(int argc, char **argv)
|
||||
{
|
||||
unsigned baudrate = BAUDRATE_DEFAULT;
|
||||
int fd;
|
||||
|
||||
if (argc > 1) {
|
||||
baudrate = _parse_baudrate(argv[1]);
|
||||
}
|
||||
if (baudrate == 0) {
|
||||
fputs("Invalid baudrate\n", stderr);
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
fd = open(argv[0], O_RDWR | O_NOCTTY | O_SYNC);
|
||||
if (fd < 0) {
|
||||
return fd;
|
||||
}
|
||||
_set_serial_attribs(fd, baudrate, 0);
|
||||
_set_blocking(fd, 0);
|
||||
return fd;
|
||||
}
|
13
makefiles/tools/riotboot_serial.inc.mk
Normal file
13
makefiles/tools/riotboot_serial.inc.mk
Normal file
@ -0,0 +1,13 @@
|
||||
RIOTBOOT_SERIAL := $(RIOTTOOLS)/riotboot_serial/riotboot_serial
|
||||
|
||||
FLASHER ?= $(RIOTBOOT_SERIAL)
|
||||
FLASHFILE ?= $(HEXFILE)
|
||||
PROG_BAUD ?= 115200
|
||||
FFLAGS ?= $(FLASHFILE) $(PORT) $(PROG_BAUD)
|
||||
|
||||
ROM_OFFSET ?= $(RIOTBOOT_LEN)
|
||||
|
||||
FLASHDEPS += $(RIOTBOOT_SERIAL)
|
||||
|
||||
$(RIOTBOOT_SERIAL):
|
||||
$(Q)env -u CC -u CFLAGS $(MAKE) -C $(RIOTTOOLS)/riotboot_serial
|
@ -643,6 +643,18 @@ ifneq (,$(filter riotboot_slot, $(USEMODULE)))
|
||||
USEMODULE += riotboot_hdr
|
||||
endif
|
||||
|
||||
ifneq (,$(filter riotboot_serial, $(USEMODULE)))
|
||||
FEATURES_REQUIRED += periph_flashpage
|
||||
FEATURES_REQUIRED += periph_uart
|
||||
USEMODULE += riotboot_reset
|
||||
USEMODULE += checksum
|
||||
endif
|
||||
|
||||
ifneq (,$(filter riotboot_reset, $(USEMODULE)))
|
||||
USEMODULE += riotboot
|
||||
USEMODULE += usb_board_reset
|
||||
endif
|
||||
|
||||
ifneq (,$(filter riotboot_hdr, $(USEMODULE)))
|
||||
USEMODULE += checksum
|
||||
USEMODULE += riotboot
|
||||
|
@ -141,7 +141,7 @@ ifneq (,$(filter prng,$(USEMODULE)))
|
||||
include $(RIOTBASE)/sys/random/Makefile.include
|
||||
endif
|
||||
|
||||
ifneq (,$(filter usbus_dfu,$(USEMODULE)))
|
||||
ifneq (,$(filter usbus_dfu riotboot_reset,$(USEMODULE)))
|
||||
CFLAGS += -DCPU_RAM_BASE=$(RAM_START_ADDR)
|
||||
CFLAGS += -DCPU_RAM_SIZE=$(RAM_LEN)
|
||||
endif
|
||||
|
47
sys/include/riotboot/magic.h
Normal file
47
sys/include/riotboot/magic.h
Normal file
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Mesotic SAS
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup sys_riotboot_magic Magic values for riotboot
|
||||
* @ingroup sys
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief USB DFU/serial initialization constants for riotboot
|
||||
*
|
||||
* @author Dylan Laduranty <dylan.laduranty@mesotic.com>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#ifndef RIOTBOOT_MAGIC_H
|
||||
#define RIOTBOOT_MAGIC_H
|
||||
|
||||
#include "riotboot/hdr.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @name USB RAM information for riotboot
|
||||
* @{
|
||||
*/
|
||||
#ifndef RIOTBOOT_MAGIC_ADDR
|
||||
#define RIOTBOOT_MAGIC_ADDR (CPU_RAM_BASE + CPU_RAM_SIZE - 4) /**< default magic address */
|
||||
#endif
|
||||
#ifndef RIOTBOOT_MAGIC_NUMBER
|
||||
#define RIOTBOOT_MAGIC_NUMBER RIOTBOOT_MAGIC /**< default magic value */
|
||||
#endif
|
||||
/** @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* RIOTBOOT_MAGIC_H */
|
120
sys/include/riotboot/serial.h
Normal file
120
sys/include/riotboot/serial.h
Normal file
@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Benjamin Valentin
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup sys_riotboot_serial Serial Bootloader Protocol
|
||||
* @ingroup sys
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief riotboot as a serial bootloader
|
||||
*
|
||||
* @author Benjamin Valentin <benjamin.valentin@ml-pa.com>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#ifndef RIOTBOOT_SERIAL_H
|
||||
#define RIOTBOOT_SERIAL_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief riotboot commands
|
||||
* Commands typically have the format [type|length|value]
|
||||
* where type and length are one byte and value is $length bytes.
|
||||
*
|
||||
* Commands are signed with a CRC-8 checksum that is calculated
|
||||
* over the entire record. The Checksum is not part of length.
|
||||
* @{
|
||||
*/
|
||||
/**
|
||||
* start application.
|
||||
* parameter:
|
||||
* - '\n': launch default application
|
||||
* - '0' : launch slot 0
|
||||
* - '1' : launch slot 1
|
||||
*/
|
||||
#define RIOTBOOT_CMD_BOOT 'b'
|
||||
|
||||
/**
|
||||
* erase page
|
||||
* parameter:
|
||||
* - length of page number (8 bit - should be 4)
|
||||
* - page number (32 bit little endian)
|
||||
*/
|
||||
#define RIOTBOOT_CMD_ERASE 'e'
|
||||
|
||||
/**
|
||||
* write data
|
||||
* parameter:
|
||||
* - length of data + address (8 bit)
|
||||
* - destination address (32 bit little endian)
|
||||
* - data (up to (RX_BUF_LEN-6) bytes)
|
||||
*/
|
||||
#define RIOTBOOT_CMD_WRITE 'w'
|
||||
|
||||
/**
|
||||
* get page of address
|
||||
* parameter:
|
||||
* - length of address (8 bit - should be 4)
|
||||
* - address (32 bit little endian)
|
||||
*
|
||||
* returns status code followed by 32 bit little endian
|
||||
* value that represents the page in which the address lies.
|
||||
*/
|
||||
#define RIOTBOOT_CMD_GET_PAGE 'P'
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @brief riotboot response codes to commands
|
||||
* @{
|
||||
*/
|
||||
/** operation successful */
|
||||
#define RIOTBOOT_STAT_OK '.'
|
||||
/** CRC error - try again */
|
||||
#define RIOTBOOT_STAT_BAD_CRC '?'
|
||||
/** illegal parameter */
|
||||
#define RIOTBOOT_STAT_ILLEGAL '!'
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @brief riotboot serial loader synchronisation
|
||||
* @{
|
||||
*/
|
||||
/* sent to stop auto-boot */
|
||||
#define RIOTBOOT_ENTER_LOADER 'B'
|
||||
/* sent to probe if auto-boot is paused and riotboot is active */
|
||||
#define RIOTBOOT_PROBE '?'
|
||||
|
||||
/* continuously sent by riotboot before booting */
|
||||
#define RIOTBOOT_STAT_WAITING 'b'
|
||||
/* indicates riotboot is ready to accept commands */
|
||||
#define RIOTBOOT_STAT_READY '>'
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @brief CRC-8 Polynom used for riotboot chunks
|
||||
*/
|
||||
#ifndef RIOTBOOT_CRC8_POLY
|
||||
#define RIOTBOOT_CRC8_POLY (0x31)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Start interactive serial bootloader
|
||||
* @return slot to boot, -1 if default slot should be started
|
||||
*/
|
||||
int riotboot_serial_loader(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* RIOTBOOT_SERIAL_H */
|
@ -29,18 +29,6 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @name USB DFU RAM information for riotboot
|
||||
* @{
|
||||
*/
|
||||
#ifndef RIOTBOOT_DFU_ADDR
|
||||
#define RIOTBOOT_DFU_ADDR (CPU_RAM_BASE + CPU_RAM_SIZE - 4) /**< DFU default magic address */
|
||||
#endif
|
||||
#ifndef RIOTBOOT_MAGIC_NUMBER
|
||||
#define RIOTBOOT_MAGIC_NUMBER RIOTBOOT_MAGIC /**< DFU default magic value */
|
||||
#endif
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name USB DFU Default slots name
|
||||
* @{
|
||||
|
32
sys/riotboot/reset.c
Normal file
32
sys/riotboot/reset.c
Normal file
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Benjamin Valentin
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser General
|
||||
* Public License v2.1. See the file LICENSE in the top level directory for more
|
||||
* details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup sys_riotboot_serial
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Trigger reset to riotboot.
|
||||
*
|
||||
* @author Benjamin Valentin <benpicco@googlemail.com>
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "periph/pm.h"
|
||||
#include "riotboot/magic.h"
|
||||
|
||||
__attribute__((weak))
|
||||
void usb_board_reset_in_bootloader(void)
|
||||
{
|
||||
uint32_t *magic = (void *)(uintptr_t)RIOTBOOT_MAGIC_ADDR;
|
||||
|
||||
irq_disable();
|
||||
*magic = RIOTBOOT_MAGIC;
|
||||
pm_reboot();
|
||||
}
|
267
sys/riotboot/serial.c
Normal file
267
sys/riotboot/serial.c
Normal file
@ -0,0 +1,267 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Benjamin Valentin
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup sys_riotboot_serial
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Serial Bootloader
|
||||
*
|
||||
* @author Benjamin Valentin <benjamin.valentin@ml-pa.com>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "stdio_uart.h"
|
||||
#include "periph/uart.h"
|
||||
#include "periph/flashpage.h"
|
||||
#include "unaligned.h"
|
||||
#include "checksum/crc8.h"
|
||||
#include "riotboot/serial.h"
|
||||
#include "riotboot/magic.h"
|
||||
|
||||
#include "board.h"
|
||||
|
||||
#ifndef RIOTBOOT_UART_DEV
|
||||
#define RIOTBOOT_UART_DEV STDIO_UART_DEV
|
||||
#endif
|
||||
|
||||
#ifndef RIOTBOOT_UART_BAUDRATE
|
||||
#define RIOTBOOT_UART_BAUDRATE STDIO_UART_BAUDRATE
|
||||
#endif
|
||||
|
||||
#ifndef RIOTBOOT_DELAY_MS
|
||||
#define RIOTBOOT_DELAY_MS (1000)
|
||||
#endif
|
||||
|
||||
#define RX_BUF_LEN (40)
|
||||
|
||||
/* we send a characters each iteration -> 8 bit + start & stop bit */
|
||||
#define RIOTBOOT_DELAY (RIOTBOOT_DELAY_MS * RIOTBOOT_UART_BAUDRATE / 10000)
|
||||
|
||||
static inline void uart_write_byte(uart_t uart, uint8_t data)
|
||||
{
|
||||
uart_write(uart, &data, 1);
|
||||
}
|
||||
|
||||
/* send 'hello' byte until we get enter bootloader byte or timeout */
|
||||
static bool _bootdelay(unsigned tries, volatile bool *boot_default)
|
||||
{
|
||||
uint32_t *magic = (void *)(uintptr_t)RIOTBOOT_MAGIC_ADDR;
|
||||
|
||||
if (*magic == RIOTBOOT_MAGIC) {
|
||||
*magic = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (tries == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
while (--tries && *boot_default) {
|
||||
uart_write_byte(RIOTBOOT_UART_DEV, 0);
|
||||
}
|
||||
|
||||
return *boot_default;
|
||||
}
|
||||
|
||||
__attribute__ ((aligned(4)))
|
||||
static struct {
|
||||
uint8_t pos; /* current pos in rx buffer */
|
||||
uint8_t remaining; /* remaining bytes to read */
|
||||
union {
|
||||
uint8_t u8[RX_BUF_LEN]; /* rx buffer */
|
||||
struct {
|
||||
uint8_t type; /* command type */
|
||||
uint8_t len; /* length of data (without checksum) */
|
||||
uint8_t data[]; /* data is aligned at word boundary */
|
||||
} val;
|
||||
} rx;
|
||||
} ctx;
|
||||
|
||||
/**
|
||||
* Format:
|
||||
* [ Type (1 byte) | Length (1 byte) | value (n bytes) | checksum (1 byte) ]
|
||||
*/
|
||||
static void _uart_rx_cmd(void *arg, uint8_t data)
|
||||
{
|
||||
bool *reading = arg;
|
||||
uint8_t crc;
|
||||
|
||||
/* ignore RX while processing buffer */
|
||||
if (!reading) {
|
||||
return;
|
||||
}
|
||||
|
||||
ctx.rx.u8[ctx.pos] = data;
|
||||
|
||||
switch (ctx.pos) {
|
||||
case 0:
|
||||
switch (data) {
|
||||
case RIOTBOOT_CMD_BOOT:
|
||||
case RIOTBOOT_CMD_ERASE:
|
||||
case RIOTBOOT_CMD_WRITE:
|
||||
case RIOTBOOT_CMD_GET_PAGE:
|
||||
ctx.remaining = 1;
|
||||
break;
|
||||
/* re-create initial sync handshake if already in bootloader */
|
||||
case RIOTBOOT_PROBE:
|
||||
uart_write_byte(RIOTBOOT_UART_DEV, RIOTBOOT_STAT_WAITING);
|
||||
return;
|
||||
case RIOTBOOT_ENTER_LOADER:
|
||||
uart_write_byte(RIOTBOOT_UART_DEV, RIOTBOOT_STAT_READY);
|
||||
*reading = false;
|
||||
return;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
case 1:
|
||||
/* data length + checksum byte + dummy byte for fall-through */
|
||||
ctx.remaining = data + 2;
|
||||
|
||||
/* boot command needs no checksum */
|
||||
if (ctx.rx.val.type == RIOTBOOT_CMD_BOOT) {
|
||||
*reading = false;
|
||||
return;
|
||||
}
|
||||
|
||||
/* bail out early if the buffer would not fit */
|
||||
/* data len + sizeof(type, len) */
|
||||
if (ctx.remaining + 2 >= RX_BUF_LEN) {
|
||||
crc = 0;
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* fall-through */
|
||||
default:
|
||||
|
||||
/* end of data block not reached */
|
||||
if (--ctx.remaining) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* fall-through */
|
||||
case (RX_BUF_LEN - 1):
|
||||
|
||||
/* calculate checksum */
|
||||
crc = crc8(ctx.rx.u8, ctx.pos, RIOTBOOT_CRC8_POLY, 0xFF);
|
||||
|
||||
error:
|
||||
ctx.remaining = 0;
|
||||
ctx.pos = 0;
|
||||
|
||||
/* checksum error */
|
||||
if (crc != data) {
|
||||
uart_write_byte(RIOTBOOT_UART_DEV, RIOTBOOT_STAT_BAD_CRC);
|
||||
break;
|
||||
}
|
||||
|
||||
*reading = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ctx.remaining) {
|
||||
++ctx.pos;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static void _get_page(uintptr_t addr)
|
||||
{
|
||||
uart_write_byte(RIOTBOOT_UART_DEV, RIOTBOOT_STAT_OK);
|
||||
uint32_t page = flashpage_page((void *)addr);
|
||||
uart_write(RIOTBOOT_UART_DEV, (void *)&page, sizeof(page));
|
||||
}
|
||||
|
||||
static void _erase(uint16_t sector)
|
||||
{
|
||||
/* don't erase bootloader */
|
||||
if ((uintptr_t)flashpage_addr(sector) < SLOT0_OFFSET) {
|
||||
uart_write_byte(RIOTBOOT_UART_DEV, RIOTBOOT_STAT_ILLEGAL);
|
||||
return;
|
||||
}
|
||||
|
||||
flashpage_erase(sector);
|
||||
|
||||
uart_write_byte(RIOTBOOT_UART_DEV, RIOTBOOT_STAT_OK);
|
||||
}
|
||||
|
||||
static void _write(uint32_t addr, uint8_t len, const uint8_t *data)
|
||||
{
|
||||
/* don't overwrite bootloader */
|
||||
if (addr < SLOT0_OFFSET) {
|
||||
uart_write_byte(RIOTBOOT_UART_DEV, RIOTBOOT_STAT_ILLEGAL);
|
||||
return;
|
||||
}
|
||||
|
||||
flashpage_write((void *)addr, data, len);
|
||||
|
||||
uart_write_byte(RIOTBOOT_UART_DEV, RIOTBOOT_STAT_OK);
|
||||
}
|
||||
|
||||
int riotboot_serial_loader(void)
|
||||
{
|
||||
volatile bool reading = true;
|
||||
|
||||
uart_init(RIOTBOOT_UART_DEV, RIOTBOOT_UART_BAUDRATE,
|
||||
_uart_rx_cmd, (void *)&reading);
|
||||
|
||||
/* give the user some time to interrupt auto boot */
|
||||
if (_bootdelay(RIOTBOOT_DELAY, &reading)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
|
||||
/* we can't use mutex in riotboot */
|
||||
while (reading) {}
|
||||
|
||||
switch (ctx.rx.val.type) {
|
||||
case RIOTBOOT_CMD_BOOT:
|
||||
switch (ctx.rx.val.len) {
|
||||
case '\n':
|
||||
uart_write_byte(RIOTBOOT_UART_DEV, RIOTBOOT_STAT_OK);
|
||||
return -1;
|
||||
#ifdef MODULE_RIOTBOOT_SLOT
|
||||
case '0':
|
||||
uart_write_byte(RIOTBOOT_UART_DEV, RIOTBOOT_STAT_OK);
|
||||
return 0;
|
||||
case '1':
|
||||
uart_write_byte(RIOTBOOT_UART_DEV, RIOTBOOT_STAT_OK);
|
||||
return 1;
|
||||
#endif
|
||||
default:
|
||||
uart_write_byte(RIOTBOOT_UART_DEV, RIOTBOOT_STAT_ILLEGAL);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case RIOTBOOT_CMD_GET_PAGE:
|
||||
_get_page(unaligned_get_u32(ctx.rx.val.data));
|
||||
break;
|
||||
case RIOTBOOT_CMD_ERASE:
|
||||
_erase(unaligned_get_u16(ctx.rx.val.data));
|
||||
break;
|
||||
case RIOTBOOT_CMD_WRITE:
|
||||
_write(unaligned_get_u32(ctx.rx.val.data), /* address */
|
||||
ctx.rx.val.len - sizeof(uint32_t), /* sizeof(data) - sizeof(address) */
|
||||
&ctx.rx.val.data[4]); /* data */
|
||||
break;
|
||||
}
|
||||
|
||||
reading = true;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
@ -24,6 +24,7 @@
|
||||
#include "usb/usbus.h"
|
||||
#include "usb/dfu.h"
|
||||
#include "usb/usbus/dfu.h"
|
||||
#include "riotboot/magic.h"
|
||||
#include "riotboot/usb_dfu.h"
|
||||
|
||||
static usbus_dfu_device_t dfu;
|
||||
@ -32,7 +33,7 @@ static usbus_t usbus;
|
||||
|
||||
void riotboot_usb_dfu_init(unsigned forced)
|
||||
{
|
||||
uint32_t *reset_addr = (uint32_t *)RIOTBOOT_DFU_ADDR;
|
||||
uint32_t *reset_addr = (uint32_t *)RIOTBOOT_MAGIC_ADDR;
|
||||
|
||||
if (forced == 1 || *reset_addr == RIOTBOOT_MAGIC_NUMBER) {
|
||||
*reset_addr = 0;
|
||||
|
@ -23,13 +23,13 @@ config USB_DFU_DETACH_TIMEOUT_MS
|
||||
the host USB. Host USB should abort the pending operation if
|
||||
device doesn't detach after this timeout.
|
||||
|
||||
config CUSTOM_RIOTBOOT_DFU_ADDR
|
||||
config CUSTOM_RIOTBOOT_MAGIC_ADDR
|
||||
bool "Use custom DFU magic address"
|
||||
help
|
||||
Say n to use the default address, which is the last in RAM.
|
||||
|
||||
config RIOTBOOT_DFU_ADDR
|
||||
config RIOTBOOT_MAGIC_ADDR
|
||||
int "DFU magic address"
|
||||
depends on CUSTOM_RIOTBOOT_DFU_ADDR
|
||||
depends on CUSTOM_RIOTBOOT_MAGIC_ADDR
|
||||
|
||||
endif # KCONFIG_USEMODULE_USBUS_DFU
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include "usb/usbus.h"
|
||||
#include "usb/usbus/control.h"
|
||||
#include "usb/usbus/dfu.h"
|
||||
#include "riotboot/magic.h"
|
||||
#include "riotboot/usb_dfu.h"
|
||||
#ifdef MODULE_RIOTBOOT_USB_DFU
|
||||
#include "xtimer.h"
|
||||
@ -169,7 +170,7 @@ static void _dfu_class_control_req(usbus_t *usbus, usbus_dfu_device_t *dfu, usb_
|
||||
/* Detach USB bus */
|
||||
usbdev_set(usbus->dev, USBOPT_ATTACH, &disable, sizeof(usbopt_enable_t));
|
||||
/* Restart and jump into the bootloader */
|
||||
uint32_t *reset_addr = (uint32_t *)RIOTBOOT_DFU_ADDR;
|
||||
uint32_t *reset_addr = (uint32_t *)RIOTBOOT_MAGIC_ADDR;
|
||||
*reset_addr = RIOTBOOT_MAGIC_NUMBER;
|
||||
pm_reboot();
|
||||
break;
|
||||
|
Loading…
Reference in New Issue
Block a user