From 0b1f33e2a26091714982447132b251496d5a48b2 Mon Sep 17 00:00:00 2001 From: Dean Camera Date: Sun, 10 Mar 2013 09:35:43 +0000 Subject: Move out the Mass Storage class bootloader to the main Bootloader directory now that it is functional. --- Bootloaders/MassStorage/Lib/VirtualFAT.c | 255 +++++++++++++++++++++++++++++++ 1 file changed, 255 insertions(+) create mode 100644 Bootloaders/MassStorage/Lib/VirtualFAT.c (limited to 'Bootloaders/MassStorage/Lib/VirtualFAT.c') diff --git a/Bootloaders/MassStorage/Lib/VirtualFAT.c b/Bootloaders/MassStorage/Lib/VirtualFAT.c new file mode 100644 index 000000000..0bcda0620 --- /dev/null +++ b/Bootloaders/MassStorage/Lib/VirtualFAT.c @@ -0,0 +1,255 @@ +/* + LUFA Library + Copyright (C) Dean Camera, 2013. + + dean [at] fourwalledcubicle [dot] com + www.lufa-lib.org +*/ + +/* + Copyright 2013 Dean Camera (dean [at] fourwalledcubicle [dot] com) + + Permission to use, copy, modify, distribute, and sell this + software and its documentation for any purpose is hereby granted + without fee, provided that the above copyright notice appear in + all copies and that both that the copyright notice and this + permission notice and warranty disclaimer appear in supporting + documentation, and that the name of the author not be used in + advertising or publicity pertaining to distribution of the + software without specific, written prior permission. + + The author disclaims all warranties with regard to this + software, including all implied warranties of merchantability + and fitness. In no event shall the author be liable for any + special, indirect or consequential damages or any damages + whatsoever resulting from loss of use, data or profits, whether + in an action of contract, negligence or other tortious action, + arising out of or in connection with the use or performance of + 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}, + .Description = "mkdosfs", + .SectorSize = SECTOR_SIZE_BYTES, + .SectorsPerCluster = SECTOR_PER_CLUSTER, + .ReservedSectors = 1, + .FATCopies = 2, + .RootDirectoryEntries = (SECTOR_SIZE_BYTES / sizeof(FATDirectoryEntry_t)), + .TotalSectors16 = LUN_MEDIA_BLOCKS, + .MediaDescriptor = 0xF8, + .SectorsPerFAT = 1, + .SectorsPerTrack = (LUN_MEDIA_BLOCKS % 64), + .Heads = (LUN_MEDIA_BLOCKS / 64), + .HiddenSectors = 0, + .TotalSectors32 = 0, + .PhysicalDriveNum = 0, + .ExtendedBootRecordSig = 0x29, + .VolumeSerialNumber = 0x12345678, + .VolumeLabel = "LUFA BOOT ", + .FilesystemIdentifier = "FAT12 ", + }; + +/** 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 = FIRMWARE_FILE_SIZE_BYTES, + }; + + +/** 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) +{ + /* Calculate the starting offset of the cluster entry in the FAT12 table */ + uint8_t FATOffset = (Index * 3) / 2; + bool UpperNibble = (((Index * 3) % 2) != 0); + + /* 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(); + + if ((BlockNumber >= 4) && (BlockNumber < (4 + FILE_SECTORS(FIRMWARE_FILE_SIZE_BYTES)))) + { + uint32_t WriteFlashAddress = (uint32_t)(BlockNumber - 4) * SECTOR_SIZE_BYTES; + + 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(); + } + + /* 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(); + } + } + } +} + +/** 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[SECTOR_SIZE_BYTES]; + memset(BlockBuffer, 0x00, sizeof(BlockBuffer)); + + switch (BlockNumber) + { + case 0: /* Block 0: Boot block sector */ + memcpy(BlockBuffer, &BootBlock, sizeof(FATBootBlock_t)); + + /* 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 */ + UpdateFAT12ClusterEntry(BlockBuffer, 0, 0xF00 | BootBlock.MediaDescriptor); + + /* Cluster 1: Reserved */ + UpdateFAT12ClusterEntry(BlockBuffer, 1, 0xFFF); + + /* Cluster 2 onwards: Cluster chain of FIRMWARE.BIN */ + 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 */ + UpdateFAT12ClusterEntry(BlockBuffer, FILE_CLUSTERS(FIRMWARE_FILE_SIZE_BYTES) + 1, 0xFFF); + break; + + case 3: /* Block 3: Root file entries */ + memcpy(BlockBuffer, &FirmwareFileEntry, sizeof(FATDirectoryEntry_t)); + break; + + default: /* Blocks 4 onwards: Data allocation section */ + if ((BlockNumber >= 4) && (BlockNumber < (4 + FILE_SECTORS(FIRMWARE_FILE_SIZE_BYTES)))) + { + uint32_t ReadFlashAddress = (uint32_t)(BlockNumber - 4) * SECTOR_SIZE_BYTES; + + for (uint16_t i = 0; i < SECTOR_SIZE_BYTES; i++) + BlockBuffer[i] = pgm_read_byte_far(ReadFlashAddress++); + } + + break; + } + + /* 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 emulated FAT block write function */ + while (TotalBlocks--) + 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 emulated FAT block read function */ + while (TotalBlocks--) + ReadVirtualBlock(CurrentBlock++); +} + -- cgit v1.2.3