From e148f7ba85c4160a37afabddba134e7f33284601 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Thu, 23 Jun 2022 09:12:43 +0200 Subject: [PATCH 01/14] mimxrt: Factor out low level flash functions from mimxrt_flash.c. These will be needed for MSC support. The change matches the changes for the bootloader. --- ports/mimxrt/Makefile | 1 + ports/mimxrt/flash.c | 134 ++++++++++++++++++++++++ ports/mimxrt/flash.h | 57 ++++++++++ ports/mimxrt/hal/flexspi_flash_config.h | 1 + ports/mimxrt/hal/flexspi_hyper_flash.c | 5 + ports/mimxrt/hal/flexspi_hyper_flash.h | 1 + ports/mimxrt/hal/flexspi_nor_flash.c | 31 ++++++ ports/mimxrt/hal/flexspi_nor_flash.h | 1 + ports/mimxrt/mimxrt_flash.c | 69 +----------- 9 files changed, 235 insertions(+), 65 deletions(-) create mode 100644 ports/mimxrt/flash.c create mode 100644 ports/mimxrt/flash.h diff --git a/ports/mimxrt/Makefile b/ports/mimxrt/Makefile index b73d3f4db9aa4..f624d26c02815 100644 --- a/ports/mimxrt/Makefile +++ b/ports/mimxrt/Makefile @@ -180,6 +180,7 @@ SRC_C += \ dma_manager.c \ drivers/bus/softspi.c \ drivers/dht/dht.c \ + flash.c \ eth.c \ fatfs_port.c \ hal/pwm_backport.c \ diff --git a/ports/mimxrt/flash.c b/ports/mimxrt/flash.c new file mode 100644 index 0000000000000..5fe0aa789adfc --- /dev/null +++ b/ports/mimxrt/flash.c @@ -0,0 +1,134 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Philipp Ebensberger + * + * 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. + */ + +#include "flash.h" + +// --------------------------------------------------------------------+ +// Global Function Definitions +// --------------------------------------------------------------------+ +void flash_init(void) { + // Upload the custom flash configuration + // This should be performed by the boot ROM but for some reason it is not. + FLEXSPI_UpdateLUT(FLEXSPI, 0, + qspiflash_config.memConfig.lookupTable, + ARRAY_SIZE(qspiflash_config.memConfig.lookupTable)); + + // Configure FLEXSPI IP FIFO access. + FLEXSPI->MCR0 &= ~(FLEXSPI_MCR0_ARDFEN_MASK); + FLEXSPI->MCR0 &= ~(FLEXSPI_MCR0_ATDFEN_MASK); + FLEXSPI->MCR0 |= FLEXSPI_MCR0_ARDFEN(0); + FLEXSPI->MCR0 |= FLEXSPI_MCR0_ATDFEN(0); + + FLEXSPI_EnableIPParallelMode(FLEXSPI, true); +} + +// flash_erase_block(erase_addr) +// erases the block starting at addr. Block size according to the flash properties. +status_t flash_erase_block(uint32_t erase_addr) { + status_t status = kStatus_Fail; + + SCB_CleanInvalidateDCache(); + SCB_DisableDCache(); + __disable_irq(); + + status = flexspi_nor_flash_erase_block(FLEXSPI, erase_addr); + + __enable_irq(); + SCB_EnableDCache(); + + return status; +} + +// flash_erase_sector(erase_addr_bytes) +// erases the sector starting at addr. Sector size according to the flash properties. +status_t flash_erase_sector(uint32_t erase_addr) { + status_t status = kStatus_Fail; + + SCB_CleanInvalidateDCache(); + SCB_DisableDCache(); + __disable_irq(); + + status = flexspi_nor_flash_erase_sector(FLEXSPI, erase_addr); + + __enable_irq(); + SCB_EnableDCache(); + + return status; +} + +// flash_read_block(flash_src_addr_bytes, data_dest, length_bytes) +// read length_byte data to the source address +// It is just a shim to provide the same structure for read_block and write_block. +void inline flash_read_block(uint32_t src_addr, uint8_t *dest, uint32_t length) { + memcpy(dest, (uint8_t *)(FlexSPI_AMBA_BASE + src_addr), length); +} + +// flash_write_block(flash_dest_addr_bytes, data_source, length_bytes) +// writes length_byte data to the destination address +// the vfs driver takes care for erasing the sector if required +status_t flash_write_block(uint32_t dest_addr, const uint8_t *src, uint32_t length) { + #if FLASH_DISABLE_OP == 1 + return kStatus_Success; + #else + status_t status = kStatus_Fail; + uint32_t size; + uint32_t next_addr; + + if (length == 0) { + status = kStatus_Success; // Nothing to do + } else { + + SCB_CleanInvalidateDCache(); + SCB_DisableDCache(); + + // write data in chunks not crossing a page boundary + while (length > 0) { + next_addr = dest_addr - (dest_addr % PAGE_SIZE_BYTES) + PAGE_SIZE_BYTES; // next page boundary + size = next_addr - dest_addr; // maximal chunk length + if (size > length) { // compare against remaining data size + size = length; + } + + __disable_irq(); + + status = flexspi_nor_flash_page_program(FLEXSPI, dest_addr, (uint32_t *)src, size); + + __enable_irq(); + + if (status != kStatus_Success) { + break; + } + length -= size; + src += size; + dest_addr += size; + } + + SCB_EnableDCache(); + + } + return status; + #endif +} diff --git a/ports/mimxrt/flash.h b/ports/mimxrt/flash.h new file mode 100644 index 0000000000000..c4085478d7316 --- /dev/null +++ b/ports/mimxrt/flash.h @@ -0,0 +1,57 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Philipp Ebensberger + * + * 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. + */ + +#ifndef MICROPY_INCLUDED_MIMXRT_FLASH_H +#define MICROPY_INCLUDED_MIMXRT_FLASH_H + +#include BOARD_FLASH_OPS_HEADER_H + +// --------------------------------------------------------------------+ +// Defines and Macros +// --------------------------------------------------------------------+ +#define SECTOR_SIZE_BYTES (qspiflash_config.sectorSize) +#define PAGE_SIZE_BYTES (qspiflash_config.pageSize) +#define BLOCK_SIZE_BYTES (qspiflash_config.blockSize) + +#define MICROPY_HW_FLASH_STORAGE_BASE (((uint32_t)&__vfs_start) - ((uint32_t)&__flash_start)) +#define MICROPY_HW_FLASH_STORAGE_BYTES (((uint32_t)&__vfs_end) - ((uint32_t)&__vfs_start)) + +extern uint8_t __vfs_start; +extern uint8_t __vfs_end; +extern uint8_t __flash_start; + +extern flexspi_nor_config_t qspiflash_config; + +// --------------------------------------------------------------------+ +// Global Function Declarations +// --------------------------------------------------------------------+ +void flash_init(void); +__attribute__((section(".ram_functions"))) status_t flash_erase_sector(uint32_t erase_addr); +__attribute__((section(".ram_functions"))) status_t flash_erase_block(uint32_t erase_addr); +__attribute__((section(".ram_functions"))) status_t flash_write_block(uint32_t dest_addr, const uint8_t *src, uint32_t length); +__attribute__((section(".ram_functions"))) void flash_read_block(uint32_t src_addr, uint8_t *dest, uint32_t length); + +#endif // MICROPY_INCLUDED_MIMXRT_FLASH_H diff --git a/ports/mimxrt/hal/flexspi_flash_config.h b/ports/mimxrt/hal/flexspi_flash_config.h index 80526880be0b1..a43010678207c 100644 --- a/ports/mimxrt/hal/flexspi_flash_config.h +++ b/ports/mimxrt/hal/flexspi_flash_config.h @@ -222,6 +222,7 @@ typedef struct _FlexSPIConfig #define NOR_CMD_LUT_SEQ_IDX_ENTERQPI 10 #define NOR_CMD_LUT_SEQ_IDX_CHIPERASE 11 #define NOR_CMD_LUT_SEQ_IDX_EXITQPI 12 +#define NOR_CMD_LUT_SEQ_IDX_ERASEBLOCK 13 #define HYPERFLASH_CMD_LUT_SEQ_IDX_READDATA 0 #define HYPERFLASH_CMD_LUT_SEQ_IDX_WRITEDATA 1 diff --git a/ports/mimxrt/hal/flexspi_hyper_flash.c b/ports/mimxrt/hal/flexspi_hyper_flash.c index a9f17f2d1c19c..5e5d87166d850 100644 --- a/ports/mimxrt/hal/flexspi_hyper_flash.c +++ b/ports/mimxrt/hal/flexspi_hyper_flash.c @@ -175,6 +175,11 @@ status_t flexspi_nor_flash_erase_sector(FLEXSPI_Type *base, uint32_t address) { return status; } +status_t flexspi_nor_flash_erase_block(FLEXSPI_Type *base, uint32_t address) __attribute__((section(".ram_functions"))); +status_t flexspi_nor_flash_erase_block(FLEXSPI_Type *base, uint32_t address) { + return flexspi_nor_flash_erase_sector(base, address); // HyperFlash does not support block erase! +} + status_t flexspi_nor_flash_page_program(FLEXSPI_Type *base, uint32_t address, const uint32_t *src, uint32_t size) __attribute__((section(".ram_functions"))); status_t flexspi_nor_flash_page_program(FLEXSPI_Type *base, uint32_t address, const uint32_t *src, uint32_t size) { status_t status; diff --git a/ports/mimxrt/hal/flexspi_hyper_flash.h b/ports/mimxrt/hal/flexspi_hyper_flash.h index fe9acea429f62..c62c8f0a5e81c 100644 --- a/ports/mimxrt/hal/flexspi_hyper_flash.h +++ b/ports/mimxrt/hal/flexspi_hyper_flash.h @@ -46,6 +46,7 @@ status_t flexspi_nor_hyperflash_cfi(FLEXSPI_Type *base); void flexspi_hyper_flash_init(void); void flexspi_nor_update_lut(void); status_t flexspi_nor_flash_erase_sector(FLEXSPI_Type *base, uint32_t address); +status_t flexspi_nor_flash_erase_block(FLEXSPI_Type *base, uint32_t address); status_t flexspi_nor_flash_page_program(FLEXSPI_Type *base, uint32_t address, const uint32_t *src, uint32_t size); static inline uint32_t flexspi_get_frequency(void) { diff --git a/ports/mimxrt/hal/flexspi_nor_flash.c b/ports/mimxrt/hal/flexspi_nor_flash.c index 48792ec487eb8..956fb657db6dd 100644 --- a/ports/mimxrt/hal/flexspi_nor_flash.c +++ b/ports/mimxrt/hal/flexspi_nor_flash.c @@ -165,6 +165,37 @@ status_t flexspi_nor_flash_erase_sector(FLEXSPI_Type *base, uint32_t address) { return status; } +status_t flexspi_nor_flash_erase_block(FLEXSPI_Type *base, uint32_t address) __attribute__((section(".ram_functions"))); +status_t flexspi_nor_flash_erase_block(FLEXSPI_Type *base, uint32_t address) { + status_t status; + flexspi_transfer_t flashXfer; + + /* Write enable */ + status = flexspi_nor_write_enable(base, address); + + if (status != kStatus_Success) { + return status; + } + + /* Erase sector */ + flashXfer.deviceAddress = address; + flashXfer.port = kFLEXSPI_PortA1; + flashXfer.cmdType = kFLEXSPI_Command; + flashXfer.SeqNumber = 1; + flashXfer.seqIndex = NOR_CMD_LUT_SEQ_IDX_ERASEBLOCK; + status = FLEXSPI_TransferBlocking(base, &flashXfer); + + if (status != kStatus_Success) { + return status; + } + + status = flexspi_nor_wait_bus_busy(base); + + flexspi_nor_reset(base); + + return status; +} + status_t flexspi_nor_flash_page_program(FLEXSPI_Type *base, uint32_t dstAddr, const uint32_t *src, uint32_t size) __attribute__((section(".ram_functions"))); status_t flexspi_nor_flash_page_program(FLEXSPI_Type *base, uint32_t dstAddr, const uint32_t *src, uint32_t size) { status_t status; diff --git a/ports/mimxrt/hal/flexspi_nor_flash.h b/ports/mimxrt/hal/flexspi_nor_flash.h index 69d57da6919e4..ae2945c3677a8 100644 --- a/ports/mimxrt/hal/flexspi_nor_flash.h +++ b/ports/mimxrt/hal/flexspi_nor_flash.h @@ -44,6 +44,7 @@ status_t flexspi_nor_get_vendor_id(FLEXSPI_Type *base, uint8_t *vendorId); status_t flexspi_nor_init(void); void flexspi_nor_update_lut(void); status_t flexspi_nor_flash_erase_sector(FLEXSPI_Type *base, uint32_t address); +status_t flexspi_nor_flash_erase_block(FLEXSPI_Type *base, uint32_t address); status_t flexspi_nor_flash_page_program(FLEXSPI_Type *base, uint32_t address, const uint32_t *src, uint32_t size); #endif // MICROPY_INCLUDED_MIMXRT_HAL_FLEXSPI_NOR_FLASH_H diff --git a/ports/mimxrt/mimxrt_flash.c b/ports/mimxrt/mimxrt_flash.c index 536fb6098170e..b6144d698de91 100644 --- a/ports/mimxrt/mimxrt_flash.c +++ b/ports/mimxrt/mimxrt_flash.c @@ -30,25 +30,10 @@ #include "py/runtime.h" #include "extmod/vfs.h" #include "modmimxrt.h" +#include "flash.h" #include BOARD_FLASH_OPS_HEADER_H -#define SECTOR_SIZE_BYTES (qspiflash_config.sectorSize) -#define PAGE_SIZE_BYTES (qspiflash_config.pageSize) - -#ifndef MICROPY_HW_FLASH_STORAGE_BYTES -#define MICROPY_HW_FLASH_STORAGE_BYTES (((uint32_t)&__vfs_end) - ((uint32_t)&__vfs_start)) -#endif - -#ifndef MICROPY_HW_FLASH_STORAGE_BASE -#define MICROPY_HW_FLASH_STORAGE_BASE (((uint32_t)&__vfs_start) - ((uint32_t)&__flash_start)) -#endif - // Linker symbols -extern uint8_t __vfs_start; -extern uint8_t __vfs_end; -extern uint8_t __flash_start; - -extern flexspi_nor_config_t qspiflash_config; typedef struct _mimxrt_flash_obj_t { mp_obj_base_t base; @@ -60,52 +45,6 @@ STATIC mimxrt_flash_obj_t mimxrt_flash_obj = { .base = { &mimxrt_flash_type } }; -// flash_erase_block(erase_addr_bytes) -// erases the sector starting at addr. Sector size according to the flash properties. -status_t flash_erase_block(uint32_t erase_addr) __attribute__((section(".ram_functions"))); -status_t flash_erase_block(uint32_t erase_addr) { - status_t status; - SCB_CleanInvalidateDCache(); - SCB_DisableDCache(); - __disable_irq(); - status = flexspi_nor_flash_erase_sector(BOARD_FLEX_SPI, erase_addr); - __enable_irq(); - SCB_EnableDCache(); - return status; -} - -// flash_write_block(flash_dest_addr_bytes, data_source, length_bytes) -// writes length_byte data to the destination address -// the vfs driver takes care for erasing the sector if required -status_t flash_write_block(uint32_t dest_addr, const uint8_t *src, uint32_t length) __attribute__((section(".ram_functions"))); -status_t flash_write_block(uint32_t dest_addr, const uint8_t *src, uint32_t length) { - status_t status = 0; - uint32_t size; - uint32_t next_addr; - - SCB_CleanInvalidateDCache(); - SCB_DisableDCache(); - // write data in chunks not crossing a page boundary - while (length > 0) { - next_addr = dest_addr - (dest_addr % PAGE_SIZE_BYTES) + PAGE_SIZE_BYTES; // next page boundary - size = next_addr - dest_addr; // maximal chunk length - if (size > length) { // compare against remaining data size - size = length; - } - __disable_irq(); - status = flexspi_nor_flash_page_program(BOARD_FLEX_SPI, dest_addr, (uint32_t *)src, size); - __enable_irq(); - if (status != kStatus_Success) { - break; - } - length -= size; - src += size; - dest_addr += size; - } - SCB_EnableDCache(); - return status; -} - STATIC mp_obj_t mimxrt_flash_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { // Check args. mp_arg_check_num(n_args, n_kw, 0, 0, false); @@ -142,7 +81,7 @@ STATIC mp_obj_t mimxrt_flash_readblocks(size_t n_args, const mp_obj_t *args) { if (n_args == 4) { offset += mp_obj_get_int(args[3]); } - memcpy(bufinfo.buf, (uint8_t *)(BOARD_FLEX_SPI_ADDR_BASE + self->flash_base + offset), bufinfo.len); + flash_read_block(self->flash_base + offset, bufinfo.buf, bufinfo.len); return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mimxrt_flash_readblocks_obj, 3, 4, mimxrt_flash_readblocks); @@ -161,7 +100,7 @@ STATIC mp_obj_t mimxrt_flash_writeblocks(size_t n_args, const mp_obj_t *args) { uint32_t offset = mp_obj_get_int(args[1]) * SECTOR_SIZE_BYTES; if (n_args == 3) { - status = flash_erase_block(self->flash_base + offset); + status = flash_erase_sector(self->flash_base + offset); if (status != kStatus_Success) { mp_raise_msg_varg(&mp_type_OSError, MP_ERROR_TEXT("flash erase command failed with %d"), status); @@ -199,7 +138,7 @@ STATIC mp_obj_t mimxrt_flash_ioctl(mp_obj_t self_in, mp_obj_t cmd_in, mp_obj_t a return MP_OBJ_NEW_SMALL_INT(SECTOR_SIZE_BYTES); case MP_BLOCKDEV_IOCTL_BLOCK_ERASE: { uint32_t offset = mp_obj_get_int(arg_in) * SECTOR_SIZE_BYTES; - status = flash_erase_block(self->flash_base + offset); + status = flash_erase_sector(self->flash_base + offset); return MP_OBJ_NEW_SMALL_INT(status != kStatus_Success); } default: From a32875a08b473581d8ec5ee2eca32609ffbd498f Mon Sep 17 00:00:00 2001 From: robert-hh Date: Fri, 24 Jun 2022 08:39:25 +0200 Subject: [PATCH 02/14] mimxrt: Reduce the stack size for MIMXRT1011. The DTCM section got too large. --- ports/mimxrt/boards/MIMXRT1011.ld | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/mimxrt/boards/MIMXRT1011.ld b/ports/mimxrt/boards/MIMXRT1011.ld index 908eefffd643f..713ab10203c30 100644 --- a/ports/mimxrt/boards/MIMXRT1011.ld +++ b/ports/mimxrt/boards/MIMXRT1011.ld @@ -28,7 +28,7 @@ ocrm_start = 0x20200000; ocrm_size = 0x00010000; /* 20kiB stack. */ -__stack_size__ = 0x5000; +__stack_size__ = 0x4800; _estack = __StackTop; _sstack = __StackLimit; From 19fbdf978448cef8b4e9cd5e48b4cb4ef73ae5f3 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Fri, 24 Jun 2022 08:48:55 +0200 Subject: [PATCH 03/14] mimxrt: Add a 'MSC' attribute to the mimxrt module. This attribute is present on builds with MSC support, allowing _boot.py to decide about creating if and which file system. --- ports/mimxrt/modmimxrt.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ports/mimxrt/modmimxrt.c b/ports/mimxrt/modmimxrt.c index 4756efc0a27e7..3a1ae0263f56e 100644 --- a/ports/mimxrt/modmimxrt.c +++ b/ports/mimxrt/modmimxrt.c @@ -31,6 +31,9 @@ STATIC const mp_rom_map_elem_t mimxrt_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_mimxrt) }, { MP_ROM_QSTR(MP_QSTR_Flash), MP_ROM_PTR(&mimxrt_flash_type) }, + #if MICROPY_HW_USB_MSC + { MP_ROM_QSTR(MP_QSTR_MSC), MP_ROM_INT(1) }, + #endif }; STATIC MP_DEFINE_CONST_DICT(mimxrt_module_globals, mimxrt_module_globals_table); From 5bed2538a684f588b8261b83c2cef6c19418039c Mon Sep 17 00:00:00 2001 From: robert-hh Date: Fri, 24 Jun 2022 09:56:24 +0200 Subject: [PATCH 04/14] mimxrt: Change _boot.py to cater for MSC support. As a benefit, it will not destroy an existing FAT partition. --- ports/mimxrt/modules/_boot.py | 44 ++++++++++++++++++++++++++++++----- 1 file changed, 38 insertions(+), 6 deletions(-) diff --git a/ports/mimxrt/modules/_boot.py b/ports/mimxrt/modules/_boot.py index 56f12a4ce1590..9b2bb4e02199f 100644 --- a/ports/mimxrt/modules/_boot.py +++ b/ports/mimxrt/modules/_boot.py @@ -7,13 +7,45 @@ import mimxrt from machine import Pin +FS_UNDEF = const(0) +FS_LITTLEFS = const(1) +FS_FAT = const(2) + +def fs_type(bdev): + b = bytearray(512) + bdev.readblocks(0, b) + if b[40:48] == b"littlefs" or b[8:16] == b"littlefs": + return FS_LITTLEFS + elif b[510:512] == b'\x55\xaa': + return FS_FAT + else: + return FS_UNDEF + bdev = mimxrt.Flash() -try: - vfs = os.VfsLfs2(bdev, progsize=256) -except: - os.VfsLfs2.mkfs(bdev, progsize=256) - vfs = os.VfsLfs2(bdev, progsize=256) -os.mount(vfs, "/flash") + +# In case of MSC support mount as FAT +# Create a FAT FS is needed +if hasattr(mimxrt, "MSC"): + try: + vfs = os.VfsFat(bdev) + os.mount(vfs, "/flash") + except: + os.VfsFat.mkfs(bdev) + vfs = os.VfsFat(bdev) + os.mount(vfs, "/flash") +# otherwise analyze the boot sector an mount accordingly +# if no valid boot sector, creat a LFS file system +else: + fs = fs_type(bdev) + if fs == FS_LITTLEFS: + vfs = os.VfsLfs2(bdev, progsize=256) + elif fs == FS_FAT: + vfs = os.VfsFat(bdev) + else: + os.VfsLfs2.mkfs(bdev, progsize=256) + vfs = os.VfsLfs2(bdev, progsize=256) + os.mount(vfs, "/flash") + os.chdir("/flash") sys.path.append("/flash") sys.path.append("/flash/lib") From b48c8278dacf3d0e8f15ad4b8e3c5e24f277915b Mon Sep 17 00:00:00 2001 From: robert-hh Date: Fri, 24 Jun 2022 09:57:45 +0200 Subject: [PATCH 05/14] mimxrt: Add MSC support as a build option. Using the good implementation by @iabdalkader for RP2 and some of @alphafred's work for the bootlaoder. Open topics: - In the descriptor the blocksize was set to 64. MIMXRT requires 512 - Adding a lock preventing local write as long as the disk is mounted. - Hyperflash has a sector size of 256k. Here, the BLOCK_SIZE == SECTOR_SIZE_BYTES is 4096, which by good luck meets the size required for SD Cards. --- ports/mimxrt/Makefile | 1 + ports/mimxrt/modules/_boot.py | 4 +- ports/mimxrt/mpconfigport.h | 15 +++++ ports/mimxrt/msc_disk.c | 122 ++++++++++++++++++++++++++++++++++ ports/mimxrt/tusb_config.h | 9 +++ ports/mimxrt/tusb_port.c | 22 +++++- 6 files changed, 170 insertions(+), 3 deletions(-) create mode 100644 ports/mimxrt/msc_disk.c diff --git a/ports/mimxrt/Makefile b/ports/mimxrt/Makefile index f624d26c02815..8c0953623c479 100644 --- a/ports/mimxrt/Makefile +++ b/ports/mimxrt/Makefile @@ -205,6 +205,7 @@ SRC_C += \ modutime.c \ mphalport.c \ mpnetworkport.c \ + msc_disk.c \ network_lan.c \ pendsv.c \ pin.c \ diff --git a/ports/mimxrt/modules/_boot.py b/ports/mimxrt/modules/_boot.py index 9b2bb4e02199f..dd63e4ea3e440 100644 --- a/ports/mimxrt/modules/_boot.py +++ b/ports/mimxrt/modules/_boot.py @@ -24,7 +24,7 @@ def fs_type(bdev): bdev = mimxrt.Flash() # In case of MSC support mount as FAT -# Create a FAT FS is needed +# Create a FAT FS if needed if hasattr(mimxrt, "MSC"): try: vfs = os.VfsFat(bdev) @@ -34,7 +34,7 @@ def fs_type(bdev): vfs = os.VfsFat(bdev) os.mount(vfs, "/flash") # otherwise analyze the boot sector an mount accordingly -# if no valid boot sector, creat a LFS file system +# without a valid boot sector create a LFS file system else: fs = fs_type(bdev) if fs == FS_LITTLEFS: diff --git a/ports/mimxrt/mpconfigport.h b/ports/mimxrt/mpconfigport.h index 873e9e0d189b6..96d4ad0941977 100644 --- a/ports/mimxrt/mpconfigport.h +++ b/ports/mimxrt/mpconfigport.h @@ -26,6 +26,8 @@ // Options controlling how MicroPython is built, overriding defaults in py/mpconfig.h +#ifndef MICROPY_INCLUDED_MPCONFIGPORT_H +#define MICROPY_INCLUDED_MPCONFIGPORT_H // Board specific definitions #include "mpconfigboard.h" #include "fsl_common.h" @@ -99,6 +101,7 @@ uint32_t trng_random_u32(void); // fatfs configuration used in ffconf.h #define MICROPY_FATFS_ENABLE_LFN (1) #define MICROPY_FATFS_RPATH (2) +// to be redefined depending on the flash type. #define MICROPY_FATFS_MAX_SS (4096) #define MICROPY_FATFS_LFN_CODE_PAGE 437 /* 1=SFN/ANSI 437=LFN/U.S.(OEM) */ @@ -133,6 +136,16 @@ uint32_t trng_random_u32(void); #define MICROPY_PY_PENDSV_REENTER atomic_state = raise_irq_pri(IRQ_PRI_PENDSV); #define MICROPY_PY_PENDSV_EXIT restore_irq_pri(atomic_state); +// Enable USB Mass Storage with FatFS filesystem. +#define MICROPY_HW_USB_MSC (1) + +#if MICROPY_HW_USB_MSC +#define MICROPY_FATFS_USE_LABEL (1) +#define MICROPY_FATFS_MULTI_PARTITION (1) +// Set FatFS block size to flash sector size to avoid caching +// the flash sector in memory to support smaller block sizes. +#endif + // Hooks to add builtins __attribute__((always_inline)) static inline void enable_irq(uint32_t state) { @@ -223,3 +236,5 @@ typedef long mp_off_t; // Need to provide a declaration/definition of alloca() #include + +#endif // MICROPY_INCLUDED_MPCONFIGPORT_H diff --git a/ports/mimxrt/msc_disk.c b/ports/mimxrt/msc_disk.c new file mode 100644 index 0000000000000..a4d784c5dfa58 --- /dev/null +++ b/ports/mimxrt/msc_disk.c @@ -0,0 +1,122 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Damien P. George + * + * 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. + * + */ +#include "tusb.h" +#if CFG_TUD_MSC +#include "flash.h" +#include BOARD_FLASH_OPS_HEADER_H +#include "stdlib.h" + +// This implementation does Not support Flash sector caching. +// MICROPY_FATFS_MAX_SS must be identical to SECTOR_SIZE_BYTES +#define BLOCK_SIZE (SECTOR_SIZE_BYTES) +#define BLOCK_COUNT (MICROPY_HW_FLASH_STORAGE_BYTES / BLOCK_SIZE) +#define FLASH_BASE_ADDR (MICROPY_HW_FLASH_STORAGE_BASE) + +bool ejected = false; + +// Invoked when received SCSI_CMD_INQUIRY +// Application fill vendor id, product id and revision with string up to 8, 16, 4 characters respectively +void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16], uint8_t product_rev[4]) { + const char vid[] = "Micropy"; + const char pid[] = "Mass Storage"; + const char rev[] = "1.0"; + + strncpy((char *)vendor_id, vid, 8); + strncpy((char *)product_id, pid, 16); + strncpy((char *)product_rev, rev, 4); +} + +// Invoked when received Test Unit Ready command. +// return true allowing host to read/write this LUN e.g SD card inserted +bool tud_msc_test_unit_ready_cb(uint8_t lun) { + if (ejected) { + tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x3a, 0x00); + return false; + } + return true; +} + +// Invoked when received SCSI_CMD_READ_CAPACITY_10 and SCSI_CMD_READ_FORMAT_CAPACITY to determine the disk size +// Application update block count and block size +void tud_msc_capacity_cb(uint8_t lun, uint32_t *block_count, uint16_t *block_size) { + *block_size = BLOCK_SIZE; + *block_count = BLOCK_COUNT; +} + +// Invoked when received Start Stop Unit command +// - Start = 0 : stopped power mode, if load_eject = 1 : unload disk storage +// - Start = 1 : active mode, if load_eject = 1 : load disk storage +bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, bool load_eject) { + if (load_eject) { + if (start) { + // load disk storage + ejected = false; + } else { + // unload disk storage + ejected = true; + } + } + return true; +} + +// Callback invoked when received READ10 command. +// Copy disk's data to buffer (up to bufsize) and return number of copied bytes. +int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void *buffer, uint32_t bufsize) { + flash_read_block(FLASH_BASE_ADDR + lba * BLOCK_SIZE, buffer, bufsize); + return bufsize; +} + +// Callback invoked when received WRITE10 command. +// Process data in buffer to disk's storage and return number of written bytes +int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t *buffer, uint32_t bufsize) { + uint32_t count = bufsize / BLOCK_SIZE; + // Erase count sectors starting at lba + for (int n = 0; n < count; n++) { + flash_erase_sector(FLASH_BASE_ADDR + (lba + n) * BLOCK_SIZE); + } + flash_write_block(FLASH_BASE_ADDR + lba * BLOCK_SIZE, buffer, count * BLOCK_SIZE); + return count * BLOCK_SIZE; +} + +// Callback invoked when received an SCSI command not in built-in list below +// - READ_CAPACITY10, READ_FORMAT_CAPACITY, INQUIRY, MODE_SENSE6, REQUEST_SENSE +// - READ10 and WRITE10 has their own callbacks +int32_t tud_msc_scsi_cb(uint8_t lun, uint8_t const scsi_cmd[16], void *buffer, uint16_t bufsize) { + int32_t resplen = 0; + switch (scsi_cmd[0]) { + case SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL: + // Sync the logical unit if needed. + break; + + default: + // Set Sense = Invalid Command Operation + tud_msc_set_sense(lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00); + // negative means error -> tinyusb could stall and/or response with failed status + resplen = -1; + break; + } + return resplen; +} +#endif diff --git a/ports/mimxrt/tusb_config.h b/ports/mimxrt/tusb_config.h index 862fb6c52d2e5..bc32695fa0fd1 100644 --- a/ports/mimxrt/tusb_config.h +++ b/ports/mimxrt/tusb_config.h @@ -25,6 +25,8 @@ #ifndef MICROPY_INCLUDED_MIMXRT_TUSB_CONFIG_H #define MICROPY_INCLUDED_MIMXRT_TUSB_CONFIG_H +#include "mpconfigport.h" + #define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE | OPT_MODE_HIGH_SPEED) #define CFG_TUSB_OS (OPT_OS_NONE) @@ -33,4 +35,11 @@ #define CFG_TUD_CDC_RX_BUFSIZE (512) #define CFG_TUD_CDC_TX_BUFSIZE (512) +#if MICROPY_HW_USB_MSC +// Board and hardware specific configuration +#define CFG_TUD_MSC (1) +// Set MSC EP buffer size to FatFS block size to avoid partial read/writes (offset arg). +#define CFG_TUD_MSC_BUFSIZE (MICROPY_FATFS_MAX_SS) +#endif + #endif // MICROPY_INCLUDED_MIMXRT_TUSB_CONFIG_H diff --git a/ports/mimxrt/tusb_port.c b/ports/mimxrt/tusb_port.c index f359d44e4d276..16bdcd054c673 100644 --- a/ports/mimxrt/tusb_port.c +++ b/ports/mimxrt/tusb_port.c @@ -39,23 +39,37 @@ #define MICROPY_HW_USB_MANUFACTURER_STRING ("MicroPython") #endif +#if CFG_TUD_MSC +#define USBD_DESC_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN + TUD_MSC_DESC_LEN) +#else #define USBD_DESC_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN) +#endif + #define USBD_MAX_POWER_MA (250) #define USBD_ITF_CDC (0) // needs 2 interfaces +#define USBD_ITF_MSC (2) +#if CFG_TUD_MSC +#define USBD_ITF_MAX (3) +#else #define USBD_ITF_MAX (2) +#endif #define USBD_CDC_EP_CMD (0x81) #define USBD_CDC_EP_OUT (0x02) -#define USBD_CDC_EP_IN (0x82) +#define USBD_CDC_EP_IN (0x82) #define USBD_CDC_CMD_MAX_SIZE (8) #define USBD_CDC_IN_OUT_MAX_SIZE (512) +#define EPNUM_MSC_OUT (0x03) +#define EPNUM_MSC_IN (0x83) + #define USBD_STR_0 (0x00) #define USBD_STR_MANUF (0x01) #define USBD_STR_PRODUCT (0x02) #define USBD_STR_SERIAL (0x03) #define USBD_STR_CDC (0x04) +#define USBD_STR_MSC (0x05) // Note: descriptors returned from callbacks must exist long enough for transfer to complete @@ -82,6 +96,9 @@ static const uint8_t usbd_desc_cfg[USBD_DESC_LEN] = { TUD_CDC_DESCRIPTOR(USBD_ITF_CDC, USBD_STR_CDC, USBD_CDC_EP_CMD, USBD_CDC_CMD_MAX_SIZE, USBD_CDC_EP_OUT, USBD_CDC_EP_IN, USBD_CDC_IN_OUT_MAX_SIZE), + #if CFG_TUD_MSC + TUD_MSC_DESCRIPTOR(USBD_ITF_MSC, 5, EPNUM_MSC_OUT, EPNUM_MSC_IN, 512), + #endif }; static const char *const usbd_desc_str[] = { @@ -89,6 +106,9 @@ static const char *const usbd_desc_str[] = { [USBD_STR_PRODUCT] = MICROPY_HW_BOARD_NAME, [USBD_STR_SERIAL] = "00000000000000000000", [USBD_STR_CDC] = "Board CDC", + #if CFG_TUD_MSC + [USBD_STR_MSC] = "Board MSC", + #endif }; const uint8_t *tud_descriptor_device_cb(void) { From 278f0a9da91f2fde00edc7e45e9de5d49ee21de3 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Sat, 25 Jun 2022 08:04:18 +0200 Subject: [PATCH 06/14] mimxrt: Change the MSC enable/disable setting. Enable MSC unless defined different for a board. Disable MSC for MIMXRT1050_EVK because of it's hyperflash chip. --- ports/mimxrt/boards/MIMXRT1050_EVK/mpconfigboard.h | 3 +++ ports/mimxrt/modules/_boot.py | 4 +++- ports/mimxrt/mpconfigport.h | 8 ++++---- ports/mimxrt/msc_disk.c | 2 +- 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/ports/mimxrt/boards/MIMXRT1050_EVK/mpconfigboard.h b/ports/mimxrt/boards/MIMXRT1050_EVK/mpconfigboard.h index 134c9637ea57e..c46bdcf53c70e 100644 --- a/ports/mimxrt/boards/MIMXRT1050_EVK/mpconfigboard.h +++ b/ports/mimxrt/boards/MIMXRT1050_EVK/mpconfigboard.h @@ -3,6 +3,9 @@ #define MICROPY_PY_NETWORK_HOSTNAME_DEFAULT "mpy-1050evk" +// Disable MSC for the device with Hyperflash +#define MICROPY_HW_USB_MSC (0) + // MIMXRT1050_EVKB has 1 user LED #define MICROPY_HW_LED1_PIN (pin_GPIO_AD_B0_09) #define MICROPY_HW_LED_ON(pin) (mp_hal_pin_low(pin)) diff --git a/ports/mimxrt/modules/_boot.py b/ports/mimxrt/modules/_boot.py index dd63e4ea3e440..e95a73bfb53f2 100644 --- a/ports/mimxrt/modules/_boot.py +++ b/ports/mimxrt/modules/_boot.py @@ -11,16 +11,18 @@ FS_LITTLEFS = const(1) FS_FAT = const(2) + def fs_type(bdev): b = bytearray(512) bdev.readblocks(0, b) if b[40:48] == b"littlefs" or b[8:16] == b"littlefs": return FS_LITTLEFS - elif b[510:512] == b'\x55\xaa': + elif b[510:512] == b"\x55\xaa": return FS_FAT else: return FS_UNDEF + bdev = mimxrt.Flash() # In case of MSC support mount as FAT diff --git a/ports/mimxrt/mpconfigport.h b/ports/mimxrt/mpconfigport.h index 96d4ad0941977..f4ae7d88205d6 100644 --- a/ports/mimxrt/mpconfigport.h +++ b/ports/mimxrt/mpconfigport.h @@ -136,14 +136,14 @@ uint32_t trng_random_u32(void); #define MICROPY_PY_PENDSV_REENTER atomic_state = raise_irq_pri(IRQ_PRI_PENDSV); #define MICROPY_PY_PENDSV_EXIT restore_irq_pri(atomic_state); -// Enable USB Mass Storage with FatFS filesystem. -#define MICROPY_HW_USB_MSC (1) +// by default do not enable MSC support +#ifndef MICROPY_HW_USB_MSC +#define MICROPY_HW_USB_MSC (0) +#endif #if MICROPY_HW_USB_MSC #define MICROPY_FATFS_USE_LABEL (1) #define MICROPY_FATFS_MULTI_PARTITION (1) -// Set FatFS block size to flash sector size to avoid caching -// the flash sector in memory to support smaller block sizes. #endif // Hooks to add builtins diff --git a/ports/mimxrt/msc_disk.c b/ports/mimxrt/msc_disk.c index a4d784c5dfa58..bc0e5305cf77d 100644 --- a/ports/mimxrt/msc_disk.c +++ b/ports/mimxrt/msc_disk.c @@ -94,7 +94,7 @@ int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t * uint32_t count = bufsize / BLOCK_SIZE; // Erase count sectors starting at lba for (int n = 0; n < count; n++) { - flash_erase_sector(FLASH_BASE_ADDR + (lba + n) * BLOCK_SIZE); + flash_erase_sector(FLASH_BASE_ADDR + (lba + n) * BLOCK_SIZE); } flash_write_block(FLASH_BASE_ADDR + lba * BLOCK_SIZE, buffer, count * BLOCK_SIZE); return count * BLOCK_SIZE; From 268f5004ca0ca0d27978ddf6f08f8a83f18815a1 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Sat, 25 Jun 2022 13:15:34 +0200 Subject: [PATCH 07/14] mimxrt: Implement mutual write permission with MSC use. Porting PR #8814 of @iabdalkader to the mimxrt platform. - When the board is mounted to a PC, local write access is blocked. - Local write access will be re-enabled again if the disk is ejected. - If the board is not connected to a PC on start-up, local write access is enabled. - The status of the write access can be determined by bdev.ioctl(7, 0). If the device is mounted to a PAC and thus local write access is blocked, it return 1, otherwise 0. - low level write/erase using the block device calls are still possible. It is up to the user to check the device status ahead. --- extmod/vfs.h | 1 + extmod/vfs_fat_diskio.c | 20 +++++++++++++------- ports/mimxrt/Makefile | 1 + ports/mimxrt/mimxrt_flash.c | 8 ++++++++ ports/mimxrt/mpconfigport.h | 7 +++++-- ports/mimxrt/msc_disk.c | 14 ++++++++++---- shared/tinyusb/mp_cdc_common.c | 28 ++++++++++++++++++++++++++++ 7 files changed, 66 insertions(+), 13 deletions(-) diff --git a/extmod/vfs.h b/extmod/vfs.h index f577d3e337c87..f05bda3c77d2c 100644 --- a/extmod/vfs.h +++ b/extmod/vfs.h @@ -51,6 +51,7 @@ #define MP_BLOCKDEV_IOCTL_BLOCK_COUNT (4) #define MP_BLOCKDEV_IOCTL_BLOCK_SIZE (5) #define MP_BLOCKDEV_IOCTL_BLOCK_ERASE (6) +#define MP_BLOCKDEV_IOCTL_STATUS (7) // At the moment the VFS protocol just has import_stat, but could be extended to other methods typedef struct _mp_vfs_proto_t { diff --git a/extmod/vfs_fat_diskio.c b/extmod/vfs_fat_diskio.c index 1bcd471f2162e..1457a2ea09573 100644 --- a/extmod/vfs_fat_diskio.c +++ b/extmod/vfs_fat_diskio.c @@ -114,6 +114,7 @@ DRESULT disk_ioctl( [GET_SECTOR_COUNT] = MP_BLOCKDEV_IOCTL_BLOCK_COUNT, [GET_SECTOR_SIZE] = MP_BLOCKDEV_IOCTL_BLOCK_SIZE, [IOCTL_INIT] = MP_BLOCKDEV_IOCTL_INIT, + [IOCTL_STATUS] = MP_BLOCKDEV_IOCTL_STATUS, }; uint8_t bp_op = op_map[cmd & 7]; mp_obj_t ret = mp_const_none; @@ -147,16 +148,21 @@ DRESULT disk_ioctl( *((DWORD *)buff) = 1; // erase block size in units of sector size return RES_OK; - case IOCTL_INIT: - case IOCTL_STATUS: { - DSTATUS stat; + case IOCTL_INIT: { + DSTATUS stat = 0; if (ret != mp_const_none && MP_OBJ_SMALL_INT_VALUE(ret) != 0) { // error initialising - stat = STA_NOINIT; - } else if (vfs->blockdev.writeblocks[0] == MP_OBJ_NULL) { + stat = STA_NOINIT; + } + *((DSTATUS *)buff) = stat; + return RES_OK; + } + + case IOCTL_STATUS: { + DSTATUS stat = 0; + if (vfs->blockdev.writeblocks[0] == MP_OBJ_NULL || + (ret != mp_const_none && MP_OBJ_SMALL_INT_VALUE(ret) != 0)) { stat = STA_PROTECT; - } else { - stat = 0; } *((DSTATUS *)buff) = stat; return RES_OK; diff --git a/ports/mimxrt/Makefile b/ports/mimxrt/Makefile index 8c0953623c479..d2244d2ae8cd0 100644 --- a/ports/mimxrt/Makefile +++ b/ports/mimxrt/Makefile @@ -227,6 +227,7 @@ SHARED_SRC_C += \ shared/runtime/pyexec.c \ shared/runtime/softtimer.c \ shared/runtime/stdout_helpers.c \ + shared/runtime/tinyusb_helpers.c \ shared/runtime/sys_stdio_mphal.c \ shared/timeutils/timeutils.c \ diff --git a/ports/mimxrt/mimxrt_flash.c b/ports/mimxrt/mimxrt_flash.c index b6144d698de91..26420c5a75c43 100644 --- a/ports/mimxrt/mimxrt_flash.c +++ b/ports/mimxrt/mimxrt_flash.c @@ -45,6 +45,8 @@ STATIC mimxrt_flash_obj_t mimxrt_flash_obj = { .base = { &mimxrt_flash_type } }; +extern bool tud_msc_ejected; + STATIC mp_obj_t mimxrt_flash_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { // Check args. mp_arg_check_num(n_args, n_kw, 0, 0, false); @@ -141,6 +143,12 @@ STATIC mp_obj_t mimxrt_flash_ioctl(mp_obj_t self_in, mp_obj_t cmd_in, mp_obj_t a status = flash_erase_sector(self->flash_base + offset); return MP_OBJ_NEW_SMALL_INT(status != kStatus_Success); } + case MP_BLOCKDEV_IOCTL_STATUS: + #if MICROPY_HW_USB_MSC + return MP_OBJ_NEW_SMALL_INT(!tud_msc_ejected); + #else + return MP_OBJ_NEW_SMALL_INT(false); + #endif default: return mp_const_none; } diff --git a/ports/mimxrt/mpconfigport.h b/ports/mimxrt/mpconfigport.h index f4ae7d88205d6..ac4c6f6bc3894 100644 --- a/ports/mimxrt/mpconfigport.h +++ b/ports/mimxrt/mpconfigport.h @@ -61,6 +61,7 @@ uint32_t trng_random_u32(void); #define MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF (1) #define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_MPZ) #define MICROPY_SCHEDULER_DEPTH (8) +#define MICROPY_SCHEDULER_STATIC_NODES (1) #define MICROPY_VFS (1) #define MICROPY_MODULE_FROZEN_MPY (1) #define MICROPY_QSTR_EXTRA_POOL mp_qstr_frozen_const_pool @@ -136,14 +137,16 @@ uint32_t trng_random_u32(void); #define MICROPY_PY_PENDSV_REENTER atomic_state = raise_irq_pri(IRQ_PRI_PENDSV); #define MICROPY_PY_PENDSV_EXIT restore_irq_pri(atomic_state); -// by default do not enable MSC support +// by default enable MSC support, unless disabled at some boards +// Boards with hyperflash must disbale MSC support. #ifndef MICROPY_HW_USB_MSC -#define MICROPY_HW_USB_MSC (0) +#define MICROPY_HW_USB_MSC (1) #endif #if MICROPY_HW_USB_MSC #define MICROPY_FATFS_USE_LABEL (1) #define MICROPY_FATFS_MULTI_PARTITION (1) +#define MICROPY_HW_USB_MSC_EXCLUSIVE_ACCESS (1) #endif // Hooks to add builtins diff --git a/ports/mimxrt/msc_disk.c b/ports/mimxrt/msc_disk.c index bc0e5305cf77d..07dea9d443a2b 100644 --- a/ports/mimxrt/msc_disk.c +++ b/ports/mimxrt/msc_disk.c @@ -34,7 +34,8 @@ #define BLOCK_COUNT (MICROPY_HW_FLASH_STORAGE_BYTES / BLOCK_SIZE) #define FLASH_BASE_ADDR (MICROPY_HW_FLASH_STORAGE_BASE) -bool ejected = false; +bool tud_msc_ejected = true; +extern void tud_msc_remount(void); // Invoked when received SCSI_CMD_INQUIRY // Application fill vendor id, product id and revision with string up to 8, 16, 4 characters respectively @@ -46,12 +47,13 @@ void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16 strncpy((char *)vendor_id, vid, 8); strncpy((char *)product_id, pid, 16); strncpy((char *)product_rev, rev, 4); + tud_msc_ejected = false; } // Invoked when received Test Unit Ready command. // return true allowing host to read/write this LUN e.g SD card inserted bool tud_msc_test_unit_ready_cb(uint8_t lun) { - if (ejected) { + if (tud_msc_ejected) { tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x3a, 0x00); return false; } @@ -63,6 +65,7 @@ bool tud_msc_test_unit_ready_cb(uint8_t lun) { void tud_msc_capacity_cb(uint8_t lun, uint32_t *block_count, uint16_t *block_size) { *block_size = BLOCK_SIZE; *block_count = BLOCK_COUNT; + tud_msc_ejected = false; } // Invoked when received Start Stop Unit command @@ -72,10 +75,13 @@ bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, boo if (load_eject) { if (start) { // load disk storage - ejected = false; + tud_msc_ejected = false; } else { // unload disk storage - ejected = true; + tud_msc_ejected = true; + #if MICROPY_HW_USB_MSC_EXCLUSIVE_ACCESS + tud_msc_remount(); + #endif } } return true; diff --git a/shared/tinyusb/mp_cdc_common.c b/shared/tinyusb/mp_cdc_common.c index cd4f5d1013b61..759d3ab3efb3b 100644 --- a/shared/tinyusb/mp_cdc_common.c +++ b/shared/tinyusb/mp_cdc_common.c @@ -52,3 +52,31 @@ void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts) { } #endif + +#if MICROPY_HW_USB_MSC_EXCLUSIVE_ACCESS +#include "tusb.h" +#include "extmod/vfs.h" +#include "extmod/vfs_fat.h" +static mp_sched_node_t mp_remount_sched_node; + +STATIC void tud_msc_remount_task(mp_sched_node_t *node) { + mp_vfs_mount_t *vfs = NULL; + for (vfs = MP_STATE_VM(vfs_mount_table); vfs != NULL; vfs = vfs->next) { + if (vfs->len == 1) { + const char *path_str = "/"; + mp_obj_t path = mp_obj_new_str(path_str, strlen(path_str)); + nlr_buf_t nlr; + if (nlr_push(&nlr) == 0) { + mp_vfs_umount(vfs->obj); + mp_vfs_mount_and_chdir_protected(vfs->obj, path); + nlr_pop(); + } + break; + } + } +} + +void tud_msc_remount(void) { + mp_sched_schedule_node(&mp_remount_sched_node, tud_msc_remount_task); +} +#endif \ No newline at end of file From 38deacc54b5741b6814566f5b56134aef077680e Mon Sep 17 00:00:00 2001 From: robert-hh Date: Sat, 25 Jun 2022 20:45:39 +0200 Subject: [PATCH 08/14] mimxrt: Run tusb_init() after executing _boot.py. That way it is guaranteed, that a new file system is created before an MSC mount attempt can be made. Also: Check for readonly mode before trying mkfs. --- extmod/vfs_fat_diskio.c | 12 ++++++++++-- ports/mimxrt/main.c | 6 +++++- shared/tinyusb/mp_cdc_common.c | 2 +- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/extmod/vfs_fat_diskio.c b/extmod/vfs_fat_diskio.c index 1457a2ea09573..47e6575f647ed 100644 --- a/extmod/vfs_fat_diskio.c +++ b/extmod/vfs_fat_diskio.c @@ -152,7 +152,15 @@ DRESULT disk_ioctl( DSTATUS stat = 0; if (ret != mp_const_none && MP_OBJ_SMALL_INT_VALUE(ret) != 0) { // error initialising - stat = STA_NOINIT; + stat = STA_NOINIT; + } else { + // IOCTL_INIT only returns non 0 for all errors/flags. To return + // a more accurate disk state, IOCTL_STATUS is called again here. + ret = mp_vfs_blockdev_ioctl(&vfs->blockdev, MP_BLOCKDEV_IOCTL_STATUS, 0); + if (vfs->blockdev.writeblocks[0] == MP_OBJ_NULL || + (ret != mp_const_none && MP_OBJ_SMALL_INT_VALUE(ret) != 0)) { + stat = STA_PROTECT; + } } *((DSTATUS *)buff) = stat; return RES_OK; @@ -161,7 +169,7 @@ DRESULT disk_ioctl( case IOCTL_STATUS: { DSTATUS stat = 0; if (vfs->blockdev.writeblocks[0] == MP_OBJ_NULL || - (ret != mp_const_none && MP_OBJ_SMALL_INT_VALUE(ret) != 0)) { + (ret != mp_const_none && MP_OBJ_SMALL_INT_VALUE(ret) != 0)) { stat = STA_PROTECT; } *((DSTATUS *)buff) = stat; diff --git a/ports/mimxrt/main.c b/ports/mimxrt/main.c index 907de373dee6a..26fd3a9ffa96f 100644 --- a/ports/mimxrt/main.c +++ b/ports/mimxrt/main.c @@ -54,7 +54,6 @@ void board_init(void); int main(void) { board_init(); ticks_init(); - tusb_init(); led_init(); pendsv_init(); @@ -87,6 +86,11 @@ int main(void) { // Execute _boot.py to set up the filesystem. pyexec_frozen_module("_boot.py"); + // deferred tusb_init allowing a fs to be created before MSC access + if (!tusb_inited()) { + tusb_init(); + } + // Execute user scripts. int ret = pyexec_file_if_exists("boot.py"); if (ret & PYEXEC_FORCED_EXIT) { diff --git a/shared/tinyusb/mp_cdc_common.c b/shared/tinyusb/mp_cdc_common.c index 759d3ab3efb3b..283e617575860 100644 --- a/shared/tinyusb/mp_cdc_common.c +++ b/shared/tinyusb/mp_cdc_common.c @@ -79,4 +79,4 @@ STATIC void tud_msc_remount_task(mp_sched_node_t *node) { void tud_msc_remount(void) { mp_sched_schedule_node(&mp_remount_sched_node, tud_msc_remount_task); } -#endif \ No newline at end of file +#endif From a2b5e95fe91ef5734709c96c61a6c17bd41de83a Mon Sep 17 00:00:00 2001 From: robert-hh Date: Sat, 25 Jun 2022 21:46:03 +0200 Subject: [PATCH 09/14] mimxrt: Allow local write only after a soft reset. Since remount after MSC eject does not update the file system's data in memory, keep file the system readonly until after a soft reset, which has to be done deliberately. That's neither comfortable nor elegant, but safe. Disabled MSC as default option. Disabled MSC for MIMXRT1050_EVK deliberately. Enabled MSC for MIMXRT1020_EVK, Teensy 4.1 and OLIMEX_RT1010 for testing. --- .../boards/MIMXRT1020_EVK/mpconfigboard.h | 2 ++ .../boards/OLIMEX_RT1010/mpconfigboard.h | 2 ++ ports/mimxrt/boards/TEENSY41/mpconfigboard.h | 1 + ports/mimxrt/flash.h | 6 ++++ ports/mimxrt/main.c | 4 +++ ports/mimxrt/mimxrt_flash.c | 4 +-- ports/mimxrt/mpconfigport.h | 3 +- ports/mimxrt/msc_disk.c | 22 ++++++++------- shared/tinyusb/mp_cdc_common.c | 28 ------------------- 9 files changed, 30 insertions(+), 42 deletions(-) diff --git a/ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.h b/ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.h index 390e91814d16a..cfd48b4244fa6 100644 --- a/ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.h +++ b/ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.h @@ -3,6 +3,8 @@ #define MICROPY_PY_NETWORK_HOSTNAME_DEFAULT "mpy-1020evk" +#define MICROPY_HW_USB_MSC (1) + // i.MX RT1020 EVK has 1 board LED // Todo: think about replacing the define with searching in the generated pins? #define MICROPY_HW_LED1_PIN (pin_GPIO_AD_B0_05) diff --git a/ports/mimxrt/boards/OLIMEX_RT1010/mpconfigboard.h b/ports/mimxrt/boards/OLIMEX_RT1010/mpconfigboard.h index d63c0dc403900..48f35f95f35dd 100644 --- a/ports/mimxrt/boards/OLIMEX_RT1010/mpconfigboard.h +++ b/ports/mimxrt/boards/OLIMEX_RT1010/mpconfigboard.h @@ -5,6 +5,8 @@ #define MICROPY_HW_USB_PID 0x0046 #define MICROPY_PY_UOS_DUPTERM_BUILTIN_STREAM (0) +#define MICROPY_HW_USB_MSC (1) + // Olimex RT1010-Py has 1 board LED #define MICROPY_HW_LED1_PIN (pin_GPIO_11) #define MICROPY_HW_LED_ON(pin) (mp_hal_pin_high(pin)) diff --git a/ports/mimxrt/boards/TEENSY41/mpconfigboard.h b/ports/mimxrt/boards/TEENSY41/mpconfigboard.h index 56740f48ea3d0..4ac1f70afcb71 100644 --- a/ports/mimxrt/boards/TEENSY41/mpconfigboard.h +++ b/ports/mimxrt/boards/TEENSY41/mpconfigboard.h @@ -2,6 +2,7 @@ #define MICROPY_HW_MCU_NAME "MIMXRT1062DVJ6A" #define MICROPY_PY_NETWORK_HOSTNAME_DEFAULT "mpy-teensy41" +#define MICROPY_HW_USB_MSC (1) // Teensy 4.1 has 1 board LED #define MICROPY_HW_LED1_PIN (pin_GPIO_B0_03) diff --git a/ports/mimxrt/flash.h b/ports/mimxrt/flash.h index c4085478d7316..9b3f5affee42d 100644 --- a/ports/mimxrt/flash.h +++ b/ports/mimxrt/flash.h @@ -45,6 +45,12 @@ extern uint8_t __flash_start; extern flexspi_nor_config_t qspiflash_config; +enum { + MOUNTED = 0, + EJECTED, + TRANSIT +}; + // --------------------------------------------------------------------+ // Global Function Declarations // --------------------------------------------------------------------+ diff --git a/ports/mimxrt/main.c b/ports/mimxrt/main.c index 26fd3a9ffa96f..0815098059b6c 100644 --- a/ports/mimxrt/main.c +++ b/ports/mimxrt/main.c @@ -50,6 +50,7 @@ extern uint8_t _sstack, _estack, _gc_heap_start, _gc_heap_end; void board_init(void); +void update_msc_state(void); int main(void) { board_init(); @@ -125,6 +126,9 @@ int main(void) { #if MICROPY_PY_NETWORK mod_network_deinit(); #endif + #if CFG_TUD_MSC + update_msc_state(); + #endif machine_pwm_deinit_all(); soft_timer_deinit(); gc_sweep_all(); diff --git a/ports/mimxrt/mimxrt_flash.c b/ports/mimxrt/mimxrt_flash.c index 26420c5a75c43..7ff2710fa022e 100644 --- a/ports/mimxrt/mimxrt_flash.c +++ b/ports/mimxrt/mimxrt_flash.c @@ -45,7 +45,7 @@ STATIC mimxrt_flash_obj_t mimxrt_flash_obj = { .base = { &mimxrt_flash_type } }; -extern bool tud_msc_ejected; +extern uint8_t tud_msc_state; STATIC mp_obj_t mimxrt_flash_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { // Check args. @@ -145,7 +145,7 @@ STATIC mp_obj_t mimxrt_flash_ioctl(mp_obj_t self_in, mp_obj_t cmd_in, mp_obj_t a } case MP_BLOCKDEV_IOCTL_STATUS: #if MICROPY_HW_USB_MSC - return MP_OBJ_NEW_SMALL_INT(!tud_msc_ejected); + return MP_OBJ_NEW_SMALL_INT(tud_msc_state != EJECTED); #else return MP_OBJ_NEW_SMALL_INT(false); #endif diff --git a/ports/mimxrt/mpconfigport.h b/ports/mimxrt/mpconfigport.h index ac4c6f6bc3894..0e8a9c4cf7716 100644 --- a/ports/mimxrt/mpconfigport.h +++ b/ports/mimxrt/mpconfigport.h @@ -140,13 +140,12 @@ uint32_t trng_random_u32(void); // by default enable MSC support, unless disabled at some boards // Boards with hyperflash must disbale MSC support. #ifndef MICROPY_HW_USB_MSC -#define MICROPY_HW_USB_MSC (1) +#define MICROPY_HW_USB_MSC (0) #endif #if MICROPY_HW_USB_MSC #define MICROPY_FATFS_USE_LABEL (1) #define MICROPY_FATFS_MULTI_PARTITION (1) -#define MICROPY_HW_USB_MSC_EXCLUSIVE_ACCESS (1) #endif // Hooks to add builtins diff --git a/ports/mimxrt/msc_disk.c b/ports/mimxrt/msc_disk.c index 07dea9d443a2b..983987cdc57e9 100644 --- a/ports/mimxrt/msc_disk.c +++ b/ports/mimxrt/msc_disk.c @@ -34,8 +34,13 @@ #define BLOCK_COUNT (MICROPY_HW_FLASH_STORAGE_BYTES / BLOCK_SIZE) #define FLASH_BASE_ADDR (MICROPY_HW_FLASH_STORAGE_BASE) -bool tud_msc_ejected = true; -extern void tud_msc_remount(void); +uint8_t tud_msc_state = EJECTED; + +void update_msc_state(void) { + if (tud_msc_state == TRANSIT) { + tud_msc_state = EJECTED; + } +} // Invoked when received SCSI_CMD_INQUIRY // Application fill vendor id, product id and revision with string up to 8, 16, 4 characters respectively @@ -47,13 +52,13 @@ void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16 strncpy((char *)vendor_id, vid, 8); strncpy((char *)product_id, pid, 16); strncpy((char *)product_rev, rev, 4); - tud_msc_ejected = false; + tud_msc_state = MOUNTED; } // Invoked when received Test Unit Ready command. // return true allowing host to read/write this LUN e.g SD card inserted bool tud_msc_test_unit_ready_cb(uint8_t lun) { - if (tud_msc_ejected) { + if (tud_msc_state != MOUNTED) { tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x3a, 0x00); return false; } @@ -65,7 +70,6 @@ bool tud_msc_test_unit_ready_cb(uint8_t lun) { void tud_msc_capacity_cb(uint8_t lun, uint32_t *block_count, uint16_t *block_size) { *block_size = BLOCK_SIZE; *block_count = BLOCK_COUNT; - tud_msc_ejected = false; } // Invoked when received Start Stop Unit command @@ -75,13 +79,10 @@ bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, boo if (load_eject) { if (start) { // load disk storage - tud_msc_ejected = false; + tud_msc_state = MOUNTED; } else { // unload disk storage - tud_msc_ejected = true; - #if MICROPY_HW_USB_MSC_EXCLUSIVE_ACCESS - tud_msc_remount(); - #endif + tud_msc_state = TRANSIT; } } return true; @@ -125,4 +126,5 @@ int32_t tud_msc_scsi_cb(uint8_t lun, uint8_t const scsi_cmd[16], void *buffer, u } return resplen; } + #endif diff --git a/shared/tinyusb/mp_cdc_common.c b/shared/tinyusb/mp_cdc_common.c index 283e617575860..cd4f5d1013b61 100644 --- a/shared/tinyusb/mp_cdc_common.c +++ b/shared/tinyusb/mp_cdc_common.c @@ -52,31 +52,3 @@ void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts) { } #endif - -#if MICROPY_HW_USB_MSC_EXCLUSIVE_ACCESS -#include "tusb.h" -#include "extmod/vfs.h" -#include "extmod/vfs_fat.h" -static mp_sched_node_t mp_remount_sched_node; - -STATIC void tud_msc_remount_task(mp_sched_node_t *node) { - mp_vfs_mount_t *vfs = NULL; - for (vfs = MP_STATE_VM(vfs_mount_table); vfs != NULL; vfs = vfs->next) { - if (vfs->len == 1) { - const char *path_str = "/"; - mp_obj_t path = mp_obj_new_str(path_str, strlen(path_str)); - nlr_buf_t nlr; - if (nlr_push(&nlr) == 0) { - mp_vfs_umount(vfs->obj); - mp_vfs_mount_and_chdir_protected(vfs->obj, path); - nlr_pop(); - } - break; - } - } -} - -void tud_msc_remount(void) { - mp_sched_schedule_node(&mp_remount_sched_node, tud_msc_remount_task); -} -#endif From 2a80b9860696caa92c92ee636f5dacb6798c96c1 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Sun, 26 Jun 2022 22:07:16 +0200 Subject: [PATCH 10/14] mimxrt: Add a simple mimxrt.usb_mode() method. To enble/disable MSC at boot time. The calls are a simplified version if pyb.usb_mode(): mimxrt.usb_mode(modestr) with modestr being either "vcp" or "vcp+msc". The call has to be placed in boot.py and requires a hard reset to get effective, since tusb_init() is executed only once after reset. As a side effect, USB is not active in boot.py. So there is not USB I/O possible in boot.py, and code running in boot.py cannot by stopped with Ctrl-C. --- ports/mimxrt/main.c | 9 ++++----- ports/mimxrt/modmimxrt.c | 23 +++++++++++++++++++++++ ports/mimxrt/modules/_boot.py | 21 ++++++++++----------- ports/mimxrt/msc_disk.c | 32 +++++++++++++++++++++----------- 4 files changed, 58 insertions(+), 27 deletions(-) diff --git a/ports/mimxrt/main.c b/ports/mimxrt/main.c index 0815098059b6c..a2a4f0ce2c398 100644 --- a/ports/mimxrt/main.c +++ b/ports/mimxrt/main.c @@ -87,16 +87,15 @@ int main(void) { // Execute _boot.py to set up the filesystem. pyexec_frozen_module("_boot.py"); - // deferred tusb_init allowing a fs to be created before MSC access - if (!tusb_inited()) { - tusb_init(); - } - // Execute user scripts. int ret = pyexec_file_if_exists("boot.py"); if (ret & PYEXEC_FORCED_EXIT) { goto soft_reset_exit; } + + // deferred tusb_init allowing a fs to be created before MSC access + tusb_init(); + // Do not execute main.py if boot.py failed if (pyexec_mode_kind == PYEXEC_MODE_FRIENDLY_REPL && ret != 0) { ret = pyexec_file_if_exists("main.py"); diff --git a/ports/mimxrt/modmimxrt.c b/ports/mimxrt/modmimxrt.c index 3a1ae0263f56e..130a5a881fb7e 100644 --- a/ports/mimxrt/modmimxrt.c +++ b/ports/mimxrt/modmimxrt.c @@ -28,10 +28,33 @@ #include "py/runtime.h" #include "modmimxrt.h" +#if MICROPY_HW_USB_MSC + +#define USB_MODE_VCP "vcp" +#define USB_MODE_VCP_MSC "vcp+msc" + +extern void set_msc_enabled(bool state); + +STATIC mp_obj_t mimxrt_usb_mode(mp_obj_t mode_in) { + size_t slen; + const char *s = mp_obj_str_get_data(mode_in, &slen); + if (strncmp(s, USB_MODE_VCP, slen) == 0) { + set_msc_enabled(false); + } else if (strncmp(s, USB_MODE_VCP_MSC, slen) == 0) { + set_msc_enabled(true); + } else { + mp_raise_ValueError("Invalid usb_mode"); + } + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(mimxrt_usb_mode_obj, mimxrt_usb_mode); +#endif + STATIC const mp_rom_map_elem_t mimxrt_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_mimxrt) }, { MP_ROM_QSTR(MP_QSTR_Flash), MP_ROM_PTR(&mimxrt_flash_type) }, #if MICROPY_HW_USB_MSC + { MP_ROM_QSTR(MP_QSTR_usb_mode), MP_ROM_PTR(&mimxrt_usb_mode_obj) }, { MP_ROM_QSTR(MP_QSTR_MSC), MP_ROM_INT(1) }, #endif }; diff --git a/ports/mimxrt/modules/_boot.py b/ports/mimxrt/modules/_boot.py index e95a73bfb53f2..773b8d56c6b21 100644 --- a/ports/mimxrt/modules/_boot.py +++ b/ports/mimxrt/modules/_boot.py @@ -25,9 +25,12 @@ def fs_type(bdev): bdev = mimxrt.Flash() -# In case of MSC support mount as FAT -# Create a FAT FS if needed -if hasattr(mimxrt, "MSC"): +# try to mount the fs accorfing to the boot sector +# if that fails, (re-)create it with a preference for FAT on +# boards with MSC support. +fs = fs_type(bdev) + +if fs == FS_FAT or (fs == FS_UNDEF and hasattr(mimxrt, "MSC")): try: vfs = os.VfsFat(bdev) os.mount(vfs, "/flash") @@ -35,18 +38,14 @@ def fs_type(bdev): os.VfsFat.mkfs(bdev) vfs = os.VfsFat(bdev) os.mount(vfs, "/flash") -# otherwise analyze the boot sector an mount accordingly -# without a valid boot sector create a LFS file system else: - fs = fs_type(bdev) - if fs == FS_LITTLEFS: + try: vfs = os.VfsLfs2(bdev, progsize=256) - elif fs == FS_FAT: - vfs = os.VfsFat(bdev) - else: + os.mount(vfs, "/flash") + except: os.VfsLfs2.mkfs(bdev, progsize=256) vfs = os.VfsLfs2(bdev, progsize=256) - os.mount(vfs, "/flash") + os.mount(vfs, "/flash") os.chdir("/flash") sys.path.append("/flash") diff --git a/ports/mimxrt/msc_disk.c b/ports/mimxrt/msc_disk.c index 983987cdc57e9..d270a9c77df9c 100644 --- a/ports/mimxrt/msc_disk.c +++ b/ports/mimxrt/msc_disk.c @@ -36,29 +36,37 @@ uint8_t tud_msc_state = EJECTED; +static bool msc_enabled = false; + void update_msc_state(void) { if (tud_msc_state == TRANSIT) { tud_msc_state = EJECTED; } } +void set_msc_enabled(bool state) { + msc_enabled = state; +} + // Invoked when received SCSI_CMD_INQUIRY // Application fill vendor id, product id and revision with string up to 8, 16, 4 characters respectively void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16], uint8_t product_rev[4]) { - const char vid[] = "Micropy"; - const char pid[] = "Mass Storage"; - const char rev[] = "1.0"; + if (msc_enabled) { + const char vid[] = "Micropy"; + const char pid[] = "Mass Storage"; + const char rev[] = "1.0"; - strncpy((char *)vendor_id, vid, 8); - strncpy((char *)product_id, pid, 16); - strncpy((char *)product_rev, rev, 4); - tud_msc_state = MOUNTED; + strncpy((char *)vendor_id, vid, 8); + strncpy((char *)product_id, pid, 16); + strncpy((char *)product_rev, rev, 4); + tud_msc_state = MOUNTED; + } } // Invoked when received Test Unit Ready command. // return true allowing host to read/write this LUN e.g SD card inserted bool tud_msc_test_unit_ready_cb(uint8_t lun) { - if (tud_msc_state != MOUNTED) { + if (tud_msc_state != MOUNTED || !msc_enabled) { tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x3a, 0x00); return false; } @@ -68,15 +76,17 @@ bool tud_msc_test_unit_ready_cb(uint8_t lun) { // Invoked when received SCSI_CMD_READ_CAPACITY_10 and SCSI_CMD_READ_FORMAT_CAPACITY to determine the disk size // Application update block count and block size void tud_msc_capacity_cb(uint8_t lun, uint32_t *block_count, uint16_t *block_size) { - *block_size = BLOCK_SIZE; - *block_count = BLOCK_COUNT; + if (msc_enabled) { + *block_size = BLOCK_SIZE; + *block_count = BLOCK_COUNT; + } } // Invoked when received Start Stop Unit command // - Start = 0 : stopped power mode, if load_eject = 1 : unload disk storage // - Start = 1 : active mode, if load_eject = 1 : load disk storage bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, bool load_eject) { - if (load_eject) { + if (load_eject && msc_enabled) { if (start) { // load disk storage tud_msc_state = MOUNTED; From 37c1cca178fe9faddf2a2f59078d33952d86d1d6 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Fri, 1 Jul 2022 20:45:55 +0200 Subject: [PATCH 11/14] mimxrt: Set MSC default modes. 1. USB will be enabled BEFORE boot.py is executed, avoiding deadlocks by user written code. 2. MSC support is enabled in _boot.py according to the following rules: a) For a FAT file system, MSC will be enabled by default. b) For a LFS file system, MSC will be disabled by default. c) The setting can be overriden by a file with the name set_usb_mode.py, which can define usb_mode with the lines: usb_mode = "vcp+msc" # enable MSC or usb_mode = "vcp" # do not enable MSC If that file does not exist or is faulty, the default is used. --- ports/mimxrt/main.c | 6 +++--- ports/mimxrt/modules/_boot.py | 15 +++++++++++++++ ports/mimxrt/msc_disk.c | 2 +- 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/ports/mimxrt/main.c b/ports/mimxrt/main.c index a2a4f0ce2c398..53f26d89cf0e4 100644 --- a/ports/mimxrt/main.c +++ b/ports/mimxrt/main.c @@ -87,15 +87,15 @@ int main(void) { // Execute _boot.py to set up the filesystem. pyexec_frozen_module("_boot.py"); + // deferred tusb_init allowing a fs to be created before MSC access + tusb_init(); + // Execute user scripts. int ret = pyexec_file_if_exists("boot.py"); if (ret & PYEXEC_FORCED_EXIT) { goto soft_reset_exit; } - // deferred tusb_init allowing a fs to be created before MSC access - tusb_init(); - // Do not execute main.py if boot.py failed if (pyexec_mode_kind == PYEXEC_MODE_FRIENDLY_REPL && ret != 0) { ret = pyexec_file_if_exists("main.py"); diff --git a/ports/mimxrt/modules/_boot.py b/ports/mimxrt/modules/_boot.py index 773b8d56c6b21..5b419e74c8f94 100644 --- a/ports/mimxrt/modules/_boot.py +++ b/ports/mimxrt/modules/_boot.py @@ -38,6 +38,7 @@ def fs_type(bdev): os.VfsFat.mkfs(bdev) vfs = os.VfsFat(bdev) os.mount(vfs, "/flash") + usb_mode = "vcp+msc" else: try: vfs = os.VfsLfs2(bdev, progsize=256) @@ -46,11 +47,25 @@ def fs_type(bdev): os.VfsLfs2.mkfs(bdev, progsize=256) vfs = os.VfsLfs2(bdev, progsize=256) os.mount(vfs, "/flash") + usb_mode = "vcp" os.chdir("/flash") sys.path.append("/flash") sys.path.append("/flash/lib") +try: + from set_usb_mode import usb_mode +except: + pass + +# Configure USB mode according to the default or the +# config file in the file system +if hasattr(mimxrt, "usb_mode"): + try: + mimxrt.usb_mode(usb_mode) + except: + pass + # do not mount the SD card if SKIPSD exists. try: os.stat("SKIPSD") diff --git a/ports/mimxrt/msc_disk.c b/ports/mimxrt/msc_disk.c index d270a9c77df9c..7fdf6e773a6de 100644 --- a/ports/mimxrt/msc_disk.c +++ b/ports/mimxrt/msc_disk.c @@ -36,7 +36,7 @@ uint8_t tud_msc_state = EJECTED; -static bool msc_enabled = false; +static bool msc_enabled = true; void update_msc_state(void) { if (tud_msc_state == TRANSIT) { From 128633c5cf1d5ee02271aed7ded2fc25d6b49918 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Sun, 26 Jun 2022 22:13:13 +0200 Subject: [PATCH 12/14] mimxrt: Add some documentation for the MSC mode. Including some notes about littlfs file system and MSC access. When the board is formatted with littlfs, the FS access method by the PC is different. --- docs/mimxrt/quickref.rst | 65 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/docs/mimxrt/quickref.rst b/docs/mimxrt/quickref.rst index 06f91f7f50312..6e016cb67dd06 100644 --- a/docs/mimxrt/quickref.rst +++ b/docs/mimxrt/quickref.rst @@ -540,6 +540,71 @@ port and LAN(1) for the 1G port. For details of the network interface refer to the class :ref:`network.LAN `. + +External drive mode +------------------- + +On some boards a mode is enabled, that mounts the board's internal file system as +external USB drive, called MSC support. Data of that drive can be accessed and changed by the PC. +In that state, access to the internal drive by the MicroPython is limited to +read-only mode, avoiding file corruption. Changes made by the PC to the file system may not be visible +at the board until the drive is ejected by the PC or a soft reset of the board +is made. + +To enable write access to by the board, eject the drive at the PC **and** perform +a soft-reset on the board, either by pushing Ctrl-D at REPL or calling machine.soft_reset(). + +The external drive mode (MSC mode) is enabled of disabled according to the following rules: + + a) For a FAT file system, MSC will be enabled by default. + b) For a LFS file system, MSC will be disabled by default. + c) The setting can be overridden by a file with the name + set_usb_mode.py, which can define the usb_mode with the lines: + + usb_mode = "vcp+msc" # enable MSC + + or + + usb_mode = "vcp" # do not enable MSC + + If the file set_usb_mode.py does not exist or is faulty, the default is used. + +The read-only state of the local file system access can be told by an IOCTL call of the file +system's block device.:: + + import mimxrt + + bdev = mimxrt.Flash() + readonly = bdev.ioctl(7, 0) + +If the drive is in read-only state, bdev.ioctl(7, 0) returns `True`. + +The file system of the board has to be of FAT type for mounting and using with standard PC +tools. But FAT is not enforced at the board. If the board's file system is littlefs, MSC +mode is disable by default. If enabled in boot.py, the file system will be attached to the +PC and will be accessible as a drive (e.g. /dev/sdc using Linux), but by default +there is no file access. In that case, the files are locally still writeable. +Changing the board's file system to FAT can be done then by formatting it from the PC. +Alternatively, you can erase the root sector. Then, the FAT file system will be +created at the next power-up. For erasing the root sector, write:: + + from mimxrt import Flash + Flash().ioctl(6, 0) + +Using littlefs-fuse for Linux you can mount the board's littlefs file system to the PC. +See: https://github.com/littlefs-project/littlefs-fuse +The block_size if 4096, the block_count depends on the size of the filesystem. e.g.:: + + # mounting the lfs2 file system of a Teensy 4.1 board at the PC + mkdir mount + sudo ./lfs --block_size=4096 --block_count=1791 -o allow_other -o nonempty /dev/sdc mount + cd mount + ls -l + +In that case, the exclusive write access is NOT enforced. So be careful to only write +to the file system by the PC. + + Transferring files ------------------ From cb14320543763467e3435f70617bc263a7f3b8de Mon Sep 17 00:00:00 2001 From: robert-hh Date: Thu, 17 Nov 2022 13:41:08 +0100 Subject: [PATCH 13/14] mimxrt/MSC: Include support for the MIMXRT117x devices. --- ports/mimxrt/Makefile | 1 - .../boards/MIMXRT1170_EVK/mpconfigboard.h | 2 ++ ports/mimxrt/flash.c | 20 +++++++++---------- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/ports/mimxrt/Makefile b/ports/mimxrt/Makefile index d2244d2ae8cd0..8c0953623c479 100644 --- a/ports/mimxrt/Makefile +++ b/ports/mimxrt/Makefile @@ -227,7 +227,6 @@ SHARED_SRC_C += \ shared/runtime/pyexec.c \ shared/runtime/softtimer.c \ shared/runtime/stdout_helpers.c \ - shared/runtime/tinyusb_helpers.c \ shared/runtime/sys_stdio_mphal.c \ shared/timeutils/timeutils.c \ diff --git a/ports/mimxrt/boards/MIMXRT1170_EVK/mpconfigboard.h b/ports/mimxrt/boards/MIMXRT1170_EVK/mpconfigboard.h index d37050eb56041..4787e80d6b29e 100644 --- a/ports/mimxrt/boards/MIMXRT1170_EVK/mpconfigboard.h +++ b/ports/mimxrt/boards/MIMXRT1170_EVK/mpconfigboard.h @@ -3,6 +3,8 @@ #define MICROPY_PY_NETWORK_HOSTNAME_DEFAULT "mpy-1070evk" +#define MICROPY_HW_USB_MSC (1) + #define MICROPY_EVENT_POLL_HOOK \ do { \ extern void mp_handle_pending(bool); \ diff --git a/ports/mimxrt/flash.c b/ports/mimxrt/flash.c index 5fe0aa789adfc..36a909f92f957 100644 --- a/ports/mimxrt/flash.c +++ b/ports/mimxrt/flash.c @@ -32,17 +32,17 @@ void flash_init(void) { // Upload the custom flash configuration // This should be performed by the boot ROM but for some reason it is not. - FLEXSPI_UpdateLUT(FLEXSPI, 0, + FLEXSPI_UpdateLUT(BOARD_FLEX_SPI, 0, qspiflash_config.memConfig.lookupTable, ARRAY_SIZE(qspiflash_config.memConfig.lookupTable)); // Configure FLEXSPI IP FIFO access. - FLEXSPI->MCR0 &= ~(FLEXSPI_MCR0_ARDFEN_MASK); - FLEXSPI->MCR0 &= ~(FLEXSPI_MCR0_ATDFEN_MASK); - FLEXSPI->MCR0 |= FLEXSPI_MCR0_ARDFEN(0); - FLEXSPI->MCR0 |= FLEXSPI_MCR0_ATDFEN(0); + BOARD_FLEX_SPI->MCR0 &= ~(FLEXSPI_MCR0_ARDFEN_MASK); + BOARD_FLEX_SPI->MCR0 &= ~(FLEXSPI_MCR0_ATDFEN_MASK); + BOARD_FLEX_SPI->MCR0 |= FLEXSPI_MCR0_ARDFEN(0); + BOARD_FLEX_SPI->MCR0 |= FLEXSPI_MCR0_ATDFEN(0); - FLEXSPI_EnableIPParallelMode(FLEXSPI, true); + FLEXSPI_EnableIPParallelMode(BOARD_FLEX_SPI, true); } // flash_erase_block(erase_addr) @@ -54,7 +54,7 @@ status_t flash_erase_block(uint32_t erase_addr) { SCB_DisableDCache(); __disable_irq(); - status = flexspi_nor_flash_erase_block(FLEXSPI, erase_addr); + status = flexspi_nor_flash_erase_block(BOARD_FLEX_SPI, erase_addr); __enable_irq(); SCB_EnableDCache(); @@ -71,7 +71,7 @@ status_t flash_erase_sector(uint32_t erase_addr) { SCB_DisableDCache(); __disable_irq(); - status = flexspi_nor_flash_erase_sector(FLEXSPI, erase_addr); + status = flexspi_nor_flash_erase_sector(BOARD_FLEX_SPI, erase_addr); __enable_irq(); SCB_EnableDCache(); @@ -83,7 +83,7 @@ status_t flash_erase_sector(uint32_t erase_addr) { // read length_byte data to the source address // It is just a shim to provide the same structure for read_block and write_block. void inline flash_read_block(uint32_t src_addr, uint8_t *dest, uint32_t length) { - memcpy(dest, (uint8_t *)(FlexSPI_AMBA_BASE + src_addr), length); + memcpy(dest, (uint8_t *)(BOARD_FLEX_SPI_ADDR_BASE + src_addr), length); } // flash_write_block(flash_dest_addr_bytes, data_source, length_bytes) @@ -114,7 +114,7 @@ status_t flash_write_block(uint32_t dest_addr, const uint8_t *src, uint32_t leng __disable_irq(); - status = flexspi_nor_flash_page_program(FLEXSPI, dest_addr, (uint32_t *)src, size); + status = flexspi_nor_flash_page_program(BOARD_FLEX_SPI, dest_addr, (uint32_t *)src, size); __enable_irq(); From c3583a7df55d1192972791f482d0262db94b0de2 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Tue, 7 Mar 2023 08:37:24 +0100 Subject: [PATCH 14/14] mimxrt: Fix error after rebase with the actual master. --- ports/mimxrt/modmimxrt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/mimxrt/modmimxrt.c b/ports/mimxrt/modmimxrt.c index 130a5a881fb7e..294f40ee3a2d0 100644 --- a/ports/mimxrt/modmimxrt.c +++ b/ports/mimxrt/modmimxrt.c @@ -43,7 +43,7 @@ STATIC mp_obj_t mimxrt_usb_mode(mp_obj_t mode_in) { } else if (strncmp(s, USB_MODE_VCP_MSC, slen) == 0) { set_msc_enabled(true); } else { - mp_raise_ValueError("Invalid usb_mode"); + mp_raise_ValueError(MP_ERROR_TEXT("Invalid usb_mode")); } return mp_const_none; }