diff options
author | Dean Camera <dean@fourwalledcubicle.com> | 2013-03-10 09:35:43 +0000 |
---|---|---|
committer | Dean Camera <dean@fourwalledcubicle.com> | 2013-03-10 09:35:43 +0000 |
commit | 0b1f33e2a26091714982447132b251496d5a48b2 (patch) | |
tree | 2500a330fc13347d63b92445bb81eb26f55f3540 /Bootloaders/MassStorage/Lib | |
parent | ef7b6a274c9ea66d301d0b3b0d2f6de7cee2b0d1 (diff) | |
download | lufa-0b1f33e2a26091714982447132b251496d5a48b2.tar.gz lufa-0b1f33e2a26091714982447132b251496d5a48b2.tar.bz2 lufa-0b1f33e2a26091714982447132b251496d5a48b2.zip |
Move out the Mass Storage class bootloader to the main Bootloader directory now that it is functional.
Diffstat (limited to 'Bootloaders/MassStorage/Lib')
-rw-r--r-- | Bootloaders/MassStorage/Lib/SCSI.c | 319 | ||||
-rw-r--r-- | Bootloaders/MassStorage/Lib/SCSI.h | 85 | ||||
-rw-r--r-- | Bootloaders/MassStorage/Lib/VirtualFAT.c | 255 | ||||
-rw-r--r-- | Bootloaders/MassStorage/Lib/VirtualFAT.h | 159 |
4 files changed, 818 insertions, 0 deletions
diff --git a/Bootloaders/MassStorage/Lib/SCSI.c b/Bootloaders/MassStorage/Lib/SCSI.c new file mode 100644 index 000000000..154fe4885 --- /dev/null +++ b/Bootloaders/MassStorage/Lib/SCSI.c @@ -0,0 +1,319 @@ +/* + 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 + * + * SCSI command processing routines, for SCSI commands issued by the host. Mass Storage + * devices use a thin "Bulk-Only Transport" protocol for issuing commands and status information, + * which wrap around standard SCSI device commands for controlling the actual storage medium. + */ + +#define INCLUDE_FROM_SCSI_C +#include "SCSI.h" + +/** Structure to hold the SCSI response data to a SCSI INQUIRY command. This gives information about the device's + * features and capabilities. + */ +static const SCSI_Inquiry_Response_t InquiryData = + { + .DeviceType = DEVICE_TYPE_BLOCK, + .PeripheralQualifier = 0, + + .Removable = true, + + .Version = 0, + + .ResponseDataFormat = 2, + .NormACA = false, + .TrmTsk = false, + .AERC = false, + + .AdditionalLength = 0x1F, + + .SoftReset = false, + .CmdQue = false, + .Linked = false, + .Sync = false, + .WideBus16Bit = false, + .WideBus32Bit = false, + .RelAddr = false, + + .VendorID = "LUFA", + .ProductID = "Bootloader", + .RevisionID = {'0','.','0','0'}, + }; + +/** Structure to hold the sense data for the last issued SCSI command, which is returned to the host after a SCSI REQUEST SENSE + * command is issued. This gives information on exactly why the last command failed to complete. + */ +static SCSI_Request_Sense_Response_t SenseData = + { + .ResponseCode = 0x70, + .AdditionalLength = 0x0A, + }; + + +/** Main routine to process the SCSI command located in the Command Block Wrapper read from the host. This dispatches + * to the appropriate SCSI command handling routine if the issued command is supported by the device, else it returns + * a command failure due to a ILLEGAL REQUEST. + * + * \param[in] MSInterfaceInfo Pointer to the Mass Storage class interface structure that the command is associated with + * + * \return Boolean true if the command completed successfully, false otherwise + */ +bool SCSI_DecodeSCSICommand(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo) +{ + bool CommandSuccess = false; + + /* Run the appropriate SCSI command hander function based on the passed command */ + switch (MSInterfaceInfo->State.CommandBlock.SCSICommandData[0]) + { + case SCSI_CMD_INQUIRY: + CommandSuccess = SCSI_Command_Inquiry(MSInterfaceInfo); + break; + case SCSI_CMD_REQUEST_SENSE: + CommandSuccess = SCSI_Command_Request_Sense(MSInterfaceInfo); + break; + case SCSI_CMD_READ_CAPACITY_10: + CommandSuccess = SCSI_Command_Read_Capacity_10(MSInterfaceInfo); + break; + case SCSI_CMD_SEND_DIAGNOSTIC: + CommandSuccess = SCSI_Command_Send_Diagnostic(MSInterfaceInfo); + break; + case SCSI_CMD_WRITE_10: + CommandSuccess = SCSI_Command_ReadWrite_10(MSInterfaceInfo, DATA_WRITE); + break; + case SCSI_CMD_READ_10: + CommandSuccess = SCSI_Command_ReadWrite_10(MSInterfaceInfo, DATA_READ); + break; + case SCSI_CMD_MODE_SENSE_6: + CommandSuccess = SCSI_Command_ModeSense_6(MSInterfaceInfo); + break; + case SCSI_CMD_START_STOP_UNIT: + case SCSI_CMD_TEST_UNIT_READY: + case SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL: + case SCSI_CMD_VERIFY_10: + /* These commands should just succeed, no handling required */ + CommandSuccess = true; + MSInterfaceInfo->State.CommandBlock.DataTransferLength = 0; + break; + default: + /* Update the SENSE key to reflect the invalid command */ + SCSI_SET_SENSE(SCSI_SENSE_KEY_ILLEGAL_REQUEST, + SCSI_ASENSE_INVALID_COMMAND, + SCSI_ASENSEQ_NO_QUALIFIER); + break; + } + + /* Check if command was successfully processed */ + if (CommandSuccess) + { + SCSI_SET_SENSE(SCSI_SENSE_KEY_GOOD, + SCSI_ASENSE_NO_ADDITIONAL_INFORMATION, + SCSI_ASENSEQ_NO_QUALIFIER); + + return true; + } + + return false; +} + +/** Command processing for an issued SCSI INQUIRY command. This command returns information about the device's features + * and capabilities to the host. + * + * \param[in] MSInterfaceInfo Pointer to the Mass Storage class interface structure that the command is associated with + * + * \return Boolean true if the command completed successfully, false otherwise. + */ +static bool SCSI_Command_Inquiry(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo) +{ + uint16_t AllocationLength = SwapEndian_16(*(uint16_t*)&MSInterfaceInfo->State.CommandBlock.SCSICommandData[3]); + uint16_t BytesTransferred = MIN(AllocationLength, sizeof(InquiryData)); + + /* Only the standard INQUIRY data is supported, check if any optional INQUIRY bits set */ + if ((MSInterfaceInfo->State.CommandBlock.SCSICommandData[1] & ((1 << 0) | (1 << 1))) || + MSInterfaceInfo->State.CommandBlock.SCSICommandData[2]) + { + /* Optional but unsupported bits set - update the SENSE key and fail the request */ + SCSI_SET_SENSE(SCSI_SENSE_KEY_ILLEGAL_REQUEST, + SCSI_ASENSE_INVALID_FIELD_IN_CDB, + SCSI_ASENSEQ_NO_QUALIFIER); + + return false; + } + + Endpoint_Write_Stream_LE(&InquiryData, BytesTransferred, NULL); + + /* Pad out remaining bytes with 0x00 */ + Endpoint_Null_Stream((AllocationLength - BytesTransferred), NULL); + + /* Finalize the stream transfer to send the last packet */ + Endpoint_ClearIN(); + + /* Succeed the command and update the bytes transferred counter */ + MSInterfaceInfo->State.CommandBlock.DataTransferLength -= BytesTransferred; + + return true; +} + +/** Command processing for an issued SCSI REQUEST SENSE command. This command returns information about the last issued command, + * including the error code and additional error information so that the host can determine why a command failed to complete. + * + * \param[in] MSInterfaceInfo Pointer to the Mass Storage class interface structure that the command is associated with + * + * \return Boolean true if the command completed successfully, false otherwise. + */ +static bool SCSI_Command_Request_Sense(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo) +{ + uint8_t AllocationLength = MSInterfaceInfo->State.CommandBlock.SCSICommandData[4]; + uint8_t BytesTransferred = MIN(AllocationLength, sizeof(SenseData)); + + Endpoint_Write_Stream_LE(&SenseData, BytesTransferred, NULL); + Endpoint_Null_Stream((AllocationLength - BytesTransferred), NULL); + Endpoint_ClearIN(); + + /* Succeed the command and update the bytes transferred counter */ + MSInterfaceInfo->State.CommandBlock.DataTransferLength -= BytesTransferred; + + return true; +} + +/** Command processing for an issued SCSI READ CAPACITY (10) command. This command returns information about the device's capacity + * on the selected Logical Unit (drive), as a number of OS-sized blocks. + * + * \param[in] MSInterfaceInfo Pointer to the Mass Storage class interface structure that the command is associated with + * + * \return Boolean true if the command completed successfully, false otherwise. + */ +static bool SCSI_Command_Read_Capacity_10(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo) +{ + uint32_t LastBlockAddressInLUN = (LUN_MEDIA_BLOCKS - 1); + uint32_t MediaBlockSize = SECTOR_SIZE_BYTES; + + Endpoint_Write_Stream_BE(&LastBlockAddressInLUN, sizeof(LastBlockAddressInLUN), NULL); + Endpoint_Write_Stream_BE(&MediaBlockSize, sizeof(MediaBlockSize), NULL); + Endpoint_ClearIN(); + + /* Succeed the command and update the bytes transferred counter */ + MSInterfaceInfo->State.CommandBlock.DataTransferLength -= 8; + + return true; +} + +/** Command processing for an issued SCSI SEND DIAGNOSTIC command. This command performs a quick check of the Dataflash ICs on the + * board, and indicates if they are present and functioning correctly. Only the Self-Test portion of the diagnostic command is + * supported. + * + * \param[in] MSInterfaceInfo Pointer to the Mass Storage class interface structure that the command is associated with + * + * \return Boolean true if the command completed successfully, false otherwise. + */ +static bool SCSI_Command_Send_Diagnostic(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo) +{ + /* Check to see if the SELF TEST bit is not set */ + if (!(MSInterfaceInfo->State.CommandBlock.SCSICommandData[1] & (1 << 2))) + { + /* Only self-test supported - update SENSE key and fail the command */ + SCSI_SET_SENSE(SCSI_SENSE_KEY_ILLEGAL_REQUEST, + SCSI_ASENSE_INVALID_FIELD_IN_CDB, + SCSI_ASENSEQ_NO_QUALIFIER); + + return false; + } + + /* Succeed the command and update the bytes transferred counter */ + MSInterfaceInfo->State.CommandBlock.DataTransferLength = 0; + + return true; +} + +/** Command processing for an issued SCSI READ (10) or WRITE (10) command. This command reads in the block start address + * and total number of blocks to process, then calls the appropriate low-level Dataflash routine to handle the actual + * reading and writing of the data. + * + * \param[in] MSInterfaceInfo Pointer to the Mass Storage class interface structure that the command is associated with + * \param[in] IsDataRead Indicates if the command is a READ (10) command or WRITE (10) command (DATA_READ or DATA_WRITE) + * + * \return Boolean true if the command completed successfully, false otherwise. + */ +static bool SCSI_Command_ReadWrite_10(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo, + const bool IsDataRead) +{ + uint32_t BlockAddress; + uint16_t TotalBlocks; + + /* Load in the 32-bit block address (SCSI uses big-endian, so have to reverse the byte order) */ + BlockAddress = SwapEndian_32(*(uint32_t*)&MSInterfaceInfo->State.CommandBlock.SCSICommandData[2]); + + /* Load in the 16-bit total blocks (SCSI uses big-endian, so have to reverse the byte order) */ + TotalBlocks = SwapEndian_16(*(uint16_t*)&MSInterfaceInfo->State.CommandBlock.SCSICommandData[7]); + + /* Check if the block address is outside the maximum allowable value for the LUN */ + if (BlockAddress >= LUN_MEDIA_BLOCKS) + { + /* Block address is invalid, update SENSE key and return command fail */ + SCSI_SET_SENSE(SCSI_SENSE_KEY_ILLEGAL_REQUEST, + SCSI_ASENSE_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE, + SCSI_ASENSEQ_NO_QUALIFIER); + + return false; + } + + /* Determine if the packet is a READ (10) or WRITE (10) command, call appropriate function */ + if (IsDataRead == DATA_READ) + VirtualFAT_ReadBlocks(MSInterfaceInfo, BlockAddress, TotalBlocks); + else + VirtualFAT_WriteBlocks(MSInterfaceInfo, BlockAddress, TotalBlocks); + + /* Update the bytes transferred counter and succeed the command */ + MSInterfaceInfo->State.CommandBlock.DataTransferLength -= ((uint32_t)TotalBlocks * SECTOR_SIZE_BYTES); + + return true; +} + +/** Command processing for an issued SCSI MODE SENSE (6) command. This command returns various informational pages about + * the SCSI device, as well as the device's Write Protect status. + * + * \param[in] MSInterfaceInfo Pointer to the Mass Storage class interface structure that the command is associated with + * + * \return Boolean true if the command completed successfully, false otherwise. + */ +static bool SCSI_Command_ModeSense_6(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo) +{ + /* Send an empty header response indicating Write Protect flag is off */ + Endpoint_Write_32_LE(0); + Endpoint_ClearIN(); + + /* Update the bytes transferred counter and succeed the command */ + MSInterfaceInfo->State.CommandBlock.DataTransferLength -= 4; + + return true; +} + diff --git a/Bootloaders/MassStorage/Lib/SCSI.h b/Bootloaders/MassStorage/Lib/SCSI.h new file mode 100644 index 000000000..54914f5eb --- /dev/null +++ b/Bootloaders/MassStorage/Lib/SCSI.h @@ -0,0 +1,85 @@ +/* + 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 + * + * Header file for SCSI.c. + */ + +#ifndef _SCSI_H_ +#define _SCSI_H_ + + /* Includes: */ + #include <avr/io.h> + #include <avr/pgmspace.h> + + #include <LUFA/Drivers/USB/USB.h> + + #include "../BootloaderMassStorage.h" + #include "../Descriptors.h" + #include "VirtualFAT.h" + + /* Macros: */ + /** Macro to set the current SCSI sense data to the given key, additional sense code and additional sense qualifier. This + * is for convenience, as it allows for all three sense values (returned upon request to the host to give information about + * the last command failure) in a quick and easy manner. + * + * \param[in] Key New SCSI sense key to set the sense code to + * \param[in] Acode New SCSI additional sense key to set the additional sense code to + * \param[in] Aqual New SCSI additional sense key qualifier to set the additional sense qualifier code to + */ + #define SCSI_SET_SENSE(Key, Acode, Aqual) MACROS{ SenseData.SenseKey = (Key); \ + SenseData.AdditionalSenseCode = (Acode); \ + SenseData.AdditionalSenseQualifier = (Aqual); }MACROE + + /** Macro for the \ref SCSI_Command_ReadWrite_10() function, to indicate that data is to be read from the storage medium. */ + #define DATA_READ true + + /** Macro for the \ref SCSI_Command_ReadWrite_10() function, to indicate that data is to be written to the storage medium. */ + #define DATA_WRITE false + + /** Value for the DeviceType entry in the SCSI_Inquiry_Response_t enum, indicating a Block Media device. */ + #define DEVICE_TYPE_BLOCK 0x00 + + /* Function Prototypes: */ + bool SCSI_DecodeSCSICommand(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo); + + #if defined(INCLUDE_FROM_SCSI_C) + static bool SCSI_Command_Inquiry(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo); + static bool SCSI_Command_Request_Sense(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo); + static bool SCSI_Command_Read_Capacity_10(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo); + static bool SCSI_Command_Send_Diagnostic(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo); + static bool SCSI_Command_ReadWrite_10(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo, + const bool IsDataRead); + static bool SCSI_Command_ModeSense_6(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo); + #endif + +#endif + 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++); +} + diff --git a/Bootloaders/MassStorage/Lib/VirtualFAT.h b/Bootloaders/MassStorage/Lib/VirtualFAT.h new file mode 100644 index 000000000..af4548898 --- /dev/null +++ b/Bootloaders/MassStorage/Lib/VirtualFAT.h @@ -0,0 +1,159 @@ +/* + 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. +*/ + +#ifndef _VIRTUALFAT_H_ +#define _VIRTUALFAT_H_ + + /* Includes: */ + #include <avr/io.h> + #include <avr/pgmspace.h> + + #include <LUFA/Drivers/USB/USB.h> + + /* Macros: */ + /** Size of the virtual FIRMWARE.BIN file in bytes. */ + #define FIRMWARE_FILE_SIZE_BYTES (FLASHEND - (FLASHEND - BOOT_START_ADDR) + 1UL) + + /** Number of sectors that comprise a single logical disk cluster. */ + #define SECTOR_PER_CLUSTER 4 + + /** Size of a single logical sector on the disk. */ + #define SECTOR_SIZE_BYTES 512 + + /** Size of a logical cluster on the disk, in bytes */ + #define CLUSTER_SIZE_BYTES (SECTOR_PER_CLUSTER * SECTOR_SIZE_BYTES) + + /** Number of sectors required to store a given size in bytes. + * + * \param[in] size Size of the data that needs to be stored + * + * \return Number of sectors required to store the given data on the disk. + */ + #define FILE_SECTORS(size) ((size / SECTOR_SIZE_BYTES) + ((size % SECTOR_SIZE_BYTES) ? 1 : 0)) + + /** Number of clusters required to store a given size in bytes. + * + * \param[in] size Size of the data that needs to be stored + * + * \return Number of clusters required to store the given data on the disk. + */ + #define FILE_CLUSTERS(size) ((size / CLUSTER_SIZE_BYTES) + ((size % CLUSTER_SIZE_BYTES) ? 1 : 0)) + + /** Total number of logical sectors/blocks on the disk. */ + #define LUN_MEDIA_BLOCKS (FILE_SECTORS(FIRMWARE_FILE_SIZE_BYTES) + 32) + + /** Converts a given time in HH:MM:SS format to a FAT filesystem time. + * + * \note The minimum seconds resolution of FAT is 2, thus odd seconds + * will be truncated to the previous integer multiple of 2 seconds. + * + * \param[in] hh Hours (0-23) + * \param[in] mm Minutes (0-59) + * \param[in] ss Seconds (0-59) + * + * \return Given time encoded as a FAT filesystem timestamp + */ + #define FAT_TIME(hh, mm, ss) ((hh << 11) | (mm << 5) | (ss >> 1)) + + /** Converts a given date in DD/MM/YYYY format to a FAT filesystem date. + * + * \param[in] dd Days in the month (1-31) + * \param[in] mm Months in the year (1-12) + * \param[in] yyyy Year (1980 - 2107) + * + * \return Given date encoded as a FAT filesystem datestamp + */ + #define FAT_DATE(dd, mm, yyyy) (((yyyy - 1980) << 9) | (mm << 5) | (dd << 0)) + + /* Type Definitions: */ + /** FAT boot block structure definition, used to identify the core + * parameters of a FAT filesystem stored on a disk. + * + * \note This definition is truncated to save space; the magic signature + * 0xAA55 must be appended to the very end of the block for it to + * be detected by the host as a valid boot block. + */ + typedef struct + { + uint8_t Bootstrap[3]; + uint8_t Description[8]; + uint16_t SectorSize; + uint8_t SectorsPerCluster; + uint16_t ReservedSectors; + uint8_t FATCopies; + uint16_t RootDirectoryEntries; + uint16_t TotalSectors16; + uint8_t MediaDescriptor; + uint16_t SectorsPerFAT; + uint16_t SectorsPerTrack; + uint16_t Heads; + uint32_t HiddenSectors; + uint32_t TotalSectors32; + uint16_t PhysicalDriveNum; + uint8_t ExtendedBootRecordSig; + uint32_t VolumeSerialNumber; + uint8_t VolumeLabel[11]; + uint8_t FilesystemIdentifier[8]; + /* uint8_t BootstrapProgram[448]; */ + /* uint16_t MagicSignature; */ + } FATBootBlock_t; + + /** FAT legacy 8.3 style directory entry structure definition, used to + * identify the files and folders of FAT filesystem stored on a disk. + */ + typedef struct + { + uint8_t Filename[8]; + uint8_t Extension[3]; + uint8_t Attributes; + uint8_t Reserved[10]; + uint16_t CreationTime; + uint16_t CreationDate; + uint16_t StartingCluster; + uint32_t FileSizeBytes; + } FATDirectoryEntry_t; + + /* Function Prototypes: */ + #if defined(INCLUDE_FROM_VIRTUAL_FAT_C) + static void UpdateFAT12ClusterEntry(uint8_t* const FATTable, + const uint16_t Index, + const uint16_t ChainEntry); + static void WriteVirtualBlock(const uint16_t BlockNumber); + static void ReadVirtualBlock(const uint16_t BlockNumber); + #endif + + void VirtualFAT_WriteBlocks(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo, + const uint32_t BlockAddress, + uint16_t TotalBlocks); + + void VirtualFAT_ReadBlocks(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo, + const uint32_t BlockAddress, + uint16_t TotalBlocks); +#endif |