/*
              LUFA Library
-     Copyright (C) Dean Camera, 2010.
+     Copyright (C) Dean Camera, 2011.
 
   dean [at] fourwalledcubicle [dot] com
            www.lufa-lib.org
 */
 
 /*
-  Copyright 2010  Dean Camera (dean [at] fourwalledcubicle [dot] com)
+  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
 /** 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,
 /** 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,
                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:
                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, sizeof(PadBytes), NO_STREAM_CALLBACK);
+       Endpoint_Null_Stream((AllocationLength - BytesTransferred), NULL);
 
        /* Finalize the stream transfer to send the last packet */
        Endpoint_ClearIN();
        uint8_t  AllocationLength = MSInterfaceInfo->State.CommandBlock.SCSICommandData[4];
        uint8_t  BytesTransferred = (AllocationLength < sizeof(SenseData))? AllocationLength : sizeof(SenseData);
 
-       uint8_t PadBytes[AllocationLength - BytesTransferred];
-
-       Endpoint_Write_Stream_LE(&SenseData, BytesTransferred, NO_STREAM_CALLBACK);
-       Endpoint_Write_Stream_LE(&PadBytes, sizeof(PadBytes), 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 */
        uint32_t LastBlockAddressInLUN = (LUN_MEDIA_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 */
        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]);
 
        /* 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);
        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;
+}