From 9f7883fa2b78fd7907162987fc65b71d3c6b19fa Mon Sep 17 00:00:00 2001 From: Dean Camera Date: Fri, 26 Nov 2010 04:16:47 +0000 Subject: [PATCH] Added ability to write protect Mass Storage disk write operations from the host OS. --- .../Device/ClassDriver/MassStorage/Lib/SCSI.c | 37 ++++++++++++++++- .../Device/ClassDriver/MassStorage/Lib/SCSI.h | 1 + .../ClassDriver/MassStorage/MassStorage.h | 3 ++ .../Lib/DataflashManager.h | 6 --- .../MassStorageKeyboard/Lib/SCSI.c | 35 ++++++++++++++++ .../MassStorageKeyboard/Lib/SCSI.h | 1 + .../MassStorageKeyboard/MassStorageKeyboard.h | 9 +++++ Demos/Device/LowLevel/MassStorage/Lib/SCSI.c | 40 ++++++++++++++++++- Demos/Device/LowLevel/MassStorage/Lib/SCSI.h | 1 + .../Device/LowLevel/MassStorage/MassStorage.h | 3 ++ LUFA/ManPages/ChangeLog.txt | 1 + .../Lib/DataflashManager.h | 9 +++-- .../StandaloneProgrammer/Lib/SCSI.c | 39 +++++++++++++++++- .../StandaloneProgrammer/Lib/SCSI.h | 1 + Projects/TempDataLogger/Lib/SCSI.c | 37 ++++++++++++++++- Projects/TempDataLogger/Lib/SCSI.h | 1 + Projects/TempDataLogger/TempDataLogger.h | 3 ++ Projects/Webserver/Lib/DataflashManager.h | 3 ++ Projects/Webserver/Lib/SCSI.c | 37 ++++++++++++++++- Projects/Webserver/Lib/SCSI.h | 1 + 20 files changed, 252 insertions(+), 16 deletions(-) diff --git a/Demos/Device/ClassDriver/MassStorage/Lib/SCSI.c b/Demos/Device/ClassDriver/MassStorage/Lib/SCSI.c index 5e0a82be2b..ac6c9e7c70 100644 --- a/Demos/Device/ClassDriver/MassStorage/Lib/SCSI.c +++ b/Demos/Device/ClassDriver/MassStorage/Lib/SCSI.c @@ -113,6 +113,9 @@ bool SCSI_DecodeSCSICommand(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo) 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: @@ -281,6 +284,17 @@ static bool SCSI_Command_ReadWrite_10(USB_ClassInfo_MS_Device_t* const MSInterfa 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]); @@ -302,7 +316,7 @@ static bool SCSI_Command_ReadWrite_10(USB_ClassInfo_MS_Device_t* const MSInterfa /* 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); @@ -315,3 +329,24 @@ static bool SCSI_Command_ReadWrite_10(USB_ClassInfo_MS_Device_t* const MSInterfa 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_Byte(0x00); + Endpoint_Write_Byte(0x00); + Endpoint_Write_Byte(DISK_READ_ONLY ? 0x80 : 0x00); + Endpoint_Write_Byte(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/MassStorage/Lib/SCSI.h b/Demos/Device/ClassDriver/MassStorage/Lib/SCSI.h index e8f5eb65e0..6f2e19d398 100644 --- a/Demos/Device/ClassDriver/MassStorage/Lib/SCSI.h +++ b/Demos/Device/ClassDriver/MassStorage/Lib/SCSI.h @@ -81,6 +81,7 @@ 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 diff --git a/Demos/Device/ClassDriver/MassStorage/MassStorage.h b/Demos/Device/ClassDriver/MassStorage/MassStorage.h index f7ac3636f6..63a864288f 100644 --- a/Demos/Device/ClassDriver/MassStorage/MassStorage.h +++ b/Demos/Device/ClassDriver/MassStorage/MassStorage.h @@ -73,6 +73,9 @@ /** Blocks in each LUN, calculated from the total capacity divided by the total number of Logical Units in the device. */ #define LUN_MEDIA_BLOCKS (VIRTUAL_MEMORY_BLOCKS / TOTAL_LUNS) + + /** Indicates if the disk is write protected or not. */ + #define DISK_READ_ONLY false /* Function Prototypes: */ void SetupHardware(void); diff --git a/Demos/Device/ClassDriver/MassStorageKeyboard/Lib/DataflashManager.h b/Demos/Device/ClassDriver/MassStorageKeyboard/Lib/DataflashManager.h index 3db19dad46..d7f8605be0 100644 --- a/Demos/Device/ClassDriver/MassStorageKeyboard/Lib/DataflashManager.h +++ b/Demos/Device/ClassDriver/MassStorageKeyboard/Lib/DataflashManager.h @@ -63,12 +63,6 @@ /** Total number of blocks of the virtual memory for reporting to the host as the device's total capacity. */ #define VIRTUAL_MEMORY_BLOCKS (VIRTUAL_MEMORY_BYTES / VIRTUAL_MEMORY_BLOCK_SIZE) - /** Total number of logical drives within the device - must be non-zero. */ - #define TOTAL_LUNS 1 - - /** Blocks in each LUN, calculated from the total capacity divided by the total number of Logical Units in the device. */ - #define LUN_MEDIA_BLOCKS (VIRTUAL_MEMORY_BLOCKS / TOTAL_LUNS) - /* Function Prototypes: */ void DataflashManager_WriteBlocks(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo, const uint32_t BlockAddress, diff --git a/Demos/Device/ClassDriver/MassStorageKeyboard/Lib/SCSI.c b/Demos/Device/ClassDriver/MassStorageKeyboard/Lib/SCSI.c index 5e0a82be2b..5d0adf90a1 100644 --- a/Demos/Device/ClassDriver/MassStorageKeyboard/Lib/SCSI.c +++ b/Demos/Device/ClassDriver/MassStorageKeyboard/Lib/SCSI.c @@ -113,6 +113,9 @@ bool SCSI_DecodeSCSICommand(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo) 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: @@ -281,6 +284,17 @@ static bool SCSI_Command_ReadWrite_10(USB_ClassInfo_MS_Device_t* const MSInterfa 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]); @@ -315,3 +329,24 @@ static bool SCSI_Command_ReadWrite_10(USB_ClassInfo_MS_Device_t* const MSInterfa 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_Byte(0x00); + Endpoint_Write_Byte(0x00); + Endpoint_Write_Byte(DISK_READ_ONLY ? 0x80 : 0x00); + Endpoint_Write_Byte(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/MassStorageKeyboard/Lib/SCSI.h b/Demos/Device/ClassDriver/MassStorageKeyboard/Lib/SCSI.h index bab018cc5f..9a5bd802cf 100644 --- a/Demos/Device/ClassDriver/MassStorageKeyboard/Lib/SCSI.h +++ b/Demos/Device/ClassDriver/MassStorageKeyboard/Lib/SCSI.h @@ -81,6 +81,7 @@ 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 diff --git a/Demos/Device/ClassDriver/MassStorageKeyboard/MassStorageKeyboard.h b/Demos/Device/ClassDriver/MassStorageKeyboard/MassStorageKeyboard.h index e07157a646..110953cbd7 100644 --- a/Demos/Device/ClassDriver/MassStorageKeyboard/MassStorageKeyboard.h +++ b/Demos/Device/ClassDriver/MassStorageKeyboard/MassStorageKeyboard.h @@ -73,6 +73,15 @@ /** LED mask for the library LED driver, to indicate that the USB interface is busy. */ #define LEDMASK_USB_BUSY LEDS_LED2 + /** Total number of logical drives within the device - must be non-zero. */ + #define TOTAL_LUNS 1 + + /** Blocks in each LUN, calculated from the total capacity divided by the total number of Logical Units in the device. */ + #define LUN_MEDIA_BLOCKS (VIRTUAL_MEMORY_BLOCKS / TOTAL_LUNS) + + /** Indicates if the disk is write protected or not. */ + #define DISK_READ_ONLY false + /* Function Prototypes: */ void SetupHardware(void); diff --git a/Demos/Device/LowLevel/MassStorage/Lib/SCSI.c b/Demos/Device/LowLevel/MassStorage/Lib/SCSI.c index 598c689fdf..63f1ec3b42 100644 --- a/Demos/Device/LowLevel/MassStorage/Lib/SCSI.c +++ b/Demos/Device/LowLevel/MassStorage/Lib/SCSI.c @@ -111,6 +111,9 @@ bool SCSI_DecodeSCSICommand(void) case SCSI_CMD_READ_10: CommandSuccess = SCSI_Command_ReadWrite_10(DATA_READ); break; + case SCSI_CMD_MODE_SENSE_6: + CommandSuccess = SCSI_Command_ModeSense_6(); + break; case SCSI_CMD_TEST_UNIT_READY: case SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL: case SCSI_CMD_VERIFY_10: @@ -278,9 +281,23 @@ static bool SCSI_Command_Send_Diagnostic(void) */ static bool SCSI_Command_ReadWrite_10(const bool IsDataRead) { - uint32_t BlockAddress = SwapEndian_32(*(uint32_t*)&CommandBlock.SCSICommandData[2]); - uint16_t TotalBlocks = SwapEndian_16(*(uint16_t*)&CommandBlock.SCSICommandData[7]); + 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; + } + + BlockAddress = SwapEndian_32(*(uint32_t*)&CommandBlock.SCSICommandData[2]); + TotalBlocks = SwapEndian_16(*(uint16_t*)&CommandBlock.SCSICommandData[7]); + /* Check if the block address is outside the maximum allowable value for the LUN */ if (BlockAddress >= LUN_MEDIA_BLOCKS) { @@ -309,3 +326,22 @@ static bool SCSI_Command_ReadWrite_10(const bool IsDataRead) 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. + * + * \return Boolean true if the command completed successfully, false otherwise. + */ +static bool SCSI_Command_ModeSense_6(void) +{ + /* Send an empty header response with the Write Protect flag status */ + Endpoint_Write_Byte(0x00); + Endpoint_Write_Byte(0x00); + Endpoint_Write_Byte(DISK_READ_ONLY ? 0x80 : 0x00); + Endpoint_Write_Byte(0x00); + Endpoint_ClearIN(); + + /* Update the bytes transferred counter and succeed the command */ + CommandBlock.DataTransferLength -= 4; + + return true; +} diff --git a/Demos/Device/LowLevel/MassStorage/Lib/SCSI.h b/Demos/Device/LowLevel/MassStorage/Lib/SCSI.h index 962a2acf7f..8710db88bc 100644 --- a/Demos/Device/LowLevel/MassStorage/Lib/SCSI.h +++ b/Demos/Device/LowLevel/MassStorage/Lib/SCSI.h @@ -143,6 +143,7 @@ static bool SCSI_Command_Read_Capacity_10(void); static bool SCSI_Command_Send_Diagnostic(void); static bool SCSI_Command_ReadWrite_10(const bool IsDataRead); + static bool SCSI_Command_ModeSense_6(void); #endif #endif diff --git a/Demos/Device/LowLevel/MassStorage/MassStorage.h b/Demos/Device/LowLevel/MassStorage/MassStorage.h index 0baa369442..d817075d87 100644 --- a/Demos/Device/LowLevel/MassStorage/MassStorage.h +++ b/Demos/Device/LowLevel/MassStorage/MassStorage.h @@ -68,6 +68,9 @@ /** LED mask for the library LED driver, to indicate that the USB interface is busy. */ #define LEDMASK_USB_BUSY LEDS_LED2 + /** Indicates if the disk is write protected or not. */ + #define DISK_READ_ONLY false + /* Global Variables: */ extern MS_CommandBlockWrapper_t CommandBlock; extern MS_CommandStatusWrapper_t CommandStatus; diff --git a/LUFA/ManPages/ChangeLog.txt b/LUFA/ManPages/ChangeLog.txt index 402865fe70..57f8677359 100644 --- a/LUFA/ManPages/ChangeLog.txt +++ b/LUFA/ManPages/ChangeLog.txt @@ -12,6 +12,7 @@ * - None * - Library Applications: * - Added new incomplete MIDIToneGenerator project + * - Added ability to write protect Mass Storage disk write operations from the host OS * * Changed: * - Core: diff --git a/Projects/Incomplete/StandaloneProgrammer/Lib/DataflashManager.h b/Projects/Incomplete/StandaloneProgrammer/Lib/DataflashManager.h index 9bd59d7e07..b963fa22b5 100644 --- a/Projects/Incomplete/StandaloneProgrammer/Lib/DataflashManager.h +++ b/Projects/Incomplete/StandaloneProgrammer/Lib/DataflashManager.h @@ -52,18 +52,21 @@ /* 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) + #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 + #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) + #define VIRTUAL_MEMORY_BLOCKS (VIRTUAL_MEMORY_BYTES / VIRTUAL_MEMORY_BLOCK_SIZE) + /** Indicates if the disk is write protected or not. */ + #define DISK_READ_ONLY false + /* Function Prototypes: */ #if defined(USB_CAN_BE_DEVICE) void DataflashManager_WriteBlocks(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo, diff --git a/Projects/Incomplete/StandaloneProgrammer/Lib/SCSI.c b/Projects/Incomplete/StandaloneProgrammer/Lib/SCSI.c index 7b2a8fa834..a23e211099 100644 --- a/Projects/Incomplete/StandaloneProgrammer/Lib/SCSI.c +++ b/Projects/Incomplete/StandaloneProgrammer/Lib/SCSI.c @@ -114,6 +114,9 @@ bool SCSI_DecodeSCSICommand(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo) 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: @@ -282,6 +285,17 @@ static bool SCSI_Command_ReadWrite_10(USB_ClassInfo_MS_Device_t* const MSInterfa 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]); @@ -298,7 +312,7 @@ static bool SCSI_Command_ReadWrite_10(USB_ClassInfo_MS_Device_t* const MSInterfa 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); @@ -310,5 +324,26 @@ static bool SCSI_Command_ReadWrite_10(USB_ClassInfo_MS_Device_t* const MSInterfa return true; } -#endif +/** 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_Byte(0x00); + Endpoint_Write_Byte(0x00); + Endpoint_Write_Byte(DISK_READ_ONLY ? 0x80 : 0x00); + Endpoint_Write_Byte(0x00); + Endpoint_ClearIN(); + + /* Update the bytes transferred counter and succeed the command */ + MSInterfaceInfo->State.CommandBlock.DataTransferLength -= 4; + + return true; +} +#endif \ No newline at end of file diff --git a/Projects/Incomplete/StandaloneProgrammer/Lib/SCSI.h b/Projects/Incomplete/StandaloneProgrammer/Lib/SCSI.h index 36ad66d909..34ed852323 100644 --- a/Projects/Incomplete/StandaloneProgrammer/Lib/SCSI.h +++ b/Projects/Incomplete/StandaloneProgrammer/Lib/SCSI.h @@ -82,6 +82,7 @@ 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 diff --git a/Projects/TempDataLogger/Lib/SCSI.c b/Projects/TempDataLogger/Lib/SCSI.c index b1e88dc708..6a51c74e8d 100644 --- a/Projects/TempDataLogger/Lib/SCSI.c +++ b/Projects/TempDataLogger/Lib/SCSI.c @@ -113,6 +113,9 @@ bool SCSI_DecodeSCSICommand(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo) 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: @@ -281,6 +284,17 @@ static bool SCSI_Command_ReadWrite_10(USB_ClassInfo_MS_Device_t* const MSInterfa 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]); @@ -297,7 +311,7 @@ static bool SCSI_Command_ReadWrite_10(USB_ClassInfo_MS_Device_t* const MSInterfa 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); @@ -310,3 +324,24 @@ static bool SCSI_Command_ReadWrite_10(USB_ClassInfo_MS_Device_t* const MSInterfa 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_Byte(0x00); + Endpoint_Write_Byte(0x00); + Endpoint_Write_Byte(DISK_READ_ONLY ? 0x80 : 0x00); + Endpoint_Write_Byte(0x00); + Endpoint_ClearIN(); + + /* Update the bytes transferred counter and succeed the command */ + MSInterfaceInfo->State.CommandBlock.DataTransferLength -= 4; + + return true; +} diff --git a/Projects/TempDataLogger/Lib/SCSI.h b/Projects/TempDataLogger/Lib/SCSI.h index 27eab5eb7e..e9aa4f0741 100644 --- a/Projects/TempDataLogger/Lib/SCSI.h +++ b/Projects/TempDataLogger/Lib/SCSI.h @@ -81,6 +81,7 @@ 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 diff --git a/Projects/TempDataLogger/TempDataLogger.h b/Projects/TempDataLogger/TempDataLogger.h index f81a57410d..39aa43c699 100644 --- a/Projects/TempDataLogger/TempDataLogger.h +++ b/Projects/TempDataLogger/TempDataLogger.h @@ -78,6 +78,9 @@ /** Default log interval when the EEPROM is blank, in 500ms ticks. */ #define DEFAULT_LOG_INTERVAL 20 + /** Indicates if the disk is write protected or not. */ + #define DISK_READ_ONLY false + /* Type Defines: */ typedef struct { diff --git a/Projects/Webserver/Lib/DataflashManager.h b/Projects/Webserver/Lib/DataflashManager.h index acbeba54ce..a53c887766 100644 --- a/Projects/Webserver/Lib/DataflashManager.h +++ b/Projects/Webserver/Lib/DataflashManager.h @@ -64,6 +64,9 @@ */ #define VIRTUAL_MEMORY_BLOCKS (VIRTUAL_MEMORY_BYTES / VIRTUAL_MEMORY_BLOCK_SIZE) + /** Indicates if the disk is write protected or not. */ + #define DISK_READ_ONLY false + /* Function Prototypes: */ void DataflashManager_WriteBlocks(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo, const uint32_t BlockAddress, diff --git a/Projects/Webserver/Lib/SCSI.c b/Projects/Webserver/Lib/SCSI.c index b1e88dc708..6a51c74e8d 100644 --- a/Projects/Webserver/Lib/SCSI.c +++ b/Projects/Webserver/Lib/SCSI.c @@ -113,6 +113,9 @@ bool SCSI_DecodeSCSICommand(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo) 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: @@ -281,6 +284,17 @@ static bool SCSI_Command_ReadWrite_10(USB_ClassInfo_MS_Device_t* const MSInterfa 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]); @@ -297,7 +311,7 @@ static bool SCSI_Command_ReadWrite_10(USB_ClassInfo_MS_Device_t* const MSInterfa 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); @@ -310,3 +324,24 @@ static bool SCSI_Command_ReadWrite_10(USB_ClassInfo_MS_Device_t* const MSInterfa 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_Byte(0x00); + Endpoint_Write_Byte(0x00); + Endpoint_Write_Byte(DISK_READ_ONLY ? 0x80 : 0x00); + Endpoint_Write_Byte(0x00); + Endpoint_ClearIN(); + + /* Update the bytes transferred counter and succeed the command */ + MSInterfaceInfo->State.CommandBlock.DataTransferLength -= 4; + + return true; +} diff --git a/Projects/Webserver/Lib/SCSI.h b/Projects/Webserver/Lib/SCSI.h index 8eae210f17..8360234b56 100644 --- a/Projects/Webserver/Lib/SCSI.h +++ b/Projects/Webserver/Lib/SCSI.h @@ -80,6 +80,7 @@ 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