/******************************************************************************
 	Function:
 		BYTE MSDWriteHandler(void)
 		
 	Description:
 		This funtion processes a write command received through 
 		the MSD class driver
 		
 	PreCondition:
 		None
 		
 	Parameters:
 		None
 		
 	Return Values:
 		BYTE - the current state of the MSDWriteHandler state
 		machine.  The valid values are defined in MSD.h under the 
 		MSDWriteHandler state machine declaration section
 		
 	Remarks:
 		None
 
 *****************************************************************************/
BYTE MSDWriteHandler(void)
{
    static BYTE MSDWriteState = MSD_WRITE10_WAIT;
    
    switch(MSDWriteState)
    {
        case MSD_WRITE10_WAIT:
         	/* Read the LBA, TransferLength fields from Command Block
               NOTE: CB is Big-Endian */
        
        	LBA.v[3]=gblCBW.CBWCB[2];
        	LBA.v[2]=gblCBW.CBWCB[3];
        	LBA.v[1]=gblCBW.CBWCB[4];
        	LBA.v[0]=gblCBW.CBWCB[5];
        	TransferLength.v[1]=gblCBW.CBWCB[7];
        	TransferLength.v[0]=gblCBW.CBWCB[8];
        
        	msd_csw.bCSWStatus=0x0;	
        	
        	MSD_State = MSD_WRITE10_BLOCK;
        	//Fall through to MSD_WRITE10_BLOCK
        case MSD_WRITE10_BLOCK:
            if(TransferLength.Val == 0)
            {
                MSDWriteState = MSD_WRITE10_WAIT;
                break;
            }
            
            MSDWriteState = MSD_WRITE10_RX_SECTOR;
            ptrNextData=(BYTE *)&msd_buffer[0];
              
        	msd_csw.dCSWDataResidue=BLOCKLEN_512;
        	
            //Fall through to MSD_WRITE10_RX_SECTOR
        case MSD_WRITE10_RX_SECTOR:
        {
      		/* Read 512B into msd_buffer*/
      		if(msd_csw.dCSWDataResidue>0) 
      		{
                if(USBHandleBusy(USBMSDOutHandle) == TRUE)
                {
                    break;
                }

                USBMSDOutHandle = USBRxOnePacket(MSD_DATA_OUT_EP,ptrNextData,MSD_OUT_EP_SIZE);
                MSDWriteState = MSD_WRITE10_RX_PACKET;
                //Fall through to MSD_WRITE10_RX_PACKET
      	    }
      	    else
      	    {
          		if(LUNWriteProtectState()) 
                {
              	    gblSenseData[LUN_INDEX].SenseKey=S_NOT_READY;
              	    gblSenseData[LUN_INDEX].ASC=ASC_WRITE_PROTECTED;
              	    gblSenseData[LUN_INDEX].ASCQ=ASCQ_WRITE_PROTECTED;
              	    msd_csw.bCSWStatus=0x01;
              	    //TODO: (DF) - what state should I return to?
              	}
              	else
              	{
      			    MSDWriteState = MSD_WRITE10_SECTOR;     
      			}
      			break;
          	}
        }
        //Fall through to MSD_WRITE10_RX_PACKET
        case MSD_WRITE10_RX_PACKET:
            if(USBHandleBusy(USBMSDOutHandle) == TRUE)
            {
                break;
            }
            
        	gblCBW.dCBWDataTransferLength-=USBHandleGetLength(USBMSDOutHandle);		// 64B read
        	msd_csw.dCSWDataResidue-=USBHandleGetLength(USBMSDOutHandle);
            ptrNextData += MSD_OUT_EP_SIZE;
            
            MSDWriteState = MSD_WRITE10_RX_SECTOR;
            break;
        case MSD_WRITE10_SECTOR:
        {
      		if(LUNSectorWrite(LBA.Val, (BYTE*)&msd_buffer[0], (LBA.Val==0)?TRUE:FALSE) != TRUE)
      		{
          		break;
      		}
      
    //		if (status) {
    //			msd_csw.bCSWStatus=0x01;
    //			/* add some sense keys here*/
    //		}
      
      		LBA.Val++;				// One LBA is written. Write the next LBA
      		TransferLength.Val--;
      
            MSDWriteState = MSD_WRITE10_BLOCK;
            break;
        } 
    }
    
    return MSDWriteState;
}
/******************************************************************************
 	Function:
 		BYTE MSDWriteHandler(void)
 		
 	Description:
 		This funtion processes a write command received through 
 		the MSD class driver
 		
 	PreCondition:
 		None
 		
 	Parameters:
 		None
 		
 	Return Values:
 		BYTE - the current state of the MSDWriteHandler state
 		machine.  The valid values are defined in MSD.h under the 
 		MSDWriteHandler state machine declaration section
 		
 	Remarks:
 		None
 
 *****************************************************************************/
BYTE MSDWriteHandler(void)
{
    switch(MSDWriteState)
    {
        case MSD_WRITE10_WAIT:
         	/* Read the LBA, TransferLength fields from Command Block
               NOTE: CB is Big-Endian */
        
        	LBA.v[3]=gblCBW.CBWCB[2];
        	LBA.v[2]=gblCBW.CBWCB[3];
        	LBA.v[1]=gblCBW.CBWCB[4];
        	LBA.v[0]=gblCBW.CBWCB[5];
        	TransferLength.v[1]=gblCBW.CBWCB[7];
        	TransferLength.v[0]=gblCBW.CBWCB[8];
        
        	//Initially assume success, unless handler code later encounters an
        	//error condition and sets the status to 0x01 or 0x02.
        	msd_csw.bCSWStatus=0x0;	
        	
        	MSD_State = MSD_WRITE10_BLOCK;
        	//Fall through to MSD_WRITE10_BLOCK
        case MSD_WRITE10_BLOCK:
            if(TransferLength.Val == 0)
            {
                MSDWriteState = MSD_WRITE10_WAIT;
                break;
            }
            
            MSDWriteState = MSD_WRITE10_RX_SECTOR;
            ptrNextData=(BYTE *)&msd_buffer[0];
              
        	msd_csw.dCSWDataResidue=BLOCKLEN_512;
        	
            //Fall through to MSD_WRITE10_RX_SECTOR
        case MSD_WRITE10_RX_SECTOR:
        {
      		/* Read 512B into msd_buffer*/
      		if(msd_csw.dCSWDataResidue>0) 
      		{
                if(USBHandleBusy(USBMSDOutHandle) == TRUE)
                {
                    break;
                }

                USBMSDOutHandle = USBRxOnePacket(MSD_DATA_OUT_EP,ptrNextData,MSD_OUT_EP_SIZE);
                MSDWriteState = MSD_WRITE10_RX_PACKET;
                //Fall through to MSD_WRITE10_RX_PACKET
      	    }
      	    else
      	    {
          		//We finished receiving a sector worth of data from the host.
          		//Check if the media is write protected before deciding what
          		//to do with the data.
          		if(LUNWriteProtectState()) 
                {
                    //The device appears to be write protected.
              	    //Let host know error occurred.  The bCSWStatus flag is also used by
              	    //the write handler, to know not to even attempt the write sequence.
              	    msd_csw.bCSWStatus=0x01;    
              	    
                    //Set sense keys so the host knows what caused the error.
              	    gblSenseData[LUN_INDEX].SenseKey=S_NOT_READY;
              	    gblSenseData[LUN_INDEX].ASC=ASC_WRITE_PROTECTED;
              	    gblSenseData[LUN_INDEX].ASCQ=ASCQ_WRITE_PROTECTED;
              	}
   			    MSDWriteState = MSD_WRITE10_SECTOR;     
      			break;
          	}
        }
        //Fall through to MSD_WRITE10_RX_PACKET
        case MSD_WRITE10_RX_PACKET:
            if(USBHandleBusy(USBMSDOutHandle) == TRUE)
            {
                break;
            }
            
        	gblCBW.dCBWDataTransferLength-=USBHandleGetLength(USBMSDOutHandle);		// 64B read
        	msd_csw.dCSWDataResidue-=USBHandleGetLength(USBMSDOutHandle);
            ptrNextData += MSD_OUT_EP_SIZE;
            
            MSDWriteState = MSD_WRITE10_RX_SECTOR;
            break;
        case MSD_WRITE10_SECTOR:
        {
            //Make sure that no error has been detected, before performing the write
            //operation.  If there was an error, skip the write operation, but allow
            //the TransferLength to continue decrementing, so that we can eventually
            //receive all OUT bytes that the host is planning on sending us.  Only
            //after that is complete will the host send the IN token for the CSW packet,
            //which will contain the bCSWStatus letting it know an error occurred.
      		if(msd_csw.bCSWStatus == 0x00)
      		{
          		if(LUNSectorWrite(LBA.Val, (BYTE*)&msd_buffer[0], (LBA.Val==0)?TRUE:FALSE) != TRUE)
          		{
              		//The write operation failed for some reason.  Keep track of retry
              		//attempts and abort if repeated write attempts also fail.
    				if(MSDRetryAttempt < MSD_FAILED_WRITE_MAX_ATTEMPTS)
    				{
    				    MSDRetryAttempt++;
                        break;
    				}
    				else
    				{  
        				//Too many consecutive failed write attempts have occurred. 
        				//Need to give up and abandon the write attempt.
        				msd_csw.bCSWStatus=0x01;		// Error 0x01 Refer page#18
                                                        // of BOT specifications
                        //Set error status sense keys, so the host can check them later
                        //to determine how to proceed.
                        gblSenseData[LUN_INDEX].SenseKey=S_MEDIUM_ERROR;
    			        gblSenseData[LUN_INDEX].ASC=ASC_NO_ADDITIONAL_SENSE_INFORMATION;
    			        gblSenseData[LUN_INDEX].ASCQ=ASCQ_NO_ADDITIONAL_SENSE_INFORMATION;
                    }              		
          		}
      		}
      
            //One LBA is written (unless an error occurred).  Advance state
            //variables so we can eventually finish handling the CBW request.
      		LBA.Val++;				
      		TransferLength.Val--;      
            MSDWriteState = MSD_WRITE10_BLOCK;
            break;
        } 
        
        default:
            //Illegal condition which should not occur.  If for some reason it
            //does, try to let the host know know an error has occurred.
            msd_csw.bCSWStatus=0x02;    //Phase Error
            MSDWriteState = MSD_WRITE10_WAIT;            
    }
    
    return MSDWriteState;
}