X-Git-Url: http://git.linex4red.de/pub/USBasp.git/blobdiff_plain/071e02c6b6b4837fa9cf0b6d4c749994e02638d7..a9abe1fdea5ccf67aaccab6fdde300d6fcf2d15a:/Projects/Incomplete/StandaloneProgrammer/Lib/SCSI.c?ds=sidebyside diff --git a/Projects/Incomplete/StandaloneProgrammer/Lib/SCSI.c b/Projects/Incomplete/StandaloneProgrammer/Lib/SCSI.c index 402160496..2d000b8ea 100644 --- a/Projects/Incomplete/StandaloneProgrammer/Lib/SCSI.c +++ b/Projects/Incomplete/StandaloneProgrammer/Lib/SCSI.c @@ -1,21 +1,21 @@ /* LUFA Library - Copyright (C) Dean Camera, 2010. - + Copyright (C) Dean Camera, 2012. + dean [at] fourwalledcubicle [dot] com - www.fourwalledcubicle.com + www.lufa-lib.org */ /* - Copyright 2010 Dean Camera (dean [at] fourwalledcubicle [dot] com) + Copyright 2012 Dean Camera (dean [at] fourwalledcubicle [dot] com) - Permission to use, copy, modify, distribute, and sell this + 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 + 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 + 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 @@ -34,7 +34,7 @@ * 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" @@ -42,22 +42,22 @@ /** Structure to hold the SCSI response data to a SCSI INQUIRY command. This gives information about the device's * features and capabilities. */ -SCSI_Inquiry_Response_t InquiryData = +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, @@ -65,7 +65,7 @@ SCSI_Inquiry_Response_t InquiryData = .WideBus16Bit = false, .WideBus32Bit = false, .RelAddr = false, - + .VendorID = "LUFA", .ProductID = "Dataflash Disk", .RevisionID = {'0','.','0','0'}, @@ -74,7 +74,7 @@ SCSI_Inquiry_Response_t InquiryData = /** 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. */ -SCSI_Request_Sense_Response_t SenseData = +static SCSI_Request_Sense_Response_t SenseData = { .ResponseCode = 0x70, .AdditionalLength = 0x0A, @@ -86,39 +86,42 @@ SCSI_Request_Sense_Response_t SenseData = * 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) { - /* Set initial sense data, before the requested command is processed */ - SCSI_SET_SENSE(SCSI_SENSE_KEY_GOOD, - SCSI_ASENSE_NO_ADDITIONAL_INFORMATION, - SCSI_ASENSEQ_NO_QUALIFIER); - + 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: - SCSI_Command_Inquiry(MSInterfaceInfo); + CommandSuccess = SCSI_Command_Inquiry(MSInterfaceInfo); break; case SCSI_CMD_REQUEST_SENSE: - SCSI_Command_Request_Sense(MSInterfaceInfo); + CommandSuccess = SCSI_Command_Request_Sense(MSInterfaceInfo); break; case SCSI_CMD_READ_CAPACITY_10: - SCSI_Command_Read_Capacity_10(MSInterfaceInfo); + CommandSuccess = SCSI_Command_Read_Capacity_10(MSInterfaceInfo); break; case SCSI_CMD_SEND_DIAGNOSTIC: - SCSI_Command_Send_Diagnostic(MSInterfaceInfo); + CommandSuccess = SCSI_Command_Send_Diagnostic(MSInterfaceInfo); break; case SCSI_CMD_WRITE_10: - SCSI_Command_ReadWrite_10(MSInterfaceInfo, DATA_WRITE); + CommandSuccess = SCSI_Command_ReadWrite_10(MSInterfaceInfo, DATA_WRITE); break; case SCSI_CMD_READ_10: - SCSI_Command_ReadWrite_10(MSInterfaceInfo, DATA_READ); + 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: @@ -129,20 +132,30 @@ bool SCSI_DecodeSCSICommand(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo) break; } - return (SenseData.SenseKey == SCSI_SENSE_KEY_GOOD); + /* 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 void SCSI_Command_Inquiry(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo) +static bool SCSI_Command_Inquiry(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo) { - uint16_t AllocationLength = (((uint16_t)MSInterfaceInfo->State.CommandBlock.SCSICommandData[3] << 8) | - MSInterfaceInfo->State.CommandBlock.SCSICommandData[4]); - uint16_t BytesTransferred = (AllocationLength < sizeof(InquiryData))? AllocationLength : - sizeof(InquiryData); + 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))) || @@ -153,59 +166,65 @@ static void SCSI_Command_Inquiry(USB_ClassInfo_MS_Device_t* const MSInterfaceInf SCSI_ASENSE_INVALID_FIELD_IN_CDB, SCSI_ASENSEQ_NO_QUALIFIER); - return; + return false; } - - Endpoint_Write_Stream_LE(&InquiryData, BytesTransferred, NO_STREAM_CALLBACK); - uint8_t PadBytes[AllocationLength - BytesTransferred]; - + Endpoint_Write_Stream_LE(&InquiryData, BytesTransferred, NULL); + /* Pad out remaining bytes with 0x00 */ - Endpoint_Write_Stream_LE(&PadBytes, (AllocationLength - BytesTransferred), NO_STREAM_CALLBACK); + 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 void SCSI_Command_Request_Sense(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo) +static bool SCSI_Command_Request_Sense(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo) { uint8_t AllocationLength = MSInterfaceInfo->State.CommandBlock.SCSICommandData[4]; - uint8_t BytesTransferred = (AllocationLength < sizeof(SenseData))? AllocationLength : sizeof(SenseData); - - uint8_t PadBytes[AllocationLength - BytesTransferred]; + uint8_t BytesTransferred = MIN(AllocationLength, sizeof(SenseData)); - Endpoint_Write_Stream_LE(&SenseData, BytesTransferred, NO_STREAM_CALLBACK); - Endpoint_Write_Stream_LE(&PadBytes, (AllocationLength - BytesTransferred), NO_STREAM_CALLBACK); + 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 void SCSI_Command_Read_Capacity_10(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo) +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), NO_STREAM_CALLBACK); - Endpoint_Write_Stream_BE(&MediaBlockSize, sizeof(MediaBlockSize), NO_STREAM_CALLBACK); + 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 @@ -213,8 +232,10 @@ static void SCSI_Command_Read_Capacity_10(USB_ClassInfo_MS_Device_t* const MSInt * 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 void SCSI_Command_Send_Diagnostic(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo) +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))) @@ -224,42 +245,58 @@ static void SCSI_Command_Send_Diagnostic(USB_ClassInfo_MS_Device_t* const MSInte SCSI_ASENSE_INVALID_FIELD_IN_CDB, SCSI_ASENSEQ_NO_QUALIFIER); - return; + 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; + 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 + * 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 void SCSI_Command_ReadWrite_10(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo, const bool IsDataRead) +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(*(uint32_t*)&MSInterfaceInfo->State.CommandBlock.SCSICommandData[7]); - + 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) { @@ -268,9 +305,9 @@ static void SCSI_Command_ReadWrite_10(USB_ClassInfo_MS_Device_t* const MSInterfa SCSI_ASENSE_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE, SCSI_ASENSEQ_NO_QUALIFIER); - return; + 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); @@ -279,5 +316,29 @@ static void SCSI_Command_ReadWrite_10(USB_ClassInfo_MS_Device_t* const MSInterfa /* 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; } #endif