adding uf2 files
This commit is contained in:
		
							
								
								
									
										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 | ||||
		Reference in New Issue
	
	Block a user