mirror of
https://gitea.osmocom.org/sim-card/simtrace2.git
synced 2026-03-17 05:38:33 +03:00
1856 lines
53 KiB
C
1856 lines
53 KiB
C
/* ----------------------------------------------------------------------------
|
|
* 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 <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(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;
|
|
}
|
|
|
|
/**@}*/
|