summaryrefslogtreecommitdiff
path: root/usb/device/massstorage/MSDDStateMachine.c
diff options
context:
space:
mode:
Diffstat (limited to 'usb/device/massstorage/MSDDStateMachine.c')
-rw-r--r--usb/device/massstorage/MSDDStateMachine.c607
1 files changed, 607 insertions, 0 deletions
diff --git a/usb/device/massstorage/MSDDStateMachine.c b/usb/device/massstorage/MSDDStateMachine.c
new file mode 100644
index 0000000..bc69a49
--- /dev/null
+++ b/usb/device/massstorage/MSDDStateMachine.c
@@ -0,0 +1,607 @@
+/* ----------------------------------------------------------------------------
+ * ATMEL Microcontroller Software Support
+ * ----------------------------------------------------------------------------
+ * Copyright (c) 2008, Atmel Corporation
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the disclaimer below.
+ *
+ * Atmel's name may not be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
+ * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ----------------------------------------------------------------------------
+ */
+
+//-----------------------------------------------------------------------------
+// Includes
+//-----------------------------------------------------------------------------
+
+#include "SBCMethods.h"
+#include "MSDDStateMachine.h"
+
+//-----------------------------------------------------------------------------
+// Internal functions
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+/// Returns the expected transfer length and direction (IN, OUT or don't care)
+/// from the host point-of-view.
+/// \param cbw Pointer to the CBW to examinate
+/// \param pLength Expected length of command
+/// \param pType Expected direction of command
+//-----------------------------------------------------------------------------
+static void MSDD_GetCommandInformation(MSCbw *cbw,
+ unsigned int *length,
+ unsigned char *type)
+{
+ // Expected host transfer direction and length
+ (*length) = cbw->dCBWDataTransferLength;
+
+ if (*length == 0) {
+
+ (*type) = MSDD_NO_TRANSFER;
+ }
+ else if ((cbw->bmCBWFlags & MSD_CBW_DEVICE_TO_HOST) != 0) {
+
+ (*type) = MSDD_DEVICE_TO_HOST;
+ }
+ else {
+
+ (*type) = MSDD_HOST_TO_DEVICE;
+ }
+}
+
+//-----------------------------------------------------------------------------
+/// Pre-processes a command by checking the differences between the host and
+/// device expectations in term of transfer type and length.
+/// Once one of the thirteen cases is identified, the actions to do during the
+/// post-processing phase are stored in the dCase variable of the command
+/// state.
+/// \param pMsdDriver Pointer to a MSDDriver instance
+/// \return 1 if the command is supported, false otherwise
+//-----------------------------------------------------------------------------
+static unsigned char MSDD_PreProcessCommand(MSDDriver *pMsdDriver)
+{
+ unsigned int hostLength = 0;
+ unsigned int deviceLength = 0;
+ unsigned char hostType;
+ unsigned char deviceType;
+ unsigned char isCommandSupported;
+ MSDCommandState *commandState = &(pMsdDriver->commandState);
+ MSCsw *csw = &(commandState->csw);
+ MSCbw *cbw = &(commandState->cbw);
+ MSDLun *lun = &(pMsdDriver->luns[(unsigned char) cbw->bCBWLUN]);
+
+ // Get information about the command
+ // Host-side
+ MSDD_GetCommandInformation(cbw, &hostLength, &hostType);
+
+ // Device-side
+ isCommandSupported = SBC_GetCommandInformation(cbw->pCommand,
+ &deviceLength,
+ &deviceType,
+ lun);
+
+ // Initialize data residue and result status
+ csw->dCSWDataResidue = 0;
+ csw->bCSWStatus = MSD_CSW_COMMAND_PASSED;
+
+ // Check if the command is supported
+ if (isCommandSupported) {
+
+ // Identify the command case
+ if(hostType == MSDD_NO_TRANSFER) {
+
+ // Case 1 (Hn = Dn)
+ if(deviceType == MSDD_NO_TRANSFER) {
+
+ //TRACE_WARNING("Case 1\n\r");
+ commandState->postprocess = 0;
+ commandState->length = 0;
+ }
+ else if(deviceType == MSDD_DEVICE_TO_HOST) {
+
+ // Case 2 (Hn < Di)
+ TRACE_WARNING(
+ "MSDD_PreProcessCommand: Case 2\n\r");
+ commandState->postprocess = MSDD_CASE_PHASE_ERROR;
+ commandState->length = 0;
+ }
+ else { //if(deviceType == MSDD_HOST_TO_DEVICE) {
+
+ // Case 3 (Hn < Do)
+ TRACE_WARNING(
+ "MSDD_PreProcessCommand: Case 3\n\r");
+ commandState->postprocess = MSDD_CASE_PHASE_ERROR;
+ commandState->length = 0;
+ }
+ }
+
+ // Case 4 (Hi > Dn)
+ else if(hostType == MSDD_DEVICE_TO_HOST) {
+
+ if(deviceType == MSDD_NO_TRANSFER) {
+
+ TRACE_WARNING(
+ "MSDD_PreProcessCommand: Case 4\n\r");
+ commandState->postprocess = MSDD_CASE_STALL_IN;
+ commandState->length = 0;
+ csw->dCSWDataResidue = hostLength;
+ }
+ else if(deviceType == MSDD_DEVICE_TO_HOST) {
+
+ if(hostLength > deviceLength) {
+
+ // Case 5 (Hi > Di)
+ TRACE_WARNING(
+ "MSDD_PreProcessCommand: Case 5\n\r");
+ commandState->postprocess = MSDD_CASE_STALL_IN;
+ commandState->length = deviceLength;
+ csw->dCSWDataResidue = hostLength - deviceLength;
+ }
+ else if(hostLength == deviceLength) {
+
+ // Case 6 (Hi = Di)
+ commandState->postprocess = 0;
+ commandState->length = deviceLength;
+ }
+ else { //if(hostLength < deviceLength) {
+
+ // Case 7 (Hi < Di)
+ TRACE_WARNING(
+ "MSDD_PreProcessCommand: Case 7\n\r");
+ commandState->postprocess = MSDD_CASE_PHASE_ERROR;
+ commandState->length = hostLength;
+ }
+ }
+ else { //if(deviceType == MSDD_HOST_TO_DEVICE) {
+
+ // Case 8 (Hi <> Do)
+ TRACE_WARNING(
+ "MSDD_PreProcessCommand: Case 8\n\r");
+ commandState->postprocess =
+ MSDD_CASE_STALL_IN | MSDD_CASE_PHASE_ERROR;
+ commandState->length = 0;
+ }
+ }
+ else if(hostType == MSDD_HOST_TO_DEVICE) {
+
+ if(deviceType == MSDD_NO_TRANSFER) {
+
+ // Case 9 (Ho > Dn)
+ TRACE_WARNING(
+ "MSDD_PreProcessCommand: Case 9\n\r");
+ commandState->postprocess = MSDD_CASE_STALL_OUT;
+ commandState->length = 0;
+ csw->dCSWDataResidue = hostLength;
+ }
+ else if(deviceType == MSDD_DEVICE_TO_HOST) {
+
+ // Case 10 (Ho <> Di)
+ TRACE_WARNING(
+ "MSDD_PreProcessCommand: Case 10\n\r");
+ commandState->postprocess =
+ MSDD_CASE_STALL_OUT | MSDD_CASE_PHASE_ERROR;
+ commandState->length = 0;
+ }
+ else { //if(deviceType == MSDD_HOST_TO_DEVICE) {
+
+ if(hostLength > deviceLength) {
+
+ // Case 11 (Ho > Do)
+ TRACE_WARNING(
+ "MSDD_PreProcessCommand: Case 11\n\r");
+ commandState->postprocess = MSDD_CASE_STALL_OUT;
+// commandState->length = deviceLength;
+// csw->dCSWDataResidue = hostLength - deviceLength;
+ commandState->length = 0;
+ csw->dCSWDataResidue = deviceLength;
+ }
+ else if(hostLength == deviceLength) {
+
+ // Case 12 (Ho = Do)
+ //TRACE_WARNING(
+ // "MSDD_PreProcessCommand: Case 12\n\r");
+ commandState->postprocess = 0;
+ commandState->length = deviceLength;
+ }
+ else { //if(hostLength < deviceLength) {
+
+ // Case 13 (Ho < Do)
+ TRACE_WARNING(
+ "MSDD_PreProcessCommand: Case 13\n\r");
+ commandState->postprocess = MSDD_CASE_PHASE_ERROR;
+ commandState->length = hostLength;
+ }
+ }
+ }
+ }
+
+ return isCommandSupported;
+}
+
+//-----------------------------------------------------------------------------
+/// Post-processes a command given the case identified during the
+/// pre-processing step.
+/// Depending on the case, one of the following actions can be done:
+/// - Bulk IN endpoint is stalled
+/// - Bulk OUT endpoint is stalled
+/// - CSW status set to phase error
+/// \param pMsdDriver Pointer to a MSDDriver instance
+/// \return If the device is halted
+//-----------------------------------------------------------------------------
+static unsigned char MSDD_PostProcessCommand(MSDDriver *pMsdDriver)
+{
+ MSDCommandState *commandState = &(pMsdDriver->commandState);
+ MSCsw *csw = &(commandState->csw);
+ unsigned char haltStatus = 0;
+
+ // STALL Bulk IN endpoint ?
+ if ((commandState->postprocess & MSDD_CASE_STALL_IN) != 0) {
+
+ TRACE_INFO_WP("StallIn ");
+ MSDD_Halt(MSDD_CASE_STALL_IN);
+ haltStatus = 1;
+ }
+
+ // STALL Bulk OUT endpoint ?
+ if ((commandState->postprocess & MSDD_CASE_STALL_OUT) != 0) {
+
+ TRACE_INFO_WP("StallOut ");
+ MSDD_Halt(MSDD_CASE_STALL_OUT);
+ haltStatus = 1;
+ }
+
+ // Set CSW status code to phase error ?
+ if ((commandState->postprocess & MSDD_CASE_PHASE_ERROR) != 0) {
+
+ TRACE_INFO_WP("PhaseErr ");
+ csw->bCSWStatus = MSD_CSW_PHASE_ERROR;
+ }
+
+ return haltStatus;
+}
+
+//-----------------------------------------------------------------------------
+/// Processes the latest command received by the %device.
+/// \param pMsdDriver Pointer to a MSDDriver instance
+/// \return 1 if the command has been completed, false otherwise.
+//-----------------------------------------------------------------------------
+static unsigned char MSDD_ProcessCommand(MSDDriver * pMsdDriver)
+{
+ unsigned char status;
+ MSDCommandState *commandState = &(pMsdDriver->commandState);
+ MSCbw *cbw = &(commandState->cbw);
+ MSCsw *csw = &(commandState->csw);
+ MSDLun *lun = &(pMsdDriver->luns[(unsigned char) cbw->bCBWLUN]);
+ unsigned char isCommandComplete = 0;
+
+ // Check if LUN is valid
+ if (cbw->bCBWLUN > pMsdDriver->maxLun) {
+
+ TRACE_WARNING(
+ "MSDD_ProcessCommand: LUN %d not exist\n\r", cbw->bCBWLUN);
+ status = MSDD_STATUS_ERROR;
+ }
+ else {
+
+ // Process command
+ if (pMsdDriver->maxLun > 0) {
+
+ TRACE_INFO_WP("LUN%d ", cbw->bCBWLUN);
+ }
+
+ status = SBC_ProcessCommand(lun, commandState);
+ }
+
+ // Check command result code
+ if (status == MSDD_STATUS_PARAMETER) {
+
+ TRACE_WARNING(
+ "MSDD_ProcessCommand: Unknown cmd 0x%02X\n\r",
+ cbw->pCommand[0]);
+
+ // Update sense data
+ SBC_UpdateSenseData(&(lun->requestSenseData),
+ SBC_SENSE_KEY_ILLEGAL_REQUEST,
+ SBC_ASC_INVALID_FIELD_IN_CDB,
+ 0);
+
+ // Result codes
+ csw->bCSWStatus = MSD_CSW_COMMAND_FAILED;
+ isCommandComplete = 1;
+
+ // stall the request, IN or OUT
+ if (((cbw->bmCBWFlags & MSD_CBW_DEVICE_TO_HOST) == 0)
+ && (cbw->dCBWDataTransferLength > 0)) {
+
+ // Stall the OUT endpoint : host to device
+ // MSDD_Halt(MSDD_CASE_STALL_OUT);
+ commandState->postprocess = MSDD_CASE_STALL_OUT;
+ TRACE_INFO_WP("StaOUT ");
+ }
+ else {
+
+ // Stall the IN endpoint : device to host
+ // MSDD_Halt(MSDD_CASE_STALL_IN);
+ commandState->postprocess = MSDD_CASE_STALL_IN;
+ TRACE_INFO_WP("StaIN ");
+ }
+ }
+ else if (status == MSDD_STATUS_ERROR) {
+
+ TRACE_WARNING("MSD_ProcessCommand: Cmd %x fail\n\r",
+ ((SBCCommand*)commandState->cbw.pCommand)->bOperationCode);
+
+ // Update sense data
+ SBC_UpdateSenseData(&(lun->requestSenseData),
+ SBC_SENSE_KEY_MEDIUM_ERROR,
+ SBC_ASC_INVALID_FIELD_IN_CDB,
+ 0);
+
+ // Result codes
+ csw->bCSWStatus = MSD_CSW_COMMAND_FAILED;
+ isCommandComplete = 1;
+ }
+ else if (status == MSDD_STATUS_RW) {
+
+ csw->bCSWStatus = MSD_CSW_COMMAND_FAILED;
+ isCommandComplete = 1;
+ }
+ else {
+
+ // Update sense data
+ SBC_UpdateSenseData(&(lun->requestSenseData),
+ SBC_SENSE_KEY_NO_SENSE,
+ 0,
+ 0);
+
+ // Is command complete ?
+ if (status == MSDD_STATUS_SUCCESS) {
+
+ isCommandComplete = 1;
+ }
+ }
+
+ // Check if command has been completed
+ if (isCommandComplete) {
+
+ TRACE_INFO_WP("Cplt ");
+
+ // Adjust data residue
+ if (commandState->length != 0) {
+
+ csw->dCSWDataResidue += commandState->length;
+
+ // STALL the endpoint waiting for data
+ if ((cbw->bmCBWFlags & MSD_CBW_DEVICE_TO_HOST) == 0) {
+
+ // Stall the OUT endpoint : host to device
+ // MSDD_Halt(MSDD_CASE_STALL_OUT);
+ commandState->postprocess = MSDD_CASE_STALL_OUT;
+ TRACE_INFO_WP("StaOUT ");
+ }
+ else {
+
+ // Stall the IN endpoint : device to host
+ // MSDD_Halt(MSDD_CASE_STALL_IN);
+ commandState->postprocess = MSDD_CASE_STALL_IN;
+ TRACE_INFO_WP("StaIN ");
+ }
+ }
+
+ // Reset command state
+ commandState->state = 0;
+ }
+
+ return isCommandComplete;
+}
+
+//-----------------------------------------------------------------------------
+/// 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;
+ }
+}
personal git repositories of Harald Welte. Your mileage may vary