mirror of
				https://github.com/mfulz/qmk_firmware.git
				synced 2025-10-30 21:02:32 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			313 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			313 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|              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 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.
 | |
| 		 */
 | |
| 		{
 | |
| 			.MSDOS_Directory =
 | |
| 				{
 | |
| 					.Name            = "LUFA BOOT  ",
 | |
| 					.Attributes      = FAT_FLAG_VOLUME_NAME,
 | |
| 					.Reserved        = {0},
 | |
| 					.CreationTime    = 0,
 | |
| 					.CreationDate    = 0,
 | |
| 					.StartingCluster = 0,
 | |
| 					.Reserved2       = 0,
 | |
| 				}
 | |
| 		},
 | |
| 
 | |
| 		/* 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. */
 | |
| 		{
 | |
| 			.VFAT_LongFileName =
 | |
| 				{
 | |
| 					.Ordinal         = FAT_ORDINAL_LAST_ENTRY | 1,
 | |
| 					.Attribute       = FAT_FLAG_LONG_FILE_NAME,
 | |
| 					.Reserved1       = 0,
 | |
| 					.Reserved2       = 0,
 | |
| 
 | |
| 					.Checksum        = 0x57,
 | |
| 
 | |
| 					.Unicode1        = 'F',
 | |
| 					.Unicode2        = 'I',
 | |
| 					.Unicode3        = 'R',
 | |
| 					.Unicode4        = 'M',
 | |
| 					.Unicode5        = 'W',
 | |
| 					.Unicode6        = 'A',
 | |
| 					.Unicode7        = 'R',
 | |
| 					.Unicode8        = 'E',
 | |
| 					.Unicode9        = '.',
 | |
| 					.Unicode10       = 'B',
 | |
| 					.Unicode11       = 'I',
 | |
| 					.Unicode12       = 'N',
 | |
| 					.Unicode13       = 0,
 | |
| 				}
 | |
| 		},
 | |
| 
 | |
| 		/* MSDOS file entry for the virtual Firmware image. */
 | |
| 		{
 | |
| 			.MSDOS_File =
 | |
| 				{
 | |
| 					.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 + (Index >> 1));
 | |
| 	bool    UpperNibble = ((Index & 1) != 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))))
 | |
| 	{
 | |
| 		#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
 | |
| 
 | |
| 		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 */
 | |
| 				BootloaderAPI_ErasePage(WriteFlashAddress);
 | |
| 			}
 | |
| 
 | |
| 			/* Write the next data word to the FLASH page */
 | |
| 			BootloaderAPI_FillWord(WriteFlashAddress, (BlockBuffer[i + 1] << 8) | BlockBuffer[i]);
 | |
| 			WriteFlashAddress += 2;
 | |
| 
 | |
| 			if ((WriteFlashAddress % SPM_PAGESIZE) == 0)
 | |
| 			{
 | |
| 				/* Write the filled FLASH page to memory */
 | |
| 				BootloaderAPI_WritePage(WriteFlashAddress - SPM_PAGESIZE);
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /** 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, 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
 | |
| 			}
 | |
| 
 | |
| 			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] 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++);
 | |
| }
 | |
| 
 | 
