From 0c2ad9eb349e1a5676468af3b407329dde4a53b4 Mon Sep 17 00:00:00 2001 From: Dean Camera Date: Fri, 15 Jul 2011 08:46:08 +0000 Subject: Add missing SVN eol-style property to files where it was missing. --- .../Lib/DataflashManager.c | 1068 ++++++++++---------- .../Lib/DataflashManager.h | 170 ++-- .../VirtualSerialMassStorage/Lib/SCSI.c | 694 ++++++------- .../VirtualSerialMassStorage/Lib/SCSI.h | 176 ++-- 4 files changed, 1054 insertions(+), 1054 deletions(-) (limited to 'Demos/Device/ClassDriver/VirtualSerialMassStorage/Lib') diff --git a/Demos/Device/ClassDriver/VirtualSerialMassStorage/Lib/DataflashManager.c b/Demos/Device/ClassDriver/VirtualSerialMassStorage/Lib/DataflashManager.c index 64573520a..f962729dc 100644 --- a/Demos/Device/ClassDriver/VirtualSerialMassStorage/Lib/DataflashManager.c +++ b/Demos/Device/ClassDriver/VirtualSerialMassStorage/Lib/DataflashManager.c @@ -1,534 +1,534 @@ -/* - LUFA Library - Copyright (C) Dean Camera, 2011. - - dean [at] fourwalledcubicle [dot] com - www.lufa-lib.org -*/ - -/* - Copyright 2011 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 disclaim 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 - * - * Functions to manage the physical Dataflash media, including reading and writing of - * blocks of data. These functions are called by the SCSI layer when data must be stored - * or retrieved to/from the physical storage media. If a different media is used (such - * as a SD card or EEPROM), functions similar to these will need to be generated. - */ - -#define INCLUDE_FROM_DATAFLASHMANAGER_C -#include "DataflashManager.h" - -/** Writes blocks (OS blocks, not Dataflash pages) to the storage medium, the board Dataflash IC(s), from - * the pre-selected data OUT endpoint. This routine reads in OS sized blocks from the endpoint and writes - * them to the Dataflash in Dataflash page sized blocks. - * - * \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 DataflashManager_WriteBlocks(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo, - const uint32_t BlockAddress, - uint16_t TotalBlocks) -{ - uint16_t CurrDFPage = ((BlockAddress * VIRTUAL_MEMORY_BLOCK_SIZE) / DATAFLASH_PAGE_SIZE); - uint16_t CurrDFPageByte = ((BlockAddress * VIRTUAL_MEMORY_BLOCK_SIZE) % DATAFLASH_PAGE_SIZE); - uint8_t CurrDFPageByteDiv16 = (CurrDFPageByte >> 4); - bool UsingSecondBuffer = false; - - /* Select the correct starting Dataflash IC for the block requested */ - Dataflash_SelectChipFromPage(CurrDFPage); - -#if (DATAFLASH_PAGE_SIZE > VIRTUAL_MEMORY_BLOCK_SIZE) - /* Copy selected dataflash's current page contents to the Dataflash buffer */ - Dataflash_SendByte(DF_CMD_MAINMEMTOBUFF1); - Dataflash_SendAddressBytes(CurrDFPage, 0); - Dataflash_WaitWhileBusy(); -#endif - - /* Send the Dataflash buffer write command */ - Dataflash_SendByte(DF_CMD_BUFF1WRITE); - Dataflash_SendAddressBytes(0, CurrDFPageByte); - - /* Wait until endpoint is ready before continuing */ - if (Endpoint_WaitUntilReady()) - return; - - while (TotalBlocks) - { - uint8_t BytesInBlockDiv16 = 0; - - /* Write an endpoint packet sized data block to the Dataflash */ - while (BytesInBlockDiv16 < (VIRTUAL_MEMORY_BLOCK_SIZE >> 4)) - { - /* Check if the endpoint is currently empty */ - if (!(Endpoint_IsReadWriteAllowed())) - { - /* Clear the current endpoint bank */ - Endpoint_ClearOUT(); - - /* Wait until the host has sent another packet */ - if (Endpoint_WaitUntilReady()) - return; - } - - /* Check if end of Dataflash page reached */ - if (CurrDFPageByteDiv16 == (DATAFLASH_PAGE_SIZE >> 4)) - { - /* Write the Dataflash buffer contents back to the Dataflash page */ - Dataflash_WaitWhileBusy(); - Dataflash_SendByte(UsingSecondBuffer ? DF_CMD_BUFF2TOMAINMEMWITHERASE : DF_CMD_BUFF1TOMAINMEMWITHERASE); - Dataflash_SendAddressBytes(CurrDFPage, 0); - - /* Reset the Dataflash buffer counter, increment the page counter */ - CurrDFPageByteDiv16 = 0; - CurrDFPage++; - - /* Once all the Dataflash ICs have had their first buffers filled, switch buffers to maintain throughput */ - if (Dataflash_GetSelectedChip() == DATAFLASH_CHIP_MASK(DATAFLASH_TOTALCHIPS)) - UsingSecondBuffer = !(UsingSecondBuffer); - - /* Select the next Dataflash chip based on the new Dataflash page index */ - Dataflash_SelectChipFromPage(CurrDFPage); - -#if (DATAFLASH_PAGE_SIZE > VIRTUAL_MEMORY_BLOCK_SIZE) - /* If less than one Dataflash page remaining, copy over the existing page to preserve trailing data */ - if ((TotalBlocks * (VIRTUAL_MEMORY_BLOCK_SIZE >> 4)) < (DATAFLASH_PAGE_SIZE >> 4)) - { - /* Copy selected dataflash's current page contents to the Dataflash buffer */ - Dataflash_WaitWhileBusy(); - Dataflash_SendByte(UsingSecondBuffer ? DF_CMD_MAINMEMTOBUFF2 : DF_CMD_MAINMEMTOBUFF1); - Dataflash_SendAddressBytes(CurrDFPage, 0); - Dataflash_WaitWhileBusy(); - } -#endif - - /* Send the Dataflash buffer write command */ - Dataflash_SendByte(UsingSecondBuffer ? DF_CMD_BUFF2WRITE : DF_CMD_BUFF1WRITE); - Dataflash_SendAddressBytes(0, 0); - } - - /* Write one 16-byte chunk of data to the Dataflash */ - Dataflash_SendByte(Endpoint_Read_8()); - Dataflash_SendByte(Endpoint_Read_8()); - Dataflash_SendByte(Endpoint_Read_8()); - Dataflash_SendByte(Endpoint_Read_8()); - Dataflash_SendByte(Endpoint_Read_8()); - Dataflash_SendByte(Endpoint_Read_8()); - Dataflash_SendByte(Endpoint_Read_8()); - Dataflash_SendByte(Endpoint_Read_8()); - Dataflash_SendByte(Endpoint_Read_8()); - Dataflash_SendByte(Endpoint_Read_8()); - Dataflash_SendByte(Endpoint_Read_8()); - Dataflash_SendByte(Endpoint_Read_8()); - Dataflash_SendByte(Endpoint_Read_8()); - Dataflash_SendByte(Endpoint_Read_8()); - Dataflash_SendByte(Endpoint_Read_8()); - Dataflash_SendByte(Endpoint_Read_8()); - - /* Increment the Dataflash page 16 byte block counter */ - CurrDFPageByteDiv16++; - - /* Increment the block 16 byte block counter */ - BytesInBlockDiv16++; - - /* Check if the current command is being aborted by the host */ - if (MSInterfaceInfo->State.IsMassStoreReset) - return; - } - - /* Decrement the blocks remaining counter */ - TotalBlocks--; - } - - /* Write the Dataflash buffer contents back to the Dataflash page */ - Dataflash_WaitWhileBusy(); - Dataflash_SendByte(UsingSecondBuffer ? DF_CMD_BUFF2TOMAINMEMWITHERASE : DF_CMD_BUFF1TOMAINMEMWITHERASE); - Dataflash_SendAddressBytes(CurrDFPage, 0x00); - Dataflash_WaitWhileBusy(); - - /* If the endpoint is empty, clear it ready for the next packet from the host */ - if (!(Endpoint_IsReadWriteAllowed())) - Endpoint_ClearOUT(); - - /* Deselect all Dataflash chips */ - Dataflash_DeselectChip(); -} - -/** Reads blocks (OS blocks, not Dataflash pages) from the storage medium, the board Dataflash IC(s), into - * the pre-selected data IN endpoint. This routine reads in Dataflash page sized blocks from the Dataflash - * and writes them in OS sized blocks to the endpoint. - * - * \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 DataflashManager_ReadBlocks(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo, - const uint32_t BlockAddress, - uint16_t TotalBlocks) -{ - uint16_t CurrDFPage = ((BlockAddress * VIRTUAL_MEMORY_BLOCK_SIZE) / DATAFLASH_PAGE_SIZE); - uint16_t CurrDFPageByte = ((BlockAddress * VIRTUAL_MEMORY_BLOCK_SIZE) % DATAFLASH_PAGE_SIZE); - uint8_t CurrDFPageByteDiv16 = (CurrDFPageByte >> 4); - - /* Select the correct starting Dataflash IC for the block requested */ - Dataflash_SelectChipFromPage(CurrDFPage); - - /* Send the Dataflash main memory page read command */ - Dataflash_SendByte(DF_CMD_MAINMEMPAGEREAD); - Dataflash_SendAddressBytes(CurrDFPage, CurrDFPageByte); - Dataflash_SendByte(0x00); - Dataflash_SendByte(0x00); - Dataflash_SendByte(0x00); - Dataflash_SendByte(0x00); - - /* Wait until endpoint is ready before continuing */ - if (Endpoint_WaitUntilReady()) - return; - - while (TotalBlocks) - { - uint8_t BytesInBlockDiv16 = 0; - - /* Write an endpoint packet sized data block to the Dataflash */ - while (BytesInBlockDiv16 < (VIRTUAL_MEMORY_BLOCK_SIZE >> 4)) - { - /* Check if the endpoint is currently full */ - if (!(Endpoint_IsReadWriteAllowed())) - { - /* Clear the endpoint bank to send its contents to the host */ - Endpoint_ClearIN(); - - /* Wait until the endpoint is ready for more data */ - if (Endpoint_WaitUntilReady()) - return; - } - - /* Check if end of Dataflash page reached */ - if (CurrDFPageByteDiv16 == (DATAFLASH_PAGE_SIZE >> 4)) - { - /* Reset the Dataflash buffer counter, increment the page counter */ - CurrDFPageByteDiv16 = 0; - CurrDFPage++; - - /* Select the next Dataflash chip based on the new Dataflash page index */ - Dataflash_SelectChipFromPage(CurrDFPage); - - /* Send the Dataflash main memory page read command */ - Dataflash_SendByte(DF_CMD_MAINMEMPAGEREAD); - Dataflash_SendAddressBytes(CurrDFPage, 0); - Dataflash_SendByte(0x00); - Dataflash_SendByte(0x00); - Dataflash_SendByte(0x00); - Dataflash_SendByte(0x00); - } - - /* Read one 16-byte chunk of data from the Dataflash */ - Endpoint_Write_8(Dataflash_ReceiveByte()); - Endpoint_Write_8(Dataflash_ReceiveByte()); - Endpoint_Write_8(Dataflash_ReceiveByte()); - Endpoint_Write_8(Dataflash_ReceiveByte()); - Endpoint_Write_8(Dataflash_ReceiveByte()); - Endpoint_Write_8(Dataflash_ReceiveByte()); - Endpoint_Write_8(Dataflash_ReceiveByte()); - Endpoint_Write_8(Dataflash_ReceiveByte()); - Endpoint_Write_8(Dataflash_ReceiveByte()); - Endpoint_Write_8(Dataflash_ReceiveByte()); - Endpoint_Write_8(Dataflash_ReceiveByte()); - Endpoint_Write_8(Dataflash_ReceiveByte()); - Endpoint_Write_8(Dataflash_ReceiveByte()); - Endpoint_Write_8(Dataflash_ReceiveByte()); - Endpoint_Write_8(Dataflash_ReceiveByte()); - Endpoint_Write_8(Dataflash_ReceiveByte()); - - /* Increment the Dataflash page 16 byte block counter */ - CurrDFPageByteDiv16++; - - /* Increment the block 16 byte block counter */ - BytesInBlockDiv16++; - - /* Check if the current command is being aborted by the host */ - if (MSInterfaceInfo->State.IsMassStoreReset) - return; - } - - /* Decrement the blocks remaining counter */ - TotalBlocks--; - } - - /* If the endpoint is full, send its contents to the host */ - if (!(Endpoint_IsReadWriteAllowed())) - Endpoint_ClearIN(); - - /* Deselect all Dataflash chips */ - Dataflash_DeselectChip(); -} - -/** Writes blocks (OS blocks, not Dataflash pages) to the storage medium, the board Dataflash IC(s), from - * the given RAM buffer. This routine reads in OS sized blocks from the buffer and writes them to the - * Dataflash in Dataflash page sized blocks. This can be linked to FAT libraries to write files to the - * Dataflash. - * - * \param[in] BlockAddress Data block starting address for the write sequence - * \param[in] TotalBlocks Number of blocks of data to write - * \param[in] BufferPtr Pointer to the data source RAM buffer - */ -void DataflashManager_WriteBlocks_RAM(const uint32_t BlockAddress, - uint16_t TotalBlocks, - uint8_t* BufferPtr) -{ - uint16_t CurrDFPage = ((BlockAddress * VIRTUAL_MEMORY_BLOCK_SIZE) / DATAFLASH_PAGE_SIZE); - uint16_t CurrDFPageByte = ((BlockAddress * VIRTUAL_MEMORY_BLOCK_SIZE) % DATAFLASH_PAGE_SIZE); - uint8_t CurrDFPageByteDiv16 = (CurrDFPageByte >> 4); - bool UsingSecondBuffer = false; - - /* Select the correct starting Dataflash IC for the block requested */ - Dataflash_SelectChipFromPage(CurrDFPage); - -#if (DATAFLASH_PAGE_SIZE > VIRTUAL_MEMORY_BLOCK_SIZE) - /* Copy selected dataflash's current page contents to the Dataflash buffer */ - Dataflash_SendByte(DF_CMD_MAINMEMTOBUFF1); - Dataflash_SendAddressBytes(CurrDFPage, 0); - Dataflash_WaitWhileBusy(); -#endif - - /* Send the Dataflash buffer write command */ - Dataflash_SendByte(DF_CMD_BUFF1WRITE); - Dataflash_SendAddressBytes(0, CurrDFPageByte); - - while (TotalBlocks) - { - uint8_t BytesInBlockDiv16 = 0; - - /* Write an endpoint packet sized data block to the Dataflash */ - while (BytesInBlockDiv16 < (VIRTUAL_MEMORY_BLOCK_SIZE >> 4)) - { - /* Check if end of Dataflash page reached */ - if (CurrDFPageByteDiv16 == (DATAFLASH_PAGE_SIZE >> 4)) - { - /* Write the Dataflash buffer contents back to the Dataflash page */ - Dataflash_WaitWhileBusy(); - Dataflash_SendByte(UsingSecondBuffer ? DF_CMD_BUFF2TOMAINMEMWITHERASE : DF_CMD_BUFF1TOMAINMEMWITHERASE); - Dataflash_SendAddressBytes(CurrDFPage, 0); - - /* Reset the Dataflash buffer counter, increment the page counter */ - CurrDFPageByteDiv16 = 0; - CurrDFPage++; - - /* Once all the Dataflash ICs have had their first buffers filled, switch buffers to maintain throughput */ - if (Dataflash_GetSelectedChip() == DATAFLASH_CHIP_MASK(DATAFLASH_TOTALCHIPS)) - UsingSecondBuffer = !(UsingSecondBuffer); - - /* Select the next Dataflash chip based on the new Dataflash page index */ - Dataflash_SelectChipFromPage(CurrDFPage); - -#if (DATAFLASH_PAGE_SIZE > VIRTUAL_MEMORY_BLOCK_SIZE) - /* If less than one Dataflash page remaining, copy over the existing page to preserve trailing data */ - if ((TotalBlocks * (VIRTUAL_MEMORY_BLOCK_SIZE >> 4)) < (DATAFLASH_PAGE_SIZE >> 4)) - { - /* Copy selected dataflash's current page contents to the Dataflash buffer */ - Dataflash_WaitWhileBusy(); - Dataflash_SendByte(UsingSecondBuffer ? DF_CMD_MAINMEMTOBUFF2 : DF_CMD_MAINMEMTOBUFF1); - Dataflash_SendAddressBytes(CurrDFPage, 0); - Dataflash_WaitWhileBusy(); - } -#endif - - /* Send the Dataflash buffer write command */ - Dataflash_ToggleSelectedChipCS(); - Dataflash_SendByte(DF_CMD_BUFF1WRITE); - Dataflash_SendAddressBytes(0, 0); - } - - /* Write one 16-byte chunk of data to the Dataflash */ - for (uint8_t ByteNum = 0; ByteNum < 16; ByteNum++) - Dataflash_SendByte(*(BufferPtr++)); - - /* Increment the Dataflash page 16 byte block counter */ - CurrDFPageByteDiv16++; - - /* Increment the block 16 byte block counter */ - BytesInBlockDiv16++; - } - - /* Decrement the blocks remaining counter */ - TotalBlocks--; - } - - /* Write the Dataflash buffer contents back to the Dataflash page */ - Dataflash_WaitWhileBusy(); - Dataflash_SendByte(UsingSecondBuffer ? DF_CMD_BUFF2TOMAINMEMWITHERASE : DF_CMD_BUFF1TOMAINMEMWITHERASE); - Dataflash_SendAddressBytes(CurrDFPage, 0x00); - Dataflash_WaitWhileBusy(); - - /* Deselect all Dataflash chips */ - Dataflash_DeselectChip(); -} - -/** Reads blocks (OS blocks, not Dataflash pages) from the storage medium, the board Dataflash IC(s), into - * the preallocated RAM buffer. This routine reads in Dataflash page sized blocks from the Dataflash - * and writes them in OS sized blocks to the given buffer. This can be linked to FAT libraries to read - * the files stored on the Dataflash. - * - * \param[in] BlockAddress Data block starting address for the read sequence - * \param[in] TotalBlocks Number of blocks of data to read - * \param[out] BufferPtr Pointer to the data destination RAM buffer - */ -void DataflashManager_ReadBlocks_RAM(const uint32_t BlockAddress, - uint16_t TotalBlocks, - uint8_t* BufferPtr) -{ - uint16_t CurrDFPage = ((BlockAddress * VIRTUAL_MEMORY_BLOCK_SIZE) / DATAFLASH_PAGE_SIZE); - uint16_t CurrDFPageByte = ((BlockAddress * VIRTUAL_MEMORY_BLOCK_SIZE) % DATAFLASH_PAGE_SIZE); - uint8_t CurrDFPageByteDiv16 = (CurrDFPageByte >> 4); - - /* Select the correct starting Dataflash IC for the block requested */ - Dataflash_SelectChipFromPage(CurrDFPage); - - /* Send the Dataflash main memory page read command */ - Dataflash_SendByte(DF_CMD_MAINMEMPAGEREAD); - Dataflash_SendAddressBytes(CurrDFPage, CurrDFPageByte); - Dataflash_SendByte(0x00); - Dataflash_SendByte(0x00); - Dataflash_SendByte(0x00); - Dataflash_SendByte(0x00); - - while (TotalBlocks) - { - uint8_t BytesInBlockDiv16 = 0; - - /* Write an endpoint packet sized data block to the Dataflash */ - while (BytesInBlockDiv16 < (VIRTUAL_MEMORY_BLOCK_SIZE >> 4)) - { - /* Check if end of Dataflash page reached */ - if (CurrDFPageByteDiv16 == (DATAFLASH_PAGE_SIZE >> 4)) - { - /* Reset the Dataflash buffer counter, increment the page counter */ - CurrDFPageByteDiv16 = 0; - CurrDFPage++; - - /* Select the next Dataflash chip based on the new Dataflash page index */ - Dataflash_SelectChipFromPage(CurrDFPage); - - /* Send the Dataflash main memory page read command */ - Dataflash_SendByte(DF_CMD_MAINMEMPAGEREAD); - Dataflash_SendAddressBytes(CurrDFPage, 0); - Dataflash_SendByte(0x00); - Dataflash_SendByte(0x00); - Dataflash_SendByte(0x00); - Dataflash_SendByte(0x00); - } - - /* Read one 16-byte chunk of data from the Dataflash */ - for (uint8_t ByteNum = 0; ByteNum < 16; ByteNum++) - *(BufferPtr++) = Dataflash_ReceiveByte(); - - /* Increment the Dataflash page 16 byte block counter */ - CurrDFPageByteDiv16++; - - /* Increment the block 16 byte block counter */ - BytesInBlockDiv16++; - } - - /* Decrement the blocks remaining counter */ - TotalBlocks--; - } - - /* Deselect all Dataflash chips */ - Dataflash_DeselectChip(); -} - -/** Disables the Dataflash memory write protection bits on the board Dataflash ICs, if enabled. */ -void DataflashManager_ResetDataflashProtections(void) -{ - /* Select first Dataflash chip, send the read status register command */ - Dataflash_SelectChip(DATAFLASH_CHIP1); - Dataflash_SendByte(DF_CMD_GETSTATUS); - - /* Check if sector protection is enabled */ - if (Dataflash_ReceiveByte() & DF_STATUS_SECTORPROTECTION_ON) - { - Dataflash_ToggleSelectedChipCS(); - - /* Send the commands to disable sector protection */ - Dataflash_SendByte(DF_CMD_SECTORPROTECTIONOFF[0]); - Dataflash_SendByte(DF_CMD_SECTORPROTECTIONOFF[1]); - Dataflash_SendByte(DF_CMD_SECTORPROTECTIONOFF[2]); - Dataflash_SendByte(DF_CMD_SECTORPROTECTIONOFF[3]); - } - - /* Select second Dataflash chip (if present on selected board), send read status register command */ - #if (DATAFLASH_TOTALCHIPS == 2) - Dataflash_SelectChip(DATAFLASH_CHIP2); - Dataflash_SendByte(DF_CMD_GETSTATUS); - - /* Check if sector protection is enabled */ - if (Dataflash_ReceiveByte() & DF_STATUS_SECTORPROTECTION_ON) - { - Dataflash_ToggleSelectedChipCS(); - - /* Send the commands to disable sector protection */ - Dataflash_SendByte(DF_CMD_SECTORPROTECTIONOFF[0]); - Dataflash_SendByte(DF_CMD_SECTORPROTECTIONOFF[1]); - Dataflash_SendByte(DF_CMD_SECTORPROTECTIONOFF[2]); - Dataflash_SendByte(DF_CMD_SECTORPROTECTIONOFF[3]); - } - #endif - - /* Deselect current Dataflash chip */ - Dataflash_DeselectChip(); -} - -/** Performs a simple test on the attached Dataflash IC(s) to ensure that they are working. - * - * \return Boolean true if all media chips are working, false otherwise - */ -bool DataflashManager_CheckDataflashOperation(void) -{ - uint8_t ReturnByte; - - /* Test first Dataflash IC is present and responding to commands */ - Dataflash_SelectChip(DATAFLASH_CHIP1); - Dataflash_SendByte(DF_CMD_READMANUFACTURERDEVICEINFO); - ReturnByte = Dataflash_ReceiveByte(); - Dataflash_DeselectChip(); - - /* If returned data is invalid, fail the command */ - if (ReturnByte != DF_MANUFACTURER_ATMEL) - return false; - - #if (DATAFLASH_TOTALCHIPS == 2) - /* Test second Dataflash IC is present and responding to commands */ - Dataflash_SelectChip(DATAFLASH_CHIP2); - Dataflash_SendByte(DF_CMD_READMANUFACTURERDEVICEINFO); - ReturnByte = Dataflash_ReceiveByte(); - Dataflash_DeselectChip(); - - /* If returned data is invalid, fail the command */ - if (ReturnByte != DF_MANUFACTURER_ATMEL) - return false; - #endif - - return true; -} - +/* + LUFA Library + Copyright (C) Dean Camera, 2011. + + dean [at] fourwalledcubicle [dot] com + www.lufa-lib.org +*/ + +/* + Copyright 2011 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 disclaim 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 + * + * Functions to manage the physical Dataflash media, including reading and writing of + * blocks of data. These functions are called by the SCSI layer when data must be stored + * or retrieved to/from the physical storage media. If a different media is used (such + * as a SD card or EEPROM), functions similar to these will need to be generated. + */ + +#define INCLUDE_FROM_DATAFLASHMANAGER_C +#include "DataflashManager.h" + +/** Writes blocks (OS blocks, not Dataflash pages) to the storage medium, the board Dataflash IC(s), from + * the pre-selected data OUT endpoint. This routine reads in OS sized blocks from the endpoint and writes + * them to the Dataflash in Dataflash page sized blocks. + * + * \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 DataflashManager_WriteBlocks(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo, + const uint32_t BlockAddress, + uint16_t TotalBlocks) +{ + uint16_t CurrDFPage = ((BlockAddress * VIRTUAL_MEMORY_BLOCK_SIZE) / DATAFLASH_PAGE_SIZE); + uint16_t CurrDFPageByte = ((BlockAddress * VIRTUAL_MEMORY_BLOCK_SIZE) % DATAFLASH_PAGE_SIZE); + uint8_t CurrDFPageByteDiv16 = (CurrDFPageByte >> 4); + bool UsingSecondBuffer = false; + + /* Select the correct starting Dataflash IC for the block requested */ + Dataflash_SelectChipFromPage(CurrDFPage); + +#if (DATAFLASH_PAGE_SIZE > VIRTUAL_MEMORY_BLOCK_SIZE) + /* Copy selected dataflash's current page contents to the Dataflash buffer */ + Dataflash_SendByte(DF_CMD_MAINMEMTOBUFF1); + Dataflash_SendAddressBytes(CurrDFPage, 0); + Dataflash_WaitWhileBusy(); +#endif + + /* Send the Dataflash buffer write command */ + Dataflash_SendByte(DF_CMD_BUFF1WRITE); + Dataflash_SendAddressBytes(0, CurrDFPageByte); + + /* Wait until endpoint is ready before continuing */ + if (Endpoint_WaitUntilReady()) + return; + + while (TotalBlocks) + { + uint8_t BytesInBlockDiv16 = 0; + + /* Write an endpoint packet sized data block to the Dataflash */ + while (BytesInBlockDiv16 < (VIRTUAL_MEMORY_BLOCK_SIZE >> 4)) + { + /* Check if the endpoint is currently empty */ + if (!(Endpoint_IsReadWriteAllowed())) + { + /* Clear the current endpoint bank */ + Endpoint_ClearOUT(); + + /* Wait until the host has sent another packet */ + if (Endpoint_WaitUntilReady()) + return; + } + + /* Check if end of Dataflash page reached */ + if (CurrDFPageByteDiv16 == (DATAFLASH_PAGE_SIZE >> 4)) + { + /* Write the Dataflash buffer contents back to the Dataflash page */ + Dataflash_WaitWhileBusy(); + Dataflash_SendByte(UsingSecondBuffer ? DF_CMD_BUFF2TOMAINMEMWITHERASE : DF_CMD_BUFF1TOMAINMEMWITHERASE); + Dataflash_SendAddressBytes(CurrDFPage, 0); + + /* Reset the Dataflash buffer counter, increment the page counter */ + CurrDFPageByteDiv16 = 0; + CurrDFPage++; + + /* Once all the Dataflash ICs have had their first buffers filled, switch buffers to maintain throughput */ + if (Dataflash_GetSelectedChip() == DATAFLASH_CHIP_MASK(DATAFLASH_TOTALCHIPS)) + UsingSecondBuffer = !(UsingSecondBuffer); + + /* Select the next Dataflash chip based on the new Dataflash page index */ + Dataflash_SelectChipFromPage(CurrDFPage); + +#if (DATAFLASH_PAGE_SIZE > VIRTUAL_MEMORY_BLOCK_SIZE) + /* If less than one Dataflash page remaining, copy over the existing page to preserve trailing data */ + if ((TotalBlocks * (VIRTUAL_MEMORY_BLOCK_SIZE >> 4)) < (DATAFLASH_PAGE_SIZE >> 4)) + { + /* Copy selected dataflash's current page contents to the Dataflash buffer */ + Dataflash_WaitWhileBusy(); + Dataflash_SendByte(UsingSecondBuffer ? DF_CMD_MAINMEMTOBUFF2 : DF_CMD_MAINMEMTOBUFF1); + Dataflash_SendAddressBytes(CurrDFPage, 0); + Dataflash_WaitWhileBusy(); + } +#endif + + /* Send the Dataflash buffer write command */ + Dataflash_SendByte(UsingSecondBuffer ? DF_CMD_BUFF2WRITE : DF_CMD_BUFF1WRITE); + Dataflash_SendAddressBytes(0, 0); + } + + /* Write one 16-byte chunk of data to the Dataflash */ + Dataflash_SendByte(Endpoint_Read_8()); + Dataflash_SendByte(Endpoint_Read_8()); + Dataflash_SendByte(Endpoint_Read_8()); + Dataflash_SendByte(Endpoint_Read_8()); + Dataflash_SendByte(Endpoint_Read_8()); + Dataflash_SendByte(Endpoint_Read_8()); + Dataflash_SendByte(Endpoint_Read_8()); + Dataflash_SendByte(Endpoint_Read_8()); + Dataflash_SendByte(Endpoint_Read_8()); + Dataflash_SendByte(Endpoint_Read_8()); + Dataflash_SendByte(Endpoint_Read_8()); + Dataflash_SendByte(Endpoint_Read_8()); + Dataflash_SendByte(Endpoint_Read_8()); + Dataflash_SendByte(Endpoint_Read_8()); + Dataflash_SendByte(Endpoint_Read_8()); + Dataflash_SendByte(Endpoint_Read_8()); + + /* Increment the Dataflash page 16 byte block counter */ + CurrDFPageByteDiv16++; + + /* Increment the block 16 byte block counter */ + BytesInBlockDiv16++; + + /* Check if the current command is being aborted by the host */ + if (MSInterfaceInfo->State.IsMassStoreReset) + return; + } + + /* Decrement the blocks remaining counter */ + TotalBlocks--; + } + + /* Write the Dataflash buffer contents back to the Dataflash page */ + Dataflash_WaitWhileBusy(); + Dataflash_SendByte(UsingSecondBuffer ? DF_CMD_BUFF2TOMAINMEMWITHERASE : DF_CMD_BUFF1TOMAINMEMWITHERASE); + Dataflash_SendAddressBytes(CurrDFPage, 0x00); + Dataflash_WaitWhileBusy(); + + /* If the endpoint is empty, clear it ready for the next packet from the host */ + if (!(Endpoint_IsReadWriteAllowed())) + Endpoint_ClearOUT(); + + /* Deselect all Dataflash chips */ + Dataflash_DeselectChip(); +} + +/** Reads blocks (OS blocks, not Dataflash pages) from the storage medium, the board Dataflash IC(s), into + * the pre-selected data IN endpoint. This routine reads in Dataflash page sized blocks from the Dataflash + * and writes them in OS sized blocks to the endpoint. + * + * \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 DataflashManager_ReadBlocks(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo, + const uint32_t BlockAddress, + uint16_t TotalBlocks) +{ + uint16_t CurrDFPage = ((BlockAddress * VIRTUAL_MEMORY_BLOCK_SIZE) / DATAFLASH_PAGE_SIZE); + uint16_t CurrDFPageByte = ((BlockAddress * VIRTUAL_MEMORY_BLOCK_SIZE) % DATAFLASH_PAGE_SIZE); + uint8_t CurrDFPageByteDiv16 = (CurrDFPageByte >> 4); + + /* Select the correct starting Dataflash IC for the block requested */ + Dataflash_SelectChipFromPage(CurrDFPage); + + /* Send the Dataflash main memory page read command */ + Dataflash_SendByte(DF_CMD_MAINMEMPAGEREAD); + Dataflash_SendAddressBytes(CurrDFPage, CurrDFPageByte); + Dataflash_SendByte(0x00); + Dataflash_SendByte(0x00); + Dataflash_SendByte(0x00); + Dataflash_SendByte(0x00); + + /* Wait until endpoint is ready before continuing */ + if (Endpoint_WaitUntilReady()) + return; + + while (TotalBlocks) + { + uint8_t BytesInBlockDiv16 = 0; + + /* Write an endpoint packet sized data block to the Dataflash */ + while (BytesInBlockDiv16 < (VIRTUAL_MEMORY_BLOCK_SIZE >> 4)) + { + /* Check if the endpoint is currently full */ + if (!(Endpoint_IsReadWriteAllowed())) + { + /* Clear the endpoint bank to send its contents to the host */ + Endpoint_ClearIN(); + + /* Wait until the endpoint is ready for more data */ + if (Endpoint_WaitUntilReady()) + return; + } + + /* Check if end of Dataflash page reached */ + if (CurrDFPageByteDiv16 == (DATAFLASH_PAGE_SIZE >> 4)) + { + /* Reset the Dataflash buffer counter, increment the page counter */ + CurrDFPageByteDiv16 = 0; + CurrDFPage++; + + /* Select the next Dataflash chip based on the new Dataflash page index */ + Dataflash_SelectChipFromPage(CurrDFPage); + + /* Send the Dataflash main memory page read command */ + Dataflash_SendByte(DF_CMD_MAINMEMPAGEREAD); + Dataflash_SendAddressBytes(CurrDFPage, 0); + Dataflash_SendByte(0x00); + Dataflash_SendByte(0x00); + Dataflash_SendByte(0x00); + Dataflash_SendByte(0x00); + } + + /* Read one 16-byte chunk of data from the Dataflash */ + Endpoint_Write_8(Dataflash_ReceiveByte()); + Endpoint_Write_8(Dataflash_ReceiveByte()); + Endpoint_Write_8(Dataflash_ReceiveByte()); + Endpoint_Write_8(Dataflash_ReceiveByte()); + Endpoint_Write_8(Dataflash_ReceiveByte()); + Endpoint_Write_8(Dataflash_ReceiveByte()); + Endpoint_Write_8(Dataflash_ReceiveByte()); + Endpoint_Write_8(Dataflash_ReceiveByte()); + Endpoint_Write_8(Dataflash_ReceiveByte()); + Endpoint_Write_8(Dataflash_ReceiveByte()); + Endpoint_Write_8(Dataflash_ReceiveByte()); + Endpoint_Write_8(Dataflash_ReceiveByte()); + Endpoint_Write_8(Dataflash_ReceiveByte()); + Endpoint_Write_8(Dataflash_ReceiveByte()); + Endpoint_Write_8(Dataflash_ReceiveByte()); + Endpoint_Write_8(Dataflash_ReceiveByte()); + + /* Increment the Dataflash page 16 byte block counter */ + CurrDFPageByteDiv16++; + + /* Increment the block 16 byte block counter */ + BytesInBlockDiv16++; + + /* Check if the current command is being aborted by the host */ + if (MSInterfaceInfo->State.IsMassStoreReset) + return; + } + + /* Decrement the blocks remaining counter */ + TotalBlocks--; + } + + /* If the endpoint is full, send its contents to the host */ + if (!(Endpoint_IsReadWriteAllowed())) + Endpoint_ClearIN(); + + /* Deselect all Dataflash chips */ + Dataflash_DeselectChip(); +} + +/** Writes blocks (OS blocks, not Dataflash pages) to the storage medium, the board Dataflash IC(s), from + * the given RAM buffer. This routine reads in OS sized blocks from the buffer and writes them to the + * Dataflash in Dataflash page sized blocks. This can be linked to FAT libraries to write files to the + * Dataflash. + * + * \param[in] BlockAddress Data block starting address for the write sequence + * \param[in] TotalBlocks Number of blocks of data to write + * \param[in] BufferPtr Pointer to the data source RAM buffer + */ +void DataflashManager_WriteBlocks_RAM(const uint32_t BlockAddress, + uint16_t TotalBlocks, + uint8_t* BufferPtr) +{ + uint16_t CurrDFPage = ((BlockAddress * VIRTUAL_MEMORY_BLOCK_SIZE) / DATAFLASH_PAGE_SIZE); + uint16_t CurrDFPageByte = ((BlockAddress * VIRTUAL_MEMORY_BLOCK_SIZE) % DATAFLASH_PAGE_SIZE); + uint8_t CurrDFPageByteDiv16 = (CurrDFPageByte >> 4); + bool UsingSecondBuffer = false; + + /* Select the correct starting Dataflash IC for the block requested */ + Dataflash_SelectChipFromPage(CurrDFPage); + +#if (DATAFLASH_PAGE_SIZE > VIRTUAL_MEMORY_BLOCK_SIZE) + /* Copy selected dataflash's current page contents to the Dataflash buffer */ + Dataflash_SendByte(DF_CMD_MAINMEMTOBUFF1); + Dataflash_SendAddressBytes(CurrDFPage, 0); + Dataflash_WaitWhileBusy(); +#endif + + /* Send the Dataflash buffer write command */ + Dataflash_SendByte(DF_CMD_BUFF1WRITE); + Dataflash_SendAddressBytes(0, CurrDFPageByte); + + while (TotalBlocks) + { + uint8_t BytesInBlockDiv16 = 0; + + /* Write an endpoint packet sized data block to the Dataflash */ + while (BytesInBlockDiv16 < (VIRTUAL_MEMORY_BLOCK_SIZE >> 4)) + { + /* Check if end of Dataflash page reached */ + if (CurrDFPageByteDiv16 == (DATAFLASH_PAGE_SIZE >> 4)) + { + /* Write the Dataflash buffer contents back to the Dataflash page */ + Dataflash_WaitWhileBusy(); + Dataflash_SendByte(UsingSecondBuffer ? DF_CMD_BUFF2TOMAINMEMWITHERASE : DF_CMD_BUFF1TOMAINMEMWITHERASE); + Dataflash_SendAddressBytes(CurrDFPage, 0); + + /* Reset the Dataflash buffer counter, increment the page counter */ + CurrDFPageByteDiv16 = 0; + CurrDFPage++; + + /* Once all the Dataflash ICs have had their first buffers filled, switch buffers to maintain throughput */ + if (Dataflash_GetSelectedChip() == DATAFLASH_CHIP_MASK(DATAFLASH_TOTALCHIPS)) + UsingSecondBuffer = !(UsingSecondBuffer); + + /* Select the next Dataflash chip based on the new Dataflash page index */ + Dataflash_SelectChipFromPage(CurrDFPage); + +#if (DATAFLASH_PAGE_SIZE > VIRTUAL_MEMORY_BLOCK_SIZE) + /* If less than one Dataflash page remaining, copy over the existing page to preserve trailing data */ + if ((TotalBlocks * (VIRTUAL_MEMORY_BLOCK_SIZE >> 4)) < (DATAFLASH_PAGE_SIZE >> 4)) + { + /* Copy selected dataflash's current page contents to the Dataflash buffer */ + Dataflash_WaitWhileBusy(); + Dataflash_SendByte(UsingSecondBuffer ? DF_CMD_MAINMEMTOBUFF2 : DF_CMD_MAINMEMTOBUFF1); + Dataflash_SendAddressBytes(CurrDFPage, 0); + Dataflash_WaitWhileBusy(); + } +#endif + + /* Send the Dataflash buffer write command */ + Dataflash_ToggleSelectedChipCS(); + Dataflash_SendByte(DF_CMD_BUFF1WRITE); + Dataflash_SendAddressBytes(0, 0); + } + + /* Write one 16-byte chunk of data to the Dataflash */ + for (uint8_t ByteNum = 0; ByteNum < 16; ByteNum++) + Dataflash_SendByte(*(BufferPtr++)); + + /* Increment the Dataflash page 16 byte block counter */ + CurrDFPageByteDiv16++; + + /* Increment the block 16 byte block counter */ + BytesInBlockDiv16++; + } + + /* Decrement the blocks remaining counter */ + TotalBlocks--; + } + + /* Write the Dataflash buffer contents back to the Dataflash page */ + Dataflash_WaitWhileBusy(); + Dataflash_SendByte(UsingSecondBuffer ? DF_CMD_BUFF2TOMAINMEMWITHERASE : DF_CMD_BUFF1TOMAINMEMWITHERASE); + Dataflash_SendAddressBytes(CurrDFPage, 0x00); + Dataflash_WaitWhileBusy(); + + /* Deselect all Dataflash chips */ + Dataflash_DeselectChip(); +} + +/** Reads blocks (OS blocks, not Dataflash pages) from the storage medium, the board Dataflash IC(s), into + * the preallocated RAM buffer. This routine reads in Dataflash page sized blocks from the Dataflash + * and writes them in OS sized blocks to the given buffer. This can be linked to FAT libraries to read + * the files stored on the Dataflash. + * + * \param[in] BlockAddress Data block starting address for the read sequence + * \param[in] TotalBlocks Number of blocks of data to read + * \param[out] BufferPtr Pointer to the data destination RAM buffer + */ +void DataflashManager_ReadBlocks_RAM(const uint32_t BlockAddress, + uint16_t TotalBlocks, + uint8_t* BufferPtr) +{ + uint16_t CurrDFPage = ((BlockAddress * VIRTUAL_MEMORY_BLOCK_SIZE) / DATAFLASH_PAGE_SIZE); + uint16_t CurrDFPageByte = ((BlockAddress * VIRTUAL_MEMORY_BLOCK_SIZE) % DATAFLASH_PAGE_SIZE); + uint8_t CurrDFPageByteDiv16 = (CurrDFPageByte >> 4); + + /* Select the correct starting Dataflash IC for the block requested */ + Dataflash_SelectChipFromPage(CurrDFPage); + + /* Send the Dataflash main memory page read command */ + Dataflash_SendByte(DF_CMD_MAINMEMPAGEREAD); + Dataflash_SendAddressBytes(CurrDFPage, CurrDFPageByte); + Dataflash_SendByte(0x00); + Dataflash_SendByte(0x00); + Dataflash_SendByte(0x00); + Dataflash_SendByte(0x00); + + while (TotalBlocks) + { + uint8_t BytesInBlockDiv16 = 0; + + /* Write an endpoint packet sized data block to the Dataflash */ + while (BytesInBlockDiv16 < (VIRTUAL_MEMORY_BLOCK_SIZE >> 4)) + { + /* Check if end of Dataflash page reached */ + if (CurrDFPageByteDiv16 == (DATAFLASH_PAGE_SIZE >> 4)) + { + /* Reset the Dataflash buffer counter, increment the page counter */ + CurrDFPageByteDiv16 = 0; + CurrDFPage++; + + /* Select the next Dataflash chip based on the new Dataflash page index */ + Dataflash_SelectChipFromPage(CurrDFPage); + + /* Send the Dataflash main memory page read command */ + Dataflash_SendByte(DF_CMD_MAINMEMPAGEREAD); + Dataflash_SendAddressBytes(CurrDFPage, 0); + Dataflash_SendByte(0x00); + Dataflash_SendByte(0x00); + Dataflash_SendByte(0x00); + Dataflash_SendByte(0x00); + } + + /* Read one 16-byte chunk of data from the Dataflash */ + for (uint8_t ByteNum = 0; ByteNum < 16; ByteNum++) + *(BufferPtr++) = Dataflash_ReceiveByte(); + + /* Increment the Dataflash page 16 byte block counter */ + CurrDFPageByteDiv16++; + + /* Increment the block 16 byte block counter */ + BytesInBlockDiv16++; + } + + /* Decrement the blocks remaining counter */ + TotalBlocks--; + } + + /* Deselect all Dataflash chips */ + Dataflash_DeselectChip(); +} + +/** Disables the Dataflash memory write protection bits on the board Dataflash ICs, if enabled. */ +void DataflashManager_ResetDataflashProtections(void) +{ + /* Select first Dataflash chip, send the read status register command */ + Dataflash_SelectChip(DATAFLASH_CHIP1); + Dataflash_SendByte(DF_CMD_GETSTATUS); + + /* Check if sector protection is enabled */ + if (Dataflash_ReceiveByte() & DF_STATUS_SECTORPROTECTION_ON) + { + Dataflash_ToggleSelectedChipCS(); + + /* Send the commands to disable sector protection */ + Dataflash_SendByte(DF_CMD_SECTORPROTECTIONOFF[0]); + Dataflash_SendByte(DF_CMD_SECTORPROTECTIONOFF[1]); + Dataflash_SendByte(DF_CMD_SECTORPROTECTIONOFF[2]); + Dataflash_SendByte(DF_CMD_SECTORPROTECTIONOFF[3]); + } + + /* Select second Dataflash chip (if present on selected board), send read status register command */ + #if (DATAFLASH_TOTALCHIPS == 2) + Dataflash_SelectChip(DATAFLASH_CHIP2); + Dataflash_SendByte(DF_CMD_GETSTATUS); + + /* Check if sector protection is enabled */ + if (Dataflash_ReceiveByte() & DF_STATUS_SECTORPROTECTION_ON) + { + Dataflash_ToggleSelectedChipCS(); + + /* Send the commands to disable sector protection */ + Dataflash_SendByte(DF_CMD_SECTORPROTECTIONOFF[0]); + Dataflash_SendByte(DF_CMD_SECTORPROTECTIONOFF[1]); + Dataflash_SendByte(DF_CMD_SECTORPROTECTIONOFF[2]); + Dataflash_SendByte(DF_CMD_SECTORPROTECTIONOFF[3]); + } + #endif + + /* Deselect current Dataflash chip */ + Dataflash_DeselectChip(); +} + +/** Performs a simple test on the attached Dataflash IC(s) to ensure that they are working. + * + * \return Boolean true if all media chips are working, false otherwise + */ +bool DataflashManager_CheckDataflashOperation(void) +{ + uint8_t ReturnByte; + + /* Test first Dataflash IC is present and responding to commands */ + Dataflash_SelectChip(DATAFLASH_CHIP1); + Dataflash_SendByte(DF_CMD_READMANUFACTURERDEVICEINFO); + ReturnByte = Dataflash_ReceiveByte(); + Dataflash_DeselectChip(); + + /* If returned data is invalid, fail the command */ + if (ReturnByte != DF_MANUFACTURER_ATMEL) + return false; + + #if (DATAFLASH_TOTALCHIPS == 2) + /* Test second Dataflash IC is present and responding to commands */ + Dataflash_SelectChip(DATAFLASH_CHIP2); + Dataflash_SendByte(DF_CMD_READMANUFACTURERDEVICEINFO); + ReturnByte = Dataflash_ReceiveByte(); + Dataflash_DeselectChip(); + + /* If returned data is invalid, fail the command */ + if (ReturnByte != DF_MANUFACTURER_ATMEL) + return false; + #endif + + return true; +} + diff --git a/Demos/Device/ClassDriver/VirtualSerialMassStorage/Lib/DataflashManager.h b/Demos/Device/ClassDriver/VirtualSerialMassStorage/Lib/DataflashManager.h index eac0653e0..0dc4d26be 100644 --- a/Demos/Device/ClassDriver/VirtualSerialMassStorage/Lib/DataflashManager.h +++ b/Demos/Device/ClassDriver/VirtualSerialMassStorage/Lib/DataflashManager.h @@ -1,85 +1,85 @@ -/* - LUFA Library - Copyright (C) Dean Camera, 2011. - - dean [at] fourwalledcubicle [dot] com - www.lufa-lib.org -*/ - -/* - Copyright 2011 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 disclaim 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 DataflashManager.c. - */ - -#ifndef _DATAFLASH_MANAGER_H_ -#define _DATAFLASH_MANAGER_H_ - - /* Includes: */ - #include - - #include "VirtualSerialMassStorage.h" - #include "Descriptors.h" - - #include - #include - #include - - /* Preprocessor Checks: */ - #if (DATAFLASH_PAGE_SIZE % 16) - #error Dataflash page size must be a multiple of 16 bytes. - #endif - - /* Defines: */ - /** Total number of bytes of the storage medium, comprised of one or more Dataflash ICs. */ - #define VIRTUAL_MEMORY_BYTES ((uint32_t)DATAFLASH_PAGES * DATAFLASH_PAGE_SIZE * DATAFLASH_TOTALCHIPS) - - /** Block size of the device. This is kept at 512 to remain compatible with the OS despite the underlying - * storage media (Dataflash) using a different native block size. Do not change this value. - */ - #define VIRTUAL_MEMORY_BLOCK_SIZE 512 - - /** Total number of blocks of the virtual memory for reporting to the host as the device's total capacity. Do not - * change this value; change VIRTUAL_MEMORY_BYTES instead to alter the media size. - */ - #define VIRTUAL_MEMORY_BLOCKS (VIRTUAL_MEMORY_BYTES / VIRTUAL_MEMORY_BLOCK_SIZE) - - /* Function Prototypes: */ - void DataflashManager_WriteBlocks(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo, - const uint32_t BlockAddress, - uint16_t TotalBlocks); - void DataflashManager_ReadBlocks(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo, - const uint32_t BlockAddress, - uint16_t TotalBlocks); - void DataflashManager_WriteBlocks_RAM(const uint32_t BlockAddress, - uint16_t TotalBlocks, - uint8_t* BufferPtr) ATTR_NON_NULL_PTR_ARG(3); - void DataflashManager_ReadBlocks_RAM(const uint32_t BlockAddress, - uint16_t TotalBlocks, - uint8_t* BufferPtr) ATTR_NON_NULL_PTR_ARG(3); - void DataflashManager_ResetDataflashProtections(void); - bool DataflashManager_CheckDataflashOperation(void); - -#endif - +/* + LUFA Library + Copyright (C) Dean Camera, 2011. + + dean [at] fourwalledcubicle [dot] com + www.lufa-lib.org +*/ + +/* + Copyright 2011 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 disclaim 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 DataflashManager.c. + */ + +#ifndef _DATAFLASH_MANAGER_H_ +#define _DATAFLASH_MANAGER_H_ + + /* Includes: */ + #include + + #include "VirtualSerialMassStorage.h" + #include "Descriptors.h" + + #include + #include + #include + + /* Preprocessor Checks: */ + #if (DATAFLASH_PAGE_SIZE % 16) + #error Dataflash page size must be a multiple of 16 bytes. + #endif + + /* Defines: */ + /** Total number of bytes of the storage medium, comprised of one or more Dataflash ICs. */ + #define VIRTUAL_MEMORY_BYTES ((uint32_t)DATAFLASH_PAGES * DATAFLASH_PAGE_SIZE * DATAFLASH_TOTALCHIPS) + + /** Block size of the device. This is kept at 512 to remain compatible with the OS despite the underlying + * storage media (Dataflash) using a different native block size. Do not change this value. + */ + #define VIRTUAL_MEMORY_BLOCK_SIZE 512 + + /** Total number of blocks of the virtual memory for reporting to the host as the device's total capacity. Do not + * change this value; change VIRTUAL_MEMORY_BYTES instead to alter the media size. + */ + #define VIRTUAL_MEMORY_BLOCKS (VIRTUAL_MEMORY_BYTES / VIRTUAL_MEMORY_BLOCK_SIZE) + + /* Function Prototypes: */ + void DataflashManager_WriteBlocks(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo, + const uint32_t BlockAddress, + uint16_t TotalBlocks); + void DataflashManager_ReadBlocks(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo, + const uint32_t BlockAddress, + uint16_t TotalBlocks); + void DataflashManager_WriteBlocks_RAM(const uint32_t BlockAddress, + uint16_t TotalBlocks, + uint8_t* BufferPtr) ATTR_NON_NULL_PTR_ARG(3); + void DataflashManager_ReadBlocks_RAM(const uint32_t BlockAddress, + uint16_t TotalBlocks, + uint8_t* BufferPtr) ATTR_NON_NULL_PTR_ARG(3); + void DataflashManager_ResetDataflashProtections(void); + bool DataflashManager_CheckDataflashOperation(void); + +#endif + diff --git a/Demos/Device/ClassDriver/VirtualSerialMassStorage/Lib/SCSI.c b/Demos/Device/ClassDriver/VirtualSerialMassStorage/Lib/SCSI.c index d7842c5f8..66b58a124 100644 --- a/Demos/Device/ClassDriver/VirtualSerialMassStorage/Lib/SCSI.c +++ b/Demos/Device/ClassDriver/VirtualSerialMassStorage/Lib/SCSI.c @@ -1,347 +1,347 @@ -/* - LUFA Library - Copyright (C) Dean Camera, 2011. - - dean [at] fourwalledcubicle [dot] com - www.lufa-lib.org -*/ - -/* - Copyright 2011 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 disclaim 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 = "Dataflash Disk", - .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_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 = VIRTUAL_MEMORY_BLOCK_SIZE; - - 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; - } - - /* Check to see if all attached Dataflash ICs are functional */ - if (!(DataflashManager_CheckDataflashOperation())) - { - /* Update SENSE key with a hardware error condition and return command fail */ - SCSI_SET_SENSE(SCSI_SENSE_KEY_HARDWARE_ERROR, - SCSI_ASENSE_NO_ADDITIONAL_INFORMATION, - 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; - - /* Check if the disk is write protected or not */ - if ((IsDataRead == DATA_WRITE) && DISK_READ_ONLY) - { - /* Block address is invalid, update SENSE key and return command fail */ - SCSI_SET_SENSE(SCSI_SENSE_KEY_DATA_PROTECT, - SCSI_ASENSE_WRITE_PROTECTED, - SCSI_ASENSEQ_NO_QUALIFIER); - - return false; - } - - /* 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; - } - - #if (TOTAL_LUNS > 1) - /* Adjust the given block address to the real media address based on the selected LUN */ - BlockAddress += ((uint32_t)MSInterfaceInfo->State.CommandBlock.LUN * LUN_MEDIA_BLOCKS); - #endif - - /* Determine if the packet is a READ (10) or WRITE (10) command, call appropriate function */ - if (IsDataRead == DATA_READ) - DataflashManager_ReadBlocks(MSInterfaceInfo, BlockAddress, TotalBlocks); - else - DataflashManager_WriteBlocks(MSInterfaceInfo, BlockAddress, TotalBlocks); - - /* Update the bytes transferred counter and succeed the command */ - MSInterfaceInfo->State.CommandBlock.DataTransferLength -= ((uint32_t)TotalBlocks * VIRTUAL_MEMORY_BLOCK_SIZE); - - 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 with the Write Protect flag status */ - Endpoint_Write_8(0x00); - Endpoint_Write_8(0x00); - Endpoint_Write_8(DISK_READ_ONLY ? 0x80 : 0x00); - Endpoint_Write_8(0x00); - Endpoint_ClearIN(); - - /* Update the bytes transferred counter and succeed the command */ - MSInterfaceInfo->State.CommandBlock.DataTransferLength -= 4; - - return true; -} +/* + LUFA Library + Copyright (C) Dean Camera, 2011. + + dean [at] fourwalledcubicle [dot] com + www.lufa-lib.org +*/ + +/* + Copyright 2011 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 disclaim 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 = "Dataflash Disk", + .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_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 = VIRTUAL_MEMORY_BLOCK_SIZE; + + 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; + } + + /* Check to see if all attached Dataflash ICs are functional */ + if (!(DataflashManager_CheckDataflashOperation())) + { + /* Update SENSE key with a hardware error condition and return command fail */ + SCSI_SET_SENSE(SCSI_SENSE_KEY_HARDWARE_ERROR, + SCSI_ASENSE_NO_ADDITIONAL_INFORMATION, + 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; + + /* Check if the disk is write protected or not */ + if ((IsDataRead == DATA_WRITE) && DISK_READ_ONLY) + { + /* Block address is invalid, update SENSE key and return command fail */ + SCSI_SET_SENSE(SCSI_SENSE_KEY_DATA_PROTECT, + SCSI_ASENSE_WRITE_PROTECTED, + SCSI_ASENSEQ_NO_QUALIFIER); + + return false; + } + + /* 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; + } + + #if (TOTAL_LUNS > 1) + /* Adjust the given block address to the real media address based on the selected LUN */ + BlockAddress += ((uint32_t)MSInterfaceInfo->State.CommandBlock.LUN * LUN_MEDIA_BLOCKS); + #endif + + /* Determine if the packet is a READ (10) or WRITE (10) command, call appropriate function */ + if (IsDataRead == DATA_READ) + DataflashManager_ReadBlocks(MSInterfaceInfo, BlockAddress, TotalBlocks); + else + DataflashManager_WriteBlocks(MSInterfaceInfo, BlockAddress, TotalBlocks); + + /* Update the bytes transferred counter and succeed the command */ + MSInterfaceInfo->State.CommandBlock.DataTransferLength -= ((uint32_t)TotalBlocks * VIRTUAL_MEMORY_BLOCK_SIZE); + + 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 with the Write Protect flag status */ + Endpoint_Write_8(0x00); + Endpoint_Write_8(0x00); + Endpoint_Write_8(DISK_READ_ONLY ? 0x80 : 0x00); + Endpoint_Write_8(0x00); + Endpoint_ClearIN(); + + /* Update the bytes transferred counter and succeed the command */ + MSInterfaceInfo->State.CommandBlock.DataTransferLength -= 4; + + return true; +} diff --git a/Demos/Device/ClassDriver/VirtualSerialMassStorage/Lib/SCSI.h b/Demos/Device/ClassDriver/VirtualSerialMassStorage/Lib/SCSI.h index 96c8d6a35..85f77c424 100644 --- a/Demos/Device/ClassDriver/VirtualSerialMassStorage/Lib/SCSI.h +++ b/Demos/Device/ClassDriver/VirtualSerialMassStorage/Lib/SCSI.h @@ -1,88 +1,88 @@ -/* - LUFA Library - Copyright (C) Dean Camera, 2011. - - dean [at] fourwalledcubicle [dot] com - www.lufa-lib.org -*/ - -/* - Copyright 2011 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 disclaim 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 - #include - - #include - - #include "VirtualSerialMassStorage.h" - #include "Descriptors.h" - #include "DataflashManager.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 - - /** Value for the DeviceType entry in the SCSI_Inquiry_Response_t enum, indicating a CD-ROM device. */ - #define DEVICE_TYPE_CDROM 0x05 - - /* 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 - +/* + LUFA Library + Copyright (C) Dean Camera, 2011. + + dean [at] fourwalledcubicle [dot] com + www.lufa-lib.org +*/ + +/* + Copyright 2011 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 disclaim 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 + #include + + #include + + #include "VirtualSerialMassStorage.h" + #include "Descriptors.h" + #include "DataflashManager.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 + + /** Value for the DeviceType entry in the SCSI_Inquiry_Response_t enum, indicating a CD-ROM device. */ + #define DEVICE_TYPE_CDROM 0x05 + + /* 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 + -- cgit v1.2.3