adding uf2 files
This commit is contained in:
parent
9927ff4d6f
commit
3f8fba843c
@ -38,25 +38,8 @@
|
||||
#define CODE_REGION_1_START SD_SIZE_GET(MBR_SIZE) /**< This field should correspond to the size of Code Region 0, (which is identical to Start of Code Region 1), found in UICR.CLEN0 register. This value is used for compile safety, as the linker will fail if application expands into bootloader. Runtime, the bootloader will use the value found in UICR.CLEN0. */
|
||||
#define SOFTDEVICE_REGION_START MBR_SIZE /**< This field should correspond to start address of the bootloader, found in UICR.RESERVED, 0x10001014, register. This value is used for sanity check, so the bootloader will fail immediately if this value differs from runtime value. The value is used to determine max application size for updating. */
|
||||
|
||||
#ifdef NRF51
|
||||
|
||||
#define CODE_PAGE_SIZE 0x0400 /**< Size of a flash codepage. Used for size of the reserved flash space in the bootloader region. Will be runtime checked against NRF_UICR->CODEPAGESIZE to ensure the region is correct. */
|
||||
|
||||
#ifdef SIGNING
|
||||
|
||||
#define BOOTLOADER_REGION_START 0x00039C00 /**< This field should correspond to start address of the bootloader, found in UICR.RESERVED, 0x10001014, register. This value is used for sanity check, so the bootloader will fail immediately if this value differs from runtime value. The value is used to determine max application size for updating. */
|
||||
#define BOOTLOADER_SETTINGS_ADDRESS 0x0003D800 /**< The field specifies the page location of the bootloader settings address. */
|
||||
|
||||
#else
|
||||
|
||||
#define BOOTLOADER_REGION_START 0x0003C000 /**< This field should correspond to start address of the bootloader, found in UICR.RESERVED, 0x10001014, register. This value is used for sanity check, so the bootloader will fail immediately if this value differs from runtime value. The value is used to determine max application size for updating. */
|
||||
#define BOOTLOADER_SETTINGS_ADDRESS 0x0003FC00 /**< The field specifies the page location of the bootloader settings address. */
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#elif defined(NRF52832_XXAA)
|
||||
#if defined(NRF52832_XXAA)
|
||||
|
||||
#define BOOTLOADER_REGION_START 0x00074000 /**< This field should correspond to start address of the bootloader, found in UICR.RESERVED, 0x10001014, register. This value is used for sanity check, so the bootloader will fail immediately if this value differs from runtime value. The value is used to determine max application size for updating. */
|
||||
#define BOOTLOADER_SETTINGS_ADDRESS 0x0007F000 /**< The field specifies the page location of the bootloader settings address. */
|
||||
@ -68,14 +51,10 @@
|
||||
|
||||
// Increase bootloader size to application data region for easy debugging
|
||||
#ifdef DEBUG_SIZE_EXPAND
|
||||
|
||||
#define BOOTLOADER_REGION_START (0x000F4000 - CODE_PAGE_SIZE*7)
|
||||
#define DFU_APP_DATA_RESERVED 0
|
||||
|
||||
#else
|
||||
|
||||
#define BOOTLOADER_REGION_START 0x000F4000 /**< This field should correspond to start address of the bootloader, found in UICR.RESERVED, 0x10001014, register. This value is used for sanity check, so the bootloader will fail immediately if this value differs from runtime value. The value is used to determine max application size for updating. */
|
||||
|
||||
#endif
|
||||
|
||||
#define BOOTLOADER_SETTINGS_ADDRESS 0x000FF000 /**< The field specifies the page location of the bootloader settings address. */
|
||||
|
21
src/uf2/LICENSE
Normal file
21
src/uf2/LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE
|
317
src/uf2/ghostfat.c
Normal file
317
src/uf2/ghostfat.c
Normal file
@ -0,0 +1,317 @@
|
||||
|
||||
#include "uf2.h"
|
||||
|
||||
#include "nrf_log.h"
|
||||
#include "nrf_nvmc.h"
|
||||
#include "nrf_sdh.h"
|
||||
#include "nrf_dfu_settings.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
typedef struct {
|
||||
uint8_t JumpInstruction[3];
|
||||
uint8_t OEMInfo[8];
|
||||
uint16_t SectorSize;
|
||||
uint8_t SectorsPerCluster;
|
||||
uint16_t ReservedSectors;
|
||||
uint8_t FATCopies;
|
||||
uint16_t RootDirectoryEntries;
|
||||
uint16_t TotalSectors16;
|
||||
uint8_t MediaDescriptor;
|
||||
uint16_t SectorsPerFAT;
|
||||
uint16_t SectorsPerTrack;
|
||||
uint16_t Heads;
|
||||
uint32_t HiddenSectors;
|
||||
uint32_t TotalSectors32;
|
||||
uint8_t PhysicalDriveNum;
|
||||
uint8_t Reserved;
|
||||
uint8_t ExtendedBootSig;
|
||||
uint32_t VolumeSerialNumber;
|
||||
uint8_t VolumeLabel[11];
|
||||
uint8_t FilesystemIdentifier[8];
|
||||
} __attribute__((packed)) FAT_BootBlock;
|
||||
|
||||
typedef struct {
|
||||
char name[8];
|
||||
char ext[3];
|
||||
uint8_t attrs;
|
||||
uint8_t reserved;
|
||||
uint8_t createTimeFine;
|
||||
uint16_t createTime;
|
||||
uint16_t createDate;
|
||||
uint16_t lastAccessDate;
|
||||
uint16_t highStartCluster;
|
||||
uint16_t updateTime;
|
||||
uint16_t updateDate;
|
||||
uint16_t startCluster;
|
||||
uint32_t size;
|
||||
} __attribute__((packed)) DirEntry;
|
||||
|
||||
STATIC_ASSERT(sizeof(DirEntry) == 32);
|
||||
|
||||
struct TextFile {
|
||||
const char name[11];
|
||||
const char *content;
|
||||
};
|
||||
|
||||
#define NUM_FAT_BLOCKS UF2_NUM_BLOCKS
|
||||
|
||||
#define STR0(x) #x
|
||||
#define STR(x) STR0(x)
|
||||
const char infoUf2File[] = //
|
||||
"UF2 Bootloader " UF2_VERSION "\r\n"
|
||||
"Model: " PRODUCT_NAME "\r\n"
|
||||
"Board-ID: " BOARD_ID "\r\n";
|
||||
|
||||
const char indexFile[] = //
|
||||
"<!doctype html>\n"
|
||||
"<html>"
|
||||
"<body>"
|
||||
"<script>\n"
|
||||
"location.replace(\"" INDEX_URL "\");\n"
|
||||
"</script>"
|
||||
"</body>"
|
||||
"</html>\n";
|
||||
|
||||
static const struct TextFile info[] = {
|
||||
{.name = "INFO_UF2TXT", .content = infoUf2File},
|
||||
{.name = "INDEX HTM", .content = indexFile},
|
||||
{.name = "CURRENT UF2"},
|
||||
};
|
||||
#define NUM_INFO (sizeof(info) / sizeof(info[0]))
|
||||
|
||||
#define UF2_SIZE (FLASH_SIZE * 2)
|
||||
#define UF2_SECTORS (UF2_SIZE / 512)
|
||||
#define UF2_FIRST_SECTOR (NUM_INFO + 1)
|
||||
#define UF2_LAST_SECTOR (UF2_FIRST_SECTOR + UF2_SECTORS - 1)
|
||||
|
||||
#define RESERVED_SECTORS 1
|
||||
#define ROOT_DIR_SECTORS 4
|
||||
#define SECTORS_PER_FAT ((NUM_FAT_BLOCKS * 2 + 511) / 512)
|
||||
|
||||
#define START_FAT0 RESERVED_SECTORS
|
||||
#define START_FAT1 (START_FAT0 + SECTORS_PER_FAT)
|
||||
#define START_ROOTDIR (START_FAT1 + SECTORS_PER_FAT)
|
||||
#define START_CLUSTERS (START_ROOTDIR + ROOT_DIR_SECTORS)
|
||||
|
||||
static const FAT_BootBlock BootBlock = {
|
||||
.JumpInstruction = {0xeb, 0x3c, 0x90},
|
||||
.OEMInfo = "UF2 UF2 ",
|
||||
.SectorSize = 512,
|
||||
.SectorsPerCluster = 1,
|
||||
.ReservedSectors = RESERVED_SECTORS,
|
||||
.FATCopies = 2,
|
||||
.RootDirectoryEntries = (ROOT_DIR_SECTORS * 512 / 32),
|
||||
.TotalSectors16 = NUM_FAT_BLOCKS - 2,
|
||||
.MediaDescriptor = 0xF8,
|
||||
.SectorsPerFAT = SECTORS_PER_FAT,
|
||||
.SectorsPerTrack = 1,
|
||||
.Heads = 1,
|
||||
.ExtendedBootSig = 0x29,
|
||||
.VolumeSerialNumber = 0x00420042,
|
||||
.VolumeLabel = VOLUME_LABEL,
|
||||
.FilesystemIdentifier = "FAT16 ",
|
||||
};
|
||||
|
||||
#define NO_CACHE 0xffffffff
|
||||
|
||||
uint32_t flashAddr = NO_CACHE;
|
||||
uint8_t flashBuf[FLASH_PAGE_SIZE] __attribute__((aligned(4)));
|
||||
bool firstFlush = true;
|
||||
bool hadWrite = false;
|
||||
|
||||
void flushFlash() {
|
||||
if (flashAddr == NO_CACHE)
|
||||
return;
|
||||
|
||||
if (firstFlush) {
|
||||
if (sdRunning) {
|
||||
// disable SD - we need sync access to flash, and we might be also overwriting the SD
|
||||
nrf_sdh_disable_request();
|
||||
nrf_dfu_settings_init(false);
|
||||
}
|
||||
|
||||
firstFlush = false;
|
||||
|
||||
s_dfu_settings.write_offset = 0;
|
||||
s_dfu_settings.sd_size = 0;
|
||||
s_dfu_settings.bank_layout = NRF_DFU_BANK_LAYOUT_DUAL;
|
||||
s_dfu_settings.bank_current = NRF_DFU_CURRENT_BANK_0;
|
||||
|
||||
memset(&s_dfu_settings.bank_0, 0, sizeof(s_dfu_settings.bank_0));
|
||||
memset(&s_dfu_settings.bank_1, 0, sizeof(s_dfu_settings.bank_1));
|
||||
|
||||
nrf_dfu_settings_write(NULL);
|
||||
}
|
||||
|
||||
int32_t sz = flashAddr + FLASH_PAGE_SIZE;
|
||||
if (s_dfu_settings.bank_0.image_size < sz)
|
||||
s_dfu_settings.bank_0.image_size = sz;
|
||||
|
||||
NRF_LOG_DEBUG("Flush at %x", flashAddr);
|
||||
if (memcmp(flashBuf, (void *)flashAddr, FLASH_PAGE_SIZE) != 0) {
|
||||
NRF_LOG_DEBUG("Write flush at %x", flashAddr);
|
||||
nrf_nvmc_page_erase(flashAddr);
|
||||
nrf_nvmc_write_words(flashAddr, (uint32_t *)flashBuf, FLASH_PAGE_SIZE / sizeof(uint32_t));
|
||||
}
|
||||
|
||||
flashAddr = NO_CACHE;
|
||||
}
|
||||
|
||||
void flash_write(uint32_t dst, const uint8_t *src, int len) {
|
||||
uint32_t newAddr = dst & ~(FLASH_PAGE_SIZE - 1);
|
||||
|
||||
hadWrite = true;
|
||||
|
||||
if (newAddr != flashAddr) {
|
||||
flushFlash();
|
||||
flashAddr = newAddr;
|
||||
memcpy(flashBuf, (void *)newAddr, FLASH_PAGE_SIZE);
|
||||
}
|
||||
memcpy(flashBuf + (dst & (FLASH_PAGE_SIZE - 1)), src, len);
|
||||
}
|
||||
|
||||
void uf2_timer(void *p_context) {
|
||||
UNUSED_PARAMETER(p_context);
|
||||
if (hadWrite) {
|
||||
flushFlash();
|
||||
s_dfu_settings.bank_0.bank_code = NRF_DFU_BANK_VALID_APP;
|
||||
int32_t start = SD_MAGIC_OK() ? MAIN_APPLICATION_START_ADDR : MBR_SIZE;
|
||||
int32_t sz = s_dfu_settings.bank_0.image_size - start;
|
||||
if (sz > 0)
|
||||
s_dfu_settings.bank_0.image_size = sz;
|
||||
nrf_dfu_settings_write(NULL);
|
||||
}
|
||||
NVIC_SystemReset();
|
||||
}
|
||||
|
||||
void uf2_timer_start(int ms);
|
||||
|
||||
void padded_memcpy(char *dst, const char *src, int len) {
|
||||
for (int i = 0; i < len; ++i) {
|
||||
if (*src)
|
||||
*dst = *src++;
|
||||
else
|
||||
*dst = ' ';
|
||||
dst++;
|
||||
}
|
||||
}
|
||||
|
||||
void read_block(uint32_t block_no, uint8_t *data) {
|
||||
memset(data, 0, 512);
|
||||
uint32_t sectionIdx = block_no;
|
||||
|
||||
if (block_no == 0) {
|
||||
memcpy(data, &BootBlock, sizeof(BootBlock));
|
||||
data[510] = 0x55;
|
||||
data[511] = 0xaa;
|
||||
// logval("data[0]", data[0]);
|
||||
} else if (block_no < START_ROOTDIR) {
|
||||
sectionIdx -= START_FAT0;
|
||||
// logval("sidx", sectionIdx);
|
||||
if (sectionIdx >= SECTORS_PER_FAT)
|
||||
sectionIdx -= SECTORS_PER_FAT;
|
||||
if (sectionIdx == 0) {
|
||||
data[0] = 0xf0;
|
||||
for (int i = 1; i < NUM_INFO * 2 + 4; ++i) {
|
||||
data[i] = 0xff;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < 256; ++i) {
|
||||
uint32_t v = sectionIdx * 256 + i;
|
||||
if (UF2_FIRST_SECTOR <= v && v <= UF2_LAST_SECTOR)
|
||||
((uint16_t *)(void *)data)[i] = v == UF2_LAST_SECTOR ? 0xffff : v + 1;
|
||||
}
|
||||
} else if (block_no < START_CLUSTERS) {
|
||||
sectionIdx -= START_ROOTDIR;
|
||||
if (sectionIdx == 0) {
|
||||
DirEntry *d = (void *)data;
|
||||
padded_memcpy(d->name, (const char *)BootBlock.VolumeLabel, 11);
|
||||
d->attrs = 0x28;
|
||||
for (int i = 0; i < NUM_INFO; ++i) {
|
||||
d++;
|
||||
const struct TextFile *inf = &info[i];
|
||||
d->size = inf->content ? strlen(inf->content) : UF2_SIZE;
|
||||
d->startCluster = i + 2;
|
||||
padded_memcpy(d->name, inf->name, 11);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
sectionIdx -= START_CLUSTERS;
|
||||
if (sectionIdx < NUM_INFO - 1) {
|
||||
memcpy(data, info[sectionIdx].content, strlen(info[sectionIdx].content));
|
||||
} else {
|
||||
sectionIdx -= NUM_INFO - 1;
|
||||
uint32_t addr = sectionIdx * 256;
|
||||
if (addr < FLASH_SIZE) {
|
||||
UF2_Block *bl = (void *)data;
|
||||
bl->magicStart0 = UF2_MAGIC_START0;
|
||||
bl->magicStart1 = UF2_MAGIC_START1;
|
||||
bl->magicEnd = UF2_MAGIC_END;
|
||||
bl->blockNo = sectionIdx;
|
||||
bl->numBlocks = FLASH_SIZE / 256;
|
||||
bl->targetAddr = addr;
|
||||
bl->payloadSize = 256;
|
||||
memcpy(bl->data, (void *)addr, bl->payloadSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void write_block(uint32_t block_no, uint8_t *data, bool quiet, WriteState *state) {
|
||||
UF2_Block *bl = (void *)data;
|
||||
|
||||
// NRF_LOG_DEBUG("Write magic: %x", bl->magicStart0);
|
||||
|
||||
if (!is_uf2_block(bl)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ((bl->flags & UF2_FLAG_NOFLASH) || bl->payloadSize > 256 || (bl->targetAddr & 0xff) ||
|
||||
bl->targetAddr < USER_FLASH_START || bl->targetAddr + bl->payloadSize > USER_FLASH_END) {
|
||||
#if USE_DBG_MSC
|
||||
if (!quiet)
|
||||
logval("invalid target addr", bl->targetAddr);
|
||||
#endif
|
||||
NRF_LOG_WARNING("Skip block at %x", bl->targetAddr);
|
||||
// this happens when we're trying to re-flash CURRENT.UF2 file previously
|
||||
// copied from a device; we still want to count these blocks to reset properly
|
||||
} else {
|
||||
// logval("write block at", bl->targetAddr);
|
||||
NRF_LOG_DEBUG("Write block at %x", bl->targetAddr);
|
||||
flash_write(bl->targetAddr, bl->data, bl->payloadSize);
|
||||
}
|
||||
|
||||
bool isSet = false;
|
||||
|
||||
if (state && bl->numBlocks) {
|
||||
if (state->numBlocks != bl->numBlocks) {
|
||||
if (bl->numBlocks >= MAX_BLOCKS || state->numBlocks)
|
||||
state->numBlocks = 0xffffffff;
|
||||
else
|
||||
state->numBlocks = bl->numBlocks;
|
||||
}
|
||||
if (bl->blockNo < MAX_BLOCKS) {
|
||||
uint8_t mask = 1 << (bl->blockNo % 8);
|
||||
uint32_t pos = bl->blockNo / 8;
|
||||
if (!(state->writtenMask[pos] & mask)) {
|
||||
// logval("incr", state->numWritten);
|
||||
state->writtenMask[pos] |= mask;
|
||||
state->numWritten++;
|
||||
}
|
||||
if (state->numWritten >= state->numBlocks) {
|
||||
// wait a little bit before resetting, to avoid Windows transmit error
|
||||
// https://github.com/Microsoft/uf2-samd21/issues/11
|
||||
if (!quiet) {
|
||||
uf2_timer_start(30);
|
||||
isSet = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
NRF_LOG_DEBUG("wr %d=%d (of %d)", state->numWritten, bl->blockNo, bl->numBlocks);
|
||||
}
|
||||
|
||||
if (!isSet && !quiet) {
|
||||
// uf2_timer_start(500);
|
||||
}
|
||||
}
|
206
src/uf2/nrf_block_dev_uf2.c
Normal file
206
src/uf2/nrf_block_dev_uf2.c
Normal file
@ -0,0 +1,206 @@
|
||||
/**
|
||||
* Copyright (c) 2016 - 2017, 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.
|
||||
*
|
||||
*/
|
||||
#include "nrf_block_dev_uf2.h"
|
||||
|
||||
/**@file
|
||||
*
|
||||
* @ingroup nrf_block_dev
|
||||
* @{
|
||||
*
|
||||
* @brief This module implements block device API. It should be used as a reference block device.
|
||||
*/
|
||||
|
||||
static ret_code_t block_dev_uf2_init(nrf_block_dev_t const * p_blk_dev,
|
||||
nrf_block_dev_ev_handler ev_handler,
|
||||
void const * p_context)
|
||||
{
|
||||
ASSERT(p_blk_dev);
|
||||
nrf_block_dev_uf2_t const * p_ram_dev = CONTAINER_OF(p_blk_dev, nrf_block_dev_uf2_t, block_dev);
|
||||
nrf_block_dev_uf2_work_t * p_work = p_ram_dev->p_work;
|
||||
|
||||
p_work->geometry.blk_size = 512;
|
||||
p_work->geometry.blk_count = UF2_NUM_BLOCKS;
|
||||
p_work->p_context = p_context;
|
||||
p_work->ev_handler = ev_handler;
|
||||
|
||||
if (p_work->ev_handler)
|
||||
{
|
||||
const nrf_block_dev_event_t ev = {
|
||||
NRF_BLOCK_DEV_EVT_INIT,
|
||||
NRF_BLOCK_DEV_RESULT_SUCCESS,
|
||||
NULL,
|
||||
p_work->p_context
|
||||
};
|
||||
|
||||
p_work->ev_handler(p_blk_dev, &ev);
|
||||
}
|
||||
|
||||
return NRF_SUCCESS;
|
||||
}
|
||||
|
||||
static ret_code_t block_dev_uf2_uninit(nrf_block_dev_t const * p_blk_dev)
|
||||
{
|
||||
ASSERT(p_blk_dev);
|
||||
nrf_block_dev_uf2_t const * p_ram_dev = CONTAINER_OF(p_blk_dev, nrf_block_dev_uf2_t, block_dev);
|
||||
nrf_block_dev_uf2_work_t * p_work = p_ram_dev->p_work;
|
||||
|
||||
if (p_work->ev_handler)
|
||||
{
|
||||
/*Asynchronous operation (simulation)*/
|
||||
const nrf_block_dev_event_t ev = {
|
||||
NRF_BLOCK_DEV_EVT_UNINIT,
|
||||
NRF_BLOCK_DEV_RESULT_SUCCESS,
|
||||
NULL,
|
||||
p_work->p_context
|
||||
};
|
||||
|
||||
p_work->ev_handler(p_blk_dev, &ev);
|
||||
}
|
||||
|
||||
memset(p_work, 0, sizeof(nrf_block_dev_uf2_work_t));
|
||||
return NRF_SUCCESS;
|
||||
}
|
||||
|
||||
void write_block(uint32_t block_no, uint8_t *data, bool quiet, WriteState *state);
|
||||
void read_block(uint32_t block_no, uint8_t *data);
|
||||
|
||||
|
||||
static WriteState wrState;
|
||||
|
||||
static ret_code_t block_dev_uf2_req(nrf_block_dev_t const * p_blk_dev,
|
||||
nrf_block_req_t const * p_blk,
|
||||
nrf_block_dev_event_type_t event)
|
||||
{
|
||||
ASSERT(p_blk_dev);
|
||||
ASSERT(p_blk);
|
||||
nrf_block_dev_uf2_t const * p_ram_dev = CONTAINER_OF(p_blk_dev, nrf_block_dev_uf2_t, block_dev);
|
||||
nrf_block_dev_uf2_work_t const * p_work = p_ram_dev->p_work;
|
||||
|
||||
if (event == NRF_BLOCK_DEV_EVT_BLK_READ_DONE) {
|
||||
for (uint32_t i = 0; i < p_blk->blk_count; ++i)
|
||||
read_block(p_blk->blk_id + i, p_blk->p_buff + i * 512);
|
||||
} else {
|
||||
for (uint32_t i = 0; i < p_blk->blk_count; ++i)
|
||||
write_block(p_blk->blk_id + i, p_blk->p_buff + i * 512, false, &wrState);
|
||||
}
|
||||
|
||||
if (p_work->ev_handler)
|
||||
{
|
||||
/*Asynchronous operation (simulation)*/
|
||||
const nrf_block_dev_event_t ev = {
|
||||
event,
|
||||
NRF_BLOCK_DEV_RESULT_SUCCESS,
|
||||
p_blk,
|
||||
p_work->p_context
|
||||
};
|
||||
|
||||
p_work->ev_handler(p_blk_dev, &ev);
|
||||
}
|
||||
|
||||
return NRF_SUCCESS;
|
||||
}
|
||||
|
||||
static ret_code_t block_dev_uf2_read_req(nrf_block_dev_t const * p_blk_dev,
|
||||
nrf_block_req_t const * p_blk)
|
||||
{
|
||||
return block_dev_uf2_req(p_blk_dev, p_blk, NRF_BLOCK_DEV_EVT_BLK_READ_DONE);
|
||||
}
|
||||
|
||||
static ret_code_t block_dev_uf2_write_req(nrf_block_dev_t const * p_blk_dev,
|
||||
nrf_block_req_t const * p_blk)
|
||||
{
|
||||
return block_dev_uf2_req(p_blk_dev, p_blk, NRF_BLOCK_DEV_EVT_BLK_WRITE_DONE);
|
||||
}
|
||||
|
||||
static ret_code_t block_dev_uf2_ioctl(nrf_block_dev_t const * p_blk_dev,
|
||||
nrf_block_dev_ioctl_req_t req,
|
||||
void * p_data)
|
||||
{
|
||||
nrf_block_dev_uf2_t const * p_ram_dev = CONTAINER_OF(p_blk_dev, nrf_block_dev_uf2_t, block_dev);
|
||||
switch (req)
|
||||
{
|
||||
case NRF_BLOCK_DEV_IOCTL_REQ_CACHE_FLUSH:
|
||||
{
|
||||
bool * p_flushing = p_data;
|
||||
if (p_flushing)
|
||||
{
|
||||
*p_flushing = false;
|
||||
}
|
||||
return NRF_SUCCESS;
|
||||
}
|
||||
case NRF_BLOCK_DEV_IOCTL_REQ_INFO_STRINGS:
|
||||
{
|
||||
if (p_data == NULL)
|
||||
{
|
||||
return NRF_ERROR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
nrf_block_dev_info_strings_t const * * pp_strings = p_data;
|
||||
*pp_strings = &p_ram_dev->info_strings;
|
||||
return NRF_SUCCESS;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return NRF_ERROR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
|
||||
static nrf_block_dev_geometry_t const * block_dev_uf2_geometry(nrf_block_dev_t const * p_blk_dev)
|
||||
{
|
||||
ASSERT(p_blk_dev);
|
||||
nrf_block_dev_uf2_t const * p_ram_dev = CONTAINER_OF(p_blk_dev, nrf_block_dev_uf2_t, block_dev);
|
||||
nrf_block_dev_uf2_work_t const * p_work = p_ram_dev->p_work;
|
||||
|
||||
return &p_work->geometry;
|
||||
}
|
||||
|
||||
const nrf_block_dev_ops_t nrf_block_device_uf2_ops = {
|
||||
.init = block_dev_uf2_init,
|
||||
.uninit = block_dev_uf2_uninit,
|
||||
.read_req = block_dev_uf2_read_req,
|
||||
.write_req = block_dev_uf2_write_req,
|
||||
.ioctl = block_dev_uf2_ioctl,
|
||||
.geometry = block_dev_uf2_geometry,
|
||||
};
|
||||
|
||||
|
||||
/** @} */
|
137
src/uf2/nrf_block_dev_uf2.h
Normal file
137
src/uf2/nrf_block_dev_uf2.h
Normal file
@ -0,0 +1,137 @@
|
||||
/**
|
||||
* Copyright (c) 2016 - 2017, 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.
|
||||
*
|
||||
*/
|
||||
#ifndef NRF_BLOCK_DEV_UF2_H__
|
||||
#define NRF_BLOCK_DEV_UF2_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "uf2.h"
|
||||
#include "nrf_block_dev.h"
|
||||
|
||||
/**@file
|
||||
*
|
||||
* @defgroup nrf_block_dev_uf2 RAM implementation
|
||||
* @ingroup nrf_block_dev
|
||||
* @{
|
||||
|
||||
*
|
||||
* @brief This module implements block device API. It should be used as a reference block device.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief RAM block device operations
|
||||
* */
|
||||
extern const nrf_block_dev_ops_t nrf_block_device_uf2_ops;
|
||||
|
||||
/**
|
||||
* @brief Work structure of RAM block device
|
||||
*/
|
||||
typedef struct {
|
||||
nrf_block_dev_geometry_t geometry; //!< Block device geometry
|
||||
nrf_block_dev_ev_handler ev_handler; //!< Block device event handler
|
||||
void const * p_context; //!< Context handle passed to event handler
|
||||
} nrf_block_dev_uf2_work_t;
|
||||
|
||||
|
||||
/**
|
||||
* @brief RAM block device config initializer (@ref nrf_block_dev_uf2_config_t)
|
||||
*
|
||||
* @param blk_size Block size
|
||||
* @param buffer RAM work buffer
|
||||
* @param buffer_size RAM work buffer size
|
||||
* */
|
||||
#define NRF_BLOCK_DEV_UF2_CONFIG(volLbl) { \
|
||||
.volume_label = volLbl, \
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Ram block device config
|
||||
*/
|
||||
typedef struct {
|
||||
const char *volume_label;
|
||||
} nrf_block_dev_uf2_config_t;
|
||||
|
||||
/**
|
||||
* @brief Ram block device
|
||||
* */
|
||||
typedef struct {
|
||||
nrf_block_dev_t block_dev; //!< Block device
|
||||
nrf_block_dev_info_strings_t info_strings; //!< Block device information strings
|
||||
nrf_block_dev_uf2_config_t ram_config; //!< Ram block device config
|
||||
nrf_block_dev_uf2_work_t * p_work; //!< Ram block device work structure
|
||||
} nrf_block_dev_uf2_t;
|
||||
|
||||
/**
|
||||
* @brief Defines a RAM block device.
|
||||
*
|
||||
* @param name Instance name
|
||||
* @param config Configuration @ref nrf_block_dev_uf2_config_t
|
||||
* @param info Info strings @ref NFR_BLOCK_DEV_INFO_CONFIG
|
||||
* */
|
||||
#define NRF_BLOCK_DEV_UF2_DEFINE(name, config, info) \
|
||||
static nrf_block_dev_uf2_work_t CONCAT_2(name, _work); \
|
||||
static const nrf_block_dev_uf2_t name = { \
|
||||
.block_dev = { .p_ops = &nrf_block_device_uf2_ops }, \
|
||||
.info_strings = BRACKET_EXTRACT(info), \
|
||||
.ram_config = config, \
|
||||
.p_work = &CONCAT_2(name, _work), \
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns block device API handle from RAM block device.
|
||||
*
|
||||
* @param[in] p_blk_ram Ram block device
|
||||
* @return Block device handle
|
||||
*/
|
||||
static inline nrf_block_dev_t const *
|
||||
nrf_block_dev_uf2_ops_get(nrf_block_dev_uf2_t const * p_blk_ram)
|
||||
{
|
||||
return &p_blk_ram->block_dev;
|
||||
}
|
||||
|
||||
/** @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* NRF_BLOCK_DEV_UF2_H__ */
|
124
src/uf2/uf2.h
Normal file
124
src/uf2/uf2.h
Normal file
@ -0,0 +1,124 @@
|
||||
#ifndef UF2FORMAT_H
|
||||
#define UF2FORMAT_H 1
|
||||
|
||||
#include "uf2cfg.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "app_util.h"
|
||||
|
||||
#include "nrf_bootloader_info.h"
|
||||
|
||||
#define SD_MAGIC_NUMBER 0x51b1e5db
|
||||
#define SD_MAGIC_OK() (*((uint32_t*)(SOFTDEVICE_INFO_STRUCT_ADDRESS+4)) == 0x51b1e5db)
|
||||
extern bool sdRunning;
|
||||
|
||||
// All entries are little endian.
|
||||
|
||||
#define UF2_MAGIC_START0 0x0A324655UL // "UF2\n"
|
||||
#define UF2_MAGIC_START1 0x9E5D5157UL // Randomly selected
|
||||
#define UF2_MAGIC_END 0x0AB16F30UL // Ditto
|
||||
|
||||
// If set, the block is "comment" and should not be flashed to the device
|
||||
#define UF2_FLAG_NOFLASH 0x00000001
|
||||
|
||||
#define MAX_BLOCKS (FLASH_SIZE / 256 + 100)
|
||||
typedef struct {
|
||||
uint32_t numBlocks;
|
||||
uint32_t numWritten;
|
||||
uint8_t writtenMask[MAX_BLOCKS / 8 + 1];
|
||||
} WriteState;
|
||||
|
||||
typedef struct {
|
||||
// 32 byte header
|
||||
uint32_t magicStart0;
|
||||
uint32_t magicStart1;
|
||||
uint32_t flags;
|
||||
uint32_t targetAddr;
|
||||
uint32_t payloadSize;
|
||||
uint32_t blockNo;
|
||||
uint32_t numBlocks;
|
||||
uint32_t reserved;
|
||||
|
||||
// raw data;
|
||||
uint8_t data[476];
|
||||
|
||||
// store magic also at the end to limit damage from partial block reads
|
||||
uint32_t magicEnd;
|
||||
} UF2_Block;
|
||||
|
||||
typedef struct {
|
||||
uint8_t version;
|
||||
uint8_t ep_in;
|
||||
uint8_t ep_out;
|
||||
uint8_t reserved0;
|
||||
uint32_t cbw_tag;
|
||||
uint32_t blocks_remaining;
|
||||
uint8_t *buffer;
|
||||
} UF2_HandoverArgs;
|
||||
|
||||
typedef void (*UF2_MSC_Handover_Handler)(UF2_HandoverArgs *handover);
|
||||
typedef void (*UF2_HID_Handover_Handler)(int ep);
|
||||
|
||||
// this is required to be exactly 16 bytes long by the linker script
|
||||
typedef struct {
|
||||
void *reserved0;
|
||||
UF2_HID_Handover_Handler handoverHID;
|
||||
UF2_MSC_Handover_Handler handoverMSC;
|
||||
const char *info_uf2;
|
||||
} UF2_BInfo;
|
||||
|
||||
#define UF2_BINFO ((UF2_BInfo *)(APP_START_ADDRESS - sizeof(UF2_BInfo)))
|
||||
|
||||
static inline bool is_uf2_block(void *data) {
|
||||
UF2_Block *bl = (UF2_Block *)data;
|
||||
return bl->magicStart0 == UF2_MAGIC_START0 && bl->magicStart1 == UF2_MAGIC_START1 &&
|
||||
bl->magicEnd == UF2_MAGIC_END;
|
||||
}
|
||||
|
||||
static inline bool in_uf2_bootloader_space(const void *addr) {
|
||||
return USER_FLASH_END <= (uint32_t)addr && (uint32_t)addr < FLASH_SIZE;
|
||||
}
|
||||
|
||||
|
||||
#ifdef UF2_DEFINE_HANDOVER
|
||||
static inline const char *uf2_info(void) {
|
||||
if (in_uf2_bootloader_space(UF2_BINFO->info_uf2))
|
||||
return UF2_BINFO->info_uf2;
|
||||
return "N/A";
|
||||
}
|
||||
|
||||
static inline void hf2_handover(uint8_t ep) {
|
||||
const char *board_info = UF2_BINFO->info_uf2;
|
||||
UF2_HID_Handover_Handler fn = UF2_BINFO->handoverHID;
|
||||
|
||||
if (in_uf2_bootloader_space(board_info) && in_uf2_bootloader_space((const void *)fn) &&
|
||||
((uint32_t)fn & 1)) {
|
||||
// Pass control to bootloader; never returns
|
||||
fn(ep & 0xf);
|
||||
}
|
||||
}
|
||||
|
||||
// the ep_in/ep_out are without the 0x80 mask
|
||||
// cbw_tag is in the same bit format as it came
|
||||
static inline void check_uf2_handover(uint8_t *buffer, uint32_t blocks_remaining, uint8_t ep_in,
|
||||
uint8_t ep_out, uint32_t cbw_tag) {
|
||||
if (!is_uf2_block(buffer))
|
||||
return;
|
||||
|
||||
const char *board_info = UF2_BINFO->info_uf2;
|
||||
UF2_MSC_Handover_Handler fn = UF2_BINFO->handoverMSC;
|
||||
|
||||
if (in_uf2_bootloader_space(board_info) && in_uf2_bootloader_space((const void *)fn) &&
|
||||
((uint32_t)fn & 1)) {
|
||||
UF2_HandoverArgs hand = {
|
||||
1, ep_in, ep_out, 0, cbw_tag, blocks_remaining, buffer,
|
||||
};
|
||||
// Pass control to bootloader; never returns
|
||||
fn(&hand);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
13
src/uf2/uf2cfg.h
Normal file
13
src/uf2/uf2cfg.h
Normal file
@ -0,0 +1,13 @@
|
||||
#define UF2_VERSION "1.00"
|
||||
#define PRODUCT_NAME "Adafruit Bluefruit nRF52"
|
||||
#define BOARD_ID "NRF52-Bluefruit-v0"
|
||||
#define INDEX_URL "https://www.adafruit.com/product/0000"
|
||||
#define UF2_NUM_BLOCKS 8000
|
||||
#define VOLUME_LABEL "NRFBOOT"
|
||||
#define FLASH_SIZE (1024*1024)
|
||||
|
||||
// Only allow to write application
|
||||
#define USER_FLASH_START 0x26000
|
||||
#define USER_FLASH_END BOOTLOADER_REGION_START
|
||||
|
||||
#define FLASH_PAGE_SIZE 4096
|
Loading…
Reference in New Issue
Block a user