diff options
| author | Andrew Leech <andrew.leech@planetinnovation.com.au> | 2025-10-28 16:20:31 +1100 |
|---|---|---|
| committer | Damien George <damien@micropython.org> | 2025-10-31 10:54:07 +1100 |
| commit | faae8e73ef654339bd2adfab2b658e6e7f5ef368 (patch) | |
| tree | ffb02e419ac20c381c9d0912197d7c8c07d8d9a3 | |
| parent | 9bb266e31162ba80ec06a6bc9a6b94b9cbafa9b2 (diff) | |
stm32/usb: Add TinyUSB Mass Storage support.
Implements USB MSC functionality for STM32 port when using TinyUSB stack,
supporting both internal Flash and SD card storage mediums.
Signed-off-by: Andrew Leech <andrew@alelec.net>
| -rw-r--r-- | ports/stm32/Makefile | 1 | ||||
| -rw-r--r-- | ports/stm32/main.c | 4 | ||||
| -rw-r--r-- | ports/stm32/mpconfigboard_common.h | 7 | ||||
| -rw-r--r-- | ports/stm32/msc_disk.c | 191 | ||||
| -rw-r--r-- | ports/stm32/storage.c | 10 | ||||
| -rw-r--r-- | ports/stm32/usb.c | 2 | ||||
| -rw-r--r-- | ports/stm32/usb.h | 11 | ||||
| -rw-r--r-- | ports/stm32/usbdev/class/src/usbd_cdc_msc_hid.c | 2 |
8 files changed, 223 insertions, 5 deletions
diff --git a/ports/stm32/Makefile b/ports/stm32/Makefile index 5105b35b1..b43f284b1 100644 --- a/ports/stm32/Makefile +++ b/ports/stm32/Makefile @@ -256,6 +256,7 @@ SRC_C += \ usbd_hid_interface.c \ usbd_msc_interface.c \ mphalport.c \ + msc_disk.c \ mpnetworkport.c \ mpthreadport.c \ irq.c \ diff --git a/ports/stm32/main.c b/ports/stm32/main.c index d56b4c407..2d97adb1d 100644 --- a/ports/stm32/main.c +++ b/ports/stm32/main.c @@ -280,7 +280,7 @@ static bool init_sdcard_fs(void) { } } - #if MICROPY_HW_ENABLE_USB && !MICROPY_HW_TINYUSB_STACK + #if MICROPY_HW_USB_MSC if (pyb_usb_storage_medium == PYB_USB_STORAGE_MEDIUM_NONE) { // if no USB MSC medium is selected then use the SD card pyb_usb_storage_medium = PYB_USB_STORAGE_MEDIUM_SDCARD; @@ -639,7 +639,7 @@ soft_reset: } #endif - #if MICROPY_HW_STM_USB_STACK + #if MICROPY_HW_USB_MSC // if the SD card isn't used as the USB MSC medium then use the internal flash if (pyb_usb_storage_medium == PYB_USB_STORAGE_MEDIUM_NONE) { pyb_usb_storage_medium = PYB_USB_STORAGE_MEDIUM_FLASH; diff --git a/ports/stm32/mpconfigboard_common.h b/ports/stm32/mpconfigboard_common.h index 47eb7f036..eefd5c05c 100644 --- a/ports/stm32/mpconfigboard_common.h +++ b/ports/stm32/mpconfigboard_common.h @@ -277,7 +277,7 @@ #define MICROPY_HW_USB_CDC_NUM (1) #endif #ifndef MICROPY_HW_USB_MSC -#define MICROPY_HW_USB_MSC (MICROPY_HW_STM_USB_STACK) +#define MICROPY_HW_USB_MSC (MICROPY_HW_ENABLE_USB) #endif #ifndef MICROPY_HW_USB_HID #define MICROPY_HW_USB_HID (MICROPY_HW_STM_USB_STACK) @@ -695,8 +695,13 @@ #define MICROPY_HW_BDEV_WRITEBLOCKS(src, bl, n) spi_bdev_writeblocks(MICROPY_HW_BDEV_SPIFLASH, (src), MICROPY_HW_BDEV_SPIFLASH_OFFSET_BLOCKS + (bl), (n)) #endif +// Define the FATFS maximum sector size. +#ifndef MICROPY_FATFS_MAX_SS #if defined(STM32N6) #define MICROPY_FATFS_MAX_SS (4096) +#else +#define MICROPY_FATFS_MAX_SS (512) +#endif #endif // Whether to enable caching for external SPI flash, to allow block writes that are diff --git a/ports/stm32/msc_disk.c b/ports/stm32/msc_disk.c new file mode 100644 index 000000000..f32957cde --- /dev/null +++ b/ports/stm32/msc_disk.c @@ -0,0 +1,191 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Damien P. George + * Copyright (c) 2025 Andrew Leech + * + * 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 "py/mpconfig.h" + +#if MICROPY_HW_USB_MSC && MICROPY_HW_TINYUSB_STACK + +#include "py/misc.h" +#include "tusb.h" +#include "storage.h" +#include "sdcard.h" +#include "usb.h" + +// Storage medium selection (shared with main.c for compatibility) +pyb_usb_storage_medium_t pyb_usb_storage_medium = PYB_USB_STORAGE_MEDIUM_NONE; + +// Ejection state +static bool msc_ejected = false; + +// Invoked on SCSI_CMD_INQUIRY. +// 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]) { + (void)lun; + memcpy(vendor_id, MICROPY_HW_USB_MSC_INQUIRY_VENDOR_STRING, MIN(strlen(MICROPY_HW_USB_MSC_INQUIRY_VENDOR_STRING), 8)); + memcpy(product_id, MICROPY_HW_USB_MSC_INQUIRY_PRODUCT_STRING, MIN(strlen(MICROPY_HW_USB_MSC_INQUIRY_PRODUCT_STRING), 16)); + memcpy(product_rev, MICROPY_HW_USB_MSC_INQUIRY_REVISION_STRING, MIN(strlen(MICROPY_HW_USB_MSC_INQUIRY_REVISION_STRING), 4)); +} + +// Invoked on 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) { + (void)lun; + + if (msc_ejected || pyb_usb_storage_medium == PYB_USB_STORAGE_MEDIUM_NONE) { + tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x3a, 0x00); + return false; + } + + #if MICROPY_HW_ENABLE_SDCARD + if (pyb_usb_storage_medium == PYB_USB_STORAGE_MEDIUM_SDCARD) { + if (!sdcard_is_present()) { + tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x3a, 0x00); + return false; + } + } + #endif + + return true; +} + +// Invoked on SCSI_CMD_READ_CAPACITY_10 and SCSI_CMD_READ_FORMAT_CAPACITY to determine the disk size. +void tud_msc_capacity_cb(uint8_t lun, uint32_t *block_count, uint16_t *block_size) { + (void)lun; + + if (pyb_usb_storage_medium == PYB_USB_STORAGE_MEDIUM_FLASH) { + *block_size = storage_get_block_size(); + *block_count = storage_get_block_count(); + #if MICROPY_HW_ENABLE_SDCARD + } else if (pyb_usb_storage_medium == PYB_USB_STORAGE_MEDIUM_SDCARD) { + *block_size = SDCARD_BLOCK_SIZE; + *block_count = sdcard_get_capacity_in_bytes() / (uint64_t)SDCARD_BLOCK_SIZE; + #endif + } else { + *block_size = 0; + *block_count = 0; + } +} + +// Invoked on 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) { + (void)lun; + (void)power_condition; + + if (load_eject) { + if (start) { + // Load disk storage + msc_ejected = false; + } else { + // Unload disk storage + msc_ejected = true; + } + } + return true; +} + +// Callback invoked on READ10 command. +// Copy disk's data to buffer (up to bufsize) and return number of read bytes. +int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void *buffer, uint32_t bufsize) { + (void)lun; + (void)offset; + + if (pyb_usb_storage_medium == PYB_USB_STORAGE_MEDIUM_FLASH) { + uint32_t block_count = bufsize / FLASH_BLOCK_SIZE; + int ret = storage_read_blocks(buffer, lba, block_count); + if (ret != 0) { + return -1; + } + return block_count * FLASH_BLOCK_SIZE; + #if MICROPY_HW_ENABLE_SDCARD + } else if (pyb_usb_storage_medium == PYB_USB_STORAGE_MEDIUM_SDCARD) { + uint32_t block_count = bufsize / SDCARD_BLOCK_SIZE; + if (sdcard_read_blocks(buffer, lba, block_count) != 0) { + return -1; + } + return block_count * SDCARD_BLOCK_SIZE; + #endif + } + + return -1; +} + +// Callback invoked on 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) { + (void)lun; + (void)offset; + + if (pyb_usb_storage_medium == PYB_USB_STORAGE_MEDIUM_FLASH) { + uint32_t block_count = bufsize / FLASH_BLOCK_SIZE; + int ret = storage_write_blocks(buffer, lba, block_count); + if (ret != 0) { + return -1; + } + return block_count * FLASH_BLOCK_SIZE; + #if MICROPY_HW_ENABLE_SDCARD + } else if (pyb_usb_storage_medium == PYB_USB_STORAGE_MEDIUM_SDCARD) { + uint32_t block_count = bufsize / SDCARD_BLOCK_SIZE; + if (sdcard_write_blocks(buffer, lba, block_count) != 0) { + return -1; + } + return block_count * SDCARD_BLOCK_SIZE; + #endif + } + + return -1; +} + +// Callback invoked on a SCSI command that's not handled by TinyUSB. +int32_t tud_msc_scsi_cb(uint8_t lun, uint8_t const scsi_cmd[16], void *buffer, uint16_t bufsize) { + (void)lun; + (void)buffer; + (void)bufsize; + + int32_t resplen = 0; + switch (scsi_cmd[0]) { + case SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL: + // Sync the logical unit if needed. + #if MICROPY_HW_ENABLE_STORAGE + if (pyb_usb_storage_medium == PYB_USB_STORAGE_MEDIUM_FLASH) { + storage_flush(); + } + #endif + 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 // MICROPY_HW_USB_MSC && MICROPY_HW_TINYUSB_STACK diff --git a/ports/stm32/storage.c b/ports/stm32/storage.c index d26ac821e..066fb48a6 100644 --- a/ports/stm32/storage.c +++ b/ports/stm32/storage.c @@ -171,6 +171,12 @@ bool storage_read_block(uint8_t *dest, uint32_t block) { return true; + } else if (block < FLASH_PART1_START_BLOCK) { + // Blocks between MBR and first partition: return zeros + // (This handles blocks 1 to FLASH_PART1_START_BLOCK-1) + memset(dest, 0, FLASH_BLOCK_SIZE); + return true; + #if defined(MICROPY_HW_BDEV_READBLOCK) } else if (FLASH_PART1_START_BLOCK <= block && block < FLASH_PART1_START_BLOCK + MICROPY_HW_BDEV_IOCTL(BDEV_IOCTL_NUM_BLOCKS, 0)) { return MICROPY_HW_BDEV_READBLOCK(dest, block - FLASH_PART1_START_BLOCK); @@ -184,6 +190,10 @@ bool storage_write_block(const uint8_t *src, uint32_t block) { if (block == 0) { // can't write MBR, but pretend we did return true; + } else if (block < FLASH_PART1_START_BLOCK) { + // Blocks between MBR and first partition: ignore writes + // (This handles blocks 1 to FLASH_PART1_START_BLOCK-1) + return true; #if defined(MICROPY_HW_BDEV_WRITEBLOCK) } else if (FLASH_PART1_START_BLOCK <= block && block < FLASH_PART1_START_BLOCK + MICROPY_HW_BDEV_IOCTL(BDEV_IOCTL_NUM_BLOCKS, 0)) { return MICROPY_HW_BDEV_WRITEBLOCK(src, block - FLASH_PART1_START_BLOCK); diff --git a/ports/stm32/usb.c b/ports/stm32/usb.c index 15d563713..c17d04912 100644 --- a/ports/stm32/usb.c +++ b/ports/stm32/usb.c @@ -92,7 +92,9 @@ typedef struct _usb_device_t { } usb_device_t; usb_device_t usb_device = {0}; +#if MICROPY_HW_USB_MSC pyb_usb_storage_medium_t pyb_usb_storage_medium = PYB_USB_STORAGE_MEDIUM_NONE; +#endif #if !MICROPY_HW_USB_IS_MULTI_OTG diff --git a/ports/stm32/usb.h b/ports/stm32/usb.h index 3c382887a..70e3ee64c 100644 --- a/ports/stm32/usb.h +++ b/ports/stm32/usb.h @@ -26,7 +26,9 @@ #ifndef MICROPY_INCLUDED_STM32_USB_H #define MICROPY_INCLUDED_STM32_USB_H +#if MICROPY_HW_STM_USB_STACK #include "usbd_cdc_msc_hid0.h" +#endif #define PYB_USB_FLAG_USB_MODE_CALLED (0x0002) @@ -41,10 +43,16 @@ typedef enum { USB_PHY_HS_ID = 1, } USB_PHY_ID; +// Storage medium variable used by both USB stacks when MSC is enabled +#if MICROPY_HW_USB_MSC +extern pyb_usb_storage_medium_t pyb_usb_storage_medium; +#endif + +#if MICROPY_HW_STM_USB_STACK + typedef struct _pyb_usb_vcp_obj_t pyb_usb_vcp_obj_t; extern mp_uint_t pyb_usb_flags; -extern pyb_usb_storage_medium_t pyb_usb_storage_medium; extern const struct _mp_rom_obj_tuple_t pyb_usb_hid_mouse_obj; extern const struct _mp_rom_obj_tuple_t pyb_usb_hid_keyboard_obj; extern const mp_obj_type_t pyb_usb_vcp_type; @@ -66,5 +74,6 @@ void usb_vcp_attach_to_repl(const pyb_usb_vcp_obj_t *self, bool attached); void pyb_usb_host_init(void); void pyb_usb_host_process(void); uint pyb_usb_host_get_keyboard(void); +#endif #endif // MICROPY_INCLUDED_STM32_USB_H diff --git a/ports/stm32/usbdev/class/src/usbd_cdc_msc_hid.c b/ports/stm32/usbdev/class/src/usbd_cdc_msc_hid.c index 65d2fd70e..e541c3213 100644 --- a/ports/stm32/usbdev/class/src/usbd_cdc_msc_hid.c +++ b/ports/stm32/usbdev/class/src/usbd_cdc_msc_hid.c @@ -28,7 +28,7 @@ #include "usbd_ioreq.h" #include "usbd_cdc_msc_hid.h" -#if MICROPY_HW_ENABLE_USB +#if MICROPY_HW_STM_USB_STACK #define HEAD_DESC_SIZE (9) #define MSC_CLASS_DESC_SIZE (9 + 7 + 7) |
