You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							344 lines
						
					
					
						
							13 KiB
						
					
					
				
			
		
		
	
	
							344 lines
						
					
					
						
							13 KiB
						
					
					
				/*
 | 
						|
             LUFA Library
 | 
						|
     Copyright (C) Dean Camera, 2012.
 | 
						|
 | 
						|
  dean [at] fourwalledcubicle [dot] com
 | 
						|
           www.lufa-lib.org
 | 
						|
*/
 | 
						|
 | 
						|
/*
 | 
						|
  Copyright 2012  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 = (VIRTUAL_MEMORY_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 >= VIRTUAL_MEMORY_BLOCKS)
 | 
						|
	{
 | 
						|
		/* Block address is invalid, update SENSE key and return command fail */
 | 
						|
		SCSI_SET_SENSE(SCSI_SENSE_KEY_ILLEGAL_REQUEST,
 | 
						|
		               SCSI_ASENSE_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE,
 | 
						|
		               SCSI_ASENSEQ_NO_QUALIFIER);
 | 
						|
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
 | 
						|
	/* Determine if the packet is a READ (10) or WRITE (10) command, call appropriate function */
 | 
						|
	if (IsDataRead == DATA_READ)
 | 
						|
	  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;
 | 
						|
}
 | 
						|
 |