📄 msddriver.c
字号:
/* ----------------------------------------------------------------------------
* ATMEL Microcontroller Software Support - ROUSSET -
* ----------------------------------------------------------------------------
* Copyright (c) 2006, 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 disclaiimer below.
*
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the disclaimer below in the documentation and/or
* other materials provided with the distribution.
*
* Atmel's name may not be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
* DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* ----------------------------------------------------------------------------
*/
//------------------------------------------------------------------------------
// Includes
//------------------------------------------------------------------------------
#include "MSDDriver.h"
#include "MSDDriverDescriptors.h"
#include "SBCMethods.h"
#include <utility/trace.h>
#include <usb/common/core/USBGenericRequest.h>
#include <usb/common/core/USBFeatureRequest.h>
#include <usb/device/core/USBD.h>
#include <usb/device/core/USBDDriver.h>
//------------------------------------------------------------------------------
// Structures
//------------------------------------------------------------------------------
//! \brief MSD driver state variables
//! \see MSDCommandState
//! \see S_std_class
//! \see MSDLun
typedef struct {
MSDLun *luns;
MSDCommandState commandState; //!< State of the currently executing command
unsigned char maxLun; //!< Maximum LUN index
unsigned char state; //!< Current state of the driver
unsigned char waitResetRecovery; //!< Indicates if the driver is
//!< waiting for a reset recovery
} MSDDriver;
//------------------------------------------------------------------------------
// Internal variables
//------------------------------------------------------------------------------
/// Mass storage device driver instance.
static MSDDriver msdDriver;
/// Standard device driver instance.
static USBDDriver usbdDriver;
//------------------------------------------------------------------------------
// Internal functions
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
//! \brief Returns the expected transfer length and direction (IN, OUT or don't
//! care) from the host point-of-view.
//! \param cbw Pointer to the CBW to examinate
//! \param pLength Expected length of command
//! \param pType Expected direction of command
//------------------------------------------------------------------------------
static void MSDDriver_GetCommandInformation(MSCbw *cbw,
unsigned int *length,
unsigned char *type)
{
// Expected host transfer direction and length
(*length) = cbw->dCBWDataTransferLength;
if (*length == 0) {
(*type) = MSDDriver_NO_TRANSFER;
}
else if ((cbw->bmCBWFlags & MSD_CBW_DEVICE_TO_HOST) != 0) {
(*type) = MSDDriver_DEVICE_TO_HOST;
}
else {
(*type) = MSDDriver_HOST_TO_DEVICE;
}
}
//------------------------------------------------------------------------------
//! \brief Pre-processes a command by checking the differences between the
//! host and device expectations in term of transfer type and length.
//!
//! Once one of the thirteen cases is identified, the actions to do
//! during the post-processing phase are stored in the dCase variable
//! of the command state.
//! \param pBot Pointer to a S_bot instance
//! \return 1 if the command is supported, false otherwise
//------------------------------------------------------------------------------
static unsigned char MSDDriver_PreProcessCommand()
{
unsigned int hostLength;
unsigned int deviceLength;
unsigned char hostType;
unsigned char deviceType;
unsigned char isCommandSupported;
MSDCommandState *commandState = &(msdDriver.commandState);
MSCsw *csw = &(commandState->csw);
MSCbw *cbw = &(commandState->cbw);
MSDLun *lun = &(msdDriver.luns[(unsigned char) cbw->bCBWLUN]);
// Get information about the command
// Host-side
MSDDriver_GetCommandInformation(cbw, &hostLength, &hostType);
// Device-side
isCommandSupported = SBC_GetCommandInformation(cbw->pCommand,
&deviceLength,
&deviceType,
lun);
// Initialize data residue and result status
csw->dCSWDataResidue = 0;
csw->bCSWStatus = MSD_CSW_COMMAND_PASSED;
// Check if the command is supported
if (isCommandSupported) {
// Identify the command case
if(hostType == MSDDriver_NO_TRANSFER) {
// Case 1 (Hn = Dn)
if(deviceType == MSDDriver_NO_TRANSFER) {
trace_LOG(trace_WARNING, "W: Case 1\n\r");
commandState->postprocess = 0;
commandState->length = 0;
}
else if(deviceType == MSDDriver_DEVICE_TO_HOST) {
// Case 2 (Hn < Di)
trace_LOG(trace_WARNING, "W: MSDDriver_PreProcessCommand: Case 2\n\r");
commandState->postprocess = MSDDriver_CASE_PHASE_ERROR;
commandState->length = 0;
}
else { //if(deviceType == MSDDriver_HOST_TO_DEVICE) {
// Case 3 (Hn < Do)
trace_LOG(trace_WARNING, "W: MSDDriver_PreProcessCommand: Case 3\n\r");
commandState->postprocess = MSDDriver_CASE_PHASE_ERROR;
commandState->length = 0;
}
}
// Case 4 (Hi > Dn)
else if(hostType == MSDDriver_DEVICE_TO_HOST) {
if(deviceType == MSDDriver_NO_TRANSFER) {
trace_LOG(trace_WARNING, "W: MSDDriver_PreProcessCommand: Case 4\n\r");
commandState->postprocess = MSDDriver_CASE_STALL_IN;
commandState->length = 0;
csw->dCSWDataResidue = hostLength;
}
else if(deviceType == MSDDriver_DEVICE_TO_HOST) {
if(hostLength > deviceLength) {
// Case 5 (Hi > Di)
trace_LOG(trace_WARNING, "W: MSDDriver_PreProcessCommand: Case 5\n\r");
commandState->postprocess = MSDDriver_CASE_STALL_IN;
commandState->length = deviceLength;
csw->dCSWDataResidue = hostLength - deviceLength;
}
else if(hostLength == deviceLength) {
// Case 6 (Hi = Di)
// trace_LOG(trace_WARNING, "W: Case 6\n\r");
commandState->postprocess = 0;
commandState->length = deviceLength;
}
else { //if(hostLength < deviceLength) {
// Case 7 (Hi < Di)
trace_LOG(trace_WARNING, "W: MSDDriver_PreProcessCommand: Case 7\n\r");
commandState->postprocess = MSDDriver_CASE_PHASE_ERROR;
commandState->length = hostLength;
}
}
else { //if(deviceType == MSDDriver_HOST_TO_DEVICE) {
// Case 8 (Hi <> Do)
trace_LOG(trace_WARNING, "W: MSDDriver_PreProcessCommand: Case 8\n\r");
commandState->postprocess = MSDDriver_CASE_STALL_IN | MSDDriver_CASE_PHASE_ERROR;
commandState->length = 0;
}
}
else if(hostType == MSDDriver_HOST_TO_DEVICE) {
if(deviceType == MSDDriver_NO_TRANSFER) {
// Case 9 (Ho > Dn)
trace_LOG(trace_WARNING, "W: MSDDriver_PreProcessCommand: Case 9\n\r");
commandState->postprocess = MSDDriver_CASE_STALL_OUT;
commandState->length = 0;
csw->dCSWDataResidue = hostLength;
}
else if(deviceType == MSDDriver_DEVICE_TO_HOST) {
// Case 10 (Ho <> Di)
trace_LOG(trace_WARNING, "W: MSDDriver_PreProcessCommand: Case 10\n\r");
commandState->postprocess = MSDDriver_CASE_STALL_OUT | MSDDriver_CASE_PHASE_ERROR;
commandState->length = 0;
}
else { //if(deviceType == MSDDriver_HOST_TO_DEVICE) {
if(hostLength > deviceLength) {
// Case 11 (Ho > Do)
trace_LOG(trace_WARNING, "W: MSDDriver_PreProcessCommand: Case 11\n\r");
commandState->postprocess = MSDDriver_CASE_STALL_OUT;
commandState->length = deviceLength;
csw->dCSWDataResidue = hostLength - deviceLength;
}
else if(hostLength == deviceLength) {
// Case 12 (Ho = Do)
trace_LOG(trace_WARNING, "W: MSDDriver_PreProcessCommand: Case 12\n\r");
commandState->postprocess = 0;
commandState->length = deviceLength;
}
else { //if(hostLength < deviceLength) {
// Case 13 (Ho < Do)
trace_LOG(trace_WARNING, "W: MSDDriver_PreProcessCommand: Case 13\n\r");
commandState->postprocess = MSDDriver_CASE_PHASE_ERROR;
commandState->length = hostLength;
}
}
}
}
return isCommandSupported;
}
//------------------------------------------------------------------------------
//! \brief Post-processes a command given the case identified during the
//! pre-processing step.
//!
//! Depending on the case, one of the following actions can be done:
//! - Bulk IN endpoint is stalled
//! - Bulk OUT endpoint is stalled
//! - CSW status set to phase error
//! \param pBot Pointer to a S_bot instance
//------------------------------------------------------------------------------
static void MSDDriver_PostProcessCommand()
{
MSDCommandState *commandState = &(msdDriver.commandState);
MSCsw *csw = &(commandState->csw);
// STALL Bulk IN endpoint ?
if ((commandState->postprocess & MSDDriver_CASE_STALL_IN) != 0) {
trace_LOG(trace_INFO, "StallIn ");
USBD_Halt(MSDDriverDescriptors_BULKIN);
}
// STALL Bulk OUT endpoint ?
if ((commandState->postprocess & MSDDriver_CASE_STALL_OUT) != 0) {
trace_LOG(trace_INFO, "StallOut ");
USBD_Halt(MSDDriverDescriptors_BULKOUT);
}
// Set CSW status code to phase error ?
if ((commandState->postprocess & MSDDriver_CASE_PHASE_ERROR) != 0) {
trace_LOG(trace_INFO, "PhaseErr ");
csw->bCSWStatus = MSD_CSW_PHASE_ERROR;
}
}
//------------------------------------------------------------------------------
//! \brief Processes the latest command received by the device.
//! \param pBot Pointer to a S_bot instance
//! \return 1 if the command has been completed, false otherwise.
//------------------------------------------------------------------------------
static unsigned char MSDDriver_ProcessCommand()
{
unsigned char status;
MSDCommandState *commandState = &(msdDriver.commandState);
MSCbw *cbw = &(commandState->cbw);
MSCsw *csw = &(commandState->csw);
MSDLun *lun = &(msdDriver.luns[(unsigned char) cbw->bCBWLUN]);
unsigned char isCommandComplete = 0;
// Check if LUN is valid
if (cbw->bCBWLUN > msdDriver.maxLun) {
trace_LOG(trace_WARNING, "W: MSDDriver_ProcessCommand: Requested LUN does not exist\n\r");
status = MSDDriver_STATUS_ERROR;
}
else {
// Process command
if (msdDriver.maxLun > 0) {
trace_LOG(trace_INFO, "LUN%d ", cbw->bCBWLUN);
}
status = SBC_ProcessCommand(lun, commandState);
}
// Check command result code
if (status == MSDDriver_STATUS_PARAMETER) {
trace_LOG(trace_WARNING, "W: MSDDriver_ProcessCommand: Unknown command 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
USBD_Halt(MSDDriverDescriptors_BULKOUT);
trace_LOG(trace_INFO, "StaOUT ");
}
else {
// Stall the IN endpoint : device to host
USBD_Halt(MSDDriverDescriptors_BULKIN);
trace_LOG(trace_INFO, "StaIN ");
}
}
else if (status == MSDDriver_STATUS_ERROR) {
trace_LOG(trace_WARNING, "W: MSD_ProcessCommand: Command failed\n\r");
// Update sense data
// TODO (jjoannic#1#): Change code
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 {
// Update sense data
SBC_UpdateSenseData(&(lun->requestSenseData),
SBC_SENSE_KEY_NO_SENSE,
0,
0);
// Is command complete ?
if (status == MSDDriver_STATUS_SUCCESS) {
isCommandComplete = 1;
}
}
// Check if command has been completed
if (isCommandComplete) {
trace_LOG(trace_INFO, "Cplt ");
// Adjust data residue
if (commandState->length != 0) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -