From 7842191315777b0bc8c37810e1a722c96c246d34 Mon Sep 17 00:00:00 2001 From: rechrtb Date: Wed, 24 Apr 2024 20:28:40 +0800 Subject: [PATCH] Implement usb drive volume --- src/Config/Pins.h | 4 + src/Config/Pins_Duet3_MB6HC.h | 7 + src/Hardware/SAME70/same70q20b_flash.ld | 4 +- src/Libraries/Fatfs/ffconf.h | 2 +- src/Storage/MassStorage.cpp | 19 +- src/Storage/MassStorage.h | 3 + src/Storage/UsbVolume.cpp | 251 ++++++++++++++++++++++++ src/Storage/UsbVolume.h | 71 +++++++ 8 files changed, 356 insertions(+), 5 deletions(-) create mode 100644 src/Storage/UsbVolume.cpp create mode 100644 src/Storage/UsbVolume.h diff --git a/src/Config/Pins.h b/src/Config/Pins.h index 45405fa9e8..86a74af6da 100644 --- a/src/Config/Pins.h +++ b/src/Config/Pins.h @@ -213,6 +213,10 @@ # define HAS_SBC_INTERFACE 0 #endif +#ifndef SUPPORT_USB_DRIVE +# define SUPPORT_USB_DRIVE 0 +#endif + #ifndef HAS_MASS_STORAGE # define HAS_MASS_STORAGE 1 #endif diff --git a/src/Config/Pins_Duet3_MB6HC.h b/src/Config/Pins_Duet3_MB6HC.h index df01e55c53..3a95fb5d3e 100644 --- a/src/Config/Pins_Duet3_MB6HC.h +++ b/src/Config/Pins_Duet3_MB6HC.h @@ -28,10 +28,12 @@ constexpr uint32_t IAP_IMAGE_START = 0x20458000; // last 32kb of RAM # define HAS_SBC_INTERFACE 0 # define HAS_MASS_STORAGE 0 # define HAS_HIGH_SPEED_SD 0 +# define SUPPORT_USB_DRIVE 0 #else # define HAS_SBC_INTERFACE 1 # define HAS_MASS_STORAGE 1 # define HAS_HIGH_SPEED_SD 1 +# define SUPPORT_USB_DRIVE 1 #endif #define HAS_CPU_TEMP_SENSOR 1 @@ -198,6 +200,11 @@ constexpr Pin SdSpiCSPins[1] = { PortDPin(22) }; // this one is allocated constexpr uint32_t ExpectedSdCardSpeed = 25000000; constexpr IRQn SdhcIRQn = HSMCI_IRQn; +#if SUPPORT_USB_DRIVE +// USB Drives +constexpr size_t NumUsbDrives = 2; +#endif + // DotStar LED control #define LEDSTRIP_USES_USART 0 diff --git a/src/Hardware/SAME70/same70q20b_flash.ld b/src/Hardware/SAME70/same70q20b_flash.ld index f1b80522d3..be6748b539 100644 --- a/src/Hardware/SAME70/same70q20b_flash.ld +++ b/src/Hardware/SAME70/same70q20b_flash.ld @@ -52,8 +52,8 @@ SEARCH_DIR(.) MEMORY { rom (rx) : ORIGIN = 0x00400000, LENGTH = 0x00100000 - ram_not_cached (rw) : ORIGIN = 0x20400000, LENGTH = 0x00018400 /* we currently allocate 97kb of non-cached RAM */ - ram (rwx) : ORIGIN = 0x20418400, LENGTH = 0x00047C00 /* that leaves 287Kb of cached RAM */ + ram_not_cached (rw) : ORIGIN = 0x20400000, LENGTH = 0x00018800 /* we currently allocate 97kb of non-cached RAM */ + ram (rwx) : ORIGIN = 0x20418800, LENGTH = 0x00047800 /* that leaves 287Kb of cached RAM */ } /* Section Definitions */ diff --git a/src/Libraries/Fatfs/ffconf.h b/src/Libraries/Fatfs/ffconf.h index 8a258bf41e..ab87bd57cf 100644 --- a/src/Libraries/Fatfs/ffconf.h +++ b/src/Libraries/Fatfs/ffconf.h @@ -166,7 +166,7 @@ / Drive/Volume Configurations /---------------------------------------------------------------------------*/ -#define FF_VOLUMES 2 +#define FF_VOLUMES 4 /* Number of volumes (logical drives) to be used. (1-10) */ diff --git a/src/Storage/MassStorage.cpp b/src/Storage/MassStorage.cpp index 9bd8300fc2..eb7fc03109 100644 --- a/src/Storage/MassStorage.cpp +++ b/src/Storage/MassStorage.cpp @@ -23,7 +23,13 @@ static_assert(FF_MAX_LFN >= MaxFilenameLength, "FF_MAX_LFN too small"); #include "SdCardVolume.h" +#if SUPPORT_USB_DRIVE +#include "UsbVolume.h" +static_assert(FF_VOLUMES >= NumSdCards + NumUsbDrives); +#else static_assert(FF_VOLUMES >= NumSdCards); +#endif + // A note on using mutexes: // Each storage volume has its own mutex. There is also one for the file table, and one for the find first/find next buffer. // The FatFS subsystem locks and releases the appropriate volume mutex when it is called. @@ -41,7 +47,16 @@ alignas(4) static __nocache char writeBufferStorage[NumFileWriteBuffers][FileWri # endif static SdCardVolume sdVolumes[NumSdCards] = { SdCardVolume("SDO", 0), SdCardVolume("SD1", 1) }; -static StorageVolume* storageVolumes[] = { &sdVolumes[0], &sdVolumes[1] }; +#if SUPPORT_USB_DRIVE +static UsbVolume usbVolumes[NumUsbDrives] = { UsbVolume("USB0", 2), UsbVolume("USB1", 3) }; +#endif + +static StorageVolume* storageVolumes[] = { &sdVolumes[0], &sdVolumes[1], +#if SUPPORT_USB_DRIVE + &usbVolumes[0], &usbVolumes[1] +#endif +}; + static DIR findDir; #endif @@ -1019,7 +1034,7 @@ void MassStorage::RecordSimulationTime(const char *printingFilePath, uint32_t si // Get information about the volume and interface speed on the specified slot MassStorage::InfoResult MassStorage::GetVolumeInfo(size_t slot, SdCardReturnedInfo& returnedInfo) noexcept { - if (slot >= GetNumVolumes() && storageVolumes[slot]->IsUseable()) + if (slot >= GetNumVolumes() || !(storageVolumes[slot]->IsUseable())) { return InfoResult::badSlot; } diff --git a/src/Storage/MassStorage.h b/src/Storage/MassStorage.h index f3740c4289..fd95029122 100644 --- a/src/Storage/MassStorage.h +++ b/src/Storage/MassStorage.h @@ -63,8 +63,11 @@ namespace MassStorage return 1; #else return NumSdCards +#if SUPPORT_USB_DRIVE + + NumUsbDrives #endif ; +#endif } #endif diff --git a/src/Storage/UsbVolume.cpp b/src/Storage/UsbVolume.cpp new file mode 100644 index 0000000000..cd5e52db6d --- /dev/null +++ b/src/Storage/UsbVolume.cpp @@ -0,0 +1,251 @@ + +#include + +#include +#include + +#include + +#if SUPPORT_USB_DRIVE + +static_assert(CORE_USES_TINYUSB && CFG_TUH_ENABLED, "USB drive support needs tinyUSB host stack"); // implementation only on tinyUSB with host support +#if CORE_USES_TINYUSB && CFG_TUH_ENABLED + +#include +#include + +#include "UsbVolume.h" + +static bool disk_io_complete(uint8_t address, tuh_msc_complete_data_t const *cb_data) +{ + (void) address; + BinarySemaphore *ioDone = reinterpret_cast(cb_data->user_arg); + ioDone->Give(); + return true; +} + +void UsbVolume::Init() noexcept +{ + StorageVolume::Init(); + address = 0; + + for (size_t i = 0; i < NumUsbDrives; i++) + { + if (usbDrives[i] == nullptr) + { + usbDrives[i] = this; + break; + } + } +} + +void UsbVolume::Spin() noexcept +{ + if (state == State::removed) + { + InternalUnmount(); + address = 0; + state = State::free; + } +} + +bool UsbVolume::IsUseable(const StringRef& reply) const noexcept +{ + if (!CoreUsbIsHostMode()) + { + if (&reply != &StorageVolume::noReply) + { + reply.copy("USB not configured as host"); + } + return false; + } + return true; +} + +GCodeResult UsbVolume::Mount(const StringRef &reply, bool reportSuccess) noexcept +{ + if (!IsDetected()) + { + reply.copy("No USB storage detected"); + return GCodeResult::error; + } + + if (IsMounted()) + { + if (MassStorage::AnyFileOpen(&fileSystem)) + { + // Don't re-mount the card if any files are open on it + reply.printf("%s has open file(s)", id); + return GCodeResult::error; + } + (void)InternalUnmount(); + } + + // Mount the file systems + const FRESULT res = f_mount(&fileSystem, path, 1); + if (res == FR_NO_FILESYSTEM) + { + reply.printf("Cannot mount %s: no FAT filesystem found on card (EXFAT is not supported)", id); + return GCodeResult::error; + } + if (res != FR_OK) + { + reply.printf("Cannot mount %s: code %d", id, res); + return GCodeResult::error; + } + state = State::mounted; + + reprap.VolumesUpdated(); + if (reportSuccess) + { + float capacity = GetCapacity() / 1000000.0f; // get capacity and convert from Kib to Mbytes + const char* capUnits = capacity >= 1000.0 ? "Gb" : "Mb"; + reply.printf("%s mounted, capacity %.2f%s", id, static_cast(capacity >= 1000.0 ? capacity / 1000 : capacity), capUnits); + } + IncrementSeqNum(); + + return GCodeResult::ok; +} + +uint64_t UsbVolume::GetCapacity() const noexcept +{ + // Get capacity of device + uint64_t const block_count = tuh_msc_get_block_count(address, lun); + uint64_t const block_size = tuh_msc_get_block_size(address, lun); + return block_count * block_size; +} + +uint32_t UsbVolume::GetInterfaceSpeed() const noexcept +{ + tusb_speed_t speed = tuh_speed_get(address); + return (speed == TUSB_SPEED_HIGH ? 480000000 : 12000000) / 8; +} + +DRESULT UsbVolume::DiskInitialize() noexcept +{ + return RES_OK; // nothing to do +} + +DRESULT UsbVolume::DiskStatus() noexcept +{ + return static_cast(tuh_msc_mounted(address) ? 0 : STA_NODISK); +} + +DRESULT UsbVolume::DiskRead(BYTE *buff, LBA_t sector, UINT count) noexcept +{ + tuh_msc_read10(address, lun, buff, sector, (uint16_t)count, disk_io_complete, reinterpret_cast(&ioDone)); + ioDone.Take(); + return RES_OK; +} + +DRESULT UsbVolume::DiskWrite(BYTE const *buff, LBA_t sector, UINT count) noexcept +{ + tuh_msc_write10(address, lun, buff, sector, (uint16_t)count, disk_io_complete, reinterpret_cast(&ioDone)); + ioDone.Take(); + return RES_OK; +} + +DRESULT UsbVolume::DiskIoctl(BYTE cmd, void *buff) noexcept +{ + switch (cmd) + { + case CTRL_SYNC: + // nothing to do since we do blocking + return RES_OK; + + case GET_SECTOR_COUNT: + *((DWORD *)buff) = (WORD)tuh_msc_get_block_count(address, lun); + return RES_OK; + + case GET_SECTOR_SIZE: + *((WORD *)buff) = (WORD)tuh_msc_get_block_size(address, lun); + return RES_OK; + + case GET_BLOCK_SIZE: + *((DWORD *)buff) = 1; // erase block size in units of sector size + return RES_OK; + + default: + return RES_PARERR; + } + + return RES_OK; +} + +void UsbVolume::DeviceUnmount() noexcept +{ + switch (state) + { + case State::removed: + state = State::free; + break; + case State::mounted: + state = State::inserted; + default: + break; + } +} + +bool UsbVolume::Accept(uint8_t address) +{ + if (state == State::free) + { + state = State::inserted; + this->address = address; + return true; + } + return false; +} + +void UsbVolume::Free() +{ + switch (state) + { + case State::inserted: + state = State::free; // immediately set free + address = 0; + break; + case State::mounted: + state = State::removed; // perform actual freeing in spin function + default: + break; + } +} + +/*static*/ void UsbVolume::VolumeInserted(uint8_t address) +{ + for (UsbVolume *drive : usbDrives) + { + // Check if there are free ones that can accept + if (drive->Accept(address)) + { + break; + } + } +} + +/*static*/ void UsbVolume::VolumeRemoved(uint8_t address) +{ + for (UsbVolume *drive : usbDrives) + { + if (drive->address == address) + { + drive->Free(); + } + } +} + +extern "C" void tuh_msc_mount_cb(uint8_t address) +{ + UsbVolume::VolumeInserted(address); +} + +extern "C" void tuh_msc_umount_cb(uint8_t address) +{ + UsbVolume::VolumeRemoved(address); +} + +/*static*/ UsbVolume *UsbVolume::usbDrives[NumUsbDrives]; + +#endif // CORE_USES_TINYUSB && CFG_TUH_ENABLED +#endif // SUPPORT_USB_DRIVE diff --git a/src/Storage/UsbVolume.h b/src/Storage/UsbVolume.h new file mode 100644 index 0000000000..0e5988ba5b --- /dev/null +++ b/src/Storage/UsbVolume.h @@ -0,0 +1,71 @@ +#pragma once + +#include + +#include + +#include "StorageVolume.h" + +#if SUPPORT_USB_DRIVE + +class UsbVolume : public StorageVolume +{ +public: + UsbVolume(const char *id, uint8_t slot) : StorageVolume(id, slot) {} + + void Init() noexcept override; + + void Spin() noexcept override; + + GCodeResult Mount(const StringRef& reply, bool reportSuccess) noexcept override; + + bool IsUseable(const StringRef& reply) const noexcept override; + bool IsMounted() const noexcept override + { + return (static_cast(state) & + (static_cast(State::mounted) | static_cast(State::removed))); + } + bool IsDetected() const noexcept override + { + return (static_cast(state) & + (static_cast(State::inserted) | static_cast(State::mounted))); + } + + uint64_t GetCapacity() const noexcept override; + uint32_t GetInterfaceSpeed() const noexcept override; + + DRESULT DiskInitialize() noexcept override; + DRESULT DiskStatus() noexcept override; + DRESULT DiskRead(BYTE *buff, LBA_t sector, UINT count) noexcept override; + DRESULT DiskWrite(BYTE const *buff, LBA_t sector, UINT count) noexcept override; + DRESULT DiskIoctl(BYTE ctrl, void *buff) noexcept override; + + static void VolumeInserted(uint8_t address); + static void VolumeRemoved(uint8_t address); + +private: + enum class State : uint8_t + { + free = 0x00, + inserted = 0x01, + mounted = 0x02, + removed = 0x04 + }; + + uint8_t address; + uint8_t lun; + BinarySemaphore ioDone; + + // The state is read and/or modified in two tasks: UsbTask during tinyUSB device insertion/removal callbacks, + // and MainTask, during GCode mounting/unmounting commands. + State state; + + static UsbVolume* usbDrives[NumUsbDrives]; + + void DeviceUnmount() noexcept override; + + bool Accept(uint8_t address); + void Free(); +}; + +#endif // SUPPORT_USB_DRIVE