Skip to content

Commit

Permalink
Implement usb drive volume
Browse files Browse the repository at this point in the history
  • Loading branch information
rechrtb committed Apr 29, 2024
1 parent 9476941 commit 7842191
Show file tree
Hide file tree
Showing 8 changed files with 356 additions and 5 deletions.
4 changes: 4 additions & 0 deletions src/Config/Pins.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
7 changes: 7 additions & 0 deletions src/Config/Pins_Duet3_MB6HC.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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

Expand Down
4 changes: 2 additions & 2 deletions src/Hardware/SAME70/same70q20b_flash.ld
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down
2 changes: 1 addition & 1 deletion src/Libraries/Fatfs/ffconf.h
Original file line number Diff line number Diff line change
Expand Up @@ -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) */


Expand Down
19 changes: 17 additions & 2 deletions src/Storage/MassStorage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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

Expand Down Expand Up @@ -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;
}
Expand Down
3 changes: 3 additions & 0 deletions src/Storage/MassStorage.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,11 @@ namespace MassStorage
return 1;
#else
return NumSdCards
#if SUPPORT_USB_DRIVE
+ NumUsbDrives
#endif
;
#endif
}
#endif

Expand Down
251 changes: 251 additions & 0 deletions src/Storage/UsbVolume.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,251 @@

#include <cstdint>

#include <Platform/Platform.h>
#include <Platform/RepRap.h>

#include <TinyUsbInterface.h>

#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 <tusb.h>
#include <class/msc/msc_host.h>

#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<BinarySemaphore*>(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<double>(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<DRESULT>(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<uintptr_t>(&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<uintptr_t>(&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
Loading

0 comments on commit 7842191

Please sign in to comment.