this software.
*/
+/** \file
+ *
+ * Virtualized FAT12 filesystem implementation, to perform self-programming
+ * in response to read and write requests to the virtual filesystem by the
+ * host PC.
+ */
+
+#define INCLUDE_FROM_VIRTUAL_FAT_C
#include "VirtualFAT.h"
+/** FAT filesystem boot sector block, must be the first sector on the physical
+ * disk so that the host can identify the presence of a FAT filesystem. This
+ * block is truncated; normally a large bootstrap section is located near the
+ * end of the block for booting purposes however as this is not meant to be a
+ * bootable disk it is omitted for space reasons.
+ *
+ * \note When returning the boot block to the host, the magic signature 0xAA55
+ * must be added to the very end of the block to identify it as a boot
+ * block.
+ */
static const FATBootBlock_t BootBlock =
{
.Bootstrap = {0xEB, 0x3C, 0x90},
.TotalSectors16 = LUN_MEDIA_BLOCKS,
.MediaDescriptor = 0xF8,
.SectorsPerFAT = 1,
- .SectorsPerTrack = LUN_MEDIA_BLOCKS % 64,
- .Heads = LUN_MEDIA_BLOCKS / 64,
+ .SectorsPerTrack = (LUN_MEDIA_BLOCKS % 64),
+ .Heads = (LUN_MEDIA_BLOCKS / 64),
.HiddenSectors = 0,
.TotalSectors32 = 0,
.PhysicalDriveNum = 0,
.VolumeSerialNumber = 0x12345678,
.VolumeLabel = "LUFA BOOT ",
.FilesystemIdentifier = "FAT12 ",
- .BootstrapProgram = {0},
- .MagicSignature = 0xAA55,
};
+/** FAT 8.3 style directory entry, for the virtual FLASH contents file. */
static FATDirectoryEntry_t FirmwareFileEntry =
{
- .Filename = "FIRMWARE",
- .Extension = "BIN",
- .Attributes = 0,
- .Reserved = {0},
- .CreationTime = FAT_TIME(1, 1, 0),
- .CreationDate = FAT_DATE(14, 2, 1989),
- .StartingCluster = 2,
- .FileSizeBytes = 2049,
+ .Filename = "FIRMWARE",
+ .Extension = "BIN",
+ .Attributes = 0,
+ .Reserved = {0},
+ .CreationTime = FAT_TIME(1, 1, 0),
+ .CreationDate = FAT_DATE(14, 2, 1989),
+ .StartingCluster = 2,
+ .FileSizeBytes = FIRMWARE_FILE_SIZE_BYTES,
};
-static void WriteBlock(uint16_t BlockNumber)
+
+/** Updates a FAT12 cluster entry in the FAT file table with the specified next
+ * chain index. If the cluster is the last in the file chain, the magic value
+ * 0xFFF is used.
+ *
+ * \note FAT data cluster indexes are offset by 2, so that cluster 2 is the
+ * first file data cluster on the disk. See the FAT specification.
+ *
+ * \param[out] FATTable Pointer to the FAT12 allocation table
+ * \param[in] Index Index of the cluster entry to update
+ * \param[in] ChainEntry Next cluster index in the file chain
+ */
+static void UpdateFAT12ClusterEntry(uint8_t* const FATTable,
+ const uint16_t Index,
+ const uint16_t ChainEntry)
{
- uint8_t BlockBuffer[512];
+ /* Calculate the starting offset of the cluster entry in the FAT12 table */
+ uint8_t FATOffset = (Index * 3) / 2;
+ bool UpperNibble = (((Index * 3) % 2) != 0);
- /* Wait until endpoint is ready before continuing */
- if (Endpoint_WaitUntilReady())
- return;
+ /* Check if the start of the entry is at an upper nibble of the byte, fill
+ * out FAT12 entry as required */
+ if (UpperNibble)
+ {
+ FATTable[FATOffset] = (FATTable[FATOffset] & 0x0F) | ((ChainEntry & 0x0F) << 4);
+ FATTable[FATOffset + 1] = (ChainEntry >> 4);
+ }
+ else
+ {
+ FATTable[FATOffset] = ChainEntry;
+ FATTable[FATOffset + 1] = (FATTable[FATOffset] & 0xF0) | (ChainEntry >> 8);
+ }
+}
+/** Writes a block of data to the virtual FAT filesystem, from the USB Mass
+ * Storage interface.
+ *
+ * \param[in] BlockNumber Index of the block to write.
+ */
+static void WriteVirtualBlock(const uint16_t BlockNumber)
+{
+ uint8_t BlockBuffer[SECTOR_SIZE_BYTES];
+
+ /* Buffer the entire block to be written from the host */
Endpoint_Read_Stream_LE(BlockBuffer, sizeof(BlockBuffer), NULL);
Endpoint_ClearOUT();
- printf("WRITE %d\r\n", BlockNumber);
- // TODO: Write to FLASH
-}
+ if ((BlockNumber >= 4) && (BlockNumber < (4 + FILE_SECTORS(FIRMWARE_FILE_SIZE_BYTES))))
+ {
+ uint32_t WriteFlashAddress = (uint32_t)(BlockNumber - 4) * SECTOR_SIZE_BYTES;
-void WriteFAT12ClusterEntry(uint8_t* FATTable, uint8_t Index, uint16_t NextEntry)
-{
- uint8_t StartOffset = ((uint16_t)Index * 3) / 2;
+ for (uint16_t i = 0; i < SECTOR_SIZE_BYTES; i += 2)
+ {
+ if ((WriteFlashAddress % SPM_PAGESIZE) == 0)
+ {
+ /* Erase the given FLASH page, ready to be programmed */
+ boot_page_erase(WriteFlashAddress);
+ boot_spm_busy_wait();
+ }
- /* Check if the start of the entry is at an upper nibble of the byte */
- if (((uint16_t)Index * 3) % 2)
- {
- FATTable[StartOffset] |= ((NextEntry & 0x0F) << 4);
- FATTable[StartOffset + 1] = (NextEntry >> 4);
- }
- else
- {
- FATTable[StartOffset] = NextEntry;
- FATTable[StartOffset + 1] = (NextEntry >> 8);
+ /* Write the next data word to the FLASH page */
+ boot_page_fill(WriteFlashAddress, (BlockBuffer[i + 1] << 8) | BlockBuffer[i]);
+ WriteFlashAddress += 2;
+
+ if ((WriteFlashAddress % SPM_PAGESIZE) == 0)
+ {
+ /* Write the filled FLASH page to memory */
+ boot_page_write(WriteFlashAddress - SPM_PAGESIZE);
+ boot_spm_busy_wait();
+ }
+ }
}
}
-static void ReadBlock(uint16_t BlockNumber)
+/** Reads a block of data from the virtual FAT filesystem, and sends it to the
+ * host via the USB Mass Storage interface.
+ *
+ * \param[in] BlockNumber Index of the block to read.
+ */
+static void ReadVirtualBlock(const uint16_t BlockNumber)
{
- uint8_t BlockBuffer[512];
+ uint8_t BlockBuffer[SECTOR_SIZE_BYTES];
memset(BlockBuffer, 0x00, sizeof(BlockBuffer));
- printf("READ %d", BlockNumber);
-
switch (BlockNumber)
{
- case 0:
+ case 0: /* Block 0: Boot block sector */
memcpy(BlockBuffer, &BootBlock, sizeof(FATBootBlock_t));
- printf(" <B>\r\n");
- break;
- case 1:
- case 2:
- printf(" <F>\r\n");
+ /* Add the magic signature to the end of the block */
+ BlockBuffer[SECTOR_SIZE_BYTES - 2] = 0x55;
+ BlockBuffer[SECTOR_SIZE_BYTES - 1] = 0xAA;
+ break;
+ case 1: /* Block 1: First FAT12 cluster chain copy */
+ case 2: /* Block 2: Second FAT12 cluster chain copy */
/* Cluster 0: Media type/Reserved */
- WriteFAT12ClusterEntry(BlockBuffer, 0, 0xF00 | BootBlock.MediaDescriptor);
+ UpdateFAT12ClusterEntry(BlockBuffer, 0, 0xF00 | BootBlock.MediaDescriptor);
/* Cluster 1: Reserved */
- WriteFAT12ClusterEntry(BlockBuffer, 1, 0xFFF);
+ UpdateFAT12ClusterEntry(BlockBuffer, 1, 0xFFF);
/* Cluster 2 onwards: Cluster chain of FIRMWARE.BIN */
- for (uint16_t i = 0; i < FILE_CLUSTERS(2049); i++)
- WriteFAT12ClusterEntry(BlockBuffer, i+2, i+3);
+ for (uint16_t i = 0; i < FILE_CLUSTERS(FIRMWARE_FILE_SIZE_BYTES); i++)
+ UpdateFAT12ClusterEntry(BlockBuffer, i+2, i+3);
/* Mark last cluster as end of file */
- WriteFAT12ClusterEntry(BlockBuffer, FILE_CLUSTERS(2049) + 1, 0xFFF);
+ UpdateFAT12ClusterEntry(BlockBuffer, FILE_CLUSTERS(FIRMWARE_FILE_SIZE_BYTES) + 1, 0xFFF);
break;
- case 3:
- printf("<R>\r\n");
+ case 3: /* Block 3: Root file entries */
memcpy(BlockBuffer, &FirmwareFileEntry, sizeof(FATDirectoryEntry_t));
break;
- default:
- if ((BlockNumber >= 4) && (BlockNumber < (4 + FILE_CLUSTERS(FIRMWARE_FILE_SIZE))))
+ default: /* Blocks 4 onwards: Data allocation section */
+ if ((BlockNumber >= 4) && (BlockNumber < (4 + FILE_SECTORS(FIRMWARE_FILE_SIZE_BYTES))))
{
- printf("<D>\r\n");
+ uint32_t ReadFlashAddress = (uint32_t)(BlockNumber - 4) * SECTOR_SIZE_BYTES;
- for (uint16_t i = 0; i < 512; i++)
- BlockBuffer[i] = '0' + BlockNumber; //A' + (i % 26);
+ for (uint16_t i = 0; i < SECTOR_SIZE_BYTES; i++)
+ BlockBuffer[i] = pgm_read_byte_far(ReadFlashAddress++);
}
break;
}
- /* Wait until endpoint is ready before continuing */
- if (Endpoint_WaitUntilReady())
- return;
-
+ /* Write the entire read block Buffer to the host */
Endpoint_Write_Stream_LE(BlockBuffer, sizeof(BlockBuffer), NULL);
Endpoint_ClearIN();
}
+/** Writes a number of blocks to the virtual FAT file system, from the host
+ * PC via the USB Mass Storage interface.
+ *
+ * \param[in] MSInterfaceInfo Pointer to a structure containing a Mass Storage Class configuration and state
+ * \param[in] BlockAddress Data block starting address for the write sequence
+ * \param[in] TotalBlocks Number of blocks of data to write
+ */
void VirtualFAT_WriteBlocks(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo,
const uint32_t BlockAddress,
uint16_t TotalBlocks)
uint16_t CurrentBlock = (uint16_t)BlockAddress;
/* Emulated FAT is performed per-block, pass each requested block index
- * to the emulation function */
+ * to the emulated FAT block write function */
while (TotalBlocks--)
- WriteBlock(CurrentBlock++);
+ WriteVirtualBlock(CurrentBlock++);
}
+/** Reads a number of blocks from the virtual FAT file system, and sends them
+ * to the host PC via the USB Mass Storage interface.
+ *
+ * \param[in] MSInterfaceInfo Pointer to a structure containing a Mass Storage Class configuration and state
+ * \param[in] BlockAddress Data block starting address for the read sequence
+ * \param[in] TotalBlocks Number of blocks of data to read
+ */
void VirtualFAT_ReadBlocks(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo,
const uint32_t BlockAddress,
uint16_t TotalBlocks)
uint16_t CurrentBlock = (uint16_t)BlockAddress;
/* Emulated FAT is performed per-block, pass each requested block index
- * to the emulation function */
+ * to the emulated FAT block read function */
while (TotalBlocks--)
- ReadBlock(CurrentBlock++);
+ ReadVirtualBlock(CurrentBlock++);
}