/* ---------------------------------------------------------------------------- * 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. * ---------------------------------------------------------------------------- */ /** \file * \addtogroup usbd_msd *@{ */ /*------------------------------------------------------------------------------ * Headers *------------------------------------------------------------------------------*/ #include "SBCMethods.h" #include "MSDDStateMachine.h" #include #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(ep, pFifo, pCb, pArg) \ USBD_Write( (ep), \ &(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(ep, pFifo,pCb,pArg) \ USBD_Read( (ep), \ &(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 */ #if 1 status = USBD_Read(commandState->pipeOUT, (void*) ((lun->media->baseAddress + (lun->baseAddress + DWORDB(command->pLogicalBlockAddress) * lun->blockSize ) ) * lun->media->blockSize ), fifo->dataTotal, (TransferCallback) MSDDriver_Callback, (void *) transfer); #else status = MSDD_Read((void*) ((lun->media->baseAddress + (lun->baseAddress + DWORDB(command->pLogicalBlockAddress) * lun->blockSize ) ) * lun->media->blockSize ), fifo->dataTotal, (TransferCallback) MSDDriver_Callback, (void *) transfer); #endif } else { #ifdef MSDIO_WRITE10_CHUNK_SIZE status = SBC_RX_CHUNK(commandState->pipeOUT, fifo, MSDDriver_Callback, transfer); #else /* Read block to buffer */ #if 1 status = USBD_Read(commandState->pipeOUT, (void*)&fifo->pBuffer[fifo->inputNdx], fifo->blockSize, (TransferCallback) MSDDriver_Callback, (void *) transfer); #else status = MSDD_Read((void*)&fifo->pBuffer[fifo->inputNdx], fifo->blockSize, (TransferCallback) MSDDriver_Callback, (void *) transfer); #endif #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) { #if 1 status = USBD_Write(commandState->pipeIN, (void*) ((lun->media->baseAddress + (lun->baseAddress + DWORDB(command->pLogicalBlockAddress) * lun->blockSize ) ) * lun->media->blockSize ), commandState->length, (TransferCallback) MSDDriver_Callback, (void *) transfer); #else status = MSDD_Write((void*) ((lun->media->baseAddress + (lun->baseAddress + DWORDB(command->pLogicalBlockAddress) * lun->blockSize ) ) * lun->media->blockSize ), commandState->length, (TransferCallback) MSDDriver_Callback, (void *) transfer); #endif } else { #ifdef MSDIO_READ10_CHUNK_SIZE status = SBC_TX_CHUNK(commandState->pipeIN, fifo, MSDDriver_Callback, transfer); #else #if 1 status = USBD_Write(commandState->pipeIN, &fifo->pBuffer[fifo->outputNdx], fifo->blockSize, (TransferCallback) MSDDriver_Callback, (void *) transfer); #else status = MSDD_Write(&fifo->pBuffer[fifo->outputNdx], fifo->blockSize, (TransferCallback) MSDDriver_Callback, (void *) transfer); #endif #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 */ #if 1 status = USBD_Write(commandState->pipeIN, &(lun->readCapacityData), commandState->length, (TransferCallback) MSDDriver_Callback, (void *) transfer); #else status = MSDD_Write(&(lun->readCapacityData), commandState->length, (TransferCallback) MSDDriver_Callback, (void *) transfer); #endif /* 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 */ #if 1 status = USBD_Write(commandState->pipeIN, (void *) lun->inquiryData, commandState->length, (TransferCallback) MSDDriver_Callback, (void *) transfer); #else status = MSDD_Write((void *) lun->inquiryData, commandState->length, (TransferCallback) MSDDriver_Callback, (void *) transfer); #endif /* 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 */ #if 1 status = USBD_Write(commandState->pipeIN, &(lun->requestSenseData), commandState->length, (TransferCallback) MSDDriver_Callback, (void *) transfer); #else status = MSDD_Write(&(lun->requestSenseData), commandState->length, (TransferCallback) MSDDriver_Callback, (void *) transfer); #endif /* 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 */ #if 1 status = USBD_Write(commandState->pipeIN, (void *) &modeParameterHeader6, commandState->length, (TransferCallback) MSDDriver_Callback, (void *) transfer); #else status = MSDD_Write((void *) &modeParameterHeader6, commandState->length, (TransferCallback) MSDDriver_Callback, (void *) transfer); #endif /* 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; /*------ */ 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; /*------ */ default: /*------ */ result = MSDD_STATUS_PARAMETER; } return result; } /**@}*/