3      Copyright (C) Dean Camera, 2020. 
   5   dean [at] fourwalledcubicle [dot] com 
  10   Copyright 2020  Dean Camera (dean [at] fourwalledcubicle [dot] com) 
  12   Permission to use, copy, modify, distribute, and sell this 
  13   software and its documentation for any purpose is hereby granted 
  14   without fee, provided that the above copyright notice appear in 
  15   all copies and that both that the copyright notice and this 
  16   permission notice and warranty disclaimer appear in supporting 
  17   documentation, and that the name of the author not be used in 
  18   advertising or publicity pertaining to distribution of the 
  19   software without specific, written prior permission. 
  21   The author disclaims all warranties with regard to this 
  22   software, including all implied warranties of merchantability 
  23   and fitness.  In no event shall the author be liable for any 
  24   special, indirect or consequential damages or any damages 
  25   whatsoever resulting from loss of use, data or profits, whether 
  26   in an action of contract, negligence or other tortious action, 
  27   arising out of or in connection with the use or performance of 
  33  *  Virtualized FAT12 filesystem implementation, to perform self-programming 
  34  *  in response to read and write requests to the virtual filesystem by the 
  38 #define  INCLUDE_FROM_VIRTUAL_FAT_C 
  39 #include "VirtualFAT.h" 
  41 /** FAT filesystem boot sector block, must be the first sector on the physical 
  42  *  disk so that the host can identify the presence of a FAT filesystem. This 
  43  *  block is truncated; normally a large bootstrap section is located near the 
  44  *  end of the block for booting purposes however as this is not meant to be a 
  45  *  bootable disk it is omitted for space reasons. 
  47  *  \note When returning the boot block to the host, the magic signature 0xAA55 
  48  *        must be added to the very end of the block to identify it as a boot 
  51 static const FATBootBlock_t BootBlock 
= 
  53                 .Bootstrap               
= {0xEB, 0x3C, 0x90}, 
  54                 .Description             
= "mkdosfs", 
  55                 .SectorSize              
= SECTOR_SIZE_BYTES
, 
  56                 .SectorsPerCluster       
= SECTOR_PER_CLUSTER
, 
  59                 .RootDirectoryEntries    
= (SECTOR_SIZE_BYTES 
/ sizeof(FATDirectoryEntry_t
)), 
  60                 .TotalSectors16          
= LUN_MEDIA_BLOCKS
, 
  61                 .MediaDescriptor         
= 0xF8, 
  63                 .SectorsPerTrack         
= (LUN_MEDIA_BLOCKS 
% 64), 
  64                 .Heads                   
= (LUN_MEDIA_BLOCKS 
/ 64), 
  67                 .PhysicalDriveNum        
= 0, 
  68                 .ExtendedBootRecordSig   
= 0x29, 
  69                 .VolumeSerialNumber      
= 0x12345678, 
  70                 .VolumeLabel             
= "LUFA BOOT  ", 
  71                 .FilesystemIdentifier    
= "FAT12   ", 
  74 /** FAT 8.3 style directory entry, for the virtual FLASH contents file. */ 
  75 static FATDirectoryEntry_t FirmwareFileEntries
[] = 
  77                 /* Root volume label entry; disk label is contained in the Filename and 
  78                  * Extension fields (concatenated) with a special attribute flag - other 
  79                  * fields are ignored. Should be the same as the label in the boot block. 
  81                 [DISK_FILE_ENTRY_VolumeID
] = 
  86                                         .Attributes      
= FAT_FLAG_VOLUME_NAME
, 
  95                 /* VFAT Long File Name entry for the virtual firmware file; required to 
  96                  * prevent corruption from systems that are unable to detect the device 
  97                  * as being a legacy MSDOS style FAT12 volume. */ 
  98                 [DISK_FILE_ENTRY_FLASH_LFN
] = 
 102                                         .Ordinal         
= 1 | FAT_ORDINAL_LAST_ENTRY
, 
 103                                         .Attribute       
= FAT_FLAG_LONG_FILE_NAME
, 
 107                                         .Checksum        
= FAT_CHECKSUM('F','L','A','S','H',' ',' ',' ','B','I','N'), 
 125                 /* MSDOS file entry for the virtual Firmware image. */ 
 126                 [DISK_FILE_ENTRY_FLASH_MSDOS
] = 
 130                                         .Filename        
= "FLASH   ", 
 134                                         .CreationTime    
= FAT_TIME(1, 1, 0), 
 135                                         .CreationDate    
= FAT_DATE(14, 2, 1989), 
 136                                         .StartingCluster 
= 2, 
 137                                         .FileSizeBytes   
= FLASH_FILE_SIZE_BYTES
, 
 141                 [DISK_FILE_ENTRY_EEPROM_LFN
] = 
 145                                         .Ordinal         
= 1 | FAT_ORDINAL_LAST_ENTRY
, 
 146                                         .Attribute       
= FAT_FLAG_LONG_FILE_NAME
, 
 150                                         .Checksum        
= FAT_CHECKSUM('E','E','P','R','O','M',' ',' ','B','I','N'), 
 168                 [DISK_FILE_ENTRY_EEPROM_MSDOS
] = 
 172                                         .Filename        
= "EEPROM  ", 
 176                                         .CreationTime    
= FAT_TIME(1, 1, 0), 
 177                                         .CreationDate    
= FAT_DATE(14, 2, 1989), 
 178                                         .StartingCluster 
= 2 + FILE_CLUSTERS(FLASH_FILE_SIZE_BYTES
), 
 179                                         .FileSizeBytes   
= EEPROM_FILE_SIZE_BYTES
, 
 184 /** Starting cluster of the virtual FLASH.BIN file on disk, tracked so that the 
 185  *  offset from the start of the data sector can be determined. On Windows 
 186  *  systems files are usually replaced using the original file's disk clusters, 
 187  *  while Linux appears to overwrite with an offset which must be compensated for. 
 189 static const uint16_t* FLASHFileStartCluster  
= &FirmwareFileEntries
[DISK_FILE_ENTRY_FLASH_MSDOS
].MSDOS_File
.StartingCluster
; 
 191 /** Starting cluster of the virtual EEPROM.BIN file on disk, tracked so that the 
 192  *  offset from the start of the data sector can be determined. On Windows 
 193  *  systems files are usually replaced using the original file's disk clusters, 
 194  *  while Linux appears to overwrite with an offset which must be compensated for. 
 196 static const uint16_t* EEPROMFileStartCluster 
= &FirmwareFileEntries
[DISK_FILE_ENTRY_EEPROM_MSDOS
].MSDOS_File
.StartingCluster
; 
 198 /** Reads a byte of EEPROM out from the EEPROM memory space. 
 200  *  \note This function is required as the avr-libc EEPROM functions do not cope 
 201  *        with linker relaxations, and a jump longer than 4K of FLASH on the 
 202  *        larger USB AVRs will break the linker. This function is marked as 
 203  *        never inlinable and placed into the normal text segment so that the 
 204  *        call to the EEPROM function will be short even if the AUX boot section 
 207  *  \param[in]  Address   Address of the EEPROM location to read from 
 209  *  \return Read byte of EEPROM data. 
 211 static uint8_t ReadEEPROMByte(const uint8_t* const Address
) 
 213         return eeprom_read_byte(Address
); 
 216 /** Writes a byte of EEPROM out to the EEPROM memory space. 
 218  *  \note This function is required as the avr-libc EEPROM functions do not cope 
 219  *        with linker relaxations, and a jump longer than 4K of FLASH on the 
 220  *        larger USB AVRs will break the linker. This function is marked as 
 221  *        never inlinable and placed into the normal text segment so that the 
 222  *        call to the EEPROM function will be short even if the AUX boot section 
 225  *  \param[in]  Address   Address of the EEPROM location to write to 
 226  *  \param[in]  Data      New data to write to the EEPROM location 
 228 static void WriteEEPROMByte(uint8_t* const Address
, 
 231          eeprom_update_byte(Address
, Data
); 
 234 /** Updates a FAT12 cluster entry in the FAT file table with the specified next 
 235  *  chain index. If the cluster is the last in the file chain, the magic value 
 236  *  \c 0xFFF should be used. 
 238  *  \note FAT data cluster indexes are offset by 2, so that cluster 2 is the 
 239  *        first file data cluster on the disk. See the FAT specification. 
 241  *  \param[out]  FATTable    Pointer to the FAT12 allocation table 
 242  *  \param[in]   Index       Index of the cluster entry to update 
 243  *  \param[in]   ChainEntry  Next cluster index in the file chain 
 245 static void UpdateFAT12ClusterEntry(uint8_t* const FATTable
, 
 246                                     const uint16_t Index
, 
 247                                     const uint16_t ChainEntry
) 
 249         /* Calculate the starting offset of the cluster entry in the FAT12 table */ 
 250         uint8_t FATOffset   
= (Index 
+ (Index 
>> 1)); 
 251         bool    UpperNibble 
= ((Index 
& 1) != 0); 
 253         /* Check if the start of the entry is at an upper nibble of the byte, fill 
 254          * out FAT12 entry as required */ 
 257                 FATTable
[FATOffset
]     = (FATTable
[FATOffset
] & 0x0F) | ((ChainEntry 
& 0x0F) << 4); 
 258                 FATTable
[FATOffset 
+ 1] = (ChainEntry 
>> 4); 
 262                 FATTable
[FATOffset
]     = ChainEntry
; 
 263                 FATTable
[FATOffset 
+ 1] = (FATTable
[FATOffset 
+ 1] & 0xF0) | (ChainEntry 
>> 8); 
 267 /** Updates a FAT12 cluster chain in the FAT file table with a linear chain of 
 268  *  the specified length. 
 270  *  \note FAT data cluster indexes are offset by 2, so that cluster 2 is the 
 271  *        first file data cluster on the disk. See the FAT specification. 
 273  *  \param[out]  FATTable     Pointer to the FAT12 allocation table 
 274  *  \param[in]   Index        Index of the start of the cluster chain to update 
 275  *  \param[in]   ChainLength  Length of the chain to write, in clusters 
 277 static void UpdateFAT12ClusterChain(uint8_t* const FATTable
, 
 278                                     const uint16_t Index
, 
 279                                     const uint8_t ChainLength
) 
 281         for (uint8_t i 
= 0; i 
< ChainLength
; i
++) 
 283                 uint16_t CurrentCluster 
= Index 
+ i
; 
 284                 uint16_t NextCluster    
= CurrentCluster 
+ 1; 
 286                 /* Mark last cluster as end of file */ 
 287                 if (i 
== (ChainLength 
- 1)) 
 290                 UpdateFAT12ClusterEntry(FATTable
, CurrentCluster
, NextCluster
); 
 294 /** Reads or writes a block of data from/to the physical device FLASH using a 
 295  *  block buffer stored in RAM, if the requested block is within the virtual 
 296  *  firmware file's sector ranges in the emulated FAT file system. 
 298  *  \param[in]      BlockNumber  Physical disk block to read from/write to 
 299  *  \param[in,out]  BlockBuffer  Pointer to the start of the block buffer in RAM 
 300  *  \param[in]      Read         If \c true, the requested block is read, if 
 301  *                               \c false, the requested block is written 
 303 static void ReadWriteFLASHFileBlock(const uint16_t BlockNumber
, 
 304                                     uint8_t* BlockBuffer
, 
 307         uint16_t FileStartBlock 
= DISK_BLOCK_DataStartBlock 
+ (*FLASHFileStartCluster 
- 2) * SECTOR_PER_CLUSTER
; 
 308         uint16_t FileEndBlock   
= FileStartBlock 
+ (FILE_SECTORS(FLASH_FILE_SIZE_BYTES
) - 1); 
 310         /* Range check the write request - abort if requested block is not within the 
 311          * virtual firmware file sector range */ 
 312         if (!((BlockNumber 
>= FileStartBlock
) && (BlockNumber 
<= FileEndBlock
))) 
 315         #if (FLASHEND > 0xFFFF) 
 316         uint32_t FlashAddress 
= (uint32_t)(BlockNumber 
- FileStartBlock
) * SECTOR_SIZE_BYTES
; 
 318         uint16_t FlashAddress 
= (uint16_t)(BlockNumber 
- FileStartBlock
) * SECTOR_SIZE_BYTES
; 
 323                 /* Read out the mapped block of data from the device's FLASH */ 
 324                 for (uint16_t i 
= 0; i 
< SECTOR_SIZE_BYTES
; i
++) 
 326                         #if (FLASHEND > 0xFFFF) 
 327                           BlockBuffer
[i
] = pgm_read_byte_far(FlashAddress
++); 
 329                           BlockBuffer
[i
] = pgm_read_byte(FlashAddress
++); 
 335                 /* Write out the mapped block of data to the device's FLASH */ 
 336                 for (uint16_t i 
= 0; i 
< SECTOR_SIZE_BYTES
; i 
+= 2) 
 338                         if ((FlashAddress 
% SPM_PAGESIZE
) == 0) 
 340                                 /* Erase the given FLASH page, ready to be programmed */ 
 341                                 BootloaderAPI_ErasePage(FlashAddress
); 
 344                         /* Write the next data word to the FLASH page */ 
 345                         BootloaderAPI_FillWord(FlashAddress
, (BlockBuffer
[i 
+ 1] << 8) | BlockBuffer
[i
]); 
 348                         if ((FlashAddress 
% SPM_PAGESIZE
) == 0) 
 350                                 /* Write the filled FLASH page to memory */ 
 351                                 BootloaderAPI_WritePage(FlashAddress 
- SPM_PAGESIZE
); 
 357 /** Reads or writes a block of data from/to the physical device EEPROM using a 
 358  *  block buffer stored in RAM, if the requested block is within the virtual 
 359  *  firmware file's sector ranges in the emulated FAT file system. 
 361  *  \param[in]      BlockNumber  Physical disk block to read from/write to 
 362  *  \param[in,out]  BlockBuffer  Pointer to the start of the block buffer in RAM 
 363  *  \param[in]      Read         If \c true, the requested block is read, if 
 364  *                               \c false, the requested block is written 
 366 static void ReadWriteEEPROMFileBlock(const uint16_t BlockNumber
, 
 367                                      uint8_t* BlockBuffer
, 
 370         uint16_t FileStartBlock 
= DISK_BLOCK_DataStartBlock 
+ (*EEPROMFileStartCluster 
- 2) * SECTOR_PER_CLUSTER
; 
 371         uint16_t FileEndBlock   
= FileStartBlock 
+ (FILE_SECTORS(EEPROM_FILE_SIZE_BYTES
) - 1); 
 373         /* Range check the write request - abort if requested block is not within the 
 374          * virtual firmware file sector range */ 
 375         if (!((BlockNumber 
>= FileStartBlock
) && (BlockNumber 
<= FileEndBlock
))) 
 378         uint16_t EEPROMAddress 
= (uint16_t)(BlockNumber 
- FileStartBlock
) * SECTOR_SIZE_BYTES
; 
 382                 /* Read out the mapped block of data from the device's EEPROM */ 
 383                 for (uint16_t i 
= 0; i 
< SECTOR_SIZE_BYTES
; i
++) 
 384                   BlockBuffer
[i
] = ReadEEPROMByte((uint8_t*)EEPROMAddress
++); 
 388                 /* Write out the mapped block of data to the device's EEPROM */ 
 389                 for (uint16_t i 
= 0; i 
< SECTOR_SIZE_BYTES
; i
++) 
 390                   WriteEEPROMByte((uint8_t*)EEPROMAddress
++, BlockBuffer
[i
]); 
 394 /** Writes a block of data to the virtual FAT filesystem, from the USB Mass 
 397  *  \param[in]  BlockNumber  Index of the block to write. 
 399 void VirtualFAT_WriteBlock(const uint16_t BlockNumber
) 
 401         uint8_t BlockBuffer
[SECTOR_SIZE_BYTES
]; 
 403         /* Buffer the entire block to be written from the host */ 
 404         Endpoint_Read_Stream_LE(BlockBuffer
, sizeof(BlockBuffer
), NULL
); 
 409                 case DISK_BLOCK_BootBlock
: 
 410                 case DISK_BLOCK_FATBlock1
: 
 411                 case DISK_BLOCK_FATBlock2
: 
 412                         /* Ignore writes to the boot and FAT blocks */ 
 416                 case DISK_BLOCK_RootFilesBlock
: 
 417                         /* Copy over the updated directory entries */ 
 418                         memcpy(FirmwareFileEntries
, BlockBuffer
, sizeof(FirmwareFileEntries
)); 
 423                         ReadWriteFLASHFileBlock(BlockNumber
, BlockBuffer
, false); 
 424                         ReadWriteEEPROMFileBlock(BlockNumber
, BlockBuffer
, false); 
 430 /** Reads a block of data from the virtual FAT filesystem, and sends it to the 
 431  *  host via the USB Mass Storage interface. 
 433  *  \param[in]  BlockNumber  Index of the block to read. 
 435 void VirtualFAT_ReadBlock(const uint16_t BlockNumber
) 
 437         uint8_t BlockBuffer
[SECTOR_SIZE_BYTES
]; 
 438         memset(BlockBuffer
, 0x00, sizeof(BlockBuffer
)); 
 442                 case DISK_BLOCK_BootBlock
: 
 443                         memcpy(BlockBuffer
, &BootBlock
, sizeof(FATBootBlock_t
)); 
 445                         /* Add the magic signature to the end of the block */ 
 446                         BlockBuffer
[SECTOR_SIZE_BYTES 
- 2] = 0x55; 
 447                         BlockBuffer
[SECTOR_SIZE_BYTES 
- 1] = 0xAA; 
 451                 case DISK_BLOCK_FATBlock1
: 
 452                 case DISK_BLOCK_FATBlock2
: 
 453                         /* Cluster 0: Media type/Reserved */ 
 454                         UpdateFAT12ClusterEntry(BlockBuffer
, 0, 0xF00 | BootBlock
.MediaDescriptor
); 
 456                         /* Cluster 1: Reserved */ 
 457                         UpdateFAT12ClusterEntry(BlockBuffer
, 1, 0xFFF); 
 459                         /* Cluster 2 onwards: Cluster chain of FLASH.BIN */ 
 460                         UpdateFAT12ClusterChain(BlockBuffer
, *FLASHFileStartCluster
, FILE_CLUSTERS(FLASH_FILE_SIZE_BYTES
)); 
 462                         /* Cluster 2+n onwards: Cluster chain of EEPROM.BIN */ 
 463                         UpdateFAT12ClusterChain(BlockBuffer
, *EEPROMFileStartCluster
, FILE_CLUSTERS(EEPROM_FILE_SIZE_BYTES
)); 
 467                 case DISK_BLOCK_RootFilesBlock
: 
 468                         memcpy(BlockBuffer
, FirmwareFileEntries
, sizeof(FirmwareFileEntries
)); 
 473                         ReadWriteFLASHFileBlock(BlockNumber
, BlockBuffer
, true); 
 474                         ReadWriteEEPROMFileBlock(BlockNumber
, BlockBuffer
, true); 
 479         /* Write the entire read block Buffer to the host */ 
 480         Endpoint_Write_Stream_LE(BlockBuffer
, sizeof(BlockBuffer
), NULL
);