//----------------------------------------------------------------------------- /// 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; } }
//------------------------------------------------------------------------------ //! \brief Performs a MODE SENSE (6) command. //! //! 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_ModeSense6(MSDCommandState *commandState) { unsigned char result = MSDD_STATUS_INCOMPLETE; unsigned char status; MSDTransfer *transfer = &(commandState->transfer); // Check if mode page is supported if (((SBCCommand *) commandState->cbw.pCommand)->modeSense6.bPageCode != SBC_PAGE_RETURN_ALL) { return MSDD_STATUS_PARAMETER; } // Initialize command state if needed if (commandState->state == 0) { commandState->state = SBC_STATE_WRITE; } // Check current command state switch (commandState->state) { //------------------- case SBC_STATE_WRITE: //------------------- // Start transfer status = MSDD_Write((void *) &modeParameterHeader6, commandState->length, (TransferCallback) MSDDriver_Callback, (void *) transfer); // Check operation result code if (status != USBD_STATUS_SUCCESS) { TRACE_WARNING( "SPC_ModeSense6: Cannot start data transfer\n\r"); result = MSDD_STATUS_ERROR; } else { // Proceed to 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 terminate command transfer->semaphore--; if (transfer->status != USBD_STATUS_SUCCESS) { TRACE_WARNING( "SPC_ModeSense6: Data transfer failed\n\r"); result = MSDD_STATUS_ERROR; } else { result = MSDD_STATUS_SUCCESS; } // Update length field commandState->length -= transfer->transferred; } break; } return result; }
//------------------------------------------------------------------------------ //! \brief Performs a REQUEST SENSE command. //! //! 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_RequestSense(MSDLun *lun, MSDCommandState *commandState) { unsigned char result = MSDD_STATUS_INCOMPLETE; unsigned char status; MSDTransfer *transfer = &(commandState->transfer); // Check if requested length is zero if (commandState->length == 0) { // Nothing to do result = MSDD_STATUS_SUCCESS; } // Initialize command state if needed else if (commandState->state == 0) { commandState->state = SBC_STATE_WRITE; } // Identify current command state switch (commandState->state) { //------------------- case SBC_STATE_WRITE: //------------------- // Start transfer status = MSDD_Write(&(lun->requestSenseData), commandState->length, (TransferCallback) MSDDriver_Callback, (void *) transfer); // Check result code if (status != USBD_STATUS_SUCCESS) { TRACE_WARNING( "RBC_RequestSense: Cannot start sending data\n\r"); result = MSDD_STATUS_ERROR; } else { // Change state commandState->state = SBC_STATE_WAIT_WRITE; } break; //------------------------ case SBC_STATE_WAIT_WRITE: //------------------------ // Check the transfer semaphore if (transfer->semaphore > 0) { // Take semaphore and finish command transfer->semaphore--; if (transfer->status != USBD_STATUS_SUCCESS) { result = MSDD_STATUS_ERROR; } else { result = MSDD_STATUS_SUCCESS; } // Update length commandState->length -= transfer->transferred; } break; } return result; }
//------------------------------------------------------------------------------ //! \brief Handles an INQUIRY command. //! //! 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_Inquiry(MSDLun *lun, MSDCommandState *commandState) { unsigned char result = MSDD_STATUS_INCOMPLETE; unsigned char status; MSDTransfer *transfer = &(commandState->transfer); // Check if required length is 0 if (commandState->length == 0) { // Nothing to do result = MSDD_STATUS_SUCCESS; } // Initialize command state if needed else if (commandState->state == 0) { commandState->state = SBC_STATE_WRITE; // Change additional length field of inquiry data lun->inquiryData->bAdditionalLength = (unsigned char) (commandState->length - 5); } // Identify current command state switch (commandState->state) { //------------------- case SBC_STATE_WRITE: //------------------- // Start write operation status = MSDD_Write((void *) lun->inquiryData, commandState->length, (TransferCallback) MSDDriver_Callback, (void *) transfer); // Check operation result code if (status != USBD_STATUS_SUCCESS) { TRACE_WARNING( "SPC_Inquiry: Cannot start sending data\n\r"); result = MSDD_STATUS_ERROR; } else { // Proceed to next state TRACE_INFO_WP("Sending "); commandState->state = SBC_STATE_WAIT_WRITE; } break; //------------------------ case SBC_STATE_WAIT_WRITE: //------------------------ // Check the semaphore value if (transfer->semaphore > 0) { // Take semaphore and terminate command transfer->semaphore--; if (transfer->status != USBD_STATUS_SUCCESS) { TRACE_WARNING( "SPC_Inquiry: Data transfer failed\n\r"); result = MSDD_STATUS_ERROR; } else { TRACE_INFO_WP("Sent "); result = MSDD_STATUS_SUCCESS; } // Update length field commandState->length -= transfer->transferred; } break; } return result; }
//------------------------------------------------------------------------------ //! \brief Performs a READ CAPACITY (10) command. //! //! 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_ReadCapacity10(MSDLun *lun, MSDCommandState *commandState) { unsigned char result = MSDD_STATUS_INCOMPLETE; unsigned char status; MSDTransfer *transfer = &(commandState->transfer); // Initialize command state if needed if (commandState->state == 0) { commandState->state = SBC_STATE_WRITE; } // Identify current command state switch (commandState->state) { //------------------- case SBC_STATE_WRITE: //------------------- // Start the write operation status = MSDD_Write(&(lun->readCapacityData), commandState->length, (TransferCallback) MSDDriver_Callback, (void *) transfer); // Check operation result code if (status != USBD_STATUS_SUCCESS) { TRACE_WARNING( "RBC_ReadCapacity: Cannot start sending data\n\r"); result = MSDD_STATUS_ERROR; } else { // Proceed to next command state TRACE_INFO_WP("Sending "); commandState->state = SBC_STATE_WAIT_WRITE; } break; //------------------------ case SBC_STATE_WAIT_WRITE: //------------------------ // Check semaphore value if (transfer->semaphore > 0) { // Take semaphore and terminate command transfer->semaphore--; if (transfer->status != USBD_STATUS_SUCCESS) { TRACE_WARNING("RBC_ReadCapacity: Cannot send data\n\r"); result = MSDD_STATUS_ERROR; } else { TRACE_INFO_WP("Sent "); result = MSDD_STATUS_SUCCESS; } commandState->length -= transfer->transferred; } break; } return result; }
//------------------------------------------------------------------------------ //! \brief Performs a READ (10) command on specified LUN. //! //! The data is first read from the media and then sent to the USB host. //! 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_Read10(MSDLun *lun, MSDCommandState *commandState) { unsigned char status; unsigned char result = MSDD_STATUS_INCOMPLETE; SBCRead10 *command = (SBCRead10 *) commandState->cbw.pCommand; MSDTransfer *transfer = &(commandState->transfer); // 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 length if (commandState->length == 0) { result = MSDD_STATUS_SUCCESS; } else { // Command state management switch (commandState->state) { //------------------ case SBC_STATE_READ: //------------------ // Read one block of data from the media #if !defined(AT91C_EBI_SDRAM) && !defined(BOARD_USB_UDPHS) status = LUN_Read(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 != LUN_STATUS_SUCCESS) { TRACE_WARNING( "RBC_Read10: Failed to start reading media\n\r"); SBC_UpdateSenseData(&(lun->requestSenseData), SBC_SENSE_KEY_NOT_READY, SBC_ASC_LOGICAL_UNIT_NOT_READY, 0); result = MSDD_STATUS_ERROR; } else { // Move to next command state commandState->state = SBC_STATE_WAIT_READ; } break; //----------------------- case SBC_STATE_WAIT_READ: //----------------------- // Check semaphore value if (transfer->semaphore > 0) { TRACE_INFO_WP("Ok "); // Take semaphore and move to next state transfer->semaphore--; commandState->state = SBC_STATE_WRITE; } break; //------------------- case SBC_STATE_WRITE: //------------------- // Check the operation result code if (transfer->status != USBD_STATUS_SUCCESS) { TRACE_WARNING( "RBC_Read10: Failed to read media\n\r"); SBC_UpdateSenseData(&(lun->requestSenseData), SBC_SENSE_KEY_RECOVERED_ERROR, SBC_ASC_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE, 0); result = MSDD_STATUS_ERROR; } else { // Send the block to the host #if !defined(AT91C_EBI_SDRAM) && !defined(BOARD_USB_UDPHS) status = MSDD_Write((void*)lun->readWriteBuffer, lun->blockSize, (TransferCallback) MSDDriver_Callback, (void *) transfer); #else status = MSDD_Write((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_Read10: Failed to start to send data\n\r"); SBC_UpdateSenseData(&(lun->requestSenseData), SBC_SENSE_KEY_HARDWARE_ERROR, 0, 0); result = MSDD_STATUS_ERROR; } else { TRACE_INFO_WP("Sending "); // Move to next command state commandState->state = SBC_STATE_WAIT_WRITE; } } break; //------------------------ case SBC_STATE_WAIT_WRITE: //------------------------ // Check semaphore value if (transfer->semaphore > 0) { TRACE_INFO_WP("Sent "); // 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_Read10: Failed to send data\n\r"); SBC_UpdateSenseData(&(lun->requestSenseData), SBC_SENSE_KEY_HARDWARE_ERROR, 0, 0); result = MSDD_STATUS_ERROR; } else { TRACE_INFO_WP("Next "); // Update transfer length and block address STORE_DWORDB(DWORDB(command->pLogicalBlockAddress) + 1, command->pLogicalBlockAddress); #if !defined(AT91C_EBI_SDRAM) && !defined(BOARD_USB_UDPHS) commandState->length--; #else commandState->length = 0; #endif // 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; }