//------------------------------------------------------------------------------ //! \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_ERROR; // Check current media state 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 "); SBC_UpdateSenseData(&(lun->requestSenseData), SBC_SENSE_KEY_NOT_READY, 0, 0); break; //------ default: //------ TRACE_INFO_WP("? "); SBC_UpdateSenseData(&(lun->requestSenseData), SBC_SENSE_KEY_NOT_READY, SBC_ASC_MEDIUM_NOT_PRESENT, 0); break; } return result; }
//----------------------------------------------------------------------------- /// 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; }
//------------------------------------------------------------------------------ // Internal functions //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ //! \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; MSDTransfer *transfer = &(commandState->transfer); SBCWrite10 *command = (SBCWrite10 *) commandState->cbw.pCommand; // Init command state if (commandState->state == 0) { commandState->state = SBC_STATE_READ; } #if !defined(AT91C_EBI_SDRAM) && !defined(BOARD_USB_UDPHS) // Convert length from bytes to blocks commandState->length /= lun->blockSize; #endif // Check if length equals 0 if (commandState->length == 0) { TRACE_INFO_WP("End "); result = MSDD_STATUS_SUCCESS; } else { // Current command status switch (commandState->state) { //------------------ case SBC_STATE_READ: //------------------ TRACE_INFO_WP("Receive "); #if !defined(AT91C_EBI_SDRAM) && !defined(BOARD_USB_UDPHS) // Read one block of data sent by the host status = MSDD_Read((void*)lun->readWriteBuffer, lun->blockSize, (TransferCallback) MSDDriver_Callback, (void *) transfer); #else status = MSDD_Read((void*)(lun->media->baseAddress + lun->baseAddress + DWORDB(command->pLogicalBlockAddress) * lun->blockSize), commandState->length, (TransferCallback) MSDDriver_Callback, (void *) transfer); #endif // Check operation result code if (status != USBD_STATUS_SUCCESS) { TRACE_WARNING( "RBC_Write10: Failed to start receiving data\n\r"); SBC_UpdateSenseData(&(lun->requestSenseData), SBC_SENSE_KEY_HARDWARE_ERROR, 0, 0); result = MSDD_STATUS_ERROR; } else { // Prepare next device state commandState->state = SBC_STATE_WAIT_READ; } break; //----------------------- case SBC_STATE_WAIT_READ: //----------------------- TRACE_INFO_WP("Wait "); // Check semaphore if (transfer->semaphore > 0) { transfer->semaphore--; commandState->state = SBC_STATE_WRITE; } break; //------------------- case SBC_STATE_WRITE: //------------------- // Check the result code of the read operation if (transfer->status != USBD_STATUS_SUCCESS) { TRACE_WARNING( "RBC_Write10: Failed to received data\n\r"); SBC_UpdateSenseData(&(lun->requestSenseData), SBC_SENSE_KEY_HARDWARE_ERROR, 0, 0); result = MSDD_STATUS_ERROR; } else { #if !defined(AT91C_EBI_SDRAM) && !defined(BOARD_USB_UDPHS) // Write the block to the media status = LUN_Write(lun, DWORDB(command->pLogicalBlockAddress), lun->readWriteBuffer, 1, (TransferCallback) MSDDriver_Callback, (void *) transfer); #else MSDDriver_Callback(transfer, MED_STATUS_SUCCESS, 0, 0); status = LUN_STATUS_SUCCESS; #endif // Check operation result code if (status != USBD_STATUS_SUCCESS) { TRACE_WARNING( "RBC_Write10: Failed to start media write\n\r"); SBC_UpdateSenseData(&(lun->requestSenseData), SBC_SENSE_KEY_NOT_READY, 0, 0); result = MSDD_STATUS_ERROR; } else { // Prepare 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 move to next state transfer->semaphore--; commandState->state = SBC_STATE_NEXT_BLOCK; } break; //------------------------ case SBC_STATE_NEXT_BLOCK: //------------------------ // Check operation result code if (transfer->status != USBD_STATUS_SUCCESS) { TRACE_WARNING( "RBC_Write10: Failed to write media\n\r"); SBC_UpdateSenseData(&(lun->requestSenseData), SBC_SENSE_KEY_RECOVERED_ERROR, SBC_ASC_TOO_MUCH_WRITE_DATA, 0); result = MSDD_STATUS_ERROR; } else { // Update transfer length and block address #if !defined(AT91C_EBI_SDRAM) && !defined(BOARD_USB_UDPHS) commandState->length--; #else commandState->length = 0; #endif STORE_DWORDB(DWORDB(command->pLogicalBlockAddress) + 1, command->pLogicalBlockAddress); // Check if transfer is finished if (commandState->length == 0) { result = MSDD_STATUS_SUCCESS; } else { commandState->state = SBC_STATE_READ; } } break; } } #if !defined(AT91C_EBI_SDRAM) && !defined(BOARD_USB_UDPHS) // Convert length from blocks to bytes commandState->length *= lun->blockSize; #endif return result; }
//------------------------------------------------------------------------------ //! \brief Performs a READ (10) command on specified LUN. //! //! The data is first read from the media and then sent to the USB host. //! This function operates asynchronously and must be called multiple //! times to complete. A result code of MSDDriver_STATUS_INCOMPLETE //! indicates that at least another call of the method is necessary. //! \param lun Pointer to the LUN affected by the command //! \param commandState Current state of the command //! \return Operation result code (SUCCESS, ERROR, INCOMPLETE or PARAMETER) //! \see MSDLun //! \see MSDCommandState //------------------------------------------------------------------------------ static unsigned char SBC_Read10(MSDLun *lun, MSDCommandState *commandState) { unsigned char status; unsigned char result = MSDD_STATUS_INCOMPLETE; SBCRead10 *command = (SBCRead10 *) commandState->cbw.pCommand; MSDTransfer *transfer = &(commandState->transfer); // Init command state if (commandState->state == 0) { commandState->state = SBC_STATE_READ; } #if !defined(AT91C_EBI_SDRAM) && !defined(BOARD_USB_UDPHS) // Convert length from bytes to blocks commandState->length /= lun->blockSize; #endif // Check length if (commandState->length == 0) { result = MSDD_STATUS_SUCCESS; } else { // Command state management switch (commandState->state) { //------------------ case SBC_STATE_READ: //------------------ // Read one block of data from the media #if !defined(AT91C_EBI_SDRAM) && !defined(BOARD_USB_UDPHS) status = LUN_Read(lun, DWORDB(command->pLogicalBlockAddress), lun->readWriteBuffer, 1, (TransferCallback) MSDDriver_Callback, (void *) transfer); #else MSDDriver_Callback(transfer, MED_STATUS_SUCCESS, 0, 0); status = LUN_STATUS_SUCCESS; #endif // Check operation result code if (status != LUN_STATUS_SUCCESS) { TRACE_WARNING( "RBC_Read10: Failed to start reading media\n\r"); SBC_UpdateSenseData(&(lun->requestSenseData), SBC_SENSE_KEY_NOT_READY, SBC_ASC_LOGICAL_UNIT_NOT_READY, 0); result = MSDD_STATUS_ERROR; } else { // Move to next command state commandState->state = SBC_STATE_WAIT_READ; } break; //----------------------- case SBC_STATE_WAIT_READ: //----------------------- // Check semaphore value if (transfer->semaphore > 0) { TRACE_INFO_WP("Ok "); // Take semaphore and move to next state transfer->semaphore--; commandState->state = SBC_STATE_WRITE; } break; //------------------- case SBC_STATE_WRITE: //------------------- // Check the operation result code if (transfer->status != USBD_STATUS_SUCCESS) { TRACE_WARNING( "RBC_Read10: Failed to read media\n\r"); SBC_UpdateSenseData(&(lun->requestSenseData), SBC_SENSE_KEY_RECOVERED_ERROR, SBC_ASC_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE, 0); result = MSDD_STATUS_ERROR; } else { // Send the block to the host #if !defined(AT91C_EBI_SDRAM) && !defined(BOARD_USB_UDPHS) status = MSDD_Write((void*)lun->readWriteBuffer, lun->blockSize, (TransferCallback) MSDDriver_Callback, (void *) transfer); #else status = MSDD_Write((void*)(lun->media->baseAddress + lun->baseAddress + DWORDB(command->pLogicalBlockAddress) * lun->blockSize), commandState->length, (TransferCallback) MSDDriver_Callback, (void *) transfer); #endif // Check operation result code if (status != USBD_STATUS_SUCCESS) { TRACE_WARNING( "RBC_Read10: Failed to start to send data\n\r"); SBC_UpdateSenseData(&(lun->requestSenseData), SBC_SENSE_KEY_HARDWARE_ERROR, 0, 0); result = MSDD_STATUS_ERROR; } else { TRACE_INFO_WP("Sending "); // Move to next command state commandState->state = SBC_STATE_WAIT_WRITE; } } break; //------------------------ case SBC_STATE_WAIT_WRITE: //------------------------ // Check semaphore value if (transfer->semaphore > 0) { TRACE_INFO_WP("Sent "); // Take semaphore and move to next state transfer->semaphore--; commandState->state = SBC_STATE_NEXT_BLOCK; } break; //------------------------------ case SBC_STATE_NEXT_BLOCK: //------------------------------ // Check operation result code if (transfer->status != USBD_STATUS_SUCCESS) { TRACE_WARNING( "RBC_Read10: Failed to send data\n\r"); SBC_UpdateSenseData(&(lun->requestSenseData), SBC_SENSE_KEY_HARDWARE_ERROR, 0, 0); result = MSDD_STATUS_ERROR; } else { TRACE_INFO_WP("Next "); // Update transfer length and block address STORE_DWORDB(DWORDB(command->pLogicalBlockAddress) + 1, command->pLogicalBlockAddress); #if !defined(AT91C_EBI_SDRAM) && !defined(BOARD_USB_UDPHS) commandState->length--; #else commandState->length = 0; #endif // Check if transfer is finished if (commandState->length == 0) { result = MSDD_STATUS_SUCCESS; } else { commandState->state = SBC_STATE_READ; } } break; } } #if !defined(AT91C_EBI_SDRAM) && !defined(BOARD_USB_UDPHS) // Convert length from blocks to bytes commandState->length *= lun->blockSize; #endif return result; }