diff options
author | Harald Welte <laforge@gnumonks.org> | 2011-07-04 20:52:54 +0200 |
---|---|---|
committer | Harald Welte <laforge@gnumonks.org> | 2011-07-04 20:52:54 +0200 |
commit | 044ad7c3987460ede48ff27afd6bdb0ca05a0432 (patch) | |
tree | 924818cdb0d39ca08aec540d18da7bd406eaae8c /usb/device/massstorage |
import at91lib from at91lib_20100901_softpack_1_9_v_1_0_svn_v1501120100901_softpack_1_9_v_1_0_svn_v15011
it's sad to see that atmel doesn't publish their svn repo or has
a centralized location or even puts proper version/release info
into the library itself
Diffstat (limited to 'usb/device/massstorage')
20 files changed, 6179 insertions, 0 deletions
diff --git a/usb/device/massstorage/MSD.h b/usb/device/massstorage/MSD.h new file mode 100644 index 0000000..decab4a --- /dev/null +++ b/usb/device/massstorage/MSD.h @@ -0,0 +1,233 @@ +/* ----------------------------------------------------------------------------
+ * 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.
+ * ----------------------------------------------------------------------------
+ */
+
+//------------------------------------------------------------------------------
+/// \unit
+/// !Purpose
+///
+/// Mass Storage class definitions.
+///
+/// See
+/// - <a
+/// href="http://www.usb.org/developers/devclass_docs/usb_msc_overview_1.2.pdf">
+/// USB Mass Storage Class Spec. Overview</a>
+/// - <a href="http://www.usb.org/developers/devclass_docs/usbmassbulk_10.pdf">
+/// USB Mass Storage Class Bulk-Only Transport</a>
+///
+/// !Usage
+///
+/// -# Uses "MSD Requests" to check incoming requests from USB Host.
+/// -# Uses "MSD Subclass Codes" and "MSD Protocol Codes" to fill %device
+/// interface descriptors for a MSD %device.
+/// -# Handle the incoming Bulk data with "MSD CBW Definitions" and MSCbw
+/// structure.
+/// -# Prepare the outgoing Bulk data with "MSD CSW Definitions" and MSCsw
+/// structure.
+//------------------------------------------------------------------------------
+
+#ifndef MSD_H
+#define MSD_H
+
+//------------------------------------------------------------------------------
+// Definitions
+//------------------------------------------------------------------------------
+
+//------------------------------------------------------------------------------
+/// \page "MSD Requests"
+/// This page lists MSD-specific requests ( Actually for Bulk-only protocol ).
+///
+/// !Requests
+/// - MSD_BULK_ONLY_RESET
+/// - MSD_GET_MAX_LUN
+
+/// Reset the mass storage %device and its associated interface.
+#define MSD_BULK_ONLY_RESET 0xFF
+/// Return the maximum LUN number supported by the %device.
+#define MSD_GET_MAX_LUN 0xFE
+//------------------------------------------------------------------------------
+
+//------------------------------------------------------------------------------
+/// \page "MSD Subclass Codes"
+/// This page lists the Subclass Codes for bInterfaceSubClass field.
+/// (Table 2.1, USB Mass Storage Class Spec. Overview)
+///
+/// !SubClasses
+/// - MSD_SUBCLASS_RBC
+/// - MSD_SUBCLASS_SFF_MCC
+/// - MSD_SUBCLASS_QIC
+/// - MSD_SUBCLASS_UFI
+/// - MSD_SUBCLASS_SFF
+/// - MSD_SUBCLASS_SCSI
+
+/// Reduced Block Commands (RBC) T10
+#define MSD_SUBCLASS_RBC 0x01
+/// C/DVD devices
+#define MSD_SUBCLASS_SFF_MCC 0x02
+/// Tape device
+#define MSD_SUBCLASS_QIC 0x03
+/// Floppy disk drive (FDD) device
+#define MSD_SUBCLASS_UFI 0x04
+/// Floppy disk drive (FDD) device
+#define MSD_SUBCLASS_SFF 0x05
+/// SCSI transparent command set
+#define MSD_SUBCLASS_SCSI 0x06
+//------------------------------------------------------------------------------
+
+
+//------------------------------------------------------------------------------
+/// \page "MSD Protocol Codes"
+/// This page lists the Transport Protocol codes for MSD.
+/// (Table 3.1, USB Mass Storage Class Spec. Overview)
+///
+/// !Protocols
+/// - MSD_PROTOCOL_CBI_COMPLETION
+/// - MSD_PROTOCOL_CBI
+/// - MSD_PROTOCOL_BULK_ONLY
+
+/// Control/Bulk/Interrupt (CBI) Transport (with command complete interrupt)
+#define MSD_PROTOCOL_CBI_COMPLETION 0x00
+/// Control/Bulk/Interrupt (CBI) Transport (no command complete interrupt)
+#define MSD_PROTOCOL_CBI 0x01
+/// Bulk-Only Transport
+#define MSD_PROTOCOL_BULK_ONLY 0x50
+//------------------------------------------------------------------------------
+
+/// Test unit control:
+#define CTRL_NOT_READY 0x00
+#define CTRL_GOOD 0x01
+#define CTRL_BUSY 0x02
+
+//------------------------------------------------------------------------------
+/// \page "MSD CBW Definitions"
+/// This page lists the Command Block Wrapper (CBW) definitions.
+///
+/// !Constants
+/// - MSD_CBW_SIZE
+/// - MSD_CBW_SIGNATURE
+///
+/// !Fields
+/// - MSD_CBW_DEVICE_TO_HOST
+
+/// Command Block Wrapper Size
+#define MSD_CBW_SIZE 31
+/// 'USBC' 0x43425355
+#define MSD_CBW_SIGNATURE 0x43425355
+
+/// CBW bmCBWFlags field
+#define MSD_CBW_DEVICE_TO_HOST (1 << 7)
+//------------------------------------------------------------------------------
+
+//------------------------------------------------------------------------------
+/// \page "MSD CSW Definitions"
+/// This page lists the Command Status Wrapper (CSW) definitions.
+///
+/// !Constants
+/// - MSD_CSW_SIZE
+/// - MSD_CSW_SIGNATURE
+///
+/// !Command Block Status Values
+/// (Table 5.3 , USB Mass Storage Class Bulk-Only Transport)
+/// - MSD_CSW_COMMAND_PASSED
+/// - MSD_CSW_COMMAND_FAILED
+/// - MSD_CSW_PHASE_ERROR
+
+/// Command Status Wrapper Size
+#define MSD_CSW_SIZE 13
+/// 'USBS' 0x53425355
+#define MSD_CSW_SIGNATURE 0x53425355
+
+/// Command Passed (good status)
+#define MSD_CSW_COMMAND_PASSED 0
+/// Command Failed
+#define MSD_CSW_COMMAND_FAILED 1
+/// Phase Error
+#define MSD_CSW_PHASE_ERROR 2
+//------------------------------------------------------------------------------
+
+
+//------------------------------------------------------------------------------
+// Structures
+//------------------------------------------------------------------------------
+
+//------------------------------------------------------------------------------
+/// Command Block Wrapper (CBW),
+/// See Table 5.1, USB Mass Storage Class Bulk-Only Transport.
+///
+/// The CBW shall start on a packet boundary and shall end as a
+/// short packet with exactly 31 (1Fh) bytes transferred.
+//------------------------------------------------------------------------------
+typedef struct {
+
+ /// 'USBC' 0x43425355 (little endian)
+ unsigned int dCBWSignature;
+ /// Must be the same as dCSWTag
+ unsigned int dCBWTag;
+ /// Number of bytes transfer
+ unsigned int dCBWDataTransferLength;
+ /// Indicates the directin of the transfer:
+ /// 0x80=IN=device-to-host,
+ /// 0x00=OUT=host-to-device
+ unsigned char bmCBWFlags;
+ /// bits 0->3: bCBWLUN
+ unsigned char bCBWLUN :4,
+ bReserved1:4; /// reserved
+ /// bits 0->4: bCBWCBLength
+ unsigned char bCBWCBLength:5,
+ bReserved2 :3; /// reserved
+ /// Command block
+ unsigned char pCommand[16];
+
+} MSCbw;
+
+//------------------------------------------------------------------------------
+/// Command Status Wrapper (CSW),
+/// See Table 5.2, USB Mass Storage Class Bulk-Only Transport.
+//------------------------------------------------------------------------------
+typedef struct
+{
+ /// 'USBS' 0x53425355 (little endian)
+ unsigned int dCSWSignature;
+ /// Must be the same as dCBWTag
+ unsigned int dCSWTag;
+ /// For Data-Out the device shall report in the dCSWDataResidue the
+ /// difference between the amount of data expected as stated in the
+ /// dCBWDataTransferLength, and the actual amount of data processed by
+ /// the device. For Data-In the device shall report in the dCSWDataResidue
+ /// the difference between the amount of data expected as stated in the
+ /// dCBWDataTransferLength and the actual amount of relevant data sent by
+ /// the device. The dCSWDataResidue shall not exceed the value sent in the
+ /// dCBWDataTransferLength.
+ unsigned int dCSWDataResidue;
+ /// Indicates the success or failure of the command.
+ unsigned char bCSWStatus;
+
+} MSCsw;
+
+#endif //#ifndef MSD_H
+
diff --git a/usb/device/massstorage/MSDAppArch.png b/usb/device/massstorage/MSDAppArch.png Binary files differnew file mode 100644 index 0000000..eec0f15 --- /dev/null +++ b/usb/device/massstorage/MSDAppArch.png 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;
+ }
+}
diff --git a/usb/device/massstorage/MSDDStateMachine.h b/usb/device/massstorage/MSDDStateMachine.h new file mode 100644 index 0000000..77e5cc8 --- /dev/null +++ b/usb/device/massstorage/MSDDStateMachine.h @@ -0,0 +1,264 @@ +/* ----------------------------------------------------------------------------
+ * 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.
+ * ----------------------------------------------------------------------------
+ */
+
+//-----------------------------------------------------------------------------
+/// \unit
+/// !Purpose
+///
+/// Definitions, structs, functions required by a Mass Storage device driver
+/// state machine..
+///
+/// !Usage
+///
+/// - For a USB device:
+/// -# MSDD_Write, MSDD_Read, MSDD_Halt should be defined for
+/// usage in the state machine procedure.
+///
+/// -# MSDD_StateMachine is invoked to run the MSD state machine.
+///
+//-----------------------------------------------------------------------------
+
+#ifndef MSDDSTATEMACHINE_H
+#define MSDDSTATEMACHINE_H
+
+//-----------------------------------------------------------------------------
+// Headers
+//-----------------------------------------------------------------------------
+
+#include "MSD.h"
+#include "MSDLun.h"
+#include <utility/trace.h>
+
+//-----------------------------------------------------------------------------
+// Definitions
+//-----------------------------------------------------------------------------
+
+//------------------------------------------------------------------------------
+/// \page "MSD Driver Possible states"
+/// ...
+///
+/// !States
+/// - MSDD_STATE_READ_CBW
+/// - MSDD_STATE_WAIT_CBW
+/// - MSDD_STATE_PROCESS_CBW
+/// - MSDD_STATE_WAIT_HALT
+/// - MSDD_STATE_SEND_CSW
+/// - MSDD_STATE_WAIT_CSW
+/// - MSDD_STATE_WAIT_RESET
+
+//! \brief Driver is expecting a command block wrapper
+#define MSDD_STATE_READ_CBW (1 << 0)
+
+//! \brief Driver is waiting for the transfer to finish
+#define MSDD_STATE_WAIT_CBW (1 << 1)
+
+//! \brief Driver is processing the received command
+#define MSDD_STATE_PROCESS_CBW (1 << 2)
+
+//! \brief Driver is halted because pipe halt or wait reset
+#define MSDD_STATE_WAIT_HALT (1 << 3)
+
+//! \brief Driver is starting the transmission of a command status wrapper
+#define MSDD_STATE_SEND_CSW (1 << 4)
+
+//! \brief Driver is waiting for the CSW transmission to finish
+#define MSDD_STATE_WAIT_CSW (1 << 5)
+
+//! \brief Driver is waiting for the MassStorageReset
+#define MSDD_STATE_WAIT_RESET (1 << 6)
+//------------------------------------------------------------------------------
+
+//------------------------------------------------------------------------------
+/// \page "MSD Driver Result Codes"
+/// This page lists result codes for MSD functions.
+///
+/// !Codes
+/// - MSDD_STATUS_SUCCESS
+/// - MSDD_STATUS_ERROR
+/// - MSDD_STATUS_INCOMPLETE
+/// - MSDD_STATUS_PARAMETER
+/// - MSDD_STATUS_RW
+
+//! \brief Method was successful
+#define MSDD_STATUS_SUCCESS 0x00
+
+//! \brief There was an error when trying to perform a method
+#define MSDD_STATUS_ERROR 0x01
+
+//! \brief No error was encountered but the application should call the
+//! method again to continue the operation
+#define MSDD_STATUS_INCOMPLETE 0x02
+
+//! \brief A wrong parameter has been passed to the method
+#define MSDD_STATUS_PARAMETER 0x03
+
+//! \brief An error when reading/writing disk (protected or not present)
+#define MSDD_STATUS_RW 0x04
+//------------------------------------------------------------------------------
+
+//------------------------------------------------------------------------------
+/// \page "MSD Driver Action Cases"
+/// This page lists actions to perform during the post-processing phase of a
+/// command.
+///
+/// !Actions
+/// - MSDD_CASE_PHASE_ERROR
+/// - MSDD_CASE_STALL_IN
+/// - MSDD_CASE_STALL_OUT
+
+//! \brief Indicates that the CSW should report a phase error
+#define MSDD_CASE_PHASE_ERROR (1 << 0)
+
+//! \brief The driver should halt the Bulk IN pipe after the transfer
+#define MSDD_CASE_STALL_IN (1 << 1)
+
+//! \brief The driver should halt the Bulk OUT pipe after the transfer
+#define MSDD_CASE_STALL_OUT (1 << 2)
+
+//------------------------------------------------------------------------------
+
+//------------------------------------------------------------------------------
+/// \page "MSD Driver Xfr Directions"
+/// This page lists possible direction values for a data transfer
+///
+/// !Directions
+/// - MSDD_DEVICE_TO_HOST
+/// - MSDD_HOST_TO_DEVICE
+/// - MSDD_NO_TRANSFER
+
+#define MSDD_DEVICE_TO_HOST 0
+#define MSDD_HOST_TO_DEVICE 1
+#define MSDD_NO_TRANSFER 2
+//------------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+// Types
+//-----------------------------------------------------------------------------
+
+//------------------------------------------------------------------------------
+//! \brief Structure for holding the result of a USB transfer
+//! \see MSDDriver_Callback
+//------------------------------------------------------------------------------
+typedef struct {
+
+ volatile unsigned int transferred; /// Number of bytes transferred
+ volatile unsigned int remaining; /// Number of bytes not transferred
+ volatile unsigned short semaphore; /// Semaphore to indicate transfer completion
+ volatile unsigned short status; /// Operation result code
+
+} MSDTransfer;
+
+//------------------------------------------------------------------------------
+//! \brief Status of an executing command
+//! \see MSDCbw
+//! \see MSDCsw
+//! \see MSDTransfer
+//------------------------------------------------------------------------------
+typedef struct {
+
+ MSDTransfer transfer; /// Current transfer status (USB)
+ MSDTransfer disktransfer;/// Current transfer status (Disk)
+ unsigned int length; /// Remaining length of command
+ MSCbw cbw; /// Received CBW (31 bytes)
+ unsigned char state; /// Current command state
+ MSCsw csw; /// CSW to send (13 bytes)
+ unsigned char postprocess; /// Actions to perform when command is complete
+
+} MSDCommandState;
+
+//------------------------------------------------------------------------------
+/// \brief MSD driver state variables
+/// \see MSDCommandState
+/// \see MSDLun
+//------------------------------------------------------------------------------
+typedef struct {
+
+ /// LUN list for the %device.
+ MSDLun *luns;
+ /// State of the currently executing command
+ MSDCommandState commandState;
+ /// Maximum LUN index
+ unsigned char maxLun;
+ /// Current state of the driver
+ unsigned char state;
+ /// Indicates if the driver is waiting for a reset recovery
+ unsigned char waitResetRecovery;
+
+} MSDDriver;
+
+//-----------------------------------------------------------------------------
+// Inline functions
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+/// This function is to be used as a callback for USB or LUN transfers.
+/// \param transfer Pointer to the transfer structure to update
+/// \param status Operation result code
+/// \param transferred Number of bytes transferred by the command
+/// \param remaining Number of bytes not transferred
+//-----------------------------------------------------------------------------
+static inline void MSDDriver_Callback(MSDTransfer *transfer,
+ unsigned char status,
+ unsigned int transferred,
+ unsigned int remaining)
+{
+ TRACE_DEBUG("Cbk ");
+ transfer->semaphore++;
+ transfer->status = status;
+ transfer->transferred = transferred;
+ transfer->remaining = remaining;
+}
+
+//-----------------------------------------------------------------------------
+// Driver functions
+//-----------------------------------------------------------------------------
+//- MSD General support function
+extern char MSDD_Read(
+ void* pData,
+ unsigned int dLength,
+ TransferCallback fCallback,
+ void* pArgument);
+
+extern char MSDD_Write(
+ void* pData,
+ unsigned int dLength,
+ TransferCallback fCallback,
+ void* pArgument);
+
+extern void MSDD_Halt(unsigned int stallCase);
+
+extern unsigned int MSDD_IsHalted(void);
+
+//-----------------------------------------------------------------------------
+// Exported functions
+//-----------------------------------------------------------------------------
+
+extern void MSDD_StateMachine(MSDDriver * pMsdDriver);
+
+#endif // #define MSDDSTATEMACHINE_H
+
diff --git a/usb/device/massstorage/MSDDriver.c b/usb/device/massstorage/MSDDriver.c new file mode 100644 index 0000000..fbaebb0 --- /dev/null +++ b/usb/device/massstorage/MSDDriver.c @@ -0,0 +1,343 @@ +/* ----------------------------------------------------------------------------
+ * 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 <memories/Media.h>
+#include "MSDDriver.h"
+#include "MSDDriverDescriptors.h"
+#include "SBCMethods.h"
+#include <utility/trace.h>
+#include <usb/common/core/USBGenericRequest.h>
+#include <usb/common/core/USBFeatureRequest.h>
+#include <usb/device/core/USBD.h>
+#include <usb/device/core/USBDCallbacks.h>
+#include <usb/device/core/USBDDriver.h>
+#include <usb/device/core/USBDDriverCallbacks.h>
+
+//-----------------------------------------------------------------------------
+// Internal variables
+//-----------------------------------------------------------------------------
+
+/// Mass storage device driver instance.
+static MSDDriver msdDriver;
+
+/// Standard device driver instance.
+static USBDDriver usbdDriver;
+
+//-----------------------------------------------------------------------------
+// Internal functions
+//-----------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+/// Resets the state of the MSD driver
+//-----------------------------------------------------------------------------
+static void MSDDriver_Reset(void)
+{
+ TRACE_INFO_WP("MSDReset ");
+
+ msdDriver.state = MSDD_STATE_READ_CBW;
+ msdDriver.waitResetRecovery = 0;
+ msdDriver.commandState.state = 0;
+}
+
+//-----------------------------------------------------------------------------
+// Callback re-implementation
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+/// Invoked when a new SETUP request is received from the host. Forwards the
+/// request to the Mass Storage device driver handler function.
+/// \param request Pointer to a USBGenericRequest instance.
+//-----------------------------------------------------------------------------
+void USBDCallbacks_RequestReceived(const USBGenericRequest *request)
+{
+ MSDDriver_RequestHandler(request);
+}
+
+//-----------------------------------------------------------------------------
+/// Invoked when the configuration of the device changes. Resets the mass
+/// storage driver.
+/// \param cfgnum New configuration number.
+//-----------------------------------------------------------------------------
+void USBDDriverCallbacks_ConfigurationChanged(unsigned char cfgnum)
+{
+ if (cfgnum > 0) {
+
+ MSDDriver_Reset();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Driver functions
+//-----------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+/// Reads from host through MSD defined bulk OUT pipe. Act as USBD_Read but by
+/// a fixed OUT endpoint.
+/// \param data Pointer to the data buffer that contains data read from host.
+/// \param size The number of bytes should be read (buffer size).
+/// \param callback Pointer to the function invoked on end of reading.
+/// \param argument Pointer to additional argument data struct.
+//-----------------------------------------------------------------------------
+char MSDD_Read(void *data,
+ unsigned int size,
+ TransferCallback callback,
+ void *argument)
+
+{
+ return USBD_Read(MSDDriverDescriptors_BULKOUT,
+ data,
+ size,
+ callback,
+ argument);
+}
+
+//-----------------------------------------------------------------------------
+/// Writes to host through MSD defined bulk IN pipe. Act as USBD_Write but by
+/// a fixed IN endpoint.
+/// \param data Pointer to the data that writes to the host.
+/// \param size The number of bytes should be write.
+/// \param callback Pointer to the function invoked on end of writing.
+/// \param argument Pointer to additional argument data struct.
+//-----------------------------------------------------------------------------
+char MSDD_Write(void *data,
+ unsigned int size,
+ TransferCallback callback,
+ void *argument)
+{
+ return USBD_Write(MSDDriverDescriptors_BULKIN,
+ data,
+ size,
+ callback,
+ argument);
+}
+
+//-----------------------------------------------------------------------------
+/// HALT Specified USB pipe.
+/// \param stallCASE Case of the stall condition (Bulk In/Out/Both).
+//-----------------------------------------------------------------------------
+void MSDD_Halt(unsigned int stallCASE)
+{
+ if (stallCASE & MSDD_CASE_STALL_OUT) {
+
+ USBD_Halt(MSDDriverDescriptors_BULKOUT);
+ }
+
+ if (stallCASE & MSDD_CASE_STALL_IN) {
+
+ USBD_Halt(MSDDriverDescriptors_BULKIN);
+ }
+}
+
+//-----------------------------------------------------------------------------
+/// Return halted status
+/// \return stallCASE bitmap, case of the stall condition
+/// (bit: MSDD_CASE_STALL_OUT or MSDD_CASE_STALL_IN)
+//-----------------------------------------------------------------------------
+unsigned int MSDD_IsHalted(void)
+{
+ unsigned int stallCASE = 0;
+ if (USBD_IsHalted(MSDDriverDescriptors_BULKOUT)) {
+
+ stallCASE |= MSDD_CASE_STALL_OUT;
+ }
+ if (USBD_IsHalted(MSDDriverDescriptors_BULKIN)) {
+
+ stallCASE |= MSDD_CASE_STALL_IN;
+ }
+ return stallCASE;
+}
+
+//-----------------------------------------------------------------------------
+// Exported functions
+//-----------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+/// Initializes the MSD driver and the associated USB driver.
+/// \param luns Pointer to a list of LUNs
+/// \param numLuns Number of LUN in list
+/// \see MSDLun
+//-----------------------------------------------------------------------------
+void MSDDriver_Initialize(MSDLun *defLuns, unsigned char numLuns)
+{
+
+ TRACE_INFO("MSD init\n\r");
+
+ // Command state initialization
+ msdDriver.commandState.state = 0;
+ msdDriver.commandState.postprocess = 0;
+ msdDriver.commandState.length = 0;
+ msdDriver.commandState.transfer.semaphore = 0;
+
+ // LUNs
+ msdDriver.luns = defLuns;
+ msdDriver.maxLun = (unsigned char) (numLuns - 1);
+
+ // Reset BOT driver
+ MSDDriver_Reset();
+
+ // Init the USB driver
+ USBDDriver_Initialize(&usbdDriver, &msdDriverDescriptors, 0);
+ USBD_Init();
+}
+
+//-----------------------------------------------------------------------------
+/// Handler for incoming SETUP requests on default Control endpoint 0.
+///
+/// Standard requests are forwarded to the USBDDriver_RequestHandler
+/// method.
+/// \param request Pointer to a USBGenericRequest instance
+//-----------------------------------------------------------------------------
+void MSDDriver_RequestHandler(const USBGenericRequest *request)
+{
+ TRACE_INFO_WP("NewReq ");
+
+ // Handle requests
+ switch (USBGenericRequest_GetRequest(request)) {
+ //---------------------
+ case USBGenericRequest_CLEARFEATURE:
+ //---------------------
+ TRACE_INFO_WP("ClrFeat ");
+
+ switch (USBFeatureRequest_GetFeatureSelector(request)) {
+
+ //---------------------
+ case USBFeatureRequest_ENDPOINTHALT:
+ //---------------------
+ TRACE_INFO_WP("Hlt ");
+
+ // Do not clear the endpoint halt status if the device is waiting
+ // for a reset recovery sequence
+ if (!msdDriver.waitResetRecovery) {
+
+ // Forward the request to the standard handler
+ USBDDriver_RequestHandler(&usbdDriver, request);
+ }
+ else {
+
+ TRACE_INFO_WP("No ");
+ }
+
+ USBD_Write(0, 0, 0, 0, 0);
+ break;
+
+ //------
+ default:
+ //------
+ // Forward the request to the standard handler
+ USBDDriver_RequestHandler(&usbdDriver, request);
+ }
+ break;
+
+ //-------------------
+ case MSD_GET_MAX_LUN:
+ //-------------------
+ TRACE_INFO_WP("gMaxLun ");
+
+ // Check request parameters
+ if ((request->wValue == 0)
+ && (request->wIndex == 0)
+ && (request->wLength == 1)) {
+
+ USBD_Write(0, &(msdDriver.maxLun), 1, 0, 0);
+
+ }
+ else {
+
+ TRACE_WARNING(
+ "MSDDriver_RequestHandler: GetMaxLUN(%d,%d,%d)\n\r",
+ request->wValue, request->wIndex, request->wLength);
+ USBD_Stall(0);
+ }
+ break;
+
+ //-----------------------
+ case MSD_BULK_ONLY_RESET:
+ //-----------------------
+ TRACE_INFO_WP("Rst ");
+
+ // Check parameters
+ if ((request->wValue == 0)
+ && (request->wIndex == 0)
+ && (request->wLength == 0)) {
+
+ // Reset the MSD driver
+ MSDDriver_Reset();
+ USBD_Write(0, 0, 0, 0, 0);
+ }
+ else {
+
+ TRACE_WARNING(
+ "MSDDriver_RequestHandler: Reset(%d,%d,%d)\n\r",
+ request->wValue, request->wIndex, request->wLength);
+ USBD_Stall(0);
+ }
+ break;
+
+ //------
+ default:
+ //------
+ // Forward request to standard handler
+ USBDDriver_RequestHandler(&usbdDriver, request);
+
+ break;
+ }
+}
+
+//-----------------------------------------------------------------------------
+/// State machine for the MSD driver
+//-----------------------------------------------------------------------------
+void MSDDriver_StateMachine(void)
+{
+ if (USBD_GetState() < USBD_STATE_CONFIGURED){}
+ else MSDD_StateMachine(&msdDriver);
+
+}
+
+//-----------------------------------------------------------------------------
+/// Starts a remote wake-up sequence if the host has explicitely enabled it
+/// by sending the appropriate SET_FEATURE request.
+//-----------------------------------------------------------------------------
+void MSDDriver_RemoteWakeUp(void)
+{
+ // Remote wake-up has been enabled
+ if (USBDDriver_IsRemoteWakeUpEnabled(&usbdDriver)) {
+
+ USBD_RemoteWakeUp();
+ }
+ // Remote wake-up NOT enabled
+ else {
+
+ TRACE_WARNING(
+ "MSD..RemoteWakeUp: Host has not enabled remote wake-up\n\r");
+ }
+}
+
diff --git a/usb/device/massstorage/MSDDriver.h b/usb/device/massstorage/MSDDriver.h new file mode 100644 index 0000000..0a8738a --- /dev/null +++ b/usb/device/massstorage/MSDDriver.h @@ -0,0 +1,75 @@ +/* ----------------------------------------------------------------------------
+ * 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.
+ * ----------------------------------------------------------------------------
+ */
+
+//------------------------------------------------------------------------------
+/// \unit
+/// !Purpose
+///
+/// Mass storage %device driver implementation.
+///
+/// !Usage
+///
+/// -# Enable and setup USB related pins (see pio & board.h).
+/// -# Configure the memory interfaces used for Mass Storage LUNs
+/// (see memories, MSDLun.h).
+/// -# Configure the USB MSD %driver using MSDDriver_Initialize.
+/// -# Invoke MSDDriver_StateMachine in main loop to handle all Mass Storage
+/// operations.
+//------------------------------------------------------------------------------
+
+#ifndef MSDDRIVER_H
+#define MSDDRIVER_H
+
+//------------------------------------------------------------------------------
+// Headers
+//------------------------------------------------------------------------------
+
+#include "MSD.h"
+#include "MSDLun.h"
+#include <utility/trace.h>
+
+//------------------------------------------------------------------------------
+// Global functions
+//------------------------------------------------------------------------------
+
+extern void MSDDriver_Initialize(MSDLun *luns, unsigned char numLuns);
+
+extern void MSDDriver_RequestHandler(const USBGenericRequest *request);
+
+extern void MSDDriver_StateMachine(void);
+
+extern void MSDDriver_RemoteWakeUp(void);
+
+extern void MSDDriver_DataCallback(unsigned char isRead,
+ unsigned int dataLength,
+ unsigned int nullCnt,
+ unsigned int fullCnt);
+
+#endif // #ifndef MSDDRIVER_H
+
diff --git a/usb/device/massstorage/MSDDriverArch.png b/usb/device/massstorage/MSDDriverArch.png Binary files differnew file mode 100644 index 0000000..4063440 --- /dev/null +++ b/usb/device/massstorage/MSDDriverArch.png diff --git a/usb/device/massstorage/MSDDriverClasses.png b/usb/device/massstorage/MSDDriverClasses.png Binary files differnew file mode 100644 index 0000000..3fd68d1 --- /dev/null +++ b/usb/device/massstorage/MSDDriverClasses.png diff --git a/usb/device/massstorage/MSDDriverDescriptors.c b/usb/device/massstorage/MSDDriverDescriptors.c new file mode 100644 index 0000000..1d8a073 --- /dev/null +++ b/usb/device/massstorage/MSDDriverDescriptors.c @@ -0,0 +1,475 @@ +/* ----------------------------------------------------------------------------
+ * 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.
+ * ----------------------------------------------------------------------------
+ */
+
+//------------------------------------------------------------------------------
+// Headers
+//------------------------------------------------------------------------------
+
+#include "MSDDriverDescriptors.h"
+#include <board.h>
+#include <usb/common/core/USBGenericDescriptor.h>
+#include <usb/common/core/USBDeviceDescriptor.h>
+#include <usb/common/core/USBConfigurationDescriptor.h>
+#include <usb/common/core/USBInterfaceDescriptor.h>
+#include <usb/common/core/USBEndpointDescriptor.h>
+#include <usb/common/core/USBStringDescriptor.h>
+#include <usb/common/massstorage/MSDeviceDescriptor.h>
+#include <usb/common/massstorage/MSInterfaceDescriptor.h>
+
+//------------------------------------------------------------------------------
+// Internal definitions
+//------------------------------------------------------------------------------
+
+//------------------------------------------------------------------------------
+/// \page "MSD Device Descriptor IDs"
+/// ...
+/// !IDs
+/// - MSDDriverDescriptors_VENDORID
+/// - MSDDriverDescriptors_PRODUCTID
+/// - MSDDriverDescriptors_RELEASE
+
+/// Vendor ID for the Mass Storage device driver.
+#define MSDDriverDescriptors_VENDORID 0x03EB
+/// Product ID for the Mass Storage device driver.
+#define MSDDriverDescriptors_PRODUCTID 0x6129
+/// Device release number for the Mass Storage device driver.
+#define MSDDriverDescriptors_RELEASE 0x0100
+//------------------------------------------------------------------------------
+
+//------------------------------------------------------------------------------
+// Macros
+//------------------------------------------------------------------------------
+
+/// Returns the minimum between two values.
+#define MIN(a, b) ((a < b) ? a : b)
+
+//------------------------------------------------------------------------------
+// Internal types
+//------------------------------------------------------------------------------
+
+//------------------------------------------------------------------------------
+/// List of configuration descriptors used by a Mass Storage device driver.
+//------------------------------------------------------------------------------
+typedef struct {
+
+ /// Standard configuration descriptor.
+ USBConfigurationDescriptor configuration;
+#if defined(CHIP_USB_OTGHS)
+ // OTG descriptor
+ USBOtgDescriptor otgDescriptor;
+#endif
+ /// Mass storage interface descriptor.
+ USBInterfaceDescriptor interface;
+ /// Bulk-out endpoint descriptor.
+ USBEndpointDescriptor bulkOut;
+ /// Bulk-in endpoint descriptor.
+ USBEndpointDescriptor bulkIn;
+
+} __attribute__ ((packed)) MSDConfigurationDescriptors;
+
+//------------------------------------------------------------------------------
+// Local variables
+//------------------------------------------------------------------------------
+
+/// Mass storage driver device descriptor.
+static const USBDeviceDescriptor deviceDescriptor = {
+
+ sizeof(USBDeviceDescriptor),
+ USBGenericDescriptor_DEVICE,
+ USBDeviceDescriptor_USB2_00,
+ MSDeviceDescriptor_CLASS,
+ MSDeviceDescriptor_SUBCLASS,
+ MSDeviceDescriptor_PROTOCOL,
+ CHIP_USB_ENDPOINTS_MAXPACKETSIZE(0),
+ MSDDriverDescriptors_VENDORID,
+ MSDDriverDescriptors_PRODUCTID,
+ MSDDriverDescriptors_RELEASE,
+ 1, // Manufacturer string descriptor index.
+ 2, // Product string descriptor index.
+ 3, // Serial number string descriptor index.
+ 1 // Device has one possible configuration.
+};
+
+#if defined(CHIP_USB_UDPHS) || defined(CHIP_USB_OTGHS)
+/// Device qualifier descriptor.
+static const USBDeviceQualifierDescriptor qualifierDescriptor = {
+
+ sizeof(USBDeviceQualifierDescriptor),
+ USBGenericDescriptor_DEVICEQUALIFIER,
+ USBDeviceDescriptor_USB2_00,
+ MSDeviceDescriptor_CLASS,
+ MSDeviceDescriptor_SUBCLASS,
+ MSDeviceDescriptor_PROTOCOL,
+ CHIP_USB_ENDPOINTS_MAXPACKETSIZE(0),
+ 1, // Device has one possible configuration.
+ 0x00
+};
+#endif
+
+/// Full-speed configuration descriptor.
+static const MSDConfigurationDescriptors configurationDescriptorsFS = {
+
+ // Standard configuration descriptor.
+ {
+ sizeof(USBConfigurationDescriptor),
+ USBGenericDescriptor_CONFIGURATION,
+ sizeof(MSDConfigurationDescriptors),
+ 1, // Configuration has one interface.
+ 1, // This is configuration #1.
+ 0, // No string descriptor for configuration.
+ BOARD_USB_BMATTRIBUTES,
+ USBConfigurationDescriptor_POWER(100)
+ },
+#if defined(CHIP_USB_OTGHS)
+ // OTG descriptor
+ {
+ sizeof(USBOtgDescriptor),
+ USBGenericDescriptor_OTG,
+ USBOTGDescriptor_HNP_SRP
+ },
+#endif
+ // Mass Storage interface descriptor.
+ {
+ sizeof(USBInterfaceDescriptor),
+ USBGenericDescriptor_INTERFACE,
+ 0, // This is interface #0.
+ 0, // This is alternate setting #0.
+ 2, // Interface uses two endpoints.
+ MSInterfaceDescriptor_CLASS,
+ MSInterfaceDescriptor_SCSI,
+ MSInterfaceDescriptor_BULKONLY,
+ 0 // No string descriptor for interface.
+ },
+ // Bulk-OUT endpoint descriptor
+ {
+ sizeof(USBEndpointDescriptor),
+ USBGenericDescriptor_ENDPOINT,
+ USBEndpointDescriptor_ADDRESS(
+ USBEndpointDescriptor_OUT,
+ MSDDriverDescriptors_BULKOUT),
+ USBEndpointDescriptor_BULK,
+ MIN(CHIP_USB_ENDPOINTS_MAXPACKETSIZE(MSDDriverDescriptors_BULKOUT),
+ USBEndpointDescriptor_MAXBULKSIZE_FS),
+ 0 // Must be 0 for full-speed Bulk endpoints.
+ },
+ // Bulk-IN endpoint descriptor
+ {
+ sizeof(USBEndpointDescriptor),
+ USBGenericDescriptor_ENDPOINT,
+ USBEndpointDescriptor_ADDRESS(
+ USBEndpointDescriptor_IN,
+ MSDDriverDescriptors_BULKIN),
+ USBEndpointDescriptor_BULK,
+ MIN(CHIP_USB_ENDPOINTS_MAXPACKETSIZE(MSDDriverDescriptors_BULKIN),
+ USBEndpointDescriptor_MAXBULKSIZE_FS),
+ 0 // Must be 0 for full-speed Bulk endpoints.
+ }
+};
+
+#if defined(CHIP_USB_UDPHS) || defined(CHIP_USB_OTGHS)
+/// Full-speed other speed configuration descriptor.
+static const MSDConfigurationDescriptors otherSpeedDescriptorsFS = {
+
+ // Standard configuration descriptor.
+ {
+ sizeof(USBConfigurationDescriptor),
+ USBGenericDescriptor_OTHERSPEEDCONFIGURATION,
+ sizeof(MSDConfigurationDescriptors),
+ 1, // Configuration has one interface.
+ 1, // This is configuration #1.
+ 0, // No string descriptor for configuration.
+ BOARD_USB_BMATTRIBUTES,
+ USBConfigurationDescriptor_POWER(100)
+ },
+#if defined(CHIP_USB_OTGHS)
+ // OTG descriptor
+ {
+ sizeof(USBOtgDescriptor),
+ USBGenericDescriptor_OTG,
+ USBOTGDescriptor_HNP_SRP
+ },
+#endif
+ // Mass Storage interface descriptor.
+ {
+ sizeof(USBInterfaceDescriptor),
+ USBGenericDescriptor_INTERFACE,
+ 0, // This is interface #0.
+ 0, // This is alternate setting #0.
+ 2, // Interface uses two endpoints.
+ MSInterfaceDescriptor_CLASS,
+ MSInterfaceDescriptor_SCSI,
+ MSInterfaceDescriptor_BULKONLY,
+ 0 // No string descriptor for interface.
+ },
+ // Bulk-OUT endpoint descriptor
+ {
+ sizeof(USBEndpointDescriptor),
+ USBGenericDescriptor_ENDPOINT,
+ USBEndpointDescriptor_ADDRESS(
+ USBEndpointDescriptor_OUT,
+ MSDDriverDescriptors_BULKOUT),
+ USBEndpointDescriptor_BULK,
+ MIN(CHIP_USB_ENDPOINTS_MAXPACKETSIZE(MSDDriverDescriptors_BULKOUT),
+ USBEndpointDescriptor_MAXBULKSIZE_HS),
+ 0 // No string descriptor for endpoint.
+ },
+ // Bulk-IN endpoint descriptor
+ {
+ sizeof(USBEndpointDescriptor),
+ USBGenericDescriptor_ENDPOINT,
+ USBEndpointDescriptor_ADDRESS(
+ USBEndpointDescriptor_IN,
+ MSDDriverDescriptors_BULKIN),
+ USBEndpointDescriptor_BULK,
+ MIN(CHIP_USB_ENDPOINTS_MAXPACKETSIZE(MSDDriverDescriptors_BULKIN),
+ USBEndpointDescriptor_MAXBULKSIZE_HS),
+ 0 // No string descriptor for endpoint.
+ }
+};
+
+/// High-speed configuration descriptor.
+static const MSDConfigurationDescriptors configurationDescriptorsHS = {
+
+ // Standard configuration descriptor.
+ {
+ sizeof(USBConfigurationDescriptor),
+ USBGenericDescriptor_CONFIGURATION,
+ sizeof(MSDConfigurationDescriptors),
+ 1, // Configuration has one interface.
+ 1, // This is configuration #1.
+ 0, // No string descriptor for configuration.
+ BOARD_USB_BMATTRIBUTES,
+ USBConfigurationDescriptor_POWER(100)
+ },
+#if defined(CHIP_USB_OTGHS)
+ // OTG descriptor
+ {
+ sizeof(USBOtgDescriptor),
+ USBGenericDescriptor_OTG,
+ USBOTGDescriptor_HNP_SRP
+ },
+#endif
+ // Mass Storage interface descriptor.
+ {
+ sizeof(USBInterfaceDescriptor),
+ USBGenericDescriptor_INTERFACE,
+ 0, // This is interface #0.
+ 0, // This is alternate setting #0.
+ 2, // Interface uses two endpoints.
+ MSInterfaceDescriptor_CLASS,
+ MSInterfaceDescriptor_SCSI,
+ MSInterfaceDescriptor_BULKONLY,
+ 0 // No string descriptor for interface.
+ },
+ // Bulk-OUT endpoint descriptor
+ {
+ sizeof(USBEndpointDescriptor),
+ USBGenericDescriptor_ENDPOINT,
+ USBEndpointDescriptor_ADDRESS(
+ USBEndpointDescriptor_OUT,
+ MSDDriverDescriptors_BULKOUT),
+ USBEndpointDescriptor_BULK,
+ MIN(CHIP_USB_ENDPOINTS_MAXPACKETSIZE(MSDDriverDescriptors_BULKOUT),
+ USBEndpointDescriptor_MAXBULKSIZE_HS),
+ 0 // No string descriptor for endpoint.
+ },
+ // Bulk-IN endpoint descriptor
+ {
+ sizeof(USBEndpointDescriptor),
+ USBGenericDescriptor_ENDPOINT,
+ USBEndpointDescriptor_ADDRESS(
+ USBEndpointDescriptor_IN,
+ MSDDriverDescriptors_BULKIN),
+ USBEndpointDescriptor_BULK,
+ MIN(CHIP_USB_ENDPOINTS_MAXPACKETSIZE(MSDDriverDescriptors_BULKIN),
+ USBEndpointDescriptor_MAXBULKSIZE_HS),
+ 0 // No string descriptor for endpoint.
+ }
+};
+
+/// High-speed other speed configuration descriptor.
+static const MSDConfigurationDescriptors otherSpeedDescriptorsHS = {
+
+ // Standard configuration descriptor.
+ {
+ sizeof(USBConfigurationDescriptor),
+ USBGenericDescriptor_OTHERSPEEDCONFIGURATION,
+ sizeof(MSDConfigurationDescriptors),
+ 1, // Configuration has one interface.
+ 1, // This is configuration #1.
+ 0, // No string descriptor for configuration.
+ BOARD_USB_BMATTRIBUTES,
+ USBConfigurationDescriptor_POWER(100)
+ },
+#if defined(CHIP_USB_OTGHS)
+ // OTG descriptor
+ {
+ sizeof(USBOtgDescriptor),
+ USBGenericDescriptor_OTG,
+ USBOTGDescriptor_HNP_SRP
+ },
+#endif
+ // Mass Storage interface descriptor.
+ {
+ sizeof(USBInterfaceDescriptor),
+ USBGenericDescriptor_INTERFACE,
+ 0, // This is interface #0.
+ 0, // This is alternate setting #0.
+ 2, // Interface uses two endpoints.
+ MSInterfaceDescriptor_CLASS,
+ MSInterfaceDescriptor_SCSI,
+ MSInterfaceDescriptor_BULKONLY,
+ 0 // No string descriptor for interface.
+ },
+ // Bulk-OUT endpoint descriptor
+ {
+ sizeof(USBEndpointDescriptor),
+ USBGenericDescriptor_ENDPOINT,
+ USBEndpointDescriptor_ADDRESS(
+ USBEndpointDescriptor_OUT,
+ MSDDriverDescriptors_BULKOUT),
+ USBEndpointDescriptor_BULK,
+ MIN(CHIP_USB_ENDPOINTS_MAXPACKETSIZE(MSDDriverDescriptors_BULKOUT),
+ USBEndpointDescriptor_MAXBULKSIZE_FS),
+ 0 // No string descriptor for endpoint.
+ },
+ // Bulk-IN endpoint descriptor
+ {
+ sizeof(USBEndpointDescriptor),
+ USBGenericDescriptor_ENDPOINT,
+ USBEndpointDescriptor_ADDRESS(
+ USBEndpointDescriptor_IN,
+ MSDDriverDescriptors_BULKIN),
+ USBEndpointDescriptor_BULK,
+ MIN(CHIP_USB_ENDPOINTS_MAXPACKETSIZE(MSDDriverDescriptors_BULKIN),
+ USBEndpointDescriptor_MAXBULKSIZE_FS),
+ 0 // No string descriptor for endpoint.
+ }
+};
+#endif
+
+/// Language ID string descriptor.
+static const unsigned char languageIdDescriptor[] = {
+
+ USBStringDescriptor_LENGTH(1),
+ USBGenericDescriptor_STRING,
+ USBStringDescriptor_ENGLISH_US
+};
+
+/// Manufacturer string descriptor.
+static const unsigned char manufacturerDescriptor[] = {
+
+ USBStringDescriptor_LENGTH(5),
+ USBGenericDescriptor_STRING,
+ USBStringDescriptor_UNICODE('A'),
+ USBStringDescriptor_UNICODE('T'),
+ USBStringDescriptor_UNICODE('M'),
+ USBStringDescriptor_UNICODE('E'),
+ USBStringDescriptor_UNICODE('L')
+};
+
+/// Product string descriptor.
+static const unsigned char productDescriptor[] = {
+
+ USBStringDescriptor_LENGTH(14),
+ USBGenericDescriptor_STRING,
+ USBStringDescriptor_UNICODE('A'),
+ USBStringDescriptor_UNICODE('T'),
+ USBStringDescriptor_UNICODE('M'),
+ USBStringDescriptor_UNICODE('E'),
+ USBStringDescriptor_UNICODE('L'),
+ USBStringDescriptor_UNICODE(' '),
+ USBStringDescriptor_UNICODE('A'),
+ USBStringDescriptor_UNICODE('T'),
+ USBStringDescriptor_UNICODE('9'),
+ USBStringDescriptor_UNICODE('1'),
+ USBStringDescriptor_UNICODE(' '),
+ USBStringDescriptor_UNICODE('M'),
+ USBStringDescriptor_UNICODE('S'),
+ USBStringDescriptor_UNICODE('D')
+};
+
+/// Serial number string descriptor. The serial number must be at least 12
+/// characters long and made up of only letters & numbers to be compliant with
+/// the MSD specification.
+static const unsigned char serialNumberDescriptor[] = {
+
+ USBStringDescriptor_LENGTH(12),
+ USBGenericDescriptor_STRING,
+ USBStringDescriptor_UNICODE('0'),
+ USBStringDescriptor_UNICODE('1'),
+ USBStringDescriptor_UNICODE('2'),
+ USBStringDescriptor_UNICODE('3'),
+ USBStringDescriptor_UNICODE('4'),
+ USBStringDescriptor_UNICODE('5'),
+ USBStringDescriptor_UNICODE('6'),
+ USBStringDescriptor_UNICODE('7'),
+ USBStringDescriptor_UNICODE('8'),
+ USBStringDescriptor_UNICODE('9'),
+ USBStringDescriptor_UNICODE('A'),
+ USBStringDescriptor_UNICODE('B')
+};
+
+/// List of all string descriptors used.
+static const unsigned char *stringDescriptors[] = {
+
+ languageIdDescriptor,
+ manufacturerDescriptor,
+ productDescriptor,
+ serialNumberDescriptor
+};
+
+//------------------------------------------------------------------------------
+// Global variables
+//------------------------------------------------------------------------------
+
+/// List of the standard descriptors used by the Mass Storage driver.
+const USBDDriverDescriptors msdDriverDescriptors = {
+
+ &deviceDescriptor,
+ (USBConfigurationDescriptor *) &configurationDescriptorsFS,
+#if defined(CHIP_USB_UDPHS) || defined(CHIP_USB_OTGHS)
+ &qualifierDescriptor,
+ (USBConfigurationDescriptor *) &otherSpeedDescriptorsFS,
+ &deviceDescriptor,
+ (USBConfigurationDescriptor *) &configurationDescriptorsHS,
+ &qualifierDescriptor,
+ (USBConfigurationDescriptor *) &otherSpeedDescriptorsHS,
+#else
+ 0, // No full-speed device qualifier descriptor
+ 0, // No full-speed other speed configuration
+ 0, // No high-speed device descriptor
+ 0, // No high-speed configuration descriptor
+ 0, // No high-speed device qualifier descriptor
+ 0, // No high-speed other speed configuration descriptor
+#endif
+ stringDescriptors,
+ 4 // Four string descriptors in array
+};
+
diff --git a/usb/device/massstorage/MSDDriverDescriptors.h b/usb/device/massstorage/MSDDriverDescriptors.h new file mode 100644 index 0000000..be046a9 --- /dev/null +++ b/usb/device/massstorage/MSDDriverDescriptors.h @@ -0,0 +1,83 @@ +/* ----------------------------------------------------------------------------
+ * 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.
+ * ----------------------------------------------------------------------------
+ */
+
+//------------------------------------------------------------------------------
+/// \unit
+/// !Purpose
+///
+/// Definitions of the descriptors required by a Mass Storage device driver.
+///
+/// !Usage
+///
+/// - For a USB %device:
+/// -# When initializing a USBDDriver instance, use msdDriverDescriptors as
+/// the list of standard descriptors.
+//------------------------------------------------------------------------------
+
+#ifndef MSDDRIVERDESCRIPTORS_H
+#define MSDDRIVERDESCRIPTORS_H
+
+//------------------------------------------------------------------------------
+// Headers
+//------------------------------------------------------------------------------
+
+#include <usb/device/core/USBDDriverDescriptors.h>
+
+//------------------------------------------------------------------------------
+// Definitions
+//------------------------------------------------------------------------------
+
+//------------------------------------------------------------------------------
+/// \page "MSD endpoint addresses"
+/// This page lists the addresses used by the Mass Storage driver %endpoints.
+///
+/// !Addresses
+///
+/// - MSDDriverDescriptors_BULKOUT
+/// - MSDDriverDescriptors_BULKIN
+
+/// Address of the Mass Storage bulk-out endpoint.
+#define MSDDriverDescriptors_BULKOUT 1
+
+/// Address of the Mass Storage bulk-in endpoint.
+#if defined(at91sam9m10)
+ #define MSDDriverDescriptors_BULKIN 6
+#else
+ #define MSDDriverDescriptors_BULKIN 2
+#endif
+//------------------------------------------------------------------------------
+
+//------------------------------------------------------------------------------
+// Exported variables
+//------------------------------------------------------------------------------
+
+extern const USBDDriverDescriptors msdDriverDescriptors;
+
+#endif //#ifndef MSDDRIVERDESCRIPTORS_H
+
diff --git a/usb/device/massstorage/MSDDriverStates.png b/usb/device/massstorage/MSDDriverStates.png Binary files differnew file mode 100644 index 0000000..a0c55c8 --- /dev/null +++ b/usb/device/massstorage/MSDDriverStates.png diff --git a/usb/device/massstorage/MSDIOFifo.c b/usb/device/massstorage/MSDIOFifo.c new file mode 100644 index 0000000..03f09a6 --- /dev/null +++ b/usb/device/massstorage/MSDIOFifo.c @@ -0,0 +1,68 @@ +/* ----------------------------------------------------------------------------
+ * 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.
+ * ----------------------------------------------------------------------------
+ */
+
+//------------------------------------------------------------------------------
+// Headers
+//------------------------------------------------------------------------------
+
+#include "MSDIOFifo.h"
+
+//------------------------------------------------------------------------------
+// Internal variables
+//------------------------------------------------------------------------------
+
+
+//------------------------------------------------------------------------------
+// Exported functions
+//------------------------------------------------------------------------------
+
+//------------------------------------------------------------------------------
+//! \brief Initializes a MSDIOFifo instance.
+//! \param pFifo Pointer to the MSDIOFifo instance to initialize
+//! \param pBuffer Pointer to a buffer used for read/write operation and
+//! which must be blockSize bytes aligned.
+//! \param bufferSize Total size of the buffer in bytes
+//------------------------------------------------------------------------------
+void MSDIOFifo_Init(MSDIOFifo *pFifo,
+ void * pBuffer, unsigned short bufferSize)
+{
+ pFifo->pBuffer = pBuffer;
+ pFifo->bufferSize = bufferSize;
+
+ pFifo->inputNdx = 0;
+ pFifo->outputNdx = 0;
+ pFifo->inputTotal = 0;
+ pFifo->outputTotal = 0;
+
+ pFifo->inputState = MSDIO_IDLE;
+ pFifo->outputState = MSDIO_IDLE;
+
+ pFifo->fullCnt = 0;
+ pFifo->nullCnt = 0;
+}
diff --git a/usb/device/massstorage/MSDIOFifo.h b/usb/device/massstorage/MSDIOFifo.h new file mode 100644 index 0000000..efe204d --- /dev/null +++ b/usb/device/massstorage/MSDIOFifo.h @@ -0,0 +1,133 @@ +/* ----------------------------------------------------------------------------
+ * 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.
+ * ----------------------------------------------------------------------------
+ */
+
+#ifndef _MSDIOFIFO_H
+#define _MSDIOFIFO_H
+
+//------------------------------------------------------------------------------
+// Headers
+//------------------------------------------------------------------------------
+
+//------------------------------------------------------------------------------
+// Definitions
+//------------------------------------------------------------------------------
+
+/// Idle state, do nothing
+#define MSDIO_IDLE 0
+/// Start, to start IO operation
+#define MSDIO_START 1
+/// Wait, waiting for IO operation done
+#define MSDIO_WAIT 2
+/// Next, to check if the next block can be performed
+#define MSDIO_NEXT 3
+/// Pause, to pause the process for buffer full or null
+#define MSDIO_PAUSE 4
+/// Abort, to abort the process
+#define MSDIO_ABORT 5
+/// Done, finish without error
+#define MSDIO_DONE 6
+/// Error, any error happens
+#define MSDIO_ERROR 7
+
+/// FIFO offset before USB transmit start
+//#define MSDIO_FIFO_OFFSET (4*512)
+
+/// FIFO trunk size (in each transfer, large amount of data)
+#define MSDIO_READ10_CHUNK_SIZE (4*512)
+#define MSDIO_WRITE10_CHUNK_SIZE (4*512)
+
+//------------------------------------------------------------------------------
+// Types
+//------------------------------------------------------------------------------
+
+/// FIFO buffer for READ/WRITE (disk) operation of a mass storage device
+typedef struct _MSDIOFifo {
+
+ /// Pointer to the ring buffer allocated for read/write
+ unsigned char * pBuffer;
+ /// The size of the buffer allocated
+ unsigned int bufferSize;
+#ifdef MSDIO_FIFO_OFFSET
+ /// The offset to start USB transfer (READ10)
+ unsigned int bufferOffset;
+#endif
+ /// The index of input data (loaded to fifo buffer)
+ unsigned int inputNdx;
+ /// The total size of the loaded data
+ unsigned int inputTotal;
+ /// The index of output data (sent from the fifo buffer)
+ unsigned int outputNdx;
+ /// The total size of the output data
+ unsigned int outputTotal;
+
+ /// The total size of the data
+ unsigned int dataTotal;
+ /// The size of the block in bytes
+ unsigned short blockSize;
+#if defined(MSDIO_READ10_CHUNK_SIZE) || defined(MSDIO_WRITE10_CHUNK_SIZE)
+ /// The size of one chunk
+ /// (1 block, or several blocks for large amount data R/W)
+ unsigned int chunkSize;
+#endif
+ /// State of input & output
+ unsigned char inputState;
+ unsigned char outputState;
+
+ //- Statistics
+ /// Times when fifo has no data to send
+ unsigned short nullCnt;
+ /// Times when fifo can not load more input data
+ unsigned short fullCnt;
+} MSDIOFifo, *PMSDIOFifo;
+
+//------------------------------------------------------------------------------
+// MACROS
+//------------------------------------------------------------------------------
+
+//------------------------------------------------------------------------------
+/// Increase the index, by defined block size, in the ring buffer
+/// \param ndx The index to be increased
+/// \param sectSize The defined block size
+/// \param bufSize The ring buffer size
+//------------------------------------------------------------------------------
+#define MSDIOFifo_IncNdx(ndx, sectSize, bufSize) \
+ if ((ndx) >= (bufSize) - (sectSize)) (ndx) = 0; \
+ else (ndx) += (sectSize)
+
+
+//------------------------------------------------------------------------------
+// Exported Functions
+//------------------------------------------------------------------------------
+
+
+extern void MSDIOFifo_Init(MSDIOFifo *pFifo,
+ void * pBuffer, unsigned short bufferSize);
+
+#endif // _MSDIOFIFO_H
+
diff --git a/usb/device/massstorage/MSDLun.c b/usb/device/massstorage/MSDLun.c new file mode 100644 index 0000000..42999d7 --- /dev/null +++ b/usb/device/massstorage/MSDLun.c @@ -0,0 +1,366 @@ +/* ----------------------------------------------------------------------------
+ * 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.
+ * ----------------------------------------------------------------------------
+ */
+
+//------------------------------------------------------------------------------
+// Headers
+//------------------------------------------------------------------------------
+
+
+#include "MSDLun.h"
+#include <utility/trace.h>
+#include <usb/device/core/USBD.h>
+
+//------------------------------------------------------------------------------
+// Constants
+//------------------------------------------------------------------------------
+
+/// Default LUN block size in bytes
+#define DEFAULT_LUN_BLOCK_SIZE 512
+
+//------------------------------------------------------------------------------
+// Internal variables
+//------------------------------------------------------------------------------
+
+/// Inquiry data to return to the host for the Lun.
+static SBCInquiryData inquiryData = {
+
+ SBC_DIRECT_ACCESS_BLOCK_DEVICE, // Direct-access block device
+ SBC_PERIPHERAL_DEVICE_CONNECTED,// Peripheral device is connected
+ 0x00, // Reserved bits
+ 0x01, // Media is removable
+ SBC_SPC_VERSION_4, // SPC-4 supported
+ 0x2, // Response data format, must be 0x2
+ 0, // Hierarchical addressing not supported
+ 0, // ACA not supported
+ 0x0, // Obsolete bits
+ sizeof(SBCInquiryData) - 5, // Additional length
+ 0, // No embedded SCC
+ 0, // No access control coordinator
+ SBC_TPGS_NONE, // No target port support group
+ 0, // Third-party copy not supported
+ 0x0, // Reserved bits
+ 0, // Protection information not supported
+ 0x0, // Obsolete bit
+ 0, // No embedded enclosure service component
+ 0x0, // ???
+ 0, // Device is not multi-port
+ 0x0, // Obsolete bits
+ 0x0, // Unused feature
+ 0x0, // Unused features
+ 0, // Task management model not supported
+ 0x0, // ???
+ {'A','T','M','E','L',' ',' ',' '},
+ {'M','a','s','s',' ',
+ 'S','t','o','r','a','g','e',' ',
+ 'M','S','D'},
+ {'0','.','0','1'},
+ {'M','a','s','s',' ',
+ 'S','t','o','r','a','g','e',' ',
+ 'E','x','a','m','p','l','e'},
+ 0x00, // Unused features
+ 0x00, // Reserved bits
+ {SBC_VERSION_DESCRIPTOR_SBC_3}, // SBC-3 compliant device
+ {0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0,
+ 0, 0} // Reserved
+};
+
+//------------------------------------------------------------------------------
+// Exported functions
+//------------------------------------------------------------------------------
+
+//------------------------------------------------------------------------------
+//! \brief Initializes a LUN instance. Must be invoked at least once even no
+//! Media is linked.
+//! \param lun Pointer to the MSDLun instance to initialize
+//! \param media Media on which the LUN is constructed, set to 0 to
+//! disconnect the Media or initialize an ejected LUN.
+//! \param ioBuffer Pointer to a buffer used for read/write operation and
+//! which must be blockSize bytes long.
+//! \param ioBufferSize Size of the allocated IO buffer.
+//! \param baseAddress Base address of the LUN in number of media blocks
+//! \param size Total size of the LUN in number of media blocks
+//! \param blockSize One block of the LUN in number of media blocks
+//! \param protected The LUN area is forced to readonly even the media
+//! is writable
+//! \param dataMonitor Pointer to a Monitor Function to analyze the flow of
+//! this LUN.
+//------------------------------------------------------------------------------
+void LUN_Init(MSDLun *lun,
+ Media *media,
+ unsigned char *ioBuffer,
+ unsigned int ioBufferSize,
+ unsigned int baseAddress,
+ unsigned int size,
+ unsigned short blockSize,
+ unsigned char protected,
+ void (*dataMonitor)(unsigned char flowDirection,
+ unsigned int dataLength,
+ unsigned int fifoNullCount,
+ unsigned int fifoFullCount))
+{
+ unsigned int logicalBlockAddress;
+ TRACE_INFO("LUN init\n\r");
+
+ // Initialize inquiry data
+ lun->inquiryData = &inquiryData;
+
+ // Initialize request sense data
+ lun->requestSenseData.bResponseCode = SBC_SENSE_DATA_FIXED_CURRENT;
+ lun->requestSenseData.isValid = 1;
+ lun->requestSenseData.bObsolete1 = 0;
+ lun->requestSenseData.bSenseKey = SBC_SENSE_KEY_NO_SENSE;
+ lun->requestSenseData.bReserved1 = 0;
+ lun->requestSenseData.isILI = 0;
+ lun->requestSenseData.isEOM = 0;
+ lun->requestSenseData.isFilemark = 0;
+ lun->requestSenseData.pInformation[0] = 0;
+ lun->requestSenseData.pInformation[1] = 0;
+ lun->requestSenseData.pInformation[2] = 0;
+ lun->requestSenseData.pInformation[3] = 0;
+ lun->requestSenseData.bAdditionalSenseLength
+ = sizeof(SBCRequestSenseData) - 8;
+ lun->requestSenseData.bAdditionalSenseCode = 0;
+ lun->requestSenseData.bAdditionalSenseCodeQualifier = 0;
+ lun->requestSenseData.bFieldReplaceableUnitCode = 0;
+ lun->requestSenseData.bSenseKeySpecific = 0;
+ lun->requestSenseData.pSenseKeySpecific[0] = 0;
+ lun->requestSenseData.pSenseKeySpecific[0] = 0;
+ lun->requestSenseData.isSKSV = 0;
+
+ STORE_DWORDB(0, lun->readCapacityData.pLogicalBlockAddress);
+ STORE_DWORDB(0, lun->readCapacityData.pLogicalBlockLength);
+
+ // Initialize LUN
+ lun->media = media;
+ if (media == 0) {
+ lun->status = LUN_NOT_PRESENT;
+ return;
+ }
+
+ lun->baseAddress = baseAddress;
+
+ // Customized block size
+ if (blockSize) {
+ lun->blockSize = blockSize;
+ }
+ else {
+ if (media->blockSize < DEFAULT_LUN_BLOCK_SIZE)
+ lun->blockSize = DEFAULT_LUN_BLOCK_SIZE / media->blockSize;
+ else
+ lun->blockSize = 1;
+ }
+
+ if (size) {
+ lun->size = size;
+ }
+ else {
+ lun->size = media->size;
+ //if (blockSize)
+ // lun->size = media->size / blockSize;
+ //else {
+ // if (media->blockSize < DEFAULT_LUN_BLOCK_SIZE)
+ // lun->size = media->size / lun->blockSize;
+ // else
+ // lun->size = media->size;
+ //}
+ }
+
+ TRACE_INFO("LUN: blkSize %d, size %d\n\r", (int)lun->blockSize, (int)lun->size);
+ if (protected) lun->protected = 1;
+ else lun->protected = media->protected;
+
+ lun->ioFifo.pBuffer = ioBuffer;
+ lun->ioFifo.bufferSize = ioBufferSize;
+
+ lun->dataMonitor = dataMonitor;
+
+ // Initialize read capacity data
+ logicalBlockAddress = lun->size / lun->blockSize - 1;
+ STORE_DWORDB(logicalBlockAddress,
+ lun->readCapacityData.pLogicalBlockAddress);
+ STORE_DWORDB(lun->blockSize * media->blockSize,
+ lun->readCapacityData.pLogicalBlockLength);
+
+ // Indicate media change
+ lun->status = LUN_CHANGED;
+}
+
+//------------------------------------------------------------------------------
+//! \brief Eject the media from a LUN
+//! \param lun Pointer to the MSDLun instance to initialize
+//! \return Operation result code
+//------------------------------------------------------------------------------
+unsigned char LUN_Eject(MSDLun *lun)
+{
+ if (lun->media) {
+
+ // Avoid any LUN R/W in progress
+ if (lun->media->state == MED_STATE_BUSY) {
+
+ return USBD_STATUS_LOCKED;
+ }
+
+ // Remove the link of the media
+ lun->media = 0;
+ }
+ // LUN is removed
+ lun->status = LUN_NOT_PRESENT;
+
+ return USBD_STATUS_SUCCESS;
+}
+
+//------------------------------------------------------------------------------
+//! \brief Writes data on the a LUN starting at the specified block address.
+//! \param lun Pointer to a MSDLun instance
+//! \param blockAddress First block address to write
+//! \param data Pointer to the data to write
+//! \param length Number of blocks to write
+//! \param callback Optional callback to invoke when the write finishes
+//! \return Operation result code
+//------------------------------------------------------------------------------
+unsigned char LUN_Write(MSDLun *lun,
+ unsigned int blockAddress,
+ void *data,
+ unsigned int length,
+ TransferCallback callback,
+ void *argument)
+{
+ unsigned int medBlk, medLen;
+ unsigned char status;
+
+ TRACE_INFO_WP("LUNWrite(%u) ", blockAddress);
+
+ // Check that the data is not too big
+ if ((length + blockAddress) * lun->blockSize > lun->size) {
+
+ TRACE_WARNING("LUN_Write: Data too big\n\r");
+ status = USBD_STATUS_ABORTED;
+ }
+ else if (lun->media == 0 || lun->status != LUN_READY) {
+
+ TRACE_WARNING("LUN_Write: Media not ready\n\r");
+ status = USBD_STATUS_ABORTED;
+ }
+ else if (lun->protected) {
+ TRACE_WARNING("LUN_Write: LUN is readonly\n\r");
+ status = USBD_STATUS_ABORTED;
+ }
+ else {
+
+ // Compute write start address
+ medBlk = lun->baseAddress + blockAddress * lun->blockSize;
+ medLen = length * lun->blockSize;
+
+ // Start write operation
+ status = MED_Write(lun->media,
+ medBlk,
+ data,
+ medLen,
+ (MediaCallback) callback,
+ argument);
+
+ // Check operation result code
+ if (status == MED_STATUS_SUCCESS) {
+
+ status = USBD_STATUS_SUCCESS;
+ }
+ else {
+
+ TRACE_WARNING("LUN_Write: Cannot write media\n\r");
+ status = USBD_STATUS_ABORTED;
+ }
+ }
+
+ return status;
+}
+
+//------------------------------------------------------------------------------
+//! \brief Reads data from a LUN, starting at the specified block address.
+//! \param lun Pointer to a MSDLun instance
+//! \param blockAddress First block address to read
+//! \param data Pointer to a data buffer in which to store the data
+//! \param length Number of blocks to read
+//! \param callback Optional callback to invoke when the read finishes
+//! \return Operation result code
+//------------------------------------------------------------------------------
+unsigned char LUN_Read(MSDLun *lun,
+ unsigned int blockAddress,
+ void *data,
+ unsigned int length,
+ TransferCallback callback,
+ void *argument)
+{
+ unsigned int medBlk, medLen;
+ unsigned char status;
+
+ // Check that the data is not too big
+ if ((length + blockAddress) * lun->blockSize > lun->size) {
+
+ TRACE_WARNING("LUN_Read: Area: (%d + %d)*%d > %d\n\r",
+ (int)length, (int)blockAddress, (int)lun->blockSize, (int)lun->size);
+ status = USBD_STATUS_ABORTED;
+ }
+ else if (lun->media == 0 || lun->status != LUN_READY) {
+
+ TRACE_WARNING("LUN_Read: Media not present\n\r");
+ status = USBD_STATUS_ABORTED;
+ }
+ else {
+
+ TRACE_INFO_WP("LUNRead(%u) ", blockAddress);
+
+ // Compute read start address
+ medBlk = lun->baseAddress + (blockAddress * lun->blockSize);
+ medLen = length * lun->blockSize;
+
+ // Start write operation
+ status = MED_Read(lun->media,
+ medBlk,
+ data,
+ medLen,
+ (MediaCallback) callback,
+ argument);
+
+ // Check result code
+ if (status == MED_STATUS_SUCCESS) {
+
+ status = USBD_STATUS_SUCCESS;
+ }
+ else {
+
+ TRACE_WARNING("LUN_Read: Cannot read media\n\r");
+ status = USBD_STATUS_ABORTED;
+ }
+ }
+
+ return status;
+}
diff --git a/usb/device/massstorage/MSDLun.h b/usb/device/massstorage/MSDLun.h new file mode 100644 index 0000000..52eea4c --- /dev/null +++ b/usb/device/massstorage/MSDLun.h @@ -0,0 +1,152 @@ +/* ----------------------------------------------------------------------------
+ * 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.
+ * ----------------------------------------------------------------------------
+ */
+
+//------------------------------------------------------------------------------
+/// \unit
+/// !Purpose
+///
+/// Logical Unit Number (LUN) used by the Mass Storage driver and the SCSI
+/// protocol. Represents a logical hard-drive.
+///
+/// !Usage
+/// -# Initialize Memory related pins (see pio & board.h).
+/// -# Initialize a Media instance for the LUN (see memories).
+/// -# Initlalize the LUN with LUN_Init, and link to the initialized Media.
+/// -# To read data from the LUN linked media, uses LUN_Read.
+/// -# To write data to the LUN linked media, uses LUN_Write.
+/// -# To unlink the media, uses LUN_Eject.
+//------------------------------------------------------------------------------
+
+#ifndef MSDLUN_H
+#define MSDLUN_H
+
+//------------------------------------------------------------------------------
+// Headers
+//------------------------------------------------------------------------------
+
+#include "SBC.h"
+#include <memories/Media.h>
+#include <usb/device/massstorage/MSDIOFifo.h>
+#include <usb/device/core/USBD.h>
+
+//------------------------------------------------------------------------------
+// Definitions
+//------------------------------------------------------------------------------
+
+/// LUN RC: success
+#define LUN_STATUS_SUCCESS 0x00
+/// LUN RC: error
+#define LUN_STATUS_ERROR 0x02
+
+/// Media of LUN is removed
+#define LUN_NOT_PRESENT 0x00
+/// LUN is ejected by host
+#define LUN_EJECTED 0x01
+/// Media of LUN is changed
+#define LUN_CHANGED 0x10
+/// LUN Not Ready to Ready transition
+#define LUN_TRANS_READY LUN_CHANGED
+/// Media of LUN is ready
+#define LUN_READY 0x11
+
+//------------------------------------------------------------------------------
+// Structures
+//------------------------------------------------------------------------------
+
+/// LUN structure
+typedef struct {
+
+ /// Pointer to a SBCInquiryData instance.
+ SBCInquiryData *inquiryData;
+ /// Fifo for USB transfer, must be assigned.
+ MSDIOFifo ioFifo;
+ /// Pointer to Media instance for the LUN.
+ Media *media;
+ /// Pointer to a Monitor Function to analyze the flow of LUN.
+ /// \param flowDirection 1 - device to host (READ10)
+ /// 0 - host to device (WRITE10)
+ /// \param dataLength Length of data transferred in bytes.
+ /// \param fifoNullCount Times that FIFO is NULL to wait
+ /// \param fifoFullCount Times that FIFO is filled to wait
+ void (*dataMonitor)(unsigned char flowDirection,
+ unsigned int dataLength,
+ unsigned int fifoNullCount,
+ unsigned int fifoFullCount);
+ /// The start position of the media (blocks) allocated to the LUN.
+ unsigned int baseAddress;
+ /// The size of the media (blocks) allocated to the LUN.
+ unsigned int size;
+ /// Sector size of the media in number of media blocks
+ unsigned short blockSize;
+ /// The LUN can be readonly even the media is writable
+ unsigned char protected;
+ /// The LUN status (Ejected/Changed/)
+ unsigned char status;
+
+ /// Data for the RequestSense command.
+ SBCRequestSenseData requestSenseData;
+ /// Data for the ReadCapacity command.
+ SBCReadCapacity10Data readCapacityData;
+
+} MSDLun;
+
+//------------------------------------------------------------------------------
+// Exported functions
+//------------------------------------------------------------------------------
+extern void LUN_Init(MSDLun *lun,
+ Media *media,
+ unsigned char *ioBuffer,
+ unsigned int ioBufferSize,
+ unsigned int baseAddress,
+ unsigned int size,
+ unsigned short blockSize,
+ unsigned char protected,
+ void (*dataMonitor)(unsigned char flowDirection,
+ unsigned int dataLength,
+ unsigned int fifoNullCount,
+ unsigned int fifoFullCount));
+
+extern unsigned char LUN_Eject(MSDLun *lun);
+
+extern unsigned char LUN_Write(MSDLun *lun,
+ unsigned int blockAddress,
+ void *data,
+ unsigned int length,
+ TransferCallback callback,
+ void *argument);
+
+extern unsigned char LUN_Read(MSDLun *lun,
+ unsigned int blockAddress,
+ void *data,
+ unsigned int length,
+ TransferCallback callback,
+ void *argument);
+
+#endif //#ifndef MSDLUN_H
+
diff --git a/usb/device/massstorage/MSDMediaArch.png b/usb/device/massstorage/MSDMediaArch.png Binary files differnew file mode 100644 index 0000000..b641e97 --- /dev/null +++ b/usb/device/massstorage/MSDMediaArch.png diff --git a/usb/device/massstorage/SBC.h b/usb/device/massstorage/SBC.h new file mode 100644 index 0000000..7c64844 --- /dev/null +++ b/usb/device/massstorage/SBC.h @@ -0,0 +1,653 @@ +/* ----------------------------------------------------------------------------
+ * 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.
+ * ----------------------------------------------------------------------------
+ */
+
+//------------------------------------------------------------------------------
+/// \unit
+/// !Purpose
+///
+/// SCSI definitions.
+///
+/// !Usage
+///
+/// -# After command block received, Access and decode the SCSI command block
+/// with SBCCommand structure.
+//------------------------------------------------------------------------------
+
+#ifndef SBC_H
+#define SBC_H
+
+//------------------------------------------------------------------------------
+// Definitions
+//------------------------------------------------------------------------------
+
+//------------------------------------------------------------------------------
+/// \page "SBC Operation Codes"
+/// This page lists operation codes of commands described in the SBC-3
+/// standard.
+///
+/// \note That most commands are actually defined in other standards,
+/// like SPC-4. Optional commands are not included here.
+///
+/// \see sbc3r07.pdf - Section 5.1 - Table 12
+/// \see spc4r06.pdf
+/// \see SBCCommand
+///
+/// !Codes
+/// - SBC_INQUIRY
+/// - SBC_READ_10
+/// - SBC_READ_CAPACITY_10
+/// - SBC_REQUEST_SENSE
+/// - SBC_TEST_UNIT_READY
+/// - SBC_WRITE_10
+///
+/// !Optional Codes but required by Windows
+/// - SBC_PREVENT_ALLOW_MEDIUM_REMOVAL
+/// - SBC_MODE_SENSE_6
+/// - SBC_VERIFY_10
+/// - SBC_READ_FORMAT_CAPACITIES
+
+/// Request information regarding parameters of the target and Logical Unit.
+#define SBC_INQUIRY 0x12
+/// Request the transfer data to the host.
+#define SBC_READ_10 0x28
+/// Request capacities of the currently installed medium.
+#define SBC_READ_CAPACITY_10 0x25
+/// Request that the device server transfer sense data.
+#define SBC_REQUEST_SENSE 0x03
+/// Check if the LUN is ready
+#define SBC_TEST_UNIT_READY 0x00
+/// Request that the device write the data transferred by the host.
+#define SBC_WRITE_10 0x2A
+
+/// Request that the target enable or disable the removal of the medium in
+/// the Logical Unit.
+#define SBC_PREVENT_ALLOW_MEDIUM_REMOVAL 0x1E
+/// Report parameters.
+#define SBC_MODE_SENSE_6 0x1A
+/// Request that the %device verify the data on the medium.
+#define SBC_VERIFY_10 0x2F
+/// Request a list of the possible capacities that can be formatted on medium
+#define SBC_READ_FORMAT_CAPACITIES 0x23
+//------------------------------------------------------------------------------
+
+//------------------------------------------------------------------------------
+/// \page "SBC Periph. Qualifiers"
+/// This page lists the peripheral qualifier values specified in the INQUIRY
+/// data
+/// \see spc4r06.pdf - Section 6.4.2 - Table 83
+/// \see SBCInquiryData
+///
+/// !Qualifiers
+/// - SBC_PERIPHERAL_DEVICE_CONNECTED
+/// - SBC_PERIPHERAL_DEVICE_NOT_CONNECTED
+/// - SBC_PERIPHERAL_DEVICE_NOT_SUPPORTED
+
+#define SBC_PERIPHERAL_DEVICE_CONNECTED 0x00
+#define SBC_PERIPHERAL_DEVICE_NOT_CONNECTED 0x01
+#define SBC_PERIPHERAL_DEVICE_NOT_SUPPORTED 0x03
+//------------------------------------------------------------------------------
+
+//------------------------------------------------------------------------------
+/// \page "SBC Periph. Types"
+/// This page lists peripheral device types specified in the INQUIRY data
+/// \see spc4r06.pdf - Section 6.4.2 - Table 84
+/// \see SBCInquiryData
+///
+/// !Types
+/// - SBC_DIRECT_ACCESS_BLOCK_DEVICE
+/// - SBC_SEQUENTIAL_ACCESS_DEVICE
+/// - SBC_PRINTER_DEVICE
+/// - SBC_PROCESSOR_DEVICE
+/// - SBC_WRITE_ONCE_DEVICE
+/// - SBC_CD_DVD_DEVICE
+/// - SBC_SCANNER_DEVICE
+/// - SBC_OPTICAL_MEMORY_DEVICE
+/// - SBC_MEDIA_CHANGER_DEVICE
+/// - SBC_COMMUNICATION_DEVICE
+/// - SBC_STORAGE_ARRAY_CONTROLLER_DEVICE
+/// - SBC_ENCLOSURE_SERVICES_DEVICE
+/// - SBC_SIMPLIFIED_DIRECT_ACCESS_DEVICE
+/// - SBC_OPTICAL_CARD_READER_WRITER_DEVICE
+/// - SBC_BRIDGE_CONTROLLER_COMMANDS
+/// - SBC_OBJECT_BASED_STORAGE_DEVICE
+
+#define SBC_DIRECT_ACCESS_BLOCK_DEVICE 0x00
+#define SBC_SEQUENTIAL_ACCESS_DEVICE 0x01
+#define SBC_PRINTER_DEVICE 0x02
+#define SBC_PROCESSOR_DEVICE 0x03
+#define SBC_WRITE_ONCE_DEVICE 0x04
+#define SBC_CD_DVD_DEVICE 0x05
+#define SBC_SCANNER_DEVICE 0x06
+#define SBC_OPTICAL_MEMORY_DEVICE 0x07
+#define SBC_MEDIA_CHANGER_DEVICE 0x08
+#define SBC_COMMUNICATION_DEVICE 0x09
+#define SBC_STORAGE_ARRAY_CONTROLLER_DEVICE 0x0C
+#define SBC_ENCLOSURE_SERVICES_DEVICE 0x0D
+#define SBC_SIMPLIFIED_DIRECT_ACCESS_DEVICE 0x0E
+#define SBC_OPTICAL_CARD_READER_WRITER_DEVICE 0x0F
+#define SBC_BRIDGE_CONTROLLER_COMMANDS 0x10
+#define SBC_OBJECT_BASED_STORAGE_DEVICE 0x11
+//------------------------------------------------------------------------------
+
+//------------------------------------------------------------------------------
+/// \brief Version value for the SBC-3 specification
+/// \see spc4r06.pdf - Section 6.4.2 - Table 85
+#define SBC_SPC_VERSION_4 0x06
+//------------------------------------------------------------------------------
+
+//------------------------------------------------------------------------------
+/// \brief Values for the TPGS field returned in INQUIRY data
+/// \see spc4r06.pdf - Section 6.4.2 - Table 86
+#define SBC_TPGS_NONE 0x0
+#define SBC_TPGS_ASYMMETRIC 0x1
+#define SBC_TPGS_SYMMETRIC 0x2
+#define SBC_TPGS_BOTH 0x3
+//------------------------------------------------------------------------------
+
+//------------------------------------------------------------------------------
+/// \brief Version descriptor value for the SBC-3 specification
+/// \see spc4r06.pdf - Section 6.4.2 - Table 87
+#define SBC_VERSION_DESCRIPTOR_SBC_3 0x04C0
+//------------------------------------------------------------------------------
+
+//------------------------------------------------------------------------------
+/// \page "SBC Sense Response Codes"
+/// This page lists sense data response codes returned in REQUEST SENSE data
+/// \see spc4r06.pdf - Section 4.5.1 - Table 12
+///
+/// !Codes
+/// - SBC_SENSE_DATA_FIXED_CURRENT
+/// - SBC_SENSE_DATA_FIXED_DEFERRED
+/// - SBC_SENSE_DATA_DESCRIPTOR_CURRENT
+/// - SBC_SENSE_DATA_DESCRIPTOR_DEFERRED
+
+#define SBC_SENSE_DATA_FIXED_CURRENT 0x70
+#define SBC_SENSE_DATA_FIXED_DEFERRED 0x71
+#define SBC_SENSE_DATA_DESCRIPTOR_CURRENT 0x72
+#define SBC_SENSE_DATA_DESCRIPTOR_DEFERRED 0x73
+//------------------------------------------------------------------------------
+
+//------------------------------------------------------------------------------
+/// \page "SBC Sense Keys"
+/// This page lists sense key values returned in the REQUEST SENSE data
+/// \see spc4r06.pdf - Section 4.5.6 - Table 27
+///
+/// !Keys
+/// - SBC_SENSE_KEY_NO_SENSE
+/// - SBC_SENSE_KEY_RECOVERED_ERROR
+/// - SBC_SENSE_KEY_NOT_READY
+/// - SBC_SENSE_KEY_MEDIUM_ERROR
+/// - SBC_SENSE_KEY_HARDWARE_ERROR
+/// - SBC_SENSE_KEY_ILLEGAL_REQUEST
+/// - SBC_SENSE_KEY_UNIT_ATTENTION
+/// - SBC_SENSE_KEY_DATA_PROTECT
+/// - SBC_SENSE_KEY_BLANK_CHECK
+/// - SBC_SENSE_KEY_VENDOR_SPECIFIC
+/// - SBC_SENSE_KEY_COPY_ABORTED
+/// - SBC_SENSE_KEY_ABORTED_COMMAND
+/// - SBC_SENSE_KEY_VOLUME_OVERFLOW
+/// - SBC_SENSE_KEY_MISCOMPARE
+
+/// No specific sense key. Successful command.
+#define SBC_SENSE_KEY_NO_SENSE 0x00
+/// Command completed succesfully with some recovery action by the %device.
+#define SBC_SENSE_KEY_RECOVERED_ERROR 0x01
+/// The device can not be accessed.
+#define SBC_SENSE_KEY_NOT_READY 0x02
+/// Command terminated with a error condition that was probably caused by a
+/// flaw in the medium or an error in the recorded data.
+#define SBC_SENSE_KEY_MEDIUM_ERROR 0x03
+/// Hardware failure while performing the command or during a self test.
+#define SBC_SENSE_KEY_HARDWARE_ERROR 0x04
+/// Illegal parameter found in the command or additional parameters.
+#define SBC_SENSE_KEY_ILLEGAL_REQUEST 0x05
+/// Removable medium may have been changed or the %device has been reset.
+#define SBC_SENSE_KEY_UNIT_ATTENTION 0x06
+/// Write on a block that is protected.
+#define SBC_SENSE_KEY_DATA_PROTECT 0x07
+/// Indicates that a write-once device or a sequential-access device
+/// encountered blank medium or format-defined end-of-data indication while
+/// reading or a write-once device encountered a non-blank medium while writing.
+#define SBC_SENSE_KEY_BLANK_CHECK 0x08
+/// Reporting vendor specific conditions.
+#define SBC_SENSE_KEY_VENDOR_SPECIFIC 0x09
+/// EXTENDED COPY command was aborted.
+#define SBC_SENSE_KEY_COPY_ABORTED 0x0A
+/// Device aborted the command.
+#define SBC_SENSE_KEY_ABORTED_COMMAND 0x0B
+/// A buffered peripheral device is overflow.
+#define SBC_SENSE_KEY_VOLUME_OVERFLOW 0x0D
+/// The source data did not match the data read from the medium.
+#define SBC_SENSE_KEY_MISCOMPARE 0x0E
+//------------------------------------------------------------------------------
+
+//------------------------------------------------------------------------------
+/// \page "SBC Sense Additionals"
+/// This page lists additional sense code values returned in REQUEST SENSE data
+/// \see spc4r06.pdf - Section 4.5.6 - Table 28
+///
+/// !Additional Codes
+/// - SBC_ASC_LOGICAL_UNIT_NOT_READY
+/// - SBC_ASC_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE
+/// - SBC_ASC_INVALID_FIELD_IN_CDB
+/// - SBC_ASC_WRITE_PROTECTED
+/// - SBC_ASC_FORMAT_CORRUPTED
+/// - SBC_ASC_INVALID_COMMAND_OPERATION_CODE
+/// - SBC_ASC_TOO_MUCH_WRITE_DATA
+/// - SBC_ASC_NOT_READY_TO_READY_CHANGE
+/// - SBC_ASC_MEDIUM_NOT_PRESENT
+
+#define SBC_ASC_LOGICAL_UNIT_NOT_READY 0x04
+#define SBC_ASC_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE 0x21
+#define SBC_ASC_INVALID_FIELD_IN_CDB 0x24
+#define SBC_ASC_WRITE_PROTECTED 0x27
+#define SBC_ASC_FORMAT_CORRUPTED 0x31
+#define SBC_ASC_INVALID_COMMAND_OPERATION_CODE 0x20
+#define SBC_ASC_TOO_MUCH_WRITE_DATA 0x26
+#define SBC_ASC_NOT_READY_TO_READY_CHANGE 0x28
+#define SBC_ASC_MEDIUM_NOT_PRESENT 0x3A
+//------------------------------------------------------------------------------
+
+//------------------------------------------------------------------------------
+/// \brief MEDIUM TYPE field value for direct-access block devices
+/// \see sbc3r06.pdf - Section 6.3.1
+#define SBC_MEDIUM_TYPE_DIRECT_ACCESS_BLOCK_DEVICE 0x00
+//------------------------------------------------------------------------------
+
+//------------------------------------------------------------------------------
+/// \brief MRIE field values
+/// \see sbc3r06.pdf - Section 7.4.11 - Table 286
+#define SBC_MRIE_NO_REPORTING 0x00
+#define SBC_MRIE_ASYNCHRONOUS 0x01
+#define SBC_MRIE_GENERATE_UNIT_ATTENTION 0x02
+#define SBC_MRIE_COND_GENERATE_RECOVERED_ERROR 0x03
+#define SBC_MRIE_UNCOND_GENERATE_RECOVERED_ERROR 0x04
+#define SBC_MRIE_GENERATE_NO_SENSE 0x05
+#define SBC_MRIE_ON_REQUEST 0x06
+//------------------------------------------------------------------------------
+
+//------------------------------------------------------------------------------
+/// \brief Supported mode pages
+/// \see sbc3r06.pdf - Section 6.3.1 - Table 115
+#define SBC_PAGE_READ_WRITE_ERROR_RECOVERY 0x01
+#define SBC_PAGE_INFORMATIONAL_EXCEPTIONS_CONTROL 0x1C
+#define SBC_PAGE_RETURN_ALL 0x3F
+#define SBC_PAGE_VENDOR_SPECIFIC 0x00
+//------------------------------------------------------------------------------
+
+//------------------------------------------------------------------------------
+/// \page "MSD Endian Macros"
+/// This page lists the macros for endianness conversion.
+///
+/// !Macros
+/// - WORDB
+/// - DWORDB
+/// - STORE_DWORDB
+/// - STORE_WORDB
+/// \brief Converts a byte array to a word value using the big endian format
+#define WORDB(bytes) ((unsigned short) ((bytes[0] << 8) | bytes[1]))
+
+/// \brief Converts a byte array to a dword value using the big endian format
+#define DWORDB(bytes) ((unsigned int) ((bytes[0] << 24) | (bytes[1] << 16) \
+ | (bytes[2] << 8) | bytes[3]))
+
+/// \brief Stores a dword value in a byte array, in big endian format
+#define STORE_DWORDB(dword, bytes) \
+ bytes[0] = (unsigned char) (((dword) >> 24) & 0xFF); \
+ bytes[1] = (unsigned char) (((dword) >> 16) & 0xFF); \
+ bytes[2] = (unsigned char) (((dword) >> 8) & 0xFF); \
+ bytes[3] = (unsigned char) ((dword) & 0xFF);
+
+/// \brief Stores a word value in a byte array, in big endian format
+#define STORE_WORDB(word, bytes) \
+ bytes[0] = (unsigned char) (((word) >> 8) & 0xFF); \
+ bytes[1] = (unsigned char) ((word) & 0xFF);
+//------------------------------------------------------------------------------
+
+//------------------------------------------------------------------------------
+// Structures
+//------------------------------------------------------------------------------
+
+#ifdef __ICCARM__ // IAR
+#pragma pack(1) // IAR
+#define __attribute__(...) // IAR
+#endif // IAR
+
+//------------------------------------------------------------------------------
+/// \brief Structure for the INQUIRY command
+/// \see spc4r06.pdf - Section 6.4.1 - Table 81
+//------------------------------------------------------------------------------
+typedef struct {
+
+ unsigned char bOperationCode; //!< 0x12 : SBC_INQUIRY
+ unsigned char isEVPD:1, //!< Type of requested data
+ bReserved1:7; //!< Reserved bits
+ unsigned char bPageCode; //!< Specifies the VPD to return
+ unsigned char pAllocationLength[2]; //!< Size of host buffer
+ unsigned char bControl; //!< 0x00
+
+} __attribute__ ((packed)) SBCInquiry; // GCC
+
+//------------------------------------------------------------------------------
+/// \brief Standard INQUIRY data format returned by the device
+/// \see spc4r06.pdf - Section 6.4.2 - Table 82
+//------------------------------------------------------------------------------
+typedef struct {
+
+ unsigned char bPeripheralDeviceType:5, //!< Peripheral device type
+ bPeripheralQualifier :3; //!< Peripheral qualifier
+ unsigned char bReserved1:7, //!< Reserved bits
+ isRMB :1; //!< Is media removable ?
+ unsigned char bVersion; //!< SPC version used
+ unsigned char bResponseDataFormat:4, //!< Must be 0x2
+ isHIGHSUP :1, //!< Hierarchical addressing used ?
+ isNORMACA :1, //!< ACA attribute supported ?
+ bObsolete1 :2; //!< Obsolete bits
+ unsigned char bAdditionalLength; //!< Length of remaining INQUIRY data
+ unsigned char isSCCS :1, //!< Embedded SCC ?
+ isACC :1, //!< Access control coordinator ?
+ bTPGS :2, //!< Target port support group
+ is3PC :1, //!< Third-party copy supported ?
+ bReserved2:2, //!< Reserved bits
+ isProtect :1; //!< Protection info supported ?
+ unsigned char bObsolete2:1, //!< Obsolete bit
+ isEncServ :1, //!< Embedded enclosure service comp?
+ isVS :1, //!< ???
+ isMultiP :1, //!< Multi-port device ?
+ bObsolete3:3, //!< Obsolete bits
+ bUnused1 :1; //!< Unused feature
+ unsigned char bUnused2:6, //!< Unused features
+ isCmdQue:1, //!< Task management model supported ?
+ isVS2 :1; //!< ???
+ unsigned char pVendorID[8]; //!< T10 vendor identification
+ unsigned char pProductID[16]; //!< Vendor-defined product ID
+ unsigned char pProductRevisionLevel[4];//!< Vendor-defined product revision
+ unsigned char pVendorSpecific[20]; //!< Vendor-specific data
+ unsigned char bUnused3; //!< Unused features
+ unsigned char bReserved3; //!< Reserved bits
+ unsigned short pVersionDescriptors[8]; //!< Standards the device complies to
+ unsigned char pReserved4[22]; //!< Reserved bytes
+
+} __attribute__ ((packed)) SBCInquiryData; // GCC
+
+//------------------------------------------------------------------------------
+/// \brief Data structure for the READ (10) command
+/// \see sbc3r07.pdf - Section 5.7 - Table 34
+//------------------------------------------------------------------------------
+typedef struct {
+
+ unsigned char bOperationCode; //!< 0x28 : SBC_READ_10
+ unsigned char bObsolete1:1, //!< Obsolete bit
+ isFUA_NV:1, //!< Cache control bit
+ bReserved1:1, //!< Reserved bit
+ isFUA:1, //!< Cache control bit
+ isDPO:1, //!< Cache control bit
+ bRdProtect:3; //!< Protection information to send
+ unsigned char pLogicalBlockAddress[4]; //!< Index of first block to read
+ unsigned char bGroupNumber:5, //!< Information grouping
+ bReserved2:3; //!< Reserved bits
+ unsigned char pTransferLength[2]; //!< Number of blocks to transmit
+ unsigned char bControl; //!< 0x00
+
+} __attribute__ ((packed)) SBCRead10; // GCC
+
+//------------------------------------------------------------------------------
+/// \brief Structure for the READ CAPACITY (10) command
+/// \see sbc3r07.pdf - Section 5.11.1 - Table 40
+//------------------------------------------------------------------------------
+typedef struct {
+
+ unsigned char bOperationCode; //!< 0x25 : RBC_READ_CAPACITY
+ unsigned char bObsolete1:1, //!< Obsolete bit
+ bReserved1:7; //!< Reserved bits
+ unsigned char pLogicalBlockAddress[4]; //!< Block to evaluate if PMI is set
+ unsigned char pReserved2[2]; //!< Reserved bytes
+ unsigned char isPMI:1, //!< Partial medium indicator bit
+ bReserved3:7; //!< Reserved bits
+ unsigned char bControl; //!< 0x00
+
+} SBCReadCapacity10;
+
+//------------------------------------------------------------------------------
+/// \brief Data returned by the device after a READ CAPACITY (10) command
+/// \see sbc3r07.pdf - Section 5.11.2 - Table 41
+//------------------------------------------------------------------------------
+typedef struct {
+
+ unsigned char pLogicalBlockAddress[4]; //!< Address of last logical block
+ unsigned char pLogicalBlockLength[4]; //!< Length of each logical block
+
+} SBCReadCapacity10Data;
+
+//------------------------------------------------------------------------------
+/// \brief Structure for the REQUEST SENSE command
+/// \see spc4r06.pdf - Section 6.26 - Table 170
+//------------------------------------------------------------------------------
+typedef struct {
+
+ unsigned char bOperationCode; //!< 0x03 : SBC_REQUEST_SENSE
+ unsigned char isDesc :1, //!< Type of information expected
+ bReserved1:7; //!< Reserved bits
+ unsigned char pReserved2[2]; //!< Reserved bytes
+ unsigned char bAllocationLength; //!< Size of host buffer
+ unsigned char bControl; //!< 0x00
+
+} SBCRequestSense;
+
+//------------------------------------------------------------------------------
+/// \brief Fixed format sense data returned after a REQUEST SENSE command has
+/// been received with a DESC bit cleared.
+/// \see spc4r06.pdf - Section 4.5.3 - Table 26
+//------------------------------------------------------------------------------
+typedef struct {
+
+ unsigned char bResponseCode:7, //!< Sense data format
+ isValid :1; //!< Information field is standard
+ unsigned char bObsolete1; //!< Obsolete byte
+ unsigned char bSenseKey :4, //!< Generic error information
+ bReserved1:1, //!< Reserved bit
+ isILI :1, //!< SSC
+ isEOM :1, //!< SSC
+ isFilemark:1; //!< SSC
+ unsigned char pInformation[4]; //!< Command-specific
+ unsigned char bAdditionalSenseLength; //!< sizeof(SBCRequestSense_data)-8
+ unsigned char pCommandSpecificInformation[4]; //!< Command-specific
+ unsigned char bAdditionalSenseCode; //!< Additional error information
+ unsigned char bAdditionalSenseCodeQualifier; //!< Further error information
+ unsigned char bFieldReplaceableUnitCode; //!< Specific component code
+ unsigned char bSenseKeySpecific:7, //!< Additional exception info
+ isSKSV :1; //!< Is sense key specific valid?
+ unsigned char pSenseKeySpecific[2]; //!< Additional exception info
+
+} SBCRequestSenseData;
+
+//------------------------------------------------------------------------------
+/// \brief Data structure for the TEST UNIT READY command
+/// \see spc4r06.pdf - Section 6.34 - Table 192
+//------------------------------------------------------------------------------
+typedef struct {
+
+ unsigned char bOperationCode; //!< 0x00 : SBC_TEST_UNIT_READY
+ unsigned char pReserved1[4]; //!< Reserved bits
+ unsigned char bControl; //!< 0x00
+
+} __attribute__ ((packed)) SBCTestUnitReady; // GCC
+
+//------------------------------------------------------------------------------
+/// \brief Structure for the WRITE (10) command
+/// \see sbc3r07.pdf - Section 5.26 - Table 70
+//------------------------------------------------------------------------------
+typedef struct {
+
+ unsigned char bOperationCode; //!< 0x2A : SBC_WRITE_10
+ unsigned char bObsolete1:1, //!< Obsolete bit
+ isFUA_NV:1, //!< Cache control bit
+ bReserved1:1, //!< Reserved bit
+ isFUA:1, //!< Cache control bit
+ isDPO:1, //!< Cache control bit
+ bWrProtect:3; //!< Protection information to send
+ unsigned char pLogicalBlockAddress[4]; //!< First block to write
+ unsigned char bGroupNumber:5, //!< Information grouping
+ bReserved2:3; //!< Reserved bits
+ unsigned char pTransferLength[2]; //!< Number of blocks to write
+ unsigned char bControl; //!< 0x00
+
+} SBCWrite10;
+
+//------------------------------------------------------------------------------
+/// \brief Structure for the PREVENT/ALLOW MEDIUM REMOVAL command
+/// \see sbc3r07.pdf - Section 5.5 - Table 30
+//------------------------------------------------------------------------------
+typedef struct {
+
+ unsigned char bOperationCode; //!< 0x1E : SBC_PREVENT_ALLOW_MEDIUM_REMOVAL
+ unsigned char pReserved1[3]; //!< Reserved bytes
+ unsigned char bPrevent:2, //!< Accept/prohibit removal
+ bReserved2:6; //!< Reserved bits
+ unsigned char bControl; //!< 0x00
+
+} __attribute__ ((packed)) SBCMediumRemoval; // GCC
+
+//------------------------------------------------------------------------------
+/// \brief Structure for the MODE SENSE (6) command
+/// \see spc4r06 - Section 6.9.1 - Table 98
+//------------------------------------------------------------------------------
+typedef struct {
+
+ unsigned char bOperationCode; //!< 0x1A : SBC_MODE_SENSE_6
+ unsigned char bReserved1:3, //!< Reserved bits
+ isDBD:1, //!< Disable block descriptors bit
+ bReserved2:4; //!< Reserved bits
+ unsigned char bPageCode:6, //!< Mode page to return
+ bPC:2; //!< Type of parameter values to return
+ unsigned char bSubpageCode; //!< Mode subpage to return
+ unsigned char bAllocationLength; //!< Host buffer allocated size
+ unsigned char bControl; //!< 0x00
+
+} __attribute__ ((packed)) SBCModeSense6; // GCC
+
+//------------------------------------------------------------------------------
+/// \brief Header for the data returned after a MODE SENSE (6) command
+/// \see spc4r06.pdf - Section 7.4.3 - Table 268
+//------------------------------------------------------------------------------
+typedef struct {
+
+ unsigned char bModeDataLength; //!< Length of mode data to follow
+ unsigned char bMediumType; //!< Type of medium (SBC_MEDIUM_TYPE_DIRECT_ACCESS_BLOCK_DEVICE)
+ unsigned char bReserved1:4, //!< Reserved bits
+ isDPOFUA:1, //!< DPO/FUA bits supported ?
+ bReserved2:2, //!< Reserved bits
+ isWP:1; //!< Is medium write-protected ?
+ unsigned char bBlockDescriptorLength; //!< Length of all block descriptors
+
+} __attribute__ ((packed)) SBCModeParameterHeader6; // GCC
+
+//------------------------------------------------------------------------------
+/// \brief Informational exceptions control mode page
+/// \see spc4r06.pdf - Section 7.4.11 - Table 285
+//------------------------------------------------------------------------------
+typedef struct {
+
+ unsigned char bPageCode:6, //!< 0x1C : SBC_PAGE_INFORMATIONAL_EXCEPTIONS_CONTROL
+ isSPF:1, //!< Page or subpage data format
+ isPS:1; //!< Parameters saveable ?
+ unsigned char bPageLength; //!< Length of page data (0x0A)
+ unsigned char isLogErr:1, //!< Should informational exceptions be logged ?
+ isEBackErr:1, //!< Enable background error bit
+ isTest:1, //!< Create a device test failure ?
+ isDExcpt:1, //!< Disable exception control bit
+ isEWasc:1, //!< Report warnings ?
+ isEBF:1, //!< Enable background function bit
+ bReserved1:1, //!< Reserved bit
+ isPerf:1; //!< Delay acceptable when treating exceptions ?
+ unsigned char bMRIE:4, //!< Method of reporting informational exceptions
+ bReserved2:4; //!< Reserved bits
+ unsigned char pIntervalTimer[4]; //!< Error reporting period
+ unsigned char pReportCount[4]; //!< Maximum number of time a report can be issued
+
+} __attribute__ ((packed)) SBCInformationalExceptionsControl; // GCC
+
+//------------------------------------------------------------------------------
+/// \brief Read/write error recovery mode page
+/// \see sbc3r07.pdf - Section 6.3.5 - Table 122
+//------------------------------------------------------------------------------
+typedef struct {
+
+ unsigned char bPageCode:6, //!< 0x01 : SBC_PAGE_READ_WRITE_ERROR_RECOVERY
+ isSPF:1, //!< Page or subpage data format
+ isPS:1; //!< Parameters saveable ?
+ unsigned char bPageLength; //!< Length of page data (0x0A)
+ unsigned char isDCR:1, //!< Disable correction bit
+ isDTE:1, //!< Data terminate on error bit
+ isPER:1, //!< Post error bit
+ isEER:1, //!< Enable early recovery bit
+ isRC:1, //!< Read continuous bit
+ isTB:1, //!< Transfer block bit
+ isARRE:1, //!< Automatic read reallocation enabled bit
+ isAWRE:1; //!< Automatic write reallocation enabled bit
+ unsigned char bReadRetryCount; //!< Number of retries when reading
+ unsigned char pObsolete1[3]; //!< Obsolete bytes
+ unsigned char bReserved1; //!< Reserved byte
+ unsigned char bWriteRetryCount; //!< Number of retries when writing
+ unsigned char bReserved2; //!< Reserved byte
+ unsigned char pRecoveryTimeLimit[2]; //!< Maximum time duration for error recovery
+
+} __attribute__ ((packed)) SBCReadWriteErrorRecovery; // GCC
+
+//------------------------------------------------------------------------------
+/// \brief Generic structure for holding information about SBC commands
+/// \see SBCInquiry
+/// \see SBCRead10
+/// \see SBCReadCapacity10
+/// \see SBCRequestSense
+/// \see SBCTestUnitReady
+/// \see SBCWrite10
+/// \see SBCMediumRemoval
+/// \see SBCModeSense6
+//------------------------------------------------------------------------------
+typedef union {
+
+ unsigned char bOperationCode; //!< Operation code of the command
+ SBCInquiry inquiry; //!< INQUIRY command
+ SBCRead10 read10; //!< READ (10) command
+ SBCReadCapacity10 readCapacity10; //!< READ CAPACITY (10) command
+ SBCRequestSense requestSense; //!< REQUEST SENSE command
+ SBCTestUnitReady testUnitReady; //!< TEST UNIT READY command
+ SBCWrite10 write10; //!< WRITE (10) command
+ SBCMediumRemoval mediumRemoval; //!< PREVENT/ALLOW MEDIUM REMOVAL command
+ SBCModeSense6 modeSense6; //!< MODE SENSE (6) command
+
+} SBCCommand;
+
+#ifdef __ICCARM__ // IAR
+#pragma pack() // IAR
+#endif // IAR
+
+#endif //#ifndef SBC_H
+
diff --git a/usb/device/massstorage/SBCMethods.c b/usb/device/massstorage/SBCMethods.c new file mode 100644 index 0000000..cd7a87a --- /dev/null +++ b/usb/device/massstorage/SBCMethods.c @@ -0,0 +1,1611 @@ +/* ----------------------------------------------------------------------------
+ * 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.
+ * ----------------------------------------------------------------------------
+ */
+
+//------------------------------------------------------------------------------
+// Headers
+//------------------------------------------------------------------------------
+
+#include "SBCMethods.h"
+#include "MSDDStateMachine.h"
+#include <usb/device/core/USBD.h>
+
+#include "MSDIOFifo.h"
+
+//------------------------------------------------------------------------------
+// Global variables
+//------------------------------------------------------------------------------
+
+//------------------------------------------------------------------------------
+// Macros
+//------------------------------------------------------------------------------
+
+#ifdef MSDIO_READ10_CHUNK_SIZE
+/// READ10 - Read data from specific LUN to FIFO
+#define SBC_READ_CHUNK(pLun, lba, pFifo, pCb, pArg) \
+ LUN_Read((pLun), (lba), \
+ &(pFifo)->pBuffer[(pFifo)->inputNdx], \
+ ((pFifo)->chunkSize/(pFifo)->blockSize), \
+ (TransferCallback)(pCb), (void*)pArg)
+/// READ10 - Transfer data from FIFO to USB
+#define SBC_TX_CHUNK(pFifo, pCb, pArg) \
+ MSDD_Write(&(pFifo)->pBuffer[(pFifo)->outputNdx], \
+ (pFifo)->chunkSize, \
+ (TransferCallback)(pCb), (void*)(pArg))
+#endif
+
+#ifdef MSDIO_WRITE10_CHUNK_SIZE
+/// WRITE10 - Read data from USB to FIFO
+#define SBC_RX_CHUNK(pFifo,pCb,pArg) \
+ MSDD_Read(&(pFifo)->pBuffer[(pFifo)->inputNdx], \
+ (pFifo)->chunkSize, \
+ (TransferCallback)(pCb), (void*)(pArg))
+/// WRITE10 - Write data from FIFO to LUN
+#define SBC_WRITE_CHUNK(pLun, lba, pFifo, pCb, pArg) \
+ LUN_Write((pLun), (lba), \
+ &(pFifo)->pBuffer[(pFifo)->outputNdx], \
+ ((pFifo)->chunkSize/(pFifo)->blockSize), \
+ (TransferCallback)(pCb), (void*)(pArg))
+#endif
+
+
+//------------------------------------------------------------------------------
+//! \brief Header for the mode pages data
+//! \see SBCModeParameterHeader6
+//------------------------------------------------------------------------------
+static const SBCModeParameterHeader6 modeParameterHeader6 = {
+
+ sizeof(SBCModeParameterHeader6) - 1, //! Length is 0x03
+ SBC_MEDIUM_TYPE_DIRECT_ACCESS_BLOCK_DEVICE, //! Direct-access block device
+ 0, //! Reserved bits
+ 0, //! DPO/FUA not supported
+ 0, //! Reserved bits
+ 0, //! not write-protected
+ 0 //! No block descriptor
+};
+
+//------------------------------------------------------------------------------
+// Internal functions
+//------------------------------------------------------------------------------
+
+//------------------------------------------------------------------------------
+//! \brief Check if the LUN is ready.
+//! \param lun Pointer to the LUN affected by the command
+//! \return 1 if the LUN is ready to be written
+//! \see MSDLun
+//------------------------------------------------------------------------------
+static unsigned char SBCLunIsReady(MSDLun *lun)
+{
+ unsigned char lunIsReady = 0;
+
+ if (lun->media == 0 || lun->status < LUN_CHANGED) {
+ TRACE_INFO("SBCLunIsReady: Not Present!\n\r");
+ SBC_UpdateSenseData(&(lun->requestSenseData),
+ SBC_SENSE_KEY_NOT_READY,
+ SBC_ASC_MEDIUM_NOT_PRESENT,
+ 0);
+
+ }
+ else if (lun->status < LUN_READY) {
+ TRACE_INFO("SBCLunIsReady: Changing!\n\r");
+ SBC_UpdateSenseData(&(lun->requestSenseData),
+ SBC_SENSE_KEY_UNIT_ATTENTION,
+ SBC_ASC_NOT_READY_TO_READY_CHANGE,
+ 0);
+ lun->status = LUN_READY;
+ }
+ else {
+
+ lunIsReady = 1;
+ }
+
+ return lunIsReady;
+}
+//------------------------------------------------------------------------------
+//! \brief Check if the LUN can write.
+//! \param lun Pointer to the LUN affected by the command
+//! \return 1 if the LUN is ready to be written
+//! \see MSDLun
+//------------------------------------------------------------------------------
+static unsigned char SBCLunCanBeWritten(MSDLun *lun)
+{
+ unsigned char canBeWritten = 0;
+
+ if (!SBCLunIsReady(lun)) {
+
+ TRACE_WARNING("SBCLunCanBeWritten: Not Ready!\n\r");
+ }
+ else if (lun->protected) {
+
+ TRACE_WARNING("SBCLunCanBeWritten: Protected!\n\r");
+ SBC_UpdateSenseData(&(lun->requestSenseData),
+ SBC_SENSE_KEY_DATA_PROTECT,
+ SBC_ASC_WRITE_PROTECTED,
+ 0);
+ }
+ else {
+
+ canBeWritten = 1;
+ }
+
+ return canBeWritten;
+}
+
+//------------------------------------------------------------------------------
+//! \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;
+ SBCRead10 *command = (SBCRead10 *) commandState->cbw.pCommand;
+ MSDTransfer *transfer = &(commandState->transfer);
+ MSDTransfer *disktransfer = &(commandState->disktransfer);
+ MSDIOFifo *fifo = &lun->ioFifo;
+
+ // Init command state
+ if (commandState->state == 0) {
+
+ commandState->state = SBC_STATE_WRITE;
+
+ // The command should not be proceeded if READONLY
+ if (!SBCLunCanBeWritten(lun)) {
+
+ return MSDD_STATUS_RW;
+ }
+ else {
+
+
+ // Initialize FIFO
+ fifo->dataTotal = commandState->length;
+ fifo->blockSize = lun->blockSize * lun->media->blockSize;
+ #ifdef MSDIO_WRITE10_CHUNK_SIZE
+ if ( fifo->dataTotal >= 64 * 1024
+ && fifo->blockSize < MSDIO_WRITE10_CHUNK_SIZE)
+ fifo->chunkSize = MSDIO_WRITE10_CHUNK_SIZE;
+ else
+ fifo->chunkSize = fifo->blockSize;
+ #endif
+ fifo->fullCnt = 0;
+ fifo->nullCnt = 0;
+
+ // Initialize FIFO output (Disk)
+ fifo->outputNdx = 0;
+ fifo->outputTotal = 0;
+ fifo->outputState = MSDIO_IDLE;
+ transfer->semaphore = 0;
+
+ // Initialize FIFO input (USB)
+ fifo->inputNdx = 0;
+ fifo->inputTotal = 0;
+ fifo->inputState = MSDIO_START;
+ disktransfer->semaphore = 0;
+ }
+
+ }
+
+ if (commandState->length == 0) {
+
+ // Perform the callback!
+ if (lun->dataMonitor) {
+
+ lun->dataMonitor(0, fifo->dataTotal, fifo->nullCnt, fifo->fullCnt);
+ }
+ return MSDD_STATUS_SUCCESS;
+ }
+
+ // USB receive task
+ switch(fifo->inputState) {
+
+ //------------------
+ case MSDIO_IDLE:
+ //------------------
+ if (fifo->inputTotal < fifo->dataTotal &&
+ fifo->inputTotal - fifo->outputTotal < fifo->bufferSize) {
+
+ fifo->inputState = MSDIO_START;
+ }
+ break;
+
+ //------------------
+ case MSDIO_START:
+ //------------------
+ // Should not start if there is any disk error
+ if (fifo->outputState == MSDIO_ERROR) {
+
+ TRACE_INFO_WP("udErr ");
+ fifo->inputState = MSDIO_ERROR;
+ break;
+ }
+
+ // Read one block of data sent by the host
+ if (lun->media->mappedWR) {
+
+ // Directly read to memory
+ status = MSDD_Read((void*)
+ ((lun->media->baseAddress
+ + (lun->baseAddress
+ + DWORDB(command->pLogicalBlockAddress)
+ * lun->blockSize
+ )
+ ) * lun->media->blockSize
+ ),
+ fifo->dataTotal,
+ (TransferCallback) MSDDriver_Callback,
+ (void *) transfer);
+ }
+ else {
+ #ifdef MSDIO_WRITE10_CHUNK_SIZE
+ status = SBC_RX_CHUNK(fifo, MSDDriver_Callback, transfer);
+ #else
+ // Read block to buffer
+ status = MSDD_Read((void*)&fifo->pBuffer[fifo->inputNdx],
+ fifo->blockSize,
+ (TransferCallback) MSDDriver_Callback,
+ (void *) transfer);
+ #endif
+ }
+
+ // Check operation result code
+ if (status != USBD_STATUS_SUCCESS) {
+
+ TRACE_WARNING(
+ "RBC_Write10: Failed to start receiving\n\r");
+ SBC_UpdateSenseData(&(lun->requestSenseData),
+ SBC_SENSE_KEY_HARDWARE_ERROR,
+ 0,
+ 0);
+ result = MSDD_STATUS_ERROR;
+ }
+ else {
+
+ TRACE_INFO_WP("uRx ");
+
+ // Prepare next device state
+ fifo->inputState = MSDIO_WAIT;
+ }
+ break; // MSDIO_START
+
+ //------------------
+ case MSDIO_WAIT:
+ //------------------
+ TRACE_INFO_WP("uWait ");
+
+ // Check semaphore
+ if (transfer->semaphore > 0) {
+
+ transfer->semaphore--;
+ fifo->inputState = MSDIO_NEXT;
+ }
+ break;
+
+ //------------------
+ case MSDIO_NEXT:
+ //------------------
+ // Check the result code of the read operation
+ if (transfer->status != USBD_STATUS_SUCCESS) {
+
+ TRACE_WARNING(
+ "RBC_Write10: Failed to received\n\r");
+ SBC_UpdateSenseData(&(lun->requestSenseData),
+ SBC_SENSE_KEY_HARDWARE_ERROR,
+ 0,
+ 0);
+ result = MSDD_STATUS_ERROR;
+ }
+ else {
+
+ TRACE_INFO_WP("uNxt ");
+
+ // Mapped read, all data done
+ if (lun->media->mappedWR) {
+
+ fifo->inputTotal = fifo->dataTotal;
+ fifo->inputState = MSDIO_IDLE;
+ }
+ else {
+
+ // Update input index
+ #ifdef MSDIO_WRITE10_CHUNK_SIZE
+ MSDIOFifo_IncNdx(fifo->inputNdx,
+ fifo->chunkSize,
+ fifo->bufferSize);
+ fifo->inputTotal += fifo->chunkSize;
+ #else
+ MSDIOFifo_IncNdx(fifo->inputNdx,
+ fifo->blockSize,
+ fifo->bufferSize);
+ fifo->inputTotal += fifo->blockSize;
+ #endif
+
+ // Start Next block
+ // - All Data done?
+ if (fifo->inputTotal >= fifo->dataTotal) {
+
+ fifo->inputState = MSDIO_IDLE;
+ }
+ // - Buffer full?
+ else if (fifo->inputNdx == fifo->outputNdx) {
+ fifo->inputState = MSDIO_IDLE;
+ fifo->fullCnt ++;
+
+ TRACE_DEBUG_WP("ufFull%d ", fifo->inputNdx);
+ }
+ // - More data to transfer?
+ else if (fifo->inputTotal < fifo->dataTotal) {
+ fifo->inputState = MSDIO_START;
+
+ TRACE_INFO_WP("uStart ");
+ }
+ // never executed !
+ //else {
+ // fifo->inputState = MSDIO_IDLE;
+ // TRACE_INFO_WP("uDone ");
+ //}
+ }
+
+ }
+ break; // MSDIO_NEXT
+
+ //------------------
+ case MSDIO_ERROR:
+ //------------------
+
+ TRACE_WARNING_WP("uErr ");
+ commandState->length -= fifo->inputTotal;
+ return MSDD_STATUS_RW;
+
+ }
+
+ // Disk write task
+ switch(fifo->outputState) {
+
+ //------------------
+ case MSDIO_IDLE:
+ //------------------
+ if (fifo->outputTotal < fifo->inputTotal) {
+
+ fifo->outputState = MSDIO_START;
+ }
+ break;
+
+ //------------------
+ case MSDIO_START:
+ //------------------
+
+ // Write the block to the media
+ if (lun->media->mappedWR) {
+
+ MSDDriver_Callback(disktransfer, MED_STATUS_SUCCESS, 0, 0);
+ status = LUN_STATUS_SUCCESS;
+ }
+ else {
+ #ifdef MSDIO_WRITE10_CHUNK_SIZE
+ status = SBC_WRITE_CHUNK(lun, DWORDB(command->pLogicalBlockAddress),
+ fifo, MSDDriver_Callback, disktransfer);
+ #else
+ status = LUN_Write(lun,
+ DWORDB(command->pLogicalBlockAddress),
+ &fifo->pBuffer[fifo->outputNdx],
+ 1,
+ (TransferCallback) MSDDriver_Callback,
+ (void *) disktransfer);
+ #endif
+ }
+
+ // Check operation result code
+ if (status != USBD_STATUS_SUCCESS) {
+
+ TRACE_WARNING(
+ "RBC_Write10: Failed to start write - ");
+
+ if (!SBCLunCanBeWritten(lun)) {
+
+ TRACE_WARNING("?\n\r");
+ SBC_UpdateSenseData(&(lun->requestSenseData),
+ SBC_SENSE_KEY_NOT_READY,
+ 0,
+ 0);
+ }
+
+ fifo->outputState = MSDIO_ERROR;
+ }
+ else {
+
+ // Prepare next state
+ fifo->outputState = MSDIO_WAIT;
+ }
+ break; // MSDIO_START
+
+ //------------------
+ case MSDIO_WAIT:
+ //------------------
+ TRACE_INFO_WP("dWait ");
+
+ // Check semaphore value
+ if (disktransfer->semaphore > 0) {
+
+ // Take semaphore and move to next state
+ disktransfer->semaphore--;
+ fifo->outputState = MSDIO_NEXT;
+ }
+ break;
+
+ //------------------
+ case MSDIO_NEXT:
+ //------------------
+ // Check operation result code
+ if (transfer->status != USBD_STATUS_SUCCESS) {
+
+ TRACE_WARNING(
+ "RBC_Write10: Failed to write\n\r");
+ SBC_UpdateSenseData(&(lun->requestSenseData),
+ SBC_SENSE_KEY_RECOVERED_ERROR,
+ SBC_ASC_TOO_MUCH_WRITE_DATA,
+ 0);
+ result = MSDD_STATUS_ERROR;
+ }
+ else {
+
+ TRACE_INFO_WP("dNxt ");
+
+ // Update transfer length and block address
+
+ // Mapped memory, done
+ if (lun->media->mappedWR) {
+
+ commandState->length = 0;
+ fifo->outputState = MSDIO_IDLE;
+ }
+ else {
+
+ // Update output index
+ #ifdef MSDIO_WRITE10_CHUNK_SIZE
+ STORE_DWORDB(DWORDB(command->pLogicalBlockAddress)
+ + fifo->chunkSize/fifo->blockSize,
+ command->pLogicalBlockAddress);
+ MSDIOFifo_IncNdx(fifo->outputNdx,
+ fifo->chunkSize,
+ fifo->bufferSize);
+ fifo->outputTotal += fifo->chunkSize;
+ #else
+ STORE_DWORDB(DWORDB(command->pLogicalBlockAddress) + 1,
+ command->pLogicalBlockAddress);
+ MSDIOFifo_IncNdx(fifo->outputNdx,
+ fifo->blockSize,
+ fifo->bufferSize);
+ fifo->outputTotal += fifo->blockSize;
+ #endif
+
+ // Start Next block
+ // - All data done?
+ if (fifo->outputTotal >= fifo->dataTotal) {
+
+ fifo->outputState = MSDIO_IDLE;
+ commandState->length = 0;
+ TRACE_INFO_WP("dDone ");
+ }
+ // - Send next?
+ else if (fifo->outputTotal < fifo->inputTotal) {
+
+ fifo->outputState = MSDIO_START;
+ TRACE_INFO_WP("dStart ");
+ }
+ // - Buffer Null?
+ else {
+ fifo->outputState = MSDIO_IDLE;
+ fifo->nullCnt ++;
+
+ TRACE_DEBUG_WP("dfNull%d ", fifo->outputNdx);
+ }
+ }
+ }
+ break; // MSDIO_NEXT
+
+ //------------------
+ case MSDIO_ERROR:
+ //------------------
+ 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);
+ MSDTransfer *disktransfer = &(commandState->disktransfer);
+ MSDIOFifo *fifo = &lun->ioFifo;
+
+ // Init command state
+ if (commandState->state == 0) {
+
+ commandState->state = SBC_STATE_READ;
+
+ if (!SBCLunIsReady(lun)) {
+
+ return MSDD_STATUS_RW;
+ }
+ else {
+
+ // Initialize FIFO
+ fifo->dataTotal = commandState->length;
+ fifo->blockSize = lun->blockSize * lun->media->blockSize;
+ #ifdef MSDIO_READ10_CHUNK_SIZE
+ if ( fifo->dataTotal >= 64*1024
+ && fifo->blockSize < MSDIO_READ10_CHUNK_SIZE)
+ fifo->chunkSize = MSDIO_READ10_CHUNK_SIZE;
+ else
+ fifo->chunkSize = fifo->blockSize;
+ #endif
+ fifo->fullCnt = 0;
+ fifo->nullCnt = 0;
+
+ #ifdef MSDIO_FIFO_OFFSET
+ // Enable offset if total size >= 2*bufferSize
+ if (fifo->dataTotal / fifo->bufferSize >= 2)
+ fifo->bufferOffset = MSDIO_FIFO_OFFSET;
+ else
+ fifo->bufferOffset = 0;
+ #endif
+
+ // Initialize FIFO output (USB)
+ fifo->outputNdx = 0;
+ fifo->outputTotal = 0;
+ fifo->outputState = MSDIO_IDLE;
+ transfer->semaphore = 0;
+
+ // Initialize FIFO input (Disk)
+ fifo->inputNdx = 0;
+ fifo->inputTotal = 0;
+ fifo->inputState = MSDIO_START;
+ disktransfer->semaphore = 0;
+ }
+ }
+
+ // Check length
+ if (commandState->length == 0) {
+
+ // Perform the callback!
+ if (lun->dataMonitor) {
+
+ lun->dataMonitor(1, fifo->dataTotal, fifo->nullCnt, fifo->fullCnt);
+ }
+ return MSDD_STATUS_SUCCESS;
+ }
+
+ // Disk reading task
+ switch(fifo->inputState) {
+
+ //------------------
+ case MSDIO_IDLE:
+ //------------------
+ if (fifo->inputTotal < fifo->dataTotal &&
+ fifo->inputTotal - fifo->outputTotal < fifo->bufferSize) {
+
+ fifo->inputState = MSDIO_START;
+ }
+ break;
+
+ //------------------
+ case MSDIO_START:
+ //------------------
+ // Read one block of data from the media
+ if (lun->media->mappedRD) {
+
+ // Directly write, no read needed
+ MSDDriver_Callback(disktransfer, MED_STATUS_SUCCESS, 0, 0);
+ status = LUN_STATUS_SUCCESS;
+ }
+ else {
+ #ifdef MSDIO_READ10_CHUNK_SIZE
+ status = SBC_READ_CHUNK(lun, DWORDB(command->pLogicalBlockAddress),
+ fifo, MSDDriver_Callback, disktransfer);
+ #else
+ status = LUN_Read(lun,
+ DWORDB(command->pLogicalBlockAddress),
+ &fifo->pBuffer[fifo->inputNdx],
+ 1,
+ (TransferCallback) MSDDriver_Callback,
+ (void *)disktransfer);
+ #endif
+ }
+
+ // Check operation result code
+ if (status != LUN_STATUS_SUCCESS) {
+
+ TRACE_WARNING("RBC_Read10: Failed to start reading\n\r");
+
+ if (SBCLunIsReady(lun)) {
+
+ SBC_UpdateSenseData(&(lun->requestSenseData),
+ SBC_SENSE_KEY_NOT_READY,
+ SBC_ASC_LOGICAL_UNIT_NOT_READY,
+ 0);
+ }
+
+ fifo->inputState = MSDIO_ERROR;
+ }
+ else {
+
+ TRACE_INFO_WP("dRd ");
+
+ // Move to next command state
+ fifo->inputState = MSDIO_WAIT;
+ }
+ break; // MSDIO_START
+
+ //------------------
+ case MSDIO_WAIT:
+ //------------------
+ // Check semaphore value
+ if (disktransfer->semaphore > 0) {
+
+ TRACE_INFO_WP("dOk ");
+
+ // Take semaphore and move to next state
+ disktransfer->semaphore--;
+ fifo->inputState = MSDIO_NEXT;
+ }
+ break;
+
+ //------------------
+ case MSDIO_NEXT:
+ //------------------
+ // Check the operation result code
+ if (disktransfer->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 {
+
+ TRACE_INFO_WP("dNxt ");
+
+ if (lun->media->mappedRD) {
+
+ // All data is ready
+ fifo->inputState = MSDIO_IDLE;
+ fifo->inputTotal = fifo->dataTotal;
+ }
+ else {
+
+ // Update block address
+ #ifdef MSDIO_READ10_CHUNK_SIZE
+ STORE_DWORDB(DWORDB(command->pLogicalBlockAddress)
+ + fifo->chunkSize/fifo->blockSize,
+ command->pLogicalBlockAddress);
+
+ // Update input index
+ MSDIOFifo_IncNdx(fifo->inputNdx,
+ fifo->chunkSize,
+ fifo->bufferSize);
+ fifo->inputTotal += fifo->chunkSize;
+ #else
+ // Update block address
+ STORE_DWORDB(DWORDB(command->pLogicalBlockAddress) + 1,
+ command->pLogicalBlockAddress);
+
+ // Update input index
+ MSDIOFifo_IncNdx(fifo->inputNdx,
+ fifo->blockSize,
+ fifo->bufferSize);
+ fifo->inputTotal += fifo->blockSize;
+ #endif
+
+ // Start Next block
+ // - All Data done?
+ if (fifo->inputTotal >= fifo->dataTotal) {
+
+ TRACE_INFO_WP("dDone ");
+ fifo->inputState = MSDIO_IDLE;
+ }
+ // - Buffer full?
+ else if (fifo->inputNdx == fifo->outputNdx) {
+
+ TRACE_INFO_WP("dfFull%d ", (int)fifo->inputNdx);
+ fifo->inputState = MSDIO_IDLE;
+ fifo->fullCnt ++;
+ }
+ // - More data to transfer?
+ else if (fifo->inputTotal < fifo->dataTotal) {
+
+ TRACE_DEBUG_WP("dStart ");
+ fifo->inputState = MSDIO_START;
+ }
+ }
+
+ }
+
+ break;
+
+ //------------------
+ case MSDIO_ERROR:
+ //------------------
+ break;
+ }
+
+ // USB sending task
+ switch(fifo->outputState) {
+
+ //------------------
+ case MSDIO_IDLE:
+ //------------------
+ if (fifo->outputTotal < fifo->inputTotal) {
+
+ #ifdef MSDIO_FIFO_OFFSET
+ // Offset buffer the input data
+ if (fifo->bufferOffset) {
+ if (fifo->inputTotal < fifo->bufferOffset) {
+ break;
+ }
+ fifo->bufferOffset = 0;
+ }
+ #endif
+ fifo->outputState = MSDIO_START;
+ }
+ break;
+
+ //------------------
+ case MSDIO_START:
+ //------------------
+ // Should not start if there is any disk error
+ if (fifo->outputState == MSDIO_ERROR) {
+
+ fifo->inputState = MSDIO_ERROR;
+ break;
+ }
+
+ // Send the block to the host
+ if (lun->media->mappedRD) {
+
+ status = MSDD_Write((void*)
+ ((lun->media->baseAddress
+ + (lun->baseAddress
+ + DWORDB(command->pLogicalBlockAddress)
+ * lun->blockSize
+ )
+ ) * lun->media->blockSize
+ ),
+ commandState->length,
+ (TransferCallback) MSDDriver_Callback,
+ (void *) transfer);
+ }
+ else {
+ #ifdef MSDIO_READ10_CHUNK_SIZE
+ status = SBC_TX_CHUNK(fifo, MSDDriver_Callback, transfer);
+ #else
+ status = MSDD_Write(&fifo->pBuffer[fifo->outputNdx],
+ fifo->blockSize,
+ (TransferCallback) MSDDriver_Callback,
+ (void *) transfer);
+ #endif
+ }
+ // Check operation result code
+ if (status != USBD_STATUS_SUCCESS) {
+
+ TRACE_WARNING(
+ "RBC_Read10: Failed to start to send\n\r");
+ SBC_UpdateSenseData(&(lun->requestSenseData),
+ SBC_SENSE_KEY_HARDWARE_ERROR,
+ 0,
+ 0);
+ result = MSDD_STATUS_ERROR;
+ }
+ else {
+
+ TRACE_INFO_WP("uTx ");
+
+ // Move to next command state
+ fifo->outputState = MSDIO_WAIT;
+ }
+ break; // MSDIO_START
+
+ //------------------
+ case MSDIO_WAIT:
+ //------------------
+ // Check semaphore value
+ if (transfer->semaphore > 0) {
+
+ TRACE_INFO_WP("uOk ");
+
+ // Take semaphore and move to next state
+ transfer->semaphore--;
+ fifo->outputState = MSDIO_NEXT;
+ }
+ break;
+
+ //------------------
+ case MSDIO_NEXT:
+ //------------------
+ // 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("uNxt ");
+
+ if (lun->media->mappedRD) {
+
+ commandState->length = 0;
+ }
+ else {
+
+ // Update output index
+ #ifdef MSDIO_READ10_CHUNK_SIZE
+ MSDIOFifo_IncNdx(fifo->outputNdx,
+ fifo->chunkSize,
+ fifo->bufferSize);
+ fifo->outputTotal += fifo->chunkSize;
+ #else
+ MSDIOFifo_IncNdx(fifo->outputNdx,
+ fifo->blockSize,
+ fifo->bufferSize);
+ fifo->outputTotal += fifo->blockSize;
+ #endif
+
+ // Start Next block
+ // - All data done?
+ if (fifo->outputTotal >= fifo->dataTotal) {
+
+ fifo->outputState = MSDIO_IDLE;
+ commandState->length = 0;
+ TRACE_INFO_WP("uDone ");
+ }
+ // - Buffer Null?
+ else if (fifo->inputNdx == fifo->outputNdx) {
+
+ TRACE_INFO_WP("ufNull%d ", (int)fifo->outputNdx);
+ fifo->outputState = MSDIO_IDLE;
+ fifo->nullCnt ++;
+ }
+ // - Send next?
+ else if (fifo->outputTotal < fifo->inputTotal) {
+
+ TRACE_DEBUG_WP("uStart ");
+ fifo->outputState = MSDIO_START;
+ }
+ }
+
+ }
+ break;
+
+ //------------------
+ case MSDIO_ERROR:
+ //------------------
+ 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 MSDD_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);
+
+ if (!SBCLunIsReady(lun)) {
+
+ TRACE_INFO("SBC_ReadCapacity10: Not Ready!\n\r");
+ return MSDD_STATUS_RW;
+ }
+
+ // 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 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 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 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(MSDLun *lun, MSDCommandState *commandState)
+{
+ unsigned char result = MSDD_STATUS_INCOMPLETE;
+ unsigned char status;
+ MSDTransfer *transfer = &(commandState->transfer);
+
+ if (!SBCLunIsReady(lun)) {
+
+ TRACE_INFO("SBC_ModeSense6: Not Ready!\n\r");
+ return MSDD_STATUS_RW;
+ }
+
+ // 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 TEST UNIT READY COMMAND command.
+//! \param lun Pointer to the LUN affected by the command
+//! \return Operation result code (SUCCESS, ERROR, INCOMPLETE or PARAMETER)
+//! \see MSDLun
+//------------------------------------------------------------------------------
+static unsigned char SBC_TestUnitReady(MSDLun *lun)
+{
+ unsigned char result = MSDD_STATUS_RW;
+ unsigned char senseKey = SBC_SENSE_KEY_NO_SENSE,
+ addSenseCode = 0,
+ addSenseCodeQual = 0;
+
+ // Check current media state
+ if (lun->status < LUN_CHANGED) {
+
+ TRACE_INFO_WP("Ejc ");
+ senseKey = SBC_SENSE_KEY_NOT_READY;
+ addSenseCode = SBC_ASC_MEDIUM_NOT_PRESENT;
+ }
+ else if (lun->status == LUN_CHANGED) {
+
+ TRACE_INFO_WP("Chg ");
+ senseKey = SBC_SENSE_KEY_UNIT_ATTENTION;
+ addSenseCode = SBC_ASC_NOT_READY_TO_READY_CHANGE;
+ lun->status = LUN_READY;
+ }
+ else {
+
+ switch(lun->media->state) {
+ //-------------------
+ case MED_STATE_READY:
+ //-------------------
+ // Nothing to do
+ TRACE_INFO_WP("Rdy ");
+ result = MSDD_STATUS_SUCCESS;
+ break;
+
+ //------------------
+ case MED_STATE_BUSY:
+ //------------------
+ TRACE_INFO_WP("Bsy ");
+ senseKey = SBC_SENSE_KEY_NOT_READY;
+ break;
+
+ //------
+ default:
+ //------
+ TRACE_INFO_WP("? ");
+ senseKey = SBC_SENSE_KEY_NOT_READY;
+ addSenseCode = SBC_ASC_MEDIUM_NOT_PRESENT;
+ break;
+ }
+ }
+ SBC_UpdateSenseData(&(lun->requestSenseData),
+ senseKey,
+ addSenseCode,
+ addSenseCodeQual);
+
+ return result;
+}
+
+//------------------------------------------------------------------------------
+// Exported functions
+//------------------------------------------------------------------------------
+//------------------------------------------------------------------------------
+//! \brief Updates the sense data of a LUN with the given key and codes
+//! \param requestSenseData Pointer to the sense data to update
+//! \param senseKey Sense key
+//! \param additionalSenseCode Additional sense code
+//! \param additionalSenseCodeQualifier Additional sense code qualifier
+//------------------------------------------------------------------------------
+void SBC_UpdateSenseData(SBCRequestSenseData *requestSenseData,
+ unsigned char senseKey,
+ unsigned char additionalSenseCode,
+ unsigned char additionalSenseCodeQualifier)
+{
+ requestSenseData->bSenseKey = senseKey;
+ requestSenseData->bAdditionalSenseCode = additionalSenseCode;
+ requestSenseData->bAdditionalSenseCodeQualifier
+ = additionalSenseCodeQualifier;
+}
+
+//------------------------------------------------------------------------------
+//! \brief Return information about the transfer length and direction expected
+//! by the device for a particular command.
+//! \param command Pointer to a buffer holding the command to evaluate
+//! \param length Expected length of the data transfer
+//! \param type Expected direction of data transfer
+//! \param lun Pointer to the LUN affected by the command
+//------------------------------------------------------------------------------
+unsigned char SBC_GetCommandInformation(void *command,
+ unsigned int *length,
+ unsigned char *type,
+ MSDLun *lun)
+{
+ SBCCommand *sbcCommand = (SBCCommand *) command;
+ unsigned char isCommandSupported = 1;
+
+ // Identify command
+ switch (sbcCommand->bOperationCode) {
+ //---------------
+ case SBC_INQUIRY:
+ //---------------
+ (*type) = MSDD_DEVICE_TO_HOST;
+
+ // Allocation length is stored in big-endian format
+ (*length) = WORDB(sbcCommand->inquiry.pAllocationLength);
+ break;
+
+ //--------------------
+ case SBC_MODE_SENSE_6:
+ //--------------------
+ (*type) = MSDD_DEVICE_TO_HOST;
+ if (sbcCommand->modeSense6.bAllocationLength >
+ sizeof(SBCModeParameterHeader6)) {
+
+ *length = sizeof(SBCModeParameterHeader6);
+ }
+ else {
+
+ *length = sbcCommand->modeSense6.bAllocationLength;
+ }
+ break;
+
+ //------------------------------------
+ case SBC_PREVENT_ALLOW_MEDIUM_REMOVAL:
+ //------------------------------------
+ (*type) = MSDD_NO_TRANSFER;
+ break;
+
+ //---------------------
+ case SBC_REQUEST_SENSE:
+ //---------------------
+ (*type) = MSDD_DEVICE_TO_HOST;
+ (*length) = sbcCommand->requestSense.bAllocationLength;
+ break;
+
+ //-----------------------
+ case SBC_TEST_UNIT_READY:
+ //-----------------------
+ (*type) = MSDD_NO_TRANSFER;
+ break;
+
+ //---------------------
+ case SBC_READ_CAPACITY_10:
+ //---------------------
+ (*type) = MSDD_DEVICE_TO_HOST;
+ (*length) = sizeof(SBCReadCapacity10Data);
+ break;
+
+ //---------------
+ case SBC_READ_10:
+ //---------------
+ (*type) = MSDD_DEVICE_TO_HOST;
+ (*length) = WORDB(sbcCommand->read10.pTransferLength)
+ * lun->blockSize * lun->media->blockSize;
+ break;
+
+ //----------------
+ case SBC_WRITE_10:
+ //----------------
+ (*type) = MSDD_HOST_TO_DEVICE;
+ (*length) = WORDB(sbcCommand->write10.pTransferLength)
+ * lun->blockSize * lun->media->blockSize;
+ break;
+
+ //-----------------
+ case SBC_VERIFY_10:
+ //-----------------
+ (*type) = MSDD_NO_TRANSFER;
+ break;
+ #if 0
+ //---------------------
+ case SBC_READ_FORMAT_CAPACITIES:
+ //---------------------
+ (*type) = MSDD_DEVICE_TO_HOST;
+ (*length) = 1;
+ break;
+ #endif
+ //------
+ default:
+ //------
+ isCommandSupported = 0;
+ }
+
+ // If length is 0, no transfer is expected
+ if ((*length) == 0) {
+
+ (*type) = MSDD_NO_TRANSFER;
+ }
+
+ return isCommandSupported;
+}
+
+//------------------------------------------------------------------------------
+//! \brief Processes a SBC command by dispatching it to a subfunction.
+//! \param lun Pointer to the affected LUN
+//! \param commandState Pointer to the current command state
+//! \return Operation result code
+//------------------------------------------------------------------------------
+unsigned char SBC_ProcessCommand(MSDLun *lun,
+ MSDCommandState *commandState)
+{
+ unsigned char result = MSDD_STATUS_INCOMPLETE;
+ SBCCommand *command = (SBCCommand *) commandState->cbw.pCommand;
+
+ // Identify command
+ switch (command->bOperationCode) {
+ //---------------
+ case SBC_READ_10:
+ //---------------
+ TRACE_DEBUG_WP("Read(10) ");
+
+ // Perform the Read10 command
+ result = SBC_Read10(lun, commandState);
+ break;
+
+ //----------------
+ case SBC_WRITE_10:
+ //----------------
+ TRACE_DEBUG_WP("Write(10) ");
+
+ // Perform the Write10 command
+ result = SBC_Write10(lun, commandState);
+ break;
+
+ //---------------------
+ case SBC_READ_CAPACITY_10:
+ //---------------------
+ TRACE_INFO_WP("RdCapacity(10) ");
+
+ // Perform the ReadCapacity command
+ result = SBC_ReadCapacity10(lun, commandState);
+ break;
+
+ //---------------------
+ case SBC_VERIFY_10:
+ //---------------------
+ TRACE_INFO_WP("Verify(10) ");
+
+ // Flush media
+ MED_Flush(lun->media);
+ result = MSDD_STATUS_SUCCESS;
+ break;
+
+ //---------------
+ case SBC_INQUIRY:
+ //---------------
+ TRACE_INFO_WP("Inquiry ");
+
+ // Process Inquiry command
+ result = SBC_Inquiry(lun, commandState);
+ break;
+
+ //--------------------
+ case SBC_MODE_SENSE_6:
+ //--------------------
+ TRACE_INFO_WP("ModeSense(6) ");
+
+ // Process ModeSense6 command
+ result = SBC_ModeSense6(lun, commandState);
+ break;
+
+ //-----------------------
+ case SBC_TEST_UNIT_READY:
+ //-----------------------
+ TRACE_INFO_WP("TstUnitRdy ");
+
+ // Process TestUnitReady command
+ //MED_Flush(lun->media);
+ result = SBC_TestUnitReady(lun);
+ break;
+
+ //---------------------
+ case SBC_REQUEST_SENSE:
+ //---------------------
+ TRACE_INFO_WP("ReqSense ");
+
+ // Perform the RequestSense command
+ result = SBC_RequestSense(lun, commandState);
+ break;
+
+ //------------------------------------
+ case SBC_PREVENT_ALLOW_MEDIUM_REMOVAL:
+ //------------------------------------
+ TRACE_INFO_WP("PrevAllowRem ");
+
+ // Check parameter
+ result = command->mediumRemoval.bPrevent ?
+ MSDD_STATUS_PARAMETER : MSDD_STATUS_SUCCESS;
+ break;
+ #if 0
+ //------------------------------------
+ case SBC_READ_FORMAT_CAPACITIES:
+ //------------------------------------
+ TRACE_INFO_WP("RdFmtCap ");
+ if (!SBCLunIsReady(lun)) {
+
+ result = MSDD_STATUS_RW;
+ break;
+ }
+ #endif
+ //------
+ default:
+ //------
+ result = MSDD_STATUS_PARAMETER;
+ }
+
+ return result;
+}
diff --git a/usb/device/massstorage/SBCMethods.h b/usb/device/massstorage/SBCMethods.h new file mode 100644 index 0000000..1ef199e --- /dev/null +++ b/usb/device/massstorage/SBCMethods.h @@ -0,0 +1,111 @@ +/* ----------------------------------------------------------------------------
+ * 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.
+ * ----------------------------------------------------------------------------
+ */
+
+//------------------------------------------------------------------------------
+/// \unit
+/// !Purpose
+///
+/// SCSI commands implementation.
+///
+/// !Usage
+///
+/// -# After a CBW is received from host, use SBC_GetCommandInformation to check
+/// if the command is supported, and get the command length and type
+/// information before processing it.
+/// -# Then SBC_ProcessCommand can be used to handle a valid command, to
+/// perform the command operations.
+/// -# SBC_UpdateSenseData is used to update the sense data that will be sent
+/// to host.
+//------------------------------------------------------------------------------
+
+#ifndef SBCMETHODS_H
+#define SBCMETHODS_H
+
+//------------------------------------------------------------------------------
+// Headers
+//------------------------------------------------------------------------------
+
+#include "SBC.h"
+#include "MSDLun.h"
+#include "MSDDStateMachine.h"
+
+//------------------------------------------------------------------------------
+// Definitions
+//------------------------------------------------------------------------------
+
+//------------------------------------------------------------------------------
+/// \page "SBC Command States"
+/// This page lists the possible states of a SBC command.
+///
+/// !States
+/// - SBC_STATE_READ
+/// - SBC_STATE_WAIT_READ
+/// - SBC_STATE_WRITE
+/// - SBC_STATE_WAIT_WRITE
+/// - SBC_STATE_NEXT_BLOCK
+
+/// Start of reading bulk data
+#define SBC_STATE_READ 0x01
+/// Waiting for the bulk data reading complete
+#define SBC_STATE_WAIT_READ 0x02
+/// Read error state
+#define SBC_STATE_READ_ERROR 0x03
+/// Start next read block
+#define SBC_STATE_NEXT_READ 0x04
+/// Start writing bulk data to host
+#define SBC_STATE_WRITE 0x05
+/// Waiting for the bulk data sending complete
+#define SBC_STATE_WAIT_WRITE 0x06
+/// Write error state
+#define SBC_STATE_WRITE_ERROR 0x07
+/// Start next write block
+#define SBC_STATE_NEXT_WRITE 0x08
+/// Start next command block
+#define SBC_STATE_NEXT_BLOCK 0x09
+//------------------------------------------------------------------------------
+
+//------------------------------------------------------------------------------
+// Exported functions
+//------------------------------------------------------------------------------
+
+void SBC_UpdateSenseData(SBCRequestSenseData *requestSenseData,
+ unsigned char senseKey,
+ unsigned char additionalSenseCode,
+ unsigned char additionalSenseCodeQualifier);
+
+unsigned char SBC_GetCommandInformation(void *command,
+ unsigned int *length,
+ unsigned char *type,
+ MSDLun *lun);
+
+unsigned char SBC_ProcessCommand(MSDLun *lun,
+ MSDCommandState *commandState);
+
+#endif //#ifndef SBCMETHODS_H
+
diff --git a/usb/device/massstorage/massstorage.dir b/usb/device/massstorage/massstorage.dir new file mode 100644 index 0000000..bc793c5 --- /dev/null +++ b/usb/device/massstorage/massstorage.dir @@ -0,0 +1,1005 @@ +/* ----------------------------------------------------------------------------
+ * 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.
+ * ----------------------------------------------------------------------------
+ */
+
+//------------------------------------------------------------------------------
+/// \dir
+///
+/// !!!Purpose
+///
+/// This directory provides definitions, structs and functions for a USB Mass
+/// Storage %device (MSD) - USB Mass Storage demo.
+///
+/// !!!Contents
+///
+/// There are four things for the implement of the USB MSD driver:
+/// - Implement the MSD driver structs and functions for the %device,
+/// to initialize, to handle MSD-specific requests and dispach
+/// standard requests in USBD callbacks, to read/write through assigned USB
+/// %endpoints,
+/// - Create the MSD device's descriptors that should be passed to
+/// the USBDDriver instance on initialization, so that the host can
+/// recognize the %device as a USB Mass Storage %device.
+/// - Implement state machine for MSD command/data/status handling.
+/// - Implement storage media interface for MSD disk accessing.
+///
+/// For more information about what a particular group contains, please refer to
+/// "USB MSD Driver".
+//------------------------------------------------------------------------------
+
+/**
+ \page "USB MSD Driver"
+ This page describes how to use the USB framework to produce a USB MSD driver,
+ which appears as a USB Disk on host.
+
+ !!!References
+ - "AT91 USB device framework"
+ - "USB Device Enumeration"
+ - <a href="http://www.usb.org/developers/docs/usb_20_040908.zip">
+ Universal Serial Bus Revision 2.0 specification
+ </a> (.zip file format, size 9.80 MB)
+ - <a href="http://www.usb.org/developers/devclass_docs/usb_msc_overview_1.2.pdf">
+ Mass Storage Overview 1.2</a>
+ - <a href="http://www.usb.org/developers/devclass_docs/usbmassbulk_10.pdf">
+ Mass Storage Bulk Only 1.0</a>
+ - <a href="http://www.t10.org/scsi-3.htm">SCSI Standards</a>
+ - SCSI Block Commands - 3 (SBC-3)
+ - SCSI Primary Commands - 4 (SPC-4)
+
+ !!!Mass Storage Class Basic
+ This section gives generic details on the MSD class.
+
+ See "USB MSD Basic".
+
+ !!!Mass Storage SCSI Disk
+
+ This section describes how to implement a USB disk by using the MSD class with
+ the SCSI transparent command set and the AT91 USB Framework. For more
+ information about the framework, please refer to the "AT91 USB device
+ framework" application note; details about the USB and the Mass Storage class
+ can be found in the USB specification 2.0 and the MSC Bulk-Only Transport
+ specification 1.0 documents, respectively.
+
+ The software example provided with this document uses the ram disk of the chip
+ as its storage medium, but has been designed in a modular way to allow easy
+ modification for any medium, e.g. internal flash, DataFlash, SD card, external
+ Flash chip.
+
+ !!Architecture
+ The MSD driver is based on framework, See "USB Device Framework Architecture".
+
+ The internal architecture of the Application layer is extended for the
+ following factors:
+ - The Command/Data/Status flow described in "USB MSD Basic" requires the use
+ of a #state machine# for non-blocking operation.
+ - The example software has been designed to be easily extended with support
+ for other media.
+ - The example software has been designed to support multiple LUNs on one or
+ more media.
+
+ \image MSDAppArch.png "Application Layer Architecture"
+
+ !!Descriptors
+ There are no class-specific descriptors for a device using the MSD class with
+ the Bulk-only transport protocol. This section thus only details the values
+ which must be set in the standard descriptors.
+
+ !Device Descriptor
+\code
+static const USBDeviceDescriptor deviceDescriptor = {
+
+ sizeof(USBDeviceDescriptor), // bLength: Size of descriptor (18 bytes)
+ USBGenericDescriptor_DEVICE, // bDescriptorType: Device descriptor
+ USBDeviceDescriptor_USB2_00, // bcdUSB: USB 2.00
+ MSDeviceDescriptor_CLASS, // bDeviceClass: 0
+ MSDeviceDescriptor_SUBCLASS, // bDeviceSubClass: 0
+ MSDeviceDescriptor_PROTOCOL, // bDeviceProtocol: 0
+ BOARD_USB_ENDPOINTS_MAXPACKETSIZE(0), // bMaxPacketSize0: Max Size EP0
+ MSDDriverDescriptors_VENDORID, // idVendor: Vendor ID ATMEL (0x03eb)
+ MSDDriverDescriptors_PRODUCTID,// idProduct: Product ID (0x6202)
+ MSDDriverDescriptors_RELEASE, // bcdDevice: 0x0001, Version 0.01
+ 1, // iManufacturer: Manufacturer string (manufacturerDescriptor) index.
+ 2, // iProduct: Product string (productDescriptor) index.
+ 3, // iSerialNumber: Serial number string (serialNumberDescriptor) index.
+ 1 // bNumConfigurations: Device has one possible configuration.
+};
+\endcode
+ Note that the Vendor ID is a special value attributed by the USB-IF
+ organization. The product ID can be chosen freely by the vendor.
+
+ !Configuration Descriptor
+ The descriptors are defined as:
+\code
+const MSDConfigurationDescriptors configurationDescriptorsFS;
+\endcode
+
+ Configuration descriptor
+\code
+// Standard configuration descriptor.
+{
+ sizeof(USBConfigurationDescriptor), // bLength: 9 bytes
+ USBGenericDescriptor_CONFIGURATION, // bDescriptorType: Configuration
+ sizeof(MSDConfigurationDescriptors),// wTotalLength: Length of all
+ 1, // bNumInterface: Configuration has one interface.
+ 1, // bConfigurationValue: This is configuration #1.
+ 0, // iConfiguration: No string descriptor for configuration.
+ BOARD_USB_BMATTRIBUTES, // bmAttributes: Power and remote wakeup
+ USBConfigurationDescriptor_POWER(100) // 100mA max power
+},
+\endcode
+
+ !Interface Descriptor
+ The interface descriptor must indicate several features:
+ - #Mass Storage Device# class code (08h) in the }bInterfaceClass} field
+ - #Data Transport Protocol# code in the }bInterfaceSubclass} field
+ - #Bulk-Only Transport# protocol code (50h) in the }bInterfaceProtocol} field
+ This example uses the SCSI transparent command set (code 06h). This is the
+ most appropriate setting for a Flash %device, given that the RBC command set
+ is not supported by Microsoft Windows.
+\code
+// Mass Storage interface descriptor.
+{
+ sizeof(USBInterfaceDescriptor), // bLength: Size of descriptor(9 bytes)
+ USBGenericDescriptor_INTERFACE, // bDescriptorType: Interface descriptor
+ 0, // bInterfaceNumber: This is interface #0.
+ 0, // bAlternateSetting: This is alternate setting #0.
+ 2, // bNumEndpoints: Interface uses two endpoints.
+ MSInterfaceDescriptor_CLASS, // bInterfaceClass: Mass Storage Device Class
+ MSInterfaceDescriptor_SCSI, // bInterfaceSubClass: SCSI transparent command
+ MSInterfaceDescriptor_BULKONLY,// bInterfaceProtocol: Bulk-Only transport
+ 0 // iInterface: No string descriptor for interface.
+},
+\endcode
+
+ !Endpoint Descriptors
+ No special requirements on these apart from being Bulk-IN and Bulk-OUT.
+\code
+// Bulk-OUT endpoint descriptor
+{
+ sizeof(USBEndpointDescriptor), // bLength: 7 bytes
+ USBGenericDescriptor_ENDPOINT, // bDescriptorType: Endpoint descriptor
+ USBEndpointDescriptor_ADDRESS(
+ USBEndpointDescriptor_OUT,
+ MSDDriverDescriptors_BULKOUT), // bEndpointAddress: OUT 0x01
+ USBEndpointDescriptor_BULK, // bmAttributes: Bulk endpoint
+ MIN(BOARD_USB_ENDPOINTS_MAXPACKETSIZE(MSDDriverDescriptors_BULKOUT),
+ USBEndpointDescriptor_MAXBULKSIZE_FS), // wMaxPacketSize: 64 bytes
+ 0 // bInterval: Must be 0 for full-speed Bulk endpoints.
+},
+// Bulk-IN endpoint descriptor
+{
+ sizeof(USBEndpointDescriptor), // bLength: 7 bytes
+ USBGenericDescriptor_ENDPOINT, // bDescriptorType: Endpoint descriptor
+ USBEndpointDescriptor_ADDRESS(
+ USBEndpointDescriptor_IN,
+ MSDDriverDescriptors_BULKIN), // bEndpointAddress: IN 0x82
+ USBEndpointDescriptor_BULK, // bmAttributes: Bulk endpoint
+ MIN(BOARD_USB_ENDPOINTS_MAXPACKETSIZE(MSDDriverDescriptors_BULKIN),
+ USBEndpointDescriptor_MAXBULKSIZE_FS), // wMaxPacketSize: 64
+ 0 // bInterval: Must be 0 for full-speed Bulk endpoints.
+}
+\endcode
+
+ !String descriptors
+ Several descriptors can be commented with a String descriptor. The latter are
+ completely optional and do not influence the detection of the device by the
+ operating system. Whether or not to include them is entirely up to the
+ programmer.
+
+ There is one exception to this rule when using the MSD class. According to the
+ specification, there must be a Serial Number string. It must contains at least
+ 12 characters, and these characters must only be either letters (a-z, A-Z) or
+ numbers (0-9). This cause no problem for the driver in practice, but this is a
+ strict requirement for certification. Also remember that string descriptors
+ use the Unicode format.
+
+ See manufacturerDescriptor, productDescriptor, serialNumberDescriptor.
+
+ !!Class-specific Requests
+ There are two Mass Storage-specific requests:
+ - GetMaxLUN
+ - Bulk-Only Mass Storage Reset
+
+ Standard requests can be forwarded to the USBDDriver_RequestHandler, with one
+ exception: #CLEAR_FEATURE#. This is necessary for Reset Recovery sequence.
+
+ !ClearFeature
+ As previously stated, the CLEAR_FEATURE request must be handled in a
+ particular way, depending on whether or not the device is waiting for a Reset
+ Recovery sequence. If it is, then CLEAR_FEATURE requests to unhalt a Bulk
+ endpoint must be discarded.
+
+ In the example software, this behavior is indicated by a boolean field in the
+ driver structure, named waitResetRecovery. The handler only has to check this
+ field value to decide whether to forward the request to the standard handler
+ or to discard it.
+\code
+// Handle requests
+switch (USBGenericRequest_GetRequest(request)) {
+//---------------------
+case USBGenericRequest_CLEARFEATURE:
+//---------------------
+
+ switch (USBFeatureRequest_GetFeatureSelector(request)) {
+
+ //---------------------
+ case USBFeatureRequest_ENDPOINTHALT:
+ //---------------------
+
+ // Do not clear the endpoint halt status if the device is waiting
+ // for a reset recovery sequence
+ if (!msdDriver.waitResetRecovery) {
+
+ // Forward the request to the standard handler
+ USBDDriver_RequestHandler(&usbdDriver, request);
+ }
+
+ USBD_Write(0, 0, 0, 0, 0);
+ break;
+
+ //------
+ default:
+ //------
+ // Forward the request to the standard handler
+ USBDDriver_RequestHandler(&usbdDriver, request);
+ }
+ break;
+}
+\endcode
+
+ !GetMaxLUN
+ Usually, the first request issued by the host right after the enumeration
+ phase will be a GET_MAX_LUN request. It enables it to discover how many
+ different logical units the device has; each of these LUNs can then be queried
+ in turn by the host when needed.
+
+ After the request is received by the device, it should return one byte of data
+ indicating the maximum Logical Unit Number (LUN). It is equal to the number of
+ LUNs used by the device minus one. For example, a device with three LUNs shall
+ return a GET_MAX_LUN value of two.
+
+ Sending this byte is done by calling the USBD_Write method on Control endpoint
+ 0. Note that the data must be held in a permanent buffer (since the transfer
+ is asynchronous); in the software provided with this application note, a
+ dedicated field is used in the driver structure (MSDDriver) to store this
+ value.
+
+ In addition due to the }Mass Storage Bulk-Only Transport} specification the
+ }wValue} should be 0, }wLength} should be 1, }wIndex} should be the interface
+ number also 0. A request which does not comply to these requirements must be
+ STALLed.
+\code
+//-------------------
+case MSD_GET_MAX_LUN:
+//-------------------
+ // Check request parameters
+ if ((request->wValue == 0)
+ && (request->wIndex == 0)
+ && (request->wLength == 1)) {
+ USBD_Write(0, &(msdDriver.maxLun), 1, 0, 0);
+ }
+ else {
+ USBD_Stall(0);
+ }
+ break;
+\endcode
+
+ !Bulk-Only Mass Storage Reset
+ The host issues #RESET# requests to return the MSD driver of the device to its
+ initial state, i.e., ready to receive a new command. However, this request
+ does not impact the USB controller state; in particular, endpoints must not be
+ reset. This means the data toggle bit must not be altered, and Halted endpoint
+ must not be returned to a normal state. After processing the reset, the device
+ must return a Zero-Length Packet (ZLP) to acknowledge the SETUP transfer.
+
+ Like GET_MAX_LUN, this request must be issued with specific parameters -
+ wValue, wIndex and wLength should be zero. A request which does not have valid
+ values in its field must be acknowledged with a STALL handshake from the
+ %device.
+
+ The handler for this request must return the state machine to its initial state.
+\code
+//-----------------------
+case MSD_BULK_ONLY_RESET:
+//-----------------------
+ // Check parameters
+ if ((request->wValue == 0)
+ && (request->wIndex == 0)
+ && (request->wLength == 0)) {
+
+ // Reset the MSD driver
+ MSDDriver_Reset();
+ USBD_Write(0, 0, 0, 0, 0);
+ }
+ else {
+ USBD_Stall(0);
+ }
+ break;
+\endcode
+
+ !!State Machine
+ ...
+
+ !Rationale
+ A state machine is necessary for #non-blocking# operation of the driver. As
+ previously stated, there are three steps when processing a command:
+ - Reception of the CBW
+ - Processing of the command (with data transfers if required)
+ - Emission of the CSW
+
+ Without a state machine, the program execution would be stopped at each step
+ to wait for transfers completion or command processing. For example, reception
+ of a CBW does not always happen immediately (the host does not have to issue
+ commands regularly) and can block the system for a long time.
+
+ Developing an asynchronous design based on a state machine is made easier when
+ using Atmel "AT91 USB device framework", as most methods are asynchronous. For
+ example, a write operation (using the USBD_Write function) returns
+ immediately; a callback function can then be invoked when the transfer
+ actually completes.
+
+ !States
+ Apart from the three states corresponding to the command processing flow (CBW,
+ command processing and CSW), two more can be identified. The
+ reception/emission of CBW/CSW will be broken down into two different states:
+ the first state is used to issue the read/write operation, while the second
+ one waits for the transfer to finish. This can be done by monitoring a
+ "transfer complete" flag which is set using a callback function.
+
+ In addition, some commands can be quite complicated to process: they may
+ require several consecutive data transfers mixed with media access. Each
+ command thus has its own second-tier state machine. During execution of a
+ command, the main state machine remains in the "processing" state, and
+ proceeds to the next one (CSW emission) only when the command is complete.
+
+ Here is the states list:
+ - MSDDriver_STATE_READ_CBW: Start of CBW reception
+ (initial state after reset)
+ - MSDDriver_STATE_WAIT_CBW: Waiting for CBW reception
+ - MSDDriver_STATE_PROCESS_CBW: Command processing
+ - MSDDriver_STATE_SEND_CSW: Start of CSW emission
+ - MSDDriver_STATE_WAIT_CSW: Waiting for CSW emission
+
+ A single function, named MSDDriver_StateMachine, is provided by the driver. It
+ must be called regularly during the program execution. The following
+ subsections describe the actions that must be performed during each state.
+
+ \image MSDDriverStates.png "MSD Driver State Machine"
+
+ #MSDDriver_STATE_READ_CBW#
+
+ As said previously, this state is used to start the reception of a new Command
+ Block Wrapper. This is done using the USB_Read method of the USB framework.
+ The result code of the function is checked for any error; the
+ USB_STATUS_SUCCESS code indicates that the transfer has been successfully
+ started.
+\code
+//----------------------
+case MSDDriver_STATE_READ_CBW:
+//----------------------
+ // Start the CBW read operation
+ transfer->semaphore = 0;
+ status = USBD_Read(MSDDriverDescriptors_BULKOUT,
+ 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
+ msdDriver.state = MSDDriver_STATE_WAIT_CBW;
+ }
+ break;
+\endcode
+ A callback function to invoke when the transfer is complete is provided to the
+ USBD_Read method, to update a MSDTransfer structure. This structure
+ indicates the transfer completion, the returned result code and the number of
+ transferred and remaining bytes.
+\code
+typedef struct {
+ unsigned int transferred; //!< Number of bytes transferred
+ unsigned int remaining; //!< Number of bytes not transferred
+ unsigned char semaphore; //!< Semaphore to indicate transfer completion
+ unsigned char status; //!< Operation result code
+} MSDTransfer;
+\endcode
+ The callback function is trivial and thus not listed here.
+
+ #MSDDriver_STATE_WAIT_CBW#
+
+ The first step here is to monitor the }semaphore} field of the MSDTransfer
+ structure (see above); this will enable detection of the transfer end. Please
+ note that this field must be declared as volatile in C, or accesses to it
+ might get optimized by the compiler; this can result in endless loops.
+
+ If the transfer is complete, then the result code must be checked to see if
+ there was an error. If the operation is successful, the state machine can
+ proceed to command processing. Otherwise, it returns to the READ_CBW state.
+\code
+//----------------------
+case MSDDriver_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) {
+
+ // Process received command
+ msdDriver.state = MSDDriver_STATE_PROCESS_CBW;
+ }
+ else if (transfer->status == USBD_STATUS_RESET) {
+
+ msdDriver.state = MSDDriver_STATE_READ_CBW;
+ }
+ else {
+
+ msdDriver.state = MSDDriver_STATE_READ_CBW;
+ }
+ }
+ break;
+\endcode
+
+ #MSDDriver_STATE_PROCESS_CBW#
+
+ Once the CBW has been received, its validity must be checked. A CBW is not
+ valid if:
+ - it has not been received right after a CSW was sent or a reset occured or
+ - it is not exactly 31 bytes long or
+ - its signature field is not equal to 43425355h
+
+ The state machine prevents the first case from happening, so only the two
+ other cases have to be verified.
+
+ The number of bytes transferred during a USBD_Read operation is passed as an
+ argument to the callback function, if one has been specified. As stated
+ previously, such a function is used to fill a MSDTransfer structure.
+ Therefore, it is trivial to check that the CBW is indeed 31 bytes by verifying
+ that the number of bytes transferred is 31, and that there are no remaining
+ bytes. The following table illustrates the three cases which may happen:
+||Number of bytes transferred||Number of bytes remaining||Meaning
+|transferred<31|remaining==0|CBW is too short
+|transferred==31|remaining>0|CBW is too long
+|transferred==31|remaining==0|CBW length is correct
+
+ Checking the signature is simply done by comparing the dCBWSignature field
+ with the expected value (43425355h).
+
+ If the CBW is not valid, then the device must immediately halt both Bulk
+ endpoints, to STALL further traffic from the host. In addition, it should stay
+ in this state until a Reset Recovery is performed by the host. This is done by
+ setting the waitResetRecovery flag in the MSDDriver structure. Finally, the
+ CSW status is set to report an error, and the state machine is returned to
+ MSDDriver_STATE_READ_CBW.
+
+ Otherwise, if the CBW is correct, then the command can be processed. Remember
+ the CBW tag must be copied regardless of the validity of the CBW.
+
+ Note that these steps are only necessary for a new command (remember commands
+ are asynchronous and are carried out in several calls, so a check can be
+ performed to avoid useless processing. A value of zero for the internal
+ command state indicates a new command.
+\code
+//-------------------------
+case MSDDriver_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)) {
+
+ // Wait for a reset recovery
+ msdDriver.waitResetRecovery = 1;
+
+ // Halt the Bulk-IN and Bulk-OUT pipes
+ USBD_Halt(MSDDriverDescriptors_BULKOUT);
+ USBD_Halt(MSDDriverDescriptors_BULKIN);
+
+ csw->bCSWStatus = MSD_CSW_COMMAND_FAILED;
+ msdDriver.state = MSDDriver_STATE_READ_CBW;
+
+ }
+ // Check the CBW Signature
+ else if (cbw->dCBWSignature != MSD_CBW_SIGNATURE) {
+
+ // Wait for a reset recovery
+ msdDriver.waitResetRecovery = 1;
+
+ // Halt the Bulk-IN and Bulk-OUT pipes
+ USBD_Halt(MSDDriverDescriptors_BULKOUT);
+ USBD_Halt(MSDDriverDescriptors_BULKIN);
+
+ csw->bCSWStatus = MSD_CSW_COMMAND_FAILED;
+ msdDriver.state = MSDDriver_STATE_READ_CBW;
+ }
+ else {
+
+ // Pre-process command
+ MSDDriver_PreProcessCommand();
+ }
+ }
+
+ // Process command
+ if (csw->bCSWStatus == MSDDriver_STATUS_SUCCESS) {
+
+ if (MSDDriver_ProcessCommand()) {
+
+ // Post-process command if it is finished
+ MSDDriver_PostProcessCommand();
+ msdDriver.state = MSDDriver_STATE_SEND_CSW;
+ }
+ }
+
+ break;
+\endcode
+
+ #MSDDriver_STATE_SEND_CSW#
+
+ This state is similar to MSDDriver_STATE_READ_CBW, except that a write
+ operation is performed instead of a read and the CSW is sent, not the CBW. The
+ same callback function is used to fill the transfer structure, which is
+ checked in the next state:
+\code
+//----------------------
+case MSDDriver_STATE_SEND_CSW:
+//----------------------
+ // Set signature
+ csw->dCSWSignature = MSD_CSW_SIGNATURE;
+
+ // Start the CSW write operation
+ status = USBD_Write(MSDDriverDescriptors_BULKIN,
+ csw,
+ MSD_CSW_SIZE,
+ (TransferCallback) MSDDriver_Callback,
+ (void *) transfer);
+
+ // Check operation result code
+ if (status == USBD_STATUS_SUCCESS) {
+
+ // Wait for end of transfer
+ msdDriver.state = MSDDriver_STATE_WAIT_CSW;
+ }
+ break;
+\endcode
+
+ #MSDDriver_STATE_WAIT_CSW#
+
+ Again, this state is very similar to MSDDriver_STATE_WAIT_CBW. The only
+ difference is that the state machine is set to MSDDriver_STATE_READ_CBW
+ regardless of the operation result code:
+\code
+//----------------------
+case MSDDriver_STATE_WAIT_CSW:
+//----------------------
+ // Check transfer semaphore
+ if (transfer->semaphore > 0) {
+
+ // Take semaphore and terminate transfer
+ transfer->semaphore--;
+
+ // Read new CBW
+ msdDriver.state = MSDDriver_STATE_READ_CBW;
+ }
+ break;
+\endcode
+
+ !!Media
+
+ USB MSD Media access is three-level abstraction.
+
+ \image MSDMediaArch.png "Media Architecture"
+
+ The bottom level is the specific driver for each media type (See memories).
+
+ In the middle, a structure Media is used to hide which specific driver a media
+ instance is using. This enables transparent use of any media driver once it
+ has been initialized (See _Media).
+
+ Finally, a LUN abstraction is made over the media structure to allow multiple
+ partitions over one media. This also makes it possible to place the LUN at any
+ address and use any block size. When performing a write or read operation on a
+ LUN, it forwards the operation to the underlying media while translating it to
+ the correct address and length.
+
+ !Media Drivers
+ A media driver must provide several functions for:
+ - Reading data from the media
+ - Writing data on the media
+ - Handling interrupts on the media
+ The last function may be empty if the media does not require interrupts for
+ asynchronous operation, or if synchronous operation produces an acceptable
+ delay.
+
+ In addition, it should also define a function for initializing a Media
+ structure with the correct values, as well as perform the necessary step for
+ the media to be useable.
+
+ For the drivers see:
+ - MEDSdram.h: }Internal Flash Driver}
+ - MEDFlash.h: }SDRAM disk driver}
+
+ !!SCSI Commands
+
+ The example software described in this application note uses SCSI commands
+ with the MSD class, since this is the most appropriate setting for a Flash
+ device. This section details how SCSI commands are processed.
+
+ !Documents
+
+ There are several documents covering SCSI commands. In this application note,
+ the reference document used is SCSI Block Commands - 3 (SBC-3). However, it
+ makes many references to another SCSI document, SCSI Primary Commands - 4
+ (SPC-4). Both are needed for full details on required commands.
+
+ !Endianness
+
+ SCSI commands use the big-endian format for storing word- and double word-
+ sized data. This means the Most Significant Bit (MSB) is stored at the
+ lowest address, and the Least Significant Bit (LSB) at the highest one.
+
+ On ARM Thumb microcontrollers, the endianness of the core is selectable.
+ However, the little-endian mode is most often used. Therefore, SCSI command
+ data must be converted before being usable. This is done by declaring
+ word- and dword-sized fields as byte arrays, and then using a macro for
+ loading or storing data. Several of them are available in the provided
+ software:
+ - Load
+ - WORDB: Converts a big-endian word value to little-endian
+ - DWORDB: Converts a big-endian double-word value to little-endian
+ - Store
+ - STORE_WORDB: Stores a little-endian word value in big-endian format
+ - STORE_DWORDB: Stores a little-endian double-word value in big-endian format
+
+ !Sense Data
+
+ When an error happens during the execution of a command, it is recorded by the
+ device. The host may then issue a Request Sense command to retrieve
+ #Sense Data#, i.e., information about previous errors.
+
+ While the sense data structure has many fields, only three are really
+ important. The first one is the Sense Key. It indicates the result of the last
+ command performed: success, media not ready, hardware error, etc. Two other
+ fields can then be specified to give a more accurate description of the
+ problem. They are named }Additional Sense Code} and }Additional Sense Code
+ Qualifier}.
+
+ In the example application, each LUN has its own sense data. It is updated
+ during command execution if there is any error.
+
+ !Commands
+
+ The SBC-3 specification gives a list of mandatory and optional commands that
+ are relevant for a block device (like a Flash drive). In practice, only a
+ subset of the mandatory commands is effectively used by operating systems;
+ conversely, several commands which are supposed to be optional are required.
+ The software provided with this application note implements the following list
+ of commands:
+ - SBC-3
+ - Prevent/Allow Medium Removal
+ - Read (10)
+ - Read Capacity (10)
+ - Verify (10)
+ - Write (10)
+ - SPC-4
+ - Inquiry
+ - Mode Sense (6)
+ - Request Sense
+ - Test Unit Ready
+ The commands are actually processed in SBC_ProcessCommand.
+
+ }Internal State Machine}
+
+ As previously stated, most commands have an internal state machine to prevent
+ blocking the whole system during a data transfer (on the USB or when accessing
+ a media). A result code is used to indicate that the corresponding function
+ must be called again for the command to complete (MSDDriver_STATUS_SUCCESS).
+
+ A command state structure is used by the driver to record several parameters
+ during command processing:
+\code
+typedef struct {
+
+ MSDTransfer transfer; //!< Current transfer status
+ MSCbw cbw; //!< Received CBW
+ MSCsw csw; //!< CSW to send
+ unsigned char state; //!< Current command state
+ unsigned char postprocess; //!< Actions to perform when command is complete
+ unsigned int length; //!< Remaining length of command
+
+} MSDCommandState;
+\endcode
+
+ Note that the }state} field must be initialized when the command is first
+ called. A value of 0 means that no command is currently being executed.
+
+ For the commands descriptions and implementation, please reffer to the SCSI
+ spec. and source code.
+
+ Functions to handle SCSI commands:
+ - SBC_Inquiry
+ - SBC_Read10
+ - SBC_ReadCapacity10
+ - SBC_RequestSense
+ - SBC_TestUnitReady
+ - SBC_Write10
+ - SBC_ModeSense6
+
+ !Command Processing
+
+ }Flow}
+
+ Command processing is actually divided into three phases in the example
+ software:
+ - Pre-processing: MSDDriver_PreProcessCommand
+ - Processing: MSDDriver_ProcessCommand
+ - Post-processing: MSDDriver_PostProcessCommand
+
+ }The Thirteen Cases}
+
+ There are basically three actions that should be performed depending on the
+ case:
+ - STALL the Bulk-IN endpoint
+ - STALL the Bulk-OUT endpoint
+ - Report a Phase Error in the CSW
+
+ The table below lists all cases along with the actions which must be taken
+ after the command, including the correct length/direction of the transfer. The
+ following notation is used to characterize host and %device expectations:
+
+ Data %Transfer Characterization
+||Notation||Meaning||Notation||Meaning
+|Hn|Host expects no data transfer|Dn|Device expects no data transfer
+|Hi|Host expects to #receive# data|Di|Device expects to #send# data
+|Ho|Host expects to #send# data|Do|Device expects to #receive# data
+|Lh|Length of data expected by the host|Ld|Length of data expected by the %device
+
+|Hx=Dx|Host and %device agree on transfer length and direction (x is either n, i or o)
+|Hx>Dx|Host and %device agree on transfer direction, host expects a larger transfer than %device
+|Hx<Dx|Host and %device agree on transfer direction, %device expects a larger transfer than host
+|Hx<>Dx|Host and %device disagree on transfer direction
+
+ The Thirteen Cases
+||\#||Case||Length||Residue||Direction||STALL IN?||STALL OUT?||Phase Error?
+|1|Hn = Dn|0|0|Irrelevant| | |
+|2|Hn < Di|0|Ld - Lh|Irrelevant| | |X
+|3|Hn < Do|0|Ld - Lh|Irrelevant| | |X
+|4|Hi > Dn|0|Lh|Irrelevant|X| |
+|5|Hi > Di|Ld|Lh - Ld|In|X| |
+|6|Hi = Di|Ld|0|In| | |
+|7|Hi < Di|Lh|Ld - Lh|In| | |X
+|8|Hi <> Do|0|0|Irrelevant|X| |X
+|9|Ho > Dn|0|Lh|Irrelevant| |X|
+|10|Ho <> Di|0|0|Irrelevant| |X|X
+|11|Ho > Do|Ld|Lh - Ld|Out| |X|
+|12|Ho = Do|Ld|0|Out| | |
+|13|Ho < Do|Lh|Lh - Ld|Out| | |X
+
+ !!Main Application
+ After the MSD driver and the media have been initialized using the
+ corresponding functions, the only requirement for the main application is to
+ regularly call the state machine function. This is necessary for processing
+ received commands in a fully asynchronous way.
+
+ The application is otherwise free of doing any other task; for example, it
+ could implement a filesystem and a serial port interface to be accessed with a
+ standard terminal. An MP3 player could also continue playing a song while its
+ memory is accessed like an external hard disk.
+
+ \image MSDDriverClasses.png "Driver Class Diagram"
+
+*/
+/**
+ \page "USB MSD Basic"
+
+ This page gives generic details on the MSD class.
+
+ !!!Purpose
+
+ The MSD class defines how devices such as a hard disk, a USB floppy disk drive
+ or a disk-on-key shall operate on the USB. These devices are referred to as
+ mass storage devices, since they usually offer a high storage capacity. When
+ plugged to a PC, a %device complying to the MSD specification is accessed like
+ any other disk on the system.
+
+ In practice, the specification only defines a way to wrap existing data
+ transfer protocols, such as SCSI or the Reduced Block Commands (RBC) set. A
+ list of the supported protocols and their uses will be given in the following
+ section.
+
+ !!!Data Transfer Protocols
+
+ The }Mass Storagae Class Specification Overview 1.2} supports the following
+ set of %devices:
+
+ Protocols for MSD %devices
+||Subclass Code||Command Block Spec.||Used by
+|01h|Reduced Block Commands(RBC)|Flash %devices
+|02h|SFF-8020i, MMC-2|CD & DVD %devices
+|03h|QIC-157|Tape %devices
+|04h|UFI|Floppy disk drives
+|05h|SFF-8070i|Floppy disk drives
+|06h|SCSI transparent command set|Any
+
+ The SCSI transparent command set comprises all SCSI-related specifications,
+ such as SCSI Primary Commands (SPC), SCSI Block Commands (SBC), and so on. A
+ command will be issued by the host to determine exactly with which standard
+ the %device is compliant.
+
+ The protocol used by the %device is specified in its Interface descriptor, in
+ the }bInterfaceSubclass} field.
+
+ !!!Transfer Protocols
+
+ There are actually two different transport protocols for the MSD class:
+ - Control/Bulk/Interface (CBI) transport
+ - Bulk-Only Transport (BOT)
+
+ These two methods are described in two separate stand-alone documents. CBI can
+ be considered obsolete and is being completely replaced by BOT. It was
+ originally targeted at full-speed floppy disk drives. Therefore, the rest of
+ this document will talk about Bulk-Only Transport exclusively.
+
+ Transport Protocol Codes
+||bInterfaceProtocol||Protocol Implementation
+|00h|Control/Bulk/Interrupt protocol (with command completion interrupt)
+|01h|Control/Bulk/Interrupt protocol (without command completion interrupt)
+|50h|Bulk-only transport
+
+ !!!Architecture
+ ...
+
+ !!Interfaces & Endpoints
+
+ An MSD %device only needs one single interface. The bInterfaceClass field of
+ the interface descriptor should be set to MSD class code (0x08), the
+ corresponding data transfer protocol code in the }bInterfaceSubclass} field
+ and transport protocol code in the }bInterfaceProtocol} field can be found in
+ the tables on above.
+
+ Exactly three %endpoints (when using the Bulk-Only Transport protocol) are
+ necessary for MSD %devices.
+
+ The first one is the Control endpoint 0, and is used for class-specific
+ requests and for clearing Halt conditions on the other two %endpoints.
+ Endpoints are halted in response to errors and host bad behavior during data
+ transfers, and the CLEAR_FEATURE request is consequently used to return them
+ to a functional state.
+
+ The other two %endpoints, which are of type Bulk, are used for transferring
+ commands and data over the bus. There must be one Bulk-IN and one Bulk-OUT
+ endpoint.
+
+ \image MSDDriverArch.png "Mass Storage Device Driver Architecture"
+
+ !!Class-Specific Descriptors
+ No class-specific descriptors for an MSD %device using the Bulk-only transfport
+ protocol.
+
+ !!Class-Specific Requests
+ Two class specific requests should be handled.
+
+ !GetMaxLUN
+ A %device can feature one or more Logical Unit (LU). Each of these units will
+ be treated as a separate disk when plugged to a computer. A %device can have up
+ to 15 logical units.
+
+ The GET_MAX_LUN request is issued by the host to determine the maximum Logical
+ Unit Number (LUN) supported by the %device. This is not equivalent to the
+ number of LU on the %device; since units are numbered starting from 0, a %device
+ with 5 LUs should report a value of 4, which will be the index of the fifth
+ unit.
+
+ Optionally, a %device with only one LUN may STALL this request instead of
+ returning a value of zero.
+
+ !Bulk-Only Mass Storage Reset
+ This request is used to reset the state of the %device and prepare it to
+ receive commands and data. Note that the data toggle bits must not be altered
+ by a RESET command; same for the Halt state of %endpoints, i.e., halted
+ %endpoints must not be reset to a normal state.
+
+ !!Command/Data/Status
+ Each MSD transaction is divided into three steps:
+ - Command stage
+ - Data stage (optional)
+ - Status stage
+
+ During the command stage, a Command Block Wrapper (CBW) is transmitted by the
+ host to the %device. The CBW describes several parameters of the transaction
+ (direction, length, LUN) and carries a variable-length command block. The
+ command block contains data in the format defined by the transfer protocol
+ used by the %device.
+
+ Command Block Wrapper Data Format
+||Offset||Field Name||Length||Comment
+|0|dCBWSignature|4 bytes|Signature to identify CBW, must be 43425355h
+|4|dCBWTag|4 bytes|Tag sent by the host, echoed in the CSW
+|8|dCBWTransferLength|4 bytes|Length of transfer during the data stage
+|12|bmCBWFlags|1 byte|Bits 0-6: Reserved/obsolete\n
+ Bit 7: Transfer direction (0 = OUT, 1 = IN)
+|13|bCBWLUN|1 byte|Bits 0-3: LUN to which the command is sent\n
+ Bits 4-7: Reserved
+|14|bCBWCBLength|1 byte|Bits 0-5: Length of command block in bytes\n
+ Bits 6-7: Reserved
+|15|CBWCB|0-16 bytes|Command block to be executed by the %device
+
+ After the %device has received and interpreted the command, an optional data
+ stage may take place if the command requires it. During this step, data is
+ transferred either to or from the %device depending on the command, in several
+ IN/OUT transfers.
+
+ Once the data stage is complete, the host issues a final IN request on the
+ Bulk-IN endpoint of the %device to request the Command Status Wrapper (CSW).
+ The CSW is used to report correct or incorrect execution of the command, as
+ well as indicating the length of remaining data that has not been transferred.
+
+ Command Status Wrapper
+||Offset||Field Name||Length||Comment
+|0|dCSWSignature|4 bytes|Signature to identify CSW, must be 53425355h
+|4|dCSWTag|4 bytes|Copy of previous CBW tag
+|8|dCSWDataResidue|4 bytes|Difference between expected and real transfer length
+|12|bCSWStatus|1 byte|Indicates the success or failure of the command
+
+ These steps are all performed on the two Bulk %endpoints, and do not involve
+ Control endpoint 0 at all.
+
+ !!Reset Recovery
+ When severe errors occur during command or data transfers (as defined in the
+ }Mass Storage Bulk-only Transport 1.0} document), the %device must halt both
+ Bulk %endpoints and wait for a #Reset Recovery# procedure. The Reset Recovery
+ sequence goes as follows:
+ - The host issues a Bulk-Only Mass Storage Reset request
+ - The host issues two #CLEAR_FEATURE# requests to unhalt each endpoint
+
+ A %device waiting for a Reset Recovery must not carry out CLEAR_FEATURE
+ requests trying to unhalt either Bulk endpoint until after a Reset request has
+ been received. This enables the host to distinguish between severe and minor
+ errors.
+
+ The only major error defined by the Bulk-Only Transport standard is when a CBW
+ is not valid. This means one or more of the following:
+ - The CBW is not received after a CSW has been sent or a reset.
+ - The CBW is not exactly 31 bytes in length.
+ - The dCBWSignature field of the CBW is not equal to 43425355h.
+
+ !!!Host Drivers
+ Almost all operating systems now provide a generic driver for the MSD class.
+ However, the set of supported data transfer protocols may vary. For example,
+ Microsoft Windows does not currently support the Reduced Block Command set.
+
+*/
|