//----------------------------------------------------------------------------- /// State machine for the MSD %device driver /// \param pMsdDriver Pointer to a MSDDriver instance //----------------------------------------------------------------------------- void MSDD_StateMachine(MSDDriver * pMsdDriver) { MSDCommandState *commandState = &(pMsdDriver->commandState); MSCbw *cbw = &(commandState->cbw); MSCsw *csw = &(commandState->csw); MSDTransfer *transfer = &(commandState->transfer); unsigned char status; // Identify current driver state switch (pMsdDriver->state) { //---------------------- case MSDD_STATE_READ_CBW: //---------------------- // Start the CBW read operation transfer->semaphore = 0; status = MSDD_Read(cbw, MSD_CBW_SIZE, (TransferCallback) MSDDriver_Callback, (void *) transfer); // Check operation result code if (status == USBD_STATUS_SUCCESS) { // If the command was successful, wait for transfer pMsdDriver->state = MSDD_STATE_WAIT_CBW; } break; //---------------------- case MSDD_STATE_WAIT_CBW: //---------------------- // Check transfer semaphore if (transfer->semaphore > 0) { // Take semaphore and terminate transfer transfer->semaphore--; // Check if transfer was successful if (transfer->status == USBD_STATUS_SUCCESS) { TRACE_INFO_WP("------------------------------\n\r"); // Process received command pMsdDriver->state = MSDD_STATE_PROCESS_CBW; } else if (transfer->status == USBD_STATUS_RESET) { TRACE_INFO("MSDD_StateMachine: EP resetted\n\r"); pMsdDriver->state = MSDD_STATE_READ_CBW; } else { TRACE_WARNING( "MSDD_StateMachine: Failed to read CBW\n\r"); pMsdDriver->state = MSDD_STATE_READ_CBW; } } break; //------------------------- case MSDD_STATE_PROCESS_CBW: //------------------------- // Check if this is a new command if (commandState->state == 0) { // Copy the CBW tag csw->dCSWTag = cbw->dCBWTag; // Check that the CBW is 31 bytes long if ((transfer->transferred != MSD_CBW_SIZE) || (transfer->remaining != 0)) { TRACE_WARNING( "MSDD_StateMachine: Invalid CBW (len %d)\n\r", (int)transfer->transferred); // Wait for a reset recovery pMsdDriver->waitResetRecovery = 1; // Halt the Bulk-IN and Bulk-OUT pipes MSDD_Halt(MSDD_CASE_STALL_OUT | MSDD_CASE_STALL_IN); csw->bCSWStatus = MSD_CSW_COMMAND_FAILED; pMsdDriver->state = MSDD_STATE_READ_CBW; } // Check the CBW Signature else if (cbw->dCBWSignature != MSD_CBW_SIGNATURE) { TRACE_WARNING( "MSD_BOTStateMachine: Invalid CBW (Bad signature)\n\r"); // Wait for a reset recovery pMsdDriver->waitResetRecovery = 1; // Halt the Bulk-IN and Bulk-OUT pipes MSDD_Halt(MSDD_CASE_STALL_OUT | MSDD_CASE_STALL_IN); csw->bCSWStatus = MSD_CSW_COMMAND_FAILED; pMsdDriver->state = MSDD_STATE_READ_CBW; } else { // Pre-process command MSDD_PreProcessCommand(pMsdDriver); } } // Process command if (csw->bCSWStatus == MSDD_STATUS_SUCCESS) { if (MSDD_ProcessCommand(pMsdDriver)) { // Post-process command if it is finished if (MSDD_PostProcessCommand(pMsdDriver)) { TRACE_INFO_WP("WaitHALT "); pMsdDriver->state = MSDD_STATE_WAIT_HALT; } else { pMsdDriver->state = MSDD_STATE_SEND_CSW; } } TRACE_INFO_WP("\n\r"); } break; //---------------------- case MSDD_STATE_SEND_CSW: //---------------------- // Set signature csw->dCSWSignature = MSD_CSW_SIGNATURE; // Start the CSW write operation status = MSDD_Write(csw, MSD_CSW_SIZE, (TransferCallback) MSDDriver_Callback, (void *) transfer); // Check operation result code if (status == USBD_STATUS_SUCCESS) { TRACE_INFO_WP("SendCSW "); // Wait for end of transfer pMsdDriver->state = MSDD_STATE_WAIT_CSW; } break; //---------------------- case MSDD_STATE_WAIT_CSW: //---------------------- // Check transfer semaphore if (transfer->semaphore > 0) { // Take semaphore and terminate transfer transfer->semaphore--; // Check if transfer was successful if (transfer->status == USBD_STATUS_RESET) { TRACE_INFO("MSDD_StateMachine: EP resetted\n\r"); } else if (transfer->status == USBD_STATUS_ABORTED) { TRACE_WARNING( "MSDD_StateMachine: Failed to send CSW\n\r"); } else { TRACE_INFO_WP("ok"); } // Read new CBW pMsdDriver->state = MSDD_STATE_READ_CBW; } break; //---------------------- case MSDD_STATE_WAIT_HALT: //---------------------- if (MSDD_IsHalted() == 0) { pMsdDriver->state = MSDD_STATE_SEND_CSW; } break; } }
//------------------------------------------------------------------------------ // Internal functions //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ //! \brief Performs a WRITE (10) command on the specified LUN. //! //! The data to write is first received from the USB host and then //! actually written on the media. //! This function operates asynchronously and must be called multiple //! times to complete. A result code of MSDDriver_STATUS_INCOMPLETE //! indicates that at least another call of the method is necessary. //! \param lun Pointer to the LUN affected by the command //! \param commandState Current state of the command //! \return Operation result code (SUCCESS, ERROR, INCOMPLETE or PARAMETER) //! \see MSDLun //! \see MSDCommandState //------------------------------------------------------------------------------ static unsigned char SBC_Write10(MSDLun *lun, MSDCommandState *commandState) { unsigned char status; unsigned char result = MSDD_STATUS_INCOMPLETE; MSDTransfer *transfer = &(commandState->transfer); SBCWrite10 *command = (SBCWrite10 *) commandState->cbw.pCommand; // Init command state if (commandState->state == 0) { commandState->state = SBC_STATE_READ; } #if !defined(AT91C_EBI_SDRAM) && !defined(BOARD_USB_UDPHS) // Convert length from bytes to blocks commandState->length /= lun->blockSize; #endif // Check if length equals 0 if (commandState->length == 0) { TRACE_INFO_WP("End "); result = MSDD_STATUS_SUCCESS; } else { // Current command status switch (commandState->state) { //------------------ case SBC_STATE_READ: //------------------ TRACE_INFO_WP("Receive "); #if !defined(AT91C_EBI_SDRAM) && !defined(BOARD_USB_UDPHS) // Read one block of data sent by the host status = MSDD_Read((void*)lun->readWriteBuffer, lun->blockSize, (TransferCallback) MSDDriver_Callback, (void *) transfer); #else status = MSDD_Read((void*)(lun->media->baseAddress + lun->baseAddress + DWORDB(command->pLogicalBlockAddress) * lun->blockSize), commandState->length, (TransferCallback) MSDDriver_Callback, (void *) transfer); #endif // Check operation result code if (status != USBD_STATUS_SUCCESS) { TRACE_WARNING( "RBC_Write10: Failed to start receiving data\n\r"); SBC_UpdateSenseData(&(lun->requestSenseData), SBC_SENSE_KEY_HARDWARE_ERROR, 0, 0); result = MSDD_STATUS_ERROR; } else { // Prepare next device state commandState->state = SBC_STATE_WAIT_READ; } break; //----------------------- case SBC_STATE_WAIT_READ: //----------------------- TRACE_INFO_WP("Wait "); // Check semaphore if (transfer->semaphore > 0) { transfer->semaphore--; commandState->state = SBC_STATE_WRITE; } break; //------------------- case SBC_STATE_WRITE: //------------------- // Check the result code of the read operation if (transfer->status != USBD_STATUS_SUCCESS) { TRACE_WARNING( "RBC_Write10: Failed to received data\n\r"); SBC_UpdateSenseData(&(lun->requestSenseData), SBC_SENSE_KEY_HARDWARE_ERROR, 0, 0); result = MSDD_STATUS_ERROR; } else { #if !defined(AT91C_EBI_SDRAM) && !defined(BOARD_USB_UDPHS) // Write the block to the media status = LUN_Write(lun, DWORDB(command->pLogicalBlockAddress), lun->readWriteBuffer, 1, (TransferCallback) MSDDriver_Callback, (void *) transfer); #else MSDDriver_Callback(transfer, MED_STATUS_SUCCESS, 0, 0); status = LUN_STATUS_SUCCESS; #endif // Check operation result code if (status != USBD_STATUS_SUCCESS) { TRACE_WARNING( "RBC_Write10: Failed to start media write\n\r"); SBC_UpdateSenseData(&(lun->requestSenseData), SBC_SENSE_KEY_NOT_READY, 0, 0); result = MSDD_STATUS_ERROR; } else { // Prepare next state commandState->state = SBC_STATE_WAIT_WRITE; } } break; //------------------------ case SBC_STATE_WAIT_WRITE: //------------------------ TRACE_INFO_WP("Wait "); // Check semaphore value if (transfer->semaphore > 0) { // Take semaphore and move to next state transfer->semaphore--; commandState->state = SBC_STATE_NEXT_BLOCK; } break; //------------------------ case SBC_STATE_NEXT_BLOCK: //------------------------ // Check operation result code if (transfer->status != USBD_STATUS_SUCCESS) { TRACE_WARNING( "RBC_Write10: Failed to write media\n\r"); SBC_UpdateSenseData(&(lun->requestSenseData), SBC_SENSE_KEY_RECOVERED_ERROR, SBC_ASC_TOO_MUCH_WRITE_DATA, 0); result = MSDD_STATUS_ERROR; } else { // Update transfer length and block address #if !defined(AT91C_EBI_SDRAM) && !defined(BOARD_USB_UDPHS) commandState->length--; #else commandState->length = 0; #endif STORE_DWORDB(DWORDB(command->pLogicalBlockAddress) + 1, command->pLogicalBlockAddress); // Check if transfer is finished if (commandState->length == 0) { result = MSDD_STATUS_SUCCESS; } else { commandState->state = SBC_STATE_READ; } } break; } } #if !defined(AT91C_EBI_SDRAM) && !defined(BOARD_USB_UDPHS) // Convert length from blocks to bytes commandState->length *= lun->blockSize; #endif return result; }