1
0
Fork 0

Squashed 'tmk_core/' changes from caca2c0..dc0e46e

dc0e46e Rename LUFA to LUFA-git
3bfa7fa Remove LUFA-120730
215b764 Merge commit 'afa0f22a9299686fd88f58ce09c5b521ac917e8f' as 'protocol/lufa/LUFA'
afa0f22 Squashed 'protocol/lufa/LUFA/' content from commit def7fca
c0c42fa Remove submodule of LUFA
30f897d Merge commit '87ced33feb74e79c3281dda36eb6d6d153399b41' as 'protocol/usb_hid/USB_Host_Shield_2.0'
87ced33 Squashed 'protocol/usb_hid/USB_Host_Shield_2.0/' content from commit aab4a69
14f6d49 Remove submodule of USB_Host_Shield_2.0

git-subtree-dir: tmk_core
git-subtree-split: dc0e46eaa4367d4e218f8816e3c117895820f07c
This commit is contained in:
tmk 2015-05-13 11:13:10 +09:00
parent 4d116a04e9
commit f6d56675f9
1575 changed files with 421901 additions and 63190 deletions

View file

@ -0,0 +1,294 @@
/*
LUFA Library
Copyright (C) Dean Camera, 2014.
dean [at] fourwalledcubicle [dot] com
www.lufa-lib.org
*/
/*
Copyright 2014 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 \c true if the command completed successfully, \c 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_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:
#if !defined(NO_APP_START_ON_EJECT)
/* If the user ejected the volume, signal bootloader exit at next opportunity. */
RunBootloader = ((MSInterfaceInfo->State.CommandBlock.SCSICommandData[4] & 0x03) != 0x02);
#endif
case SCSI_CMD_SEND_DIAGNOSTIC:
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 \c true if the command completed successfully, \c 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 \c true if the command completed successfully, \c 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 \c true if the command completed successfully, \c false otherwise.
*/
static bool SCSI_Command_Read_Capacity_10(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo)
{
Endpoint_Write_32_BE(LUN_MEDIA_BLOCKS - 1);
Endpoint_Write_32_BE(SECTOR_SIZE_BYTES);
Endpoint_ClearIN();
/* Succeed the command and update the bytes transferred counter */
MSInterfaceInfo->State.CommandBlock.DataTransferLength -= 8;
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 \c true if the command completed successfully, \c false otherwise.
*/
static bool SCSI_Command_ReadWrite_10(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo,
const bool IsDataRead)
{
uint16_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 */
for (uint16_t i = 0; i < TotalBlocks; i++)
{
if (IsDataRead == DATA_READ)
VirtualFAT_ReadBlock(BlockAddress + i);
else
VirtualFAT_WriteBlock(BlockAddress + i);
}
/* 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 \c true if the command completed successfully, \c 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;
}

View file

@ -0,0 +1,84 @@
/*
LUFA Library
Copyright (C) Dean Camera, 2014.
dean [at] fourwalledcubicle [dot] com
www.lufa-lib.org
*/
/*
Copyright 2014 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) do { SenseData.SenseKey = (Key); \
SenseData.AdditionalSenseCode = (Acode); \
SenseData.AdditionalSenseQualifier = (Aqual); } while (0)
/** 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) AUX_BOOT_SECTION;
#if defined(INCLUDE_FROM_SCSI_C)
static bool SCSI_Command_Inquiry(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo) AUX_BOOT_SECTION;
static bool SCSI_Command_Request_Sense(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo) AUX_BOOT_SECTION;
static bool SCSI_Command_Read_Capacity_10(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo) AUX_BOOT_SECTION;
static bool SCSI_Command_ReadWrite_10(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo,
const bool IsDataRead) AUX_BOOT_SECTION;
static bool SCSI_Command_ModeSense_6(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo) AUX_BOOT_SECTION;
#endif
#endif

View file

@ -0,0 +1,482 @@
/*
LUFA Library
Copyright (C) Dean Camera, 2014.
dean [at] fourwalledcubicle [dot] com
www.lufa-lib.org
*/
/*
Copyright 2014 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.
*/
[DISK_FILE_ENTRY_VolumeID] =
{
.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. */
[DISK_FILE_ENTRY_FLASH_LFN] =
{
.VFAT_LongFileName =
{
.Ordinal = 1 | FAT_ORDINAL_LAST_ENTRY,
.Attribute = FAT_FLAG_LONG_FILE_NAME,
.Reserved1 = 0,
.Reserved2 = 0,
.Checksum = FAT_CHECKSUM('F','L','A','S','H',' ',' ',' ','B','I','N'),
.Unicode1 = 'F',
.Unicode2 = 'L',
.Unicode3 = 'A',
.Unicode4 = 'S',
.Unicode5 = 'H',
.Unicode6 = '.',
.Unicode7 = 'B',
.Unicode8 = 'I',
.Unicode9 = 'N',
.Unicode10 = 0,
.Unicode11 = 0,
.Unicode12 = 0,
.Unicode13 = 0,
}
},
/* MSDOS file entry for the virtual Firmware image. */
[DISK_FILE_ENTRY_FLASH_MSDOS] =
{
.MSDOS_File =
{
.Filename = "FLASH ",
.Extension = "BIN",
.Attributes = 0,
.Reserved = {0},
.CreationTime = FAT_TIME(1, 1, 0),
.CreationDate = FAT_DATE(14, 2, 1989),
.StartingCluster = 2,
.FileSizeBytes = FLASH_FILE_SIZE_BYTES,
}
},
[DISK_FILE_ENTRY_EEPROM_LFN] =
{
.VFAT_LongFileName =
{
.Ordinal = 1 | FAT_ORDINAL_LAST_ENTRY,
.Attribute = FAT_FLAG_LONG_FILE_NAME,
.Reserved1 = 0,
.Reserved2 = 0,
.Checksum = FAT_CHECKSUM('E','E','P','R','O','M',' ',' ','B','I','N'),
.Unicode1 = 'E',
.Unicode2 = 'E',
.Unicode3 = 'P',
.Unicode4 = 'R',
.Unicode5 = 'O',
.Unicode6 = 'M',
.Unicode7 = '.',
.Unicode8 = 'B',
.Unicode9 = 'I',
.Unicode10 = 'N',
.Unicode11 = 0,
.Unicode12 = 0,
.Unicode13 = 0,
}
},
[DISK_FILE_ENTRY_EEPROM_MSDOS] =
{
.MSDOS_File =
{
.Filename = "EEPROM ",
.Extension = "BIN",
.Attributes = 0,
.Reserved = {0},
.CreationTime = FAT_TIME(1, 1, 0),
.CreationDate = FAT_DATE(14, 2, 1989),
.StartingCluster = 2 + FILE_CLUSTERS(FLASH_FILE_SIZE_BYTES),
.FileSizeBytes = EEPROM_FILE_SIZE_BYTES,
}
},
};
/** Starting cluster of the virtual FLASH.BIN file on disk, tracked so that the
* offset from the start of the data sector can be determined. On Windows
* systems files are usually replaced using the original file's disk clusters,
* while Linux appears to overwrite with an offset which must be compensated for.
*/
static const uint16_t* FLASHFileStartCluster = &FirmwareFileEntries[DISK_FILE_ENTRY_FLASH_MSDOS].MSDOS_File.StartingCluster;
/** Starting cluster of the virtual EEPROM.BIN file on disk, tracked so that the
* offset from the start of the data sector can be determined. On Windows
* systems files are usually replaced using the original file's disk clusters,
* while Linux appears to overwrite with an offset which must be compensated for.
*/
static const uint16_t* EEPROMFileStartCluster = &FirmwareFileEntries[DISK_FILE_ENTRY_EEPROM_MSDOS].MSDOS_File.StartingCluster;
/** Reads a byte of EEPROM out from the EEPROM memory space.
*
* \note This function is required as the avr-libc EEPROM functions do not cope
* with linker relaxations, and a jump longer than 4K of FLASH on the
* larger USB AVRs will break the linker. This function is marked as
* never inlinable and placed into the normal text segment so that the
* call to the EEPROM function will be short even if the AUX boot section
* is used.
*
* \param[in] Address Address of the EEPROM location to read from
*
* \return Read byte of EEPROM data.
*/
static uint8_t ReadEEPROMByte(const uint8_t* const Address)
{
return eeprom_read_byte(Address);
}
/** Writes a byte of EEPROM out to the EEPROM memory space.
*
* \note This function is required as the avr-libc EEPROM functions do not cope
* with linker relaxations, and a jump longer than 4K of FLASH on the
* larger USB AVRs will break the linker. This function is marked as
* never inlinable and placed into the normal text segment so that the
* call to the EEPROM function will be short even if the AUX boot section
* is used.
*
* \param[in] Address Address of the EEPROM location to write to
* \param[in] Data New data to write to the EEPROM location
*/
static void WriteEEPROMByte(uint8_t* const Address,
const uint8_t Data)
{
eeprom_update_byte(Address, Data);
}
/** 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
* \c 0xFFF should be 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);
}
}
/** Updates a FAT12 cluster chain in the FAT file table with a linear chain of
* the specified length.
*
* \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 start of the cluster chain to update
* \param[in] ChainLength Length of the chain to write, in clusters
*/
static void UpdateFAT12ClusterChain(uint8_t* const FATTable,
const uint16_t Index,
const uint8_t ChainLength)
{
for (uint8_t i = 0; i < ChainLength; i++)
{
uint16_t CurrentCluster = Index + i;
uint16_t NextCluster = CurrentCluster + 1;
/* Mark last cluster as end of file */
if (i == (ChainLength - 1))
NextCluster = 0xFFF;
UpdateFAT12ClusterEntry(FATTable, CurrentCluster, NextCluster);
}
}
/** 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 Physical disk block to read from/write to
* \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 ReadWriteFLASHFileBlock(const uint16_t BlockNumber,
uint8_t* BlockBuffer,
const bool Read)
{
uint16_t FileStartBlock = DISK_BLOCK_DataStartBlock + (*FLASHFileStartCluster - 2) * SECTOR_PER_CLUSTER;
uint16_t FileEndBlock = FileStartBlock + (FILE_SECTORS(FLASH_FILE_SIZE_BYTES) - 1);
/* Range check the write request - abort if requested block is not within the
* virtual firmware file sector range */
if (!((BlockNumber >= FileStartBlock) && (BlockNumber <= FileEndBlock)))
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)
{
/* 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 ((FlashAddress % SPM_PAGESIZE) == 0)
{
/* Erase the given FLASH page, ready to be programmed */
BootloaderAPI_ErasePage(FlashAddress);
}
/* Write the next data word to the FLASH page */
BootloaderAPI_FillWord(FlashAddress, (BlockBuffer[i + 1] << 8) | BlockBuffer[i]);
FlashAddress += 2;
if ((FlashAddress % SPM_PAGESIZE) == 0)
{
/* Write the filled FLASH page to memory */
BootloaderAPI_WritePage(FlashAddress - SPM_PAGESIZE);
}
}
}
}
/** Reads or writes a block of data from/to the physical device EEPROM 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 Physical disk block to read from/write to
* \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 ReadWriteEEPROMFileBlock(const uint16_t BlockNumber,
uint8_t* BlockBuffer,
const bool Read)
{
uint16_t FileStartBlock = DISK_BLOCK_DataStartBlock + (*EEPROMFileStartCluster - 2) * SECTOR_PER_CLUSTER;
uint16_t FileEndBlock = FileStartBlock + (FILE_SECTORS(EEPROM_FILE_SIZE_BYTES) - 1);
/* Range check the write request - abort if requested block is not within the
* virtual firmware file sector range */
if (!((BlockNumber >= FileStartBlock) && (BlockNumber <= FileEndBlock)))
return;
uint16_t EEPROMAddress = (uint16_t)(BlockNumber - FileStartBlock) * SECTOR_SIZE_BYTES;
if (Read)
{
/* Read out the mapped block of data from the device's EEPROM */
for (uint16_t i = 0; i < SECTOR_SIZE_BYTES; i++)
BlockBuffer[i] = ReadEEPROMByte((uint8_t*)EEPROMAddress++);
}
else
{
/* Write out the mapped block of data to the device's EEPROM */
for (uint16_t i = 0; i < SECTOR_SIZE_BYTES; i++)
WriteEEPROMByte((uint8_t*)EEPROMAddress++, BlockBuffer[i]);
}
}
/** 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();
switch (BlockNumber)
{
case DISK_BLOCK_BootBlock:
case DISK_BLOCK_FATBlock1:
case DISK_BLOCK_FATBlock2:
/* Ignore writes to the boot and FAT blocks */
break;
case DISK_BLOCK_RootFilesBlock:
/* Copy over the updated directory entries */
memcpy(FirmwareFileEntries, BlockBuffer, sizeof(FirmwareFileEntries));
break;
default:
ReadWriteFLASHFileBlock(BlockNumber, BlockBuffer, false);
ReadWriteEEPROMFileBlock(BlockNumber, BlockBuffer, false);
break;
}
}
/** 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.
*/
void VirtualFAT_ReadBlock(const uint16_t BlockNumber)
{
uint8_t BlockBuffer[SECTOR_SIZE_BYTES];
memset(BlockBuffer, 0x00, sizeof(BlockBuffer));
switch (BlockNumber)
{
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 DISK_BLOCK_FATBlock1:
case DISK_BLOCK_FATBlock2:
/* Cluster 0: Media type/Reserved */
UpdateFAT12ClusterEntry(BlockBuffer, 0, 0xF00 | BootBlock.MediaDescriptor);
/* Cluster 1: Reserved */
UpdateFAT12ClusterEntry(BlockBuffer, 1, 0xFFF);
/* Cluster 2 onwards: Cluster chain of FLASH.BIN */
UpdateFAT12ClusterChain(BlockBuffer, *FLASHFileStartCluster, FILE_CLUSTERS(FLASH_FILE_SIZE_BYTES));
/* Cluster 2+n onwards: Cluster chain of EEPROM.BIN */
UpdateFAT12ClusterChain(BlockBuffer, *EEPROMFileStartCluster, FILE_CLUSTERS(EEPROM_FILE_SIZE_BYTES));
break;
case DISK_BLOCK_RootFilesBlock:
memcpy(BlockBuffer, FirmwareFileEntries, sizeof(FirmwareFileEntries));
break;
default:
ReadWriteFLASHFileBlock(BlockNumber, BlockBuffer, true);
ReadWriteEEPROMFileBlock(BlockNumber, BlockBuffer, true);
break;
}
/* Write the entire read block Buffer to the host */
Endpoint_Write_Stream_LE(BlockBuffer, sizeof(BlockBuffer), NULL);
Endpoint_ClearIN();
}

View file

@ -0,0 +1,302 @@
/*
LUFA Library
Copyright (C) Dean Camera, 2014.
dean [at] fourwalledcubicle [dot] com
www.lufa-lib.org
*/
/*
Copyright 2014 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>
#include "../BootloaderAPI.h"
/* Macros: */
/** Size of the virtual FLASH.BIN file in bytes. */
#define FLASH_FILE_SIZE_BYTES (FLASHEND - (FLASHEND - BOOT_START_ADDR) - AUX_BOOT_SECTION_SIZE)
/** Size of the virtual EEPROM.BIN file in bytes. */
#define EEPROM_FILE_SIZE_BYTES E2END
/** 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(FLASH_FILE_SIZE_BYTES) + FILE_SECTORS(EEPROM_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))
/** Bit-rotates a given 8-bit value once to the right.
*
* \param x Value to rotate right once
*
* \return Bit-rotated input value, rotated once to the right.
*/
#define _ROT8(x) ((((x) & 0xFE) >> 1) | (((x) & 1) ? 0x80 : 0x00))
/** Computes the LFN entry checksum of a MSDOS 8.3 format file entry,
* to associate a LFN entry with its short file entry.
*
* \param n0 MSDOS Filename character 1
* \param n1 MSDOS Filename character 2
* \param n2 MSDOS Filename character 3
* \param n3 MSDOS Filename character 4
* \param n4 MSDOS Filename character 5
* \param n5 MSDOS Filename character 6
* \param n6 MSDOS Filename character 7
* \param n7 MSDOS Filename character 8
* \param e0 MSDOS Extension character 1
* \param e1 MSDOS Extension character 2
* \param e2 MSDOS Extension character 3
*
* \return LFN checksum of the given MSDOS 8.3 filename.
*/
#define FAT_CHECKSUM(n0, n1, n2, n3, n4, n5, n6, n7, e0, e1, e2) \
(uint8_t)(_ROT8(_ROT8(_ROT8(_ROT8(_ROT8(_ROT8(_ROT8(_ROT8(_ROT8(_ROT8(n0)+n1)+n2)+n3)+n4)+n5)+n6)+n7)+e0)+e1)+e2)
/** \name FAT Filesystem Flags */
//@{
/** FAT attribute flag to indicate a read-only file. */
#define FAT_FLAG_READONLY (1 << 0)
/** FAT attribute flag to indicate a hidden file. */
#define FAT_FLAG_HIDDEN (1 << 1)
/** FAT attribute flag to indicate a system file. */
#define FAT_FLAG_SYSTEM (1 << 2)
/** FAT attribute flag to indicate a Volume name entry. */
#define FAT_FLAG_VOLUME_NAME (1 << 3)
/** FAT attribute flag to indicate a directory entry. */
#define FAT_FLAG_DIRECTORY (1 << 4)
/** FAT attribute flag to indicate a file ready for archiving. */
#define FAT_FLAG_ARCHIVE (1 << 5)
/** FAT pseudo-attribute flag to indicate a Long File Name entry. */
#define FAT_FLAG_LONG_FILE_NAME 0x0F
/** Ordinal flag marker for FAT Long File Name entries to mark the last entry. */
#define FAT_ORDINAL_LAST_ENTRY (1 << 6)
//@}
/* Enums: */
/** Enum for the Root FAT file entry indexes on the disk. This can be used
* to retrieve the current contents of a known directory entry.
*/
enum
{
/** Volume ID directory entry, giving the name of the virtual disk. */
DISK_FILE_ENTRY_VolumeID = 0,
/** Long File Name FAT file entry of the virtual FLASH.BIN image file. */
DISK_FILE_ENTRY_FLASH_LFN = 1,
/** Legacy MSDOS FAT file entry of the virtual FLASH.BIN image file. */
DISK_FILE_ENTRY_FLASH_MSDOS = 2,
/** Long File Name FAT file entry of the virtual EEPROM.BIN image file. */
DISK_FILE_ENTRY_EEPROM_LFN = 3,
/** Legacy MSDOS FAT file entry of the virtual EEPROM.BIN image file. */
DISK_FILE_ENTRY_EEPROM_MSDOS = 4,
};
/** Enum for the physical disk blocks of the virtual disk. */
enum
{
/** Boot sector disk block. */
DISK_BLOCK_BootBlock = 0,
/** First copy of the FAT table block. */
DISK_BLOCK_FATBlock1 = 1,
/** Second copy of the FAT table block. */
DISK_BLOCK_FATBlock2 = 2,
/** Root file and directory entries block. */
DISK_BLOCK_RootFilesBlock = 3,
/** Start block of the disk data section. */
DISK_BLOCK_DataStartBlock = 4,
};
/* Type Definitions: */
/** FAT boot block structure definition, used to identify the core
* parameters of a FAT file system stored on a disk.
*
* \note This definition is truncated to save space; the magic signature
* \c 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 directory entry structure, for the various kinds of File and
* directory descriptors on a FAT disk.
*/
typedef union
{
/** VFAT Long File Name file entry. */
struct
{
uint8_t Ordinal;
uint16_t Unicode1;
uint16_t Unicode2;
uint16_t Unicode3;
uint16_t Unicode4;
uint16_t Unicode5;
uint8_t Attribute;
uint8_t Reserved1;
uint8_t Checksum;
uint16_t Unicode6;
uint16_t Unicode7;
uint16_t Unicode8;
uint16_t Unicode9;
uint16_t Unicode10;
uint16_t Unicode11;
uint16_t Reserved2;
uint16_t Unicode12;
uint16_t Unicode13;
} VFAT_LongFileName;
/** Legacy FAT MSDOS 8.3 file entry. */
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;
} MSDOS_File;
/** Legacy FAT MSDOS (sub-)directory entry. */
struct
{
uint8_t Name[11];
uint8_t Attributes;
uint8_t Reserved[10];
uint16_t CreationTime;
uint16_t CreationDate;
uint16_t StartingCluster;
uint32_t Reserved2;
} MSDOS_Directory;
} FATDirectoryEntry_t;
/* Function Prototypes: */
#if defined(INCLUDE_FROM_VIRTUAL_FAT_C)
static uint8_t ReadEEPROMByte(const uint8_t* const Address) ATTR_NO_INLINE;
static void WriteEEPROMByte(uint8_t* const Address,
const uint8_t Data) ATTR_NO_INLINE;
static void UpdateFAT12ClusterEntry(uint8_t* const FATTable,
const uint16_t Index,
const uint16_t ChainEntry) AUX_BOOT_SECTION;
static void UpdateFAT12ClusterChain(uint8_t* const FATTable,
const uint16_t StartIndex,
const uint8_t ChainLength) AUX_BOOT_SECTION;
static void ReadWriteFLASHFileBlock(const uint16_t BlockNumber,
uint8_t* BlockBuffer,
const bool Read) AUX_BOOT_SECTION;
static void ReadWriteEEPROMFileBlock(const uint16_t BlockNumber,
uint8_t* BlockBuffer,
const bool Read) AUX_BOOT_SECTION;
#endif
void VirtualFAT_WriteBlock(const uint16_t BlockNumber) AUX_BOOT_SECTION;
void VirtualFAT_ReadBlock(const uint16_t BlockNumber) AUX_BOOT_SECTION;
#endif