From 83d5c4729e3cb014d2f75a5ac4132658ad6b7ea8 Mon Sep 17 00:00:00 2001 From: Dean Camera Date: Fri, 29 Mar 2013 10:23:09 +0000 Subject: Update Mass Storage bootloader for Linux compatibility, and to reduce the compiled bootloader size. Linux appears to replace files with a cluster offset on the disk rather than re-using the same disk clusters (unlike Windows) so the file offset needs to be tracked and compensated for. --- Bootloaders/MassStorage/Lib/SCSI.c | 13 ++- Bootloaders/MassStorage/Lib/VirtualFAT.c | 181 +++++++++++++++++-------------- Bootloaders/MassStorage/Lib/VirtualFAT.h | 29 ++++- 3 files changed, 131 insertions(+), 92 deletions(-) diff --git a/Bootloaders/MassStorage/Lib/SCSI.c b/Bootloaders/MassStorage/Lib/SCSI.c index ea0700a27..b23a5a07b 100644 --- a/Bootloaders/MassStorage/Lib/SCSI.c +++ b/Bootloaders/MassStorage/Lib/SCSI.c @@ -255,10 +255,15 @@ static bool SCSI_Command_ReadWrite_10(USB_ClassInfo_MS_Device_t* const MSInterfa } /* Determine if the packet is a READ (10) or WRITE (10) command, call appropriate function */ - if (IsDataRead == DATA_READ) - VirtualFAT_ReadBlocks(BlockAddress, TotalBlocks); - else - VirtualFAT_WriteBlocks(BlockAddress, TotalBlocks); + while (TotalBlocks--) + { + if (IsDataRead == DATA_READ) + VirtualFAT_ReadBlock(BlockAddress); + else + VirtualFAT_WriteBlock(BlockAddress); + + BlockAddress++; + } /* Update the bytes transferred counter and succeed the command */ MSInterfaceInfo->State.CommandBlock.DataTransferLength -= ((uint32_t)TotalBlocks * SECTOR_SIZE_BYTES); diff --git a/Bootloaders/MassStorage/Lib/VirtualFAT.c b/Bootloaders/MassStorage/Lib/VirtualFAT.c index 2e90a8101..e50014be8 100644 --- a/Bootloaders/MassStorage/Lib/VirtualFAT.c +++ b/Bootloaders/MassStorage/Lib/VirtualFAT.c @@ -72,12 +72,13 @@ static const FATBootBlock_t BootBlock = }; /** FAT 8.3 style directory entry, for the virtual FLASH contents file. */ -static FATDirectoryEntry_t FirmwareFileEntries[] = +FATDirectoryEntry_t FirmwareFileEntries[] = { /* Root volume label entry; disk label is contained in the Filename and * Extension fields (concatenated) with a special attribute flag - other * fields are ignored. Should be the same as the label in the boot block. */ + [DISK_FILE_ENTRY_VolumeID] = { .MSDOS_Directory = { @@ -94,6 +95,7 @@ static FATDirectoryEntry_t FirmwareFileEntries[] = /* VFAT Long File Name entry for the virtual firmware file; required to * prevent corruption from systems that are unable to detect the device * as being a legacy MSDOS style FAT12 volume. */ + [DISK_FILE_ENTRY_FirmwareLFN] = { .VFAT_LongFileName = { @@ -121,6 +123,7 @@ static FATDirectoryEntry_t FirmwareFileEntries[] = }, /* MSDOS file entry for the virtual Firmware image. */ + [DISK_FILE_ENTRY_FirmwareMSDOS] = { .MSDOS_File = { @@ -136,6 +139,12 @@ static FATDirectoryEntry_t FirmwareFileEntries[] = }, }; +/** Starting block of the virtual firmware file image on disk. On Windows, files + * are (usually?) replaced using the original file's physical sectors. On Linux + * file replacements are performed with an offset. + */ +uint16_t FileStartBlock = DISK_BLOCK_DataStartBlock; + /** 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 @@ -170,70 +179,118 @@ static void UpdateFAT12ClusterEntry(uint8_t* const FATTable, } } -/** Writes a block of data to the virtual FAT filesystem, from the USB Mass - * Storage interface. +/** Reads or writes a block of data from/to the physical device FLASH using a + * block buffer stored in RAM, if the requested block is within the virtual + * firmware file's sector ranges in the emulated FAT file system. * - * \param[in] BlockNumber Index of the block to write. + * \param[in] BlockNumber Physical disk block to read from + * \param[in,out] BlockBuffer Pointer to the start of the block buffer in RAM + * \param[in] Read If \c true, the requested block is read, if + * \c false, the requested block is written */ -static void WriteVirtualBlock(const uint16_t BlockNumber) +static void ReadWriteFirmwareFileBlock(const uint16_t BlockNumber, + uint8_t* BlockBuffer, + const bool Read) { - 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)))) + /* Range check the write request - abort if requested block is not within the + * virtual firmware file sector range */ + if (!((BlockNumber >= FileStartBlock) && (BlockNumber < (FileStartBlock + FILE_SECTORS(FIRMWARE_FILE_SIZE_BYTES))))) + return; + + #if (FLASHEND > 0xFFFF) + uint32_t FlashAddress = (uint32_t)(BlockNumber - FileStartBlock) * SECTOR_SIZE_BYTES; + #else + uint16_t FlashAddress = (uint16_t)(BlockNumber - FileStartBlock) * SECTOR_SIZE_BYTES; + #endif + + if (Read) { - #if (FLASHEND > 0xFFFF) - uint32_t WriteFlashAddress = (uint32_t)(BlockNumber - 4) * SECTOR_SIZE_BYTES; - #else - uint16_t WriteFlashAddress = (uint16_t)(BlockNumber - 4) * SECTOR_SIZE_BYTES; - #endif - + /* Read out the mapped block of data from the device's FLASH */ + for (uint16_t i = 0; i < SECTOR_SIZE_BYTES; i++) + { + #if (FLASHEND > 0xFFFF) + BlockBuffer[i] = pgm_read_byte_far(FlashAddress++); + #else + BlockBuffer[i] = pgm_read_byte(FlashAddress++); + #endif + } + } + else + { + /* Write out the mapped block of data to the device's FLASH */ for (uint16_t i = 0; i < SECTOR_SIZE_BYTES; i += 2) { - if ((WriteFlashAddress % SPM_PAGESIZE) == 0) + if ((FlashAddress % SPM_PAGESIZE) == 0) { /* Erase the given FLASH page, ready to be programmed */ - BootloaderAPI_ErasePage(WriteFlashAddress); + BootloaderAPI_ErasePage(FlashAddress); } /* Write the next data word to the FLASH page */ - BootloaderAPI_FillWord(WriteFlashAddress, (BlockBuffer[i + 1] << 8) | BlockBuffer[i]); - WriteFlashAddress += 2; + BootloaderAPI_FillWord(FlashAddress, (BlockBuffer[i + 1] << 8) | BlockBuffer[i]); + FlashAddress += 2; - if ((WriteFlashAddress % SPM_PAGESIZE) == 0) + if ((FlashAddress % SPM_PAGESIZE) == 0) { /* Write the filled FLASH page to memory */ - BootloaderAPI_WritePage(WriteFlashAddress - SPM_PAGESIZE); + BootloaderAPI_WritePage(FlashAddress - SPM_PAGESIZE); } } } } +/** 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. + */ +void VirtualFAT_WriteBlock(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 == DISK_BLOCK_RootFilesBlock) + { + /* Copy over the updated directory entries */ + memcpy(FirmwareFileEntries, BlockBuffer, sizeof(FirmwareFileEntries)); + + /* Save the new firmware file block offset so the written and read file + * contents can be correctly mapped to the device's FLASH pages */ + FileStartBlock = DISK_BLOCK_DataStartBlock + + (FirmwareFileEntries[DISK_FILE_ENTRY_FirmwareMSDOS].MSDOS_File.StartingCluster - 2) * SECTOR_PER_CLUSTER; + } + else + { + ReadWriteFirmwareFileBlock(BlockNumber, BlockBuffer, false); + } +} + /** 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) +void VirtualFAT_ReadBlock(const uint16_t BlockNumber) { uint8_t BlockBuffer[SECTOR_SIZE_BYTES]; memset(BlockBuffer, 0x00, sizeof(BlockBuffer)); switch (BlockNumber) { - case 0: /* Block 0: Boot block sector */ + case DISK_BLOCK_BootBlock: 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 */ + case DISK_BLOCK_FATBlock1: + case DISK_BLOCK_FATBlock2: /* Cluster 0: Media type/Reserved */ UpdateFAT12ClusterEntry(BlockBuffer, 0, 0xF00 | BootBlock.MediaDescriptor); @@ -241,32 +298,27 @@ static void ReadVirtualBlock(const uint16_t BlockNumber) 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); + for (uint16_t i = 0; i <= FILE_CLUSTERS(FIRMWARE_FILE_SIZE_BYTES); i++) + { + uint16_t CurrentCluster = FirmwareFileEntries[DISK_FILE_ENTRY_FirmwareMSDOS].MSDOS_File.StartingCluster + i; + uint16_t NextCluster = CurrentCluster + 1; + + /* Mark last cluster as end of file */ + if (i == FILE_CLUSTERS(FIRMWARE_FILE_SIZE_BYTES)) + NextCluster = 0xFFF; + + UpdateFAT12ClusterEntry(BlockBuffer, CurrentCluster, NextCluster); + } - /* 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 */ + case DISK_BLOCK_RootFilesBlock: memcpy(BlockBuffer, FirmwareFileEntries, sizeof(FirmwareFileEntries)); + break; default: /* Blocks 4 onwards: Data allocation section */ - if ((BlockNumber >= 4) && (BlockNumber < (4 + FILE_SECTORS(FIRMWARE_FILE_SIZE_BYTES)))) - { - #if (FLASHEND > 0xFFFF) - 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++); - #else - uint16_t ReadFlashAddress = (uint16_t)(BlockNumber - 4) * SECTOR_SIZE_BYTES; - - for (uint16_t i = 0; i < SECTOR_SIZE_BYTES; i++) - BlockBuffer[i] = pgm_read_byte(ReadFlashAddress++); - #endif - } + ReadWriteFirmwareFileBlock(BlockNumber, BlockBuffer, true); break; } @@ -275,38 +327,3 @@ static void ReadVirtualBlock(const uint16_t BlockNumber) 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] BlockAddress Data block starting address for the write sequence - * \param[in] TotalBlocks Number of blocks of data to write - */ -void VirtualFAT_WriteBlocks(const uint16_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] BlockAddress Data block starting address for the read sequence - * \param[in] TotalBlocks Number of blocks of data to read - */ -void VirtualFAT_ReadBlocks(const uint16_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++); -} - diff --git a/Bootloaders/MassStorage/Lib/VirtualFAT.h b/Bootloaders/MassStorage/Lib/VirtualFAT.h index 37095eae5..820fe54d5 100644 --- a/Bootloaders/MassStorage/Lib/VirtualFAT.h +++ b/Bootloaders/MassStorage/Lib/VirtualFAT.h @@ -121,6 +121,23 @@ #define FAT_ORDINAL_LAST_ENTRY (1 << 6) //@} + /* Enums: */ + enum + { + DISK_FILE_ENTRY_VolumeID = 0, + DISK_FILE_ENTRY_FirmwareLFN = 1, + DISK_FILE_ENTRY_FirmwareMSDOS = 2, + }; + + enum + { + DISK_BLOCK_BootBlock = 0, + DISK_BLOCK_FATBlock1 = 1, + DISK_BLOCK_FATBlock2 = 2, + DISK_BLOCK_RootFilesBlock = 3, + DISK_BLOCK_DataStartBlock = 4, + }; + /* Type Definitions: */ /** FAT boot block structure definition, used to identify the core * parameters of a FAT filesystem stored on a disk. @@ -213,13 +230,13 @@ static void UpdateFAT12ClusterEntry(uint8_t* const FATTable, const uint16_t Index, const uint16_t ChainEntry) AUX_BOOT_SECTION; - static void WriteVirtualBlock(const uint16_t BlockNumber) AUX_BOOT_SECTION; - static void ReadVirtualBlock(const uint16_t BlockNumber) AUX_BOOT_SECTION; + + static void ReadWriteFirmwareFileBlock(const uint16_t BlockNumber, + uint8_t* BlockBuffer, + const bool Read) AUX_BOOT_SECTION; #endif - void VirtualFAT_WriteBlocks(const uint16_t BlockAddress, - uint16_t TotalBlocks) AUX_BOOT_SECTION; + void VirtualFAT_WriteBlock(const uint16_t BlockNumber) AUX_BOOT_SECTION; + void VirtualFAT_ReadBlock(const uint16_t BlockNumber) AUX_BOOT_SECTION; - void VirtualFAT_ReadBlocks(const uint16_t BlockAddress, - uint16_t TotalBlocks) AUX_BOOT_SECTION; #endif -- cgit v1.2.3