Files
test/middleware/usb/uC-USBD/Class/MSC/usbd_msc.c
2025-11-07 20:19:23 +08:00

2280 lines
97 KiB
C

/*
*********************************************************************************************************
* uC/USB-Device
* The Embedded USB Device Stack
*
* Copyright 2004-2021 Silicon Laboratories Inc. www.silabs.com
*
* SPDX-License-Identifier: APACHE-2.0
*
* This software is subject to an open source license and is distributed by
* Silicon Laboratories Inc. pursuant to the terms of the Apache License,
* Version 2.0 available at www.apache.org/licenses/LICENSE-2.0.
*
*********************************************************************************************************
*/
/*
*********************************************************************************************************
* NOTICE
*
* Semidrive modified this file to adapt it for ssdk platform.
* The modifications are only intended for use with Semidrive chips.
* Copyright of all the modifications belongs to Semidrive Semiconductor.
*
*********************************************************************************************************
*/
/*
*********************************************************************************************************
*
* USB DEVICE MSC CLASS
*
* Filename : usbd_msc.c
* Version : V4.06.01
*********************************************************************************************************
*/
/*
*********************************************************************************************************
* INCLUDE FILES
*********************************************************************************************************
*/
#define MICRIUM_SOURCE
#define USBD_MSC_MODULE
#include "usbd_msc.h"
#include "usbd_scsi.h"
#include "usbd_msc_os.h"
#include <lib_str.h>
/*
*********************************************************************************************************
* EXTERNAL C LANGUAGE LINKAGE
*********************************************************************************************************
*/
/*
*********************************************************************************************************
* LOCAL DEFINES
*********************************************************************************************************
*/
#define USBD_MSC_SIG_CBW 0x43425355
#define USBD_MSC_SIG_CSW 0x53425355
#define USBD_MSC_LEN_CBW 31
#define USBD_MSC_LEN_CSW 13
#define USBD_MSC_DEV_STR_LEN 12u
#define USBD_MSC_CTRL_REQ_TIMEOUT_mS 5000u
#define USBD_DBG_MSC_MSG(msg) USBD_DBG_GENERIC ((msg), \
USBD_EP_ADDR_NONE, \
USBD_IF_NBR_NONE)
#define USBD_DBG_MSC_ARG(msg, arg) USBD_DBG_GENERIC_ARG((msg), \
USBD_EP_ADDR_NONE, \
USBD_IF_NBR_NONE, \
(arg))
#define USBD_MSC_COM_NBR_MAX (USBD_MSC_CFG_MAX_NBR_DEV * \
USBD_MSC_CFG_MAX_NBR_CFG)
/*
*********************************************************************************************************
* SUBCLASS CODES
*
* Note(s) : (1) See 'USB Mass Storage Class Specification Overview', Revision 1.2, Section 2
*********************************************************************************************************
*/
#define USBD_MSC_SUBCLASS_CODE_RBC 0x01
#define USBD_MSC_SUBCLASS_CODE_SFF_8020i 0x02
#define USBD_MSC_SUBCLASS_CODE_MMC_2 0x02
#define USBD_MSC_SUBCLASS_CODE_QIC_157 0x03
#define USBD_MSC_SUBCLASS_CODE_UFI 0x04
#define USBD_MSC_SUBCLASS_CODE_SFF_8070i 0x05
#define USBD_MSC_SUBCLASS_CODE_SCSI 0x06
/*
*********************************************************************************************************
* PROTOCOL CODES
*
* Note(s) : (1) See 'USB Mass Storage Class Specification Overview', Revision 1.2, Section 3
*********************************************************************************************************
*/
#define USBD_MSC_PROTOCOL_CODE_CTRL_BULK_INTR_CMD_INTR 0x00
#define USBD_MSC_PROTOCOL_CODE_CTRL_BULK_INTR 0x01
#define USBD_MSC_PROTOCOL_CODE_BULK_ONLY 0x50
/*
*********************************************************************************************************
* CLASS-SPECIFIC REQUESTS
*
* Note(s) : (1) See 'USB Mass Storage Class - Bulk Only Transport', Section 3.
*
* (2) The 'bRequest' field of a class-specific setup request may contain one of these values.
*
* (3) The mass storage reset request is "used to reset the mass storage device and its
* associated interface". The setup request packet will consist of :
*
* (a) bmRequestType = 00100001b (class, interface, host-to-device)
* (b) bRequest = 0xFF
* (c) wValue = 0x0000
* (d) wIndex = Interface number
* (e) wLength = 0x0000
*
* (4) The get max LUN is used to determine the number of LUN's supported by the device. The
* setup request packet will consist of :
*
* (a) bmRequestType = 10100001b (class, interface, device-to-host)
* (b) bRequest = 0xFE
* (c) wValue = 0x0000
* (d) wIndex = Interface number
* (e) wLength = 0x0001
*********************************************************************************************************
*/
#define USBD_MSC_REQ_MASS_STORAGE_RESET 0xFF /* See Notes #3. */
#define USBD_MSC_REQ_GET_MAX_LUN 0xFE /* See Notes #4. */
/*
*********************************************************************************************************
* COMMAND BLOCK FLAG VALUES
*
* Note(s) : (1) See 'USB Mass Storage Class - Bulk Only Transport', Section 5.1.
*
* (2) The 'bmCBWFlags' field of a command block wrapper may contain one of these values.
*********************************************************************************************************
*/
#define USBD_MSC_BMCBWFLAGS_DIR_HOST_TO_DEVICE 0x00
#define USBD_MSC_BMCBWFLAGS_DIR_DEVICE_TO_HOST 0x80
/*
*********************************************************************************************************
* COMMAND BLOCK STATUS VALUES
*
* Note(s) : (1) See 'USB Mass Storage Class - Bulk Only Transport', Section 5.3, Table 5.3.
*
* (2) The 'bCSWStatus' field of a command status wrapper may contain one of these values.
*********************************************************************************************************
*/
#define USBD_MSC_BCSWSTATUS_CMD_PASSED 0x00
#define USBD_MSC_BCSWSTATUS_CMD_FAILED 0x01
#define USBD_MSC_BCSWSTATUS_PHASE_ERROR 0x02
/*
*********************************************************************************************************
* LOCAL CONSTANTS
*********************************************************************************************************
*/
/*
*********************************************************************************************************
* LOCAL DATA TYPES
*********************************************************************************************************
*/
/*
*********************************************************************************************************
* COMMAND BLOCK WRAPPER DATA TYPE
*
* Note(s) : (1) See 'USB Mass Storage Class - Bulk Only Transport', Section 5.1.
*
* (2) The 'bmCBWFlags' field is a bit-mapped datum with three subfields :
*
* (a) Bit 7 : Data transfer direction :
*
* (1) 0 = Data-out from host to device.
* (2) 1 = Data-in from device to host.
*
* (b) Bit 6 : Obsolete. Should be set to zero.
* (c) Bits 5-0: Reserved. Should be set to zero.
*********************************************************************************************************
*/
typedef struct usbd_msc_cbw {
CPU_INT32U dCBWSignature; /* Sig that helps identify this data pkt as CBW. */
CPU_INT32U dCBWTag; /* A cmd block tag sent by the host. */
CPU_INT32U dCBWDataTransferLength; /* Nbr of bytes of data that host expects to xfer. */
CPU_INT08U bmCBWFlags; /* Flags (see Notes #2). */
CPU_INT08U bCBWLUN; /* LUN to which the cmd blk is being sent. */
CPU_INT08U bCBWCBLength; /* Length of the CBWB in bytes. */
CPU_INT08U CBWCB[16]; /* Cmd blk to be executed by the dev. */
} USBD_MSC_CBW;
/*
*********************************************************************************************************
* COMMAND STATUS WRAPPER DATA TYPE
*
* Note(s) : (1) See 'USB Mass Storage Class - Bulk Only Transport', Section 5.2.
*********************************************************************************************************
*/
typedef struct usbd_msc_csw {
CPU_INT32U dCSWSignature; /* Sig that helps identify this data pkt as CSW. */
CPU_INT32U dCSWTag; /* Dev shall set this to the value in CBW's dCBWTag. */
CPU_INT32U dCSWDataResidue; /* Difference between expected & actual nbr data bytes. */
CPU_INT08U bCSWStatus; /* Indicates the success or failure of the cmd. */
} USBD_MSC_CSW;
/*
*********************************************************************************************************
* FORWARD DECLARATIONS
*********************************************************************************************************
*/
typedef struct usbd_msc_ctrl USBD_MSC_CTRL;
/*
*********************************************************************************************************
* MSC STATES
*********************************************************************************************************
*/
typedef enum usbd_msc_state {
USBD_MSC_STATE_NONE = 0,
USBD_MSC_STATE_INIT,
USBD_MSC_STATE_CFG
} USBD_MSC_STATE;
typedef enum usbd_msc_comm_state {
USBD_MSC_COMM_STATE_NONE = 0,
USBD_MSC_COMM_STATE_CBW,
USBD_MSC_COMM_STATE_DATA,
USBD_MSC_COMM_STATE_CSW,
USBD_MSC_COMM_STATE_RESET_RECOVERY_BULK_IN_STALL,
USBD_MSC_COMM_STATE_RESET_RECOVERY_BULK_OUT_STALL,
USBD_MSC_COMM_STATE_RESET_RECOVERY,
USBD_MSC_COMM_STATE_BULK_IN_STALL,
USBD_MSC_COMM_STATE_BULK_OUT_STALL
} USBD_MSC_COMM_STATE;
/*
**********************************************************************************************************
* MSC EP REQUIREMENTS DATA TYPE
**********************************************************************************************************
*/
typedef struct usbd_msc_comm { /* ---------- MSC COMMUNICATION INFORMATION ----------- */
USBD_MSC_CTRL *CtrlPtr; /* Ptr to ctrl information. */
CPU_INT08U DataBulkInEpAddr;
CPU_INT08U DataBulkOutEpAddr;
USBD_MSC_COMM_STATE NextCommState; /* Next comm state of the MSC device. */
USBD_MSC_CBW CBW; /* Cmd blk wrapper (CBW). */
USBD_MSC_CSW CSW; /* Cmd status word (CSW). */
CPU_BOOLEAN Stall; /* Used to stall the endpoints. */
CPU_INT32U BytesToXfer; /* Current bytes to xfer during data xfer stage. */
void *SCSIWrBufPtr; /* Ptr to the SCSI buf used to wr to SCSI. */
CPU_INT32U SCSIWrBuflen; /* SCSI buf len used to wr to SCSI. */
} USBD_MSC_COMM;
struct usbd_msc_ctrl { /* ------------- MSC CONTROL INFORMATION -------------- */
CPU_INT08U DevNbr; /* MSC dev nbr. */
USBD_MSC_STATE State; /* MSC dev state. */
CPU_INT08U MaxLun; /* Max logical unit number (LUN). */
USBD_MSC_LUN_CTRL Lun[USBD_MSC_CFG_MAX_LUN]; /* Array of struct pointing to LUN's */
USBD_MSC_COMM *CommPtr; /* MSC comm info ptr. */
CPU_INT08U *CBW_BufPtr; /* Buf to rx Cmd Blk Wrapper. */
CPU_INT08U *CSW_BufPtr; /* Buf to send Cmd Status Wrapper. */
CPU_INT08U *DataBufPtr; /* Buf to handle data stage. */
CPU_INT08U *CtrlStatusBufPtr; /* Buf used for ctrl status xfers. */
CPU_INT32U USBD_MSC_SCSI_Data_Len;
CPU_INT08U USBD_MSC_SCSI_Data_Dir;
};
/*
**********************************************************************************************************
* LOCAL TABLES
**********************************************************************************************************
*/
/*
**********************************************************************************************************
* LOCAL GLOBAL VARIABLES
**********************************************************************************************************
*/
/* MSC instance array. */
static USBD_MSC_CTRL USBD_MSCCtrlTbl[USBD_MSC_CFG_MAX_NBR_DEV];
static CPU_INT08U USBD_MSCCtrlNbrNext;
/* MSC comm array. */
static USBD_MSC_COMM USBD_MSCCommTbl[USBD_MSC_COM_NBR_MAX];
static CPU_INT16U USBD_MSCCommNbrNext;
/*
*********************************************************************************************************
* LOCAL FUNCTION PROTOTYPES
*********************************************************************************************************
*/
static void USBD_MSC_Conn ( CPU_INT08U dev_nbr,
CPU_INT08U cfg_nbr,
void *p_if_class_arg);
static void USBD_MSC_Disconn ( CPU_INT08U dev_nbr,
CPU_INT08U cfg_nbr,
void *p_if_class_arg);
static void USBD_MSC_EP_StateUpdate( CPU_INT08U dev_nbr,
CPU_INT08U cfg_nbr,
CPU_INT08U if_nbr,
CPU_INT08U if_alt_nbr,
CPU_INT08U ep_addr,
void *p_if_class_arg,
void *p_if_alt_class_arg);
static CPU_BOOLEAN USBD_MSC_ClassReq ( CPU_INT08U dev_nbr,
const USBD_SETUP_REQ *p_setup_req,
void *p_if_class_arg);
static USBD_MSC_COMM_STATE USBD_MSC_CommStateGet ( CPU_INT08U class_nbr);
static void USBD_MSC_RxCBW ( USBD_MSC_CTRL *p_ctrl,
USBD_MSC_COMM *p_comm,
USBD_ERR *p_err);
static void USBD_MSC_CMD_Process ( USBD_MSC_CTRL *p_ctrl,
USBD_MSC_COMM *p_comm);
static void USBD_MSC_TxCSW ( USBD_MSC_CTRL *p_ctrl,
USBD_MSC_COMM *p_comm,
USBD_ERR *p_err);
static void USBD_MSC_CBW_Verify (const USBD_MSC_CTRL *p_ctrl,
USBD_MSC_COMM *p_comm,
void *p_cbw_buf,
CPU_INT32U cbw_len,
USBD_ERR *p_err);
static void USBD_MSC_RespVerify ( USBD_MSC_COMM *p_comm,
CPU_INT32U scsi_data_len,
CPU_INT08U scsi_data_dir,
USBD_ERR *p_err);
static void USBD_MSC_SCSI_TxData ( USBD_MSC_CTRL *p_ctrl,
USBD_MSC_COMM *p_comm);
static void USBD_MSC_SCSI_Rd ( USBD_MSC_CTRL *p_ctrl,
USBD_MSC_COMM *p_comm);
static void USBD_MSC_SCSI_Wr (const USBD_MSC_CTRL *p_ctrl,
USBD_MSC_COMM *p_comm,
void *p_buf,
CPU_INT32U xfer_len);
static void USBD_MSC_SCSI_RxData ( USBD_MSC_CTRL *p_ctrl,
USBD_MSC_COMM *p_comm);
static void USBD_MSC_LunClr ( USBD_MSC_LUN_CTRL *p_lun);
static void USBD_MSC_CBW_Parse ( USBD_MSC_CBW *p_cbw,
void *p_buf_src);
static void USBD_MSC_CSW_Fmt (const USBD_MSC_CSW *p_csw,
void *p_buf_dest);
/*
*********************************************************************************************************
* MSC CLASS DRIVER
*********************************************************************************************************
*/
USBD_CLASS_DRV USBD_MSC_Drv = {
USBD_MSC_Conn,
USBD_MSC_Disconn,
DEF_NULL, /* MSC does NOT use alternate IF(s). */
USBD_MSC_EP_StateUpdate,
DEF_NULL, /* MSC does NOT use IF functional desc. */
DEF_NULL,
DEF_NULL, /* MSC does NOT use EP functional desc. */
DEF_NULL,
DEF_NULL, /* MSC does NOT handle std req with IF recipient. */
USBD_MSC_ClassReq,
DEF_NULL,
#if (USBD_CFG_MS_OS_DESC_EN == DEF_ENABLED)
DEF_NULL,
DEF_NULL
#endif
};
/*
*********************************************************************************************************
* LOCAL CONFIGURATION ERRORS
*********************************************************************************************************
*/
/*
*********************************************************************************************************
*********************************************************************************************************
* GLOBAL FUNCTIONS
*********************************************************************************************************
*********************************************************************************************************
*/
/*
**********************************************************************************************************
* USBD_MSC_Init()
*
* Description : Initialize internal structures and variables used by the Mass Storage Class
* Bulk Only Transport.
*
* Argument(s) : p_err Pointer to variable that will receive the return error code from this function :
*
* USBD_ERR_NONE MSC class successfully initialized.
*
* Return(s) : None.
*
* Note(s) : None.
**********************************************************************************************************
*/
void USBD_MSC_Init (USBD_ERR *p_err)
{
CPU_INT08U ix;
USBD_MSC_CTRL *p_ctrl;
USBD_MSC_COMM *p_comm;
LIB_ERR err_lib;
#if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED) /* ---------------- VALIDATE ARGUMENTS ---------------- */
if (p_err == (USBD_ERR *)0) { /* Validate error ptr. */
CPU_SW_EXCEPTION(;);
}
#endif
for (ix = 0u; ix < USBD_MSC_CFG_MAX_NBR_DEV; ix++) { /* Init MSC class struct. */
p_ctrl = &USBD_MSCCtrlTbl[ix];
p_ctrl->State = USBD_MSC_STATE_NONE;
p_ctrl->CommPtr = (USBD_MSC_COMM *)0;
p_ctrl->MaxLun = (CPU_INT08U )0;
p_ctrl->USBD_MSC_SCSI_Data_Len = (CPU_INT08U )0;
p_ctrl->USBD_MSC_SCSI_Data_Dir = (CPU_INT08U )0;
p_ctrl->CBW_BufPtr = (CPU_INT08U *)Mem_HeapAlloc( USBD_MSC_LEN_CBW,
CONFIG_ARCH_CACHE_LINE,
(CPU_SIZE_T *)DEF_NULL,
&err_lib);
if (err_lib != LIB_MEM_ERR_NONE) {
*p_err = USBD_ERR_ALLOC;
return;
}
Mem_Clr((void *)p_ctrl->CBW_BufPtr,
USBD_MSC_LEN_CBW);
p_ctrl->CSW_BufPtr = (CPU_INT08U *)Mem_HeapAlloc( USBD_MSC_LEN_CSW,
CONFIG_ARCH_CACHE_LINE,
(CPU_SIZE_T *)DEF_NULL,
&err_lib);
if (err_lib != LIB_MEM_ERR_NONE) {
*p_err = USBD_ERR_ALLOC;
return;
}
Mem_Clr((void *)p_ctrl->CSW_BufPtr,
USBD_MSC_LEN_CSW);
p_ctrl->DataBufPtr = (CPU_INT08U *)Mem_HeapAlloc( USBD_MSC_CFG_DATA_LEN,
CONFIG_ARCH_CACHE_LINE,
(CPU_SIZE_T *)DEF_NULL,
&err_lib);
if (err_lib != LIB_MEM_ERR_NONE) {
*p_err = USBD_ERR_ALLOC;
return;
}
Mem_Clr((void *)p_ctrl->DataBufPtr,
USBD_MSC_CFG_DATA_LEN);
p_ctrl->CtrlStatusBufPtr = (CPU_INT08U *)Mem_HeapAlloc( sizeof(CPU_ADDR),
USBD_CFG_BUF_ALIGN_OCTETS,
(CPU_SIZE_T *)DEF_NULL,
&err_lib);
if (err_lib != LIB_MEM_ERR_NONE) {
*p_err = USBD_ERR_ALLOC;
return;
}
}
for (ix = 0u; ix < USBD_MSC_COM_NBR_MAX; ix++) { /* Init test class EP tbl. */
p_comm = &USBD_MSCCommTbl[ix];
p_comm->DataBulkInEpAddr = USBD_EP_ADDR_NONE;
p_comm->DataBulkOutEpAddr = USBD_EP_ADDR_NONE;
p_comm->CtrlPtr = (USBD_MSC_CTRL *)0;
p_comm->CBW.dCBWSignature = (CPU_INT32U )0;
p_comm->CBW.dCBWTag = (CPU_INT32U )0;
p_comm->CBW.dCBWDataTransferLength = (CPU_INT32U )0;
p_comm->CBW.bmCBWFlags = (CPU_INT08U )0;
p_comm->CBW.bCBWLUN = (CPU_INT08U )0;
p_comm->CBW.bCBWCBLength = (CPU_INT08U )0;
Mem_Clr((void *) p_comm->CBW.CBWCB,
(CPU_SIZE_T)sizeof(p_comm->CBW.CBWCB));
p_comm->CSW.dCSWSignature = (CPU_INT32U )0;
p_comm->CSW.dCSWTag = (CPU_INT32U )0;
p_comm->CSW.dCSWDataResidue = (CPU_INT32U )0;
p_comm->CSW.bCSWStatus = (CPU_INT32U )0;
p_comm->Stall = (CPU_BOOLEAN)0;
p_comm->BytesToXfer = (CPU_INT32U )0;
p_comm->SCSIWrBufPtr = (void *)0;
p_comm->SCSIWrBuflen = (CPU_INT32U )0;
}
USBD_MSCCtrlNbrNext = 0u;
USBD_MSCCommNbrNext = 0u;
USBD_SCSI_Init(p_err); /* Init SCSI layer. */
if (*p_err != USBD_ERR_NONE) {
return;
}
USBD_MSC_OS_Init(p_err); /* Init MSC OS layer. */
if (*p_err != USBD_ERR_NONE) {
return;
}
}
/*
*********************************************************************************************************
* USBD_MSC_Add()
*
* Description : Add a new instance of the Mass Storage Class.
*
* Argument(s) : p_err Pointer to variable that will receive the return error code from this function :
*
* USBD_ERR_NONE Vendor class instance successfully added.
*
* USBD_ERR_ALLOC No more class instance structure available.
*
* Return(s) : Class instance number, if NO error(s).
*
* USBD_CLASS_NBR_NONE, otherwise.
*
* Note(s) : none.
*********************************************************************************************************
*/
CPU_INT08U USBD_MSC_Add (USBD_ERR *p_err)
{
CPU_INT08U msc_nbr;
CPU_SR_ALLOC();
#if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED) /* ---------------- VALIDATE ARGUMENTS ---------------- */
if (p_err == (USBD_ERR *)0) { /* Validate error ptr. */
CPU_SW_EXCEPTION(0);
}
#endif
CPU_CRITICAL_ENTER();
msc_nbr = USBD_MSCCtrlNbrNext; /* Alloc new MSC class. */
if (msc_nbr >= USBD_MSC_CFG_MAX_NBR_DEV) {
CPU_CRITICAL_EXIT();
*p_err = USBD_ERR_MSC_INSTANCE_ALLOC;
return (USBD_CLASS_NBR_NONE);
}
USBD_MSCCtrlNbrNext++;
CPU_CRITICAL_EXIT();
*p_err = USBD_ERR_NONE;
return (msc_nbr);
}
/*
*********************************************************************************************************
* USBD_MSC_CfgAdd()
*
* Description : Add an existing MSC instance to the specified configuration and device.
*
* Argument(s) : class_nbr MSC instance number.
*
* dev_nbr Device number.
*
* cfg_nbr Configuration index to add existing MSC interface to.
*
* p_err Pointer to variable that will receive the return error code from this function:
* USBD_ERR_NONE MSC class instance successfully added.
* USBD_ERR_INVALID_ARG Invalid argument(s) passed to 'class_nbr'/
* 'dev_nbr'/'intr_en'/'interval'.
* USBD_ERR_ALLOC No more class communication structure available.
* ---------- RETURNED BY USBD_IF_Add() : ----------
* USBD_ERR_INVALID_ARG Invalid argument(s) passed to 'class_desc'/
* 'class_desc_size'.
* USBD_ERR_NULL_PTR Argument 'p_class_drv'/'p_class_drv->Conn'/
* 'p_class_drv->Disconn' passed a NULL
* pointer.
* USBD_ERR_DEV_INVALID_NBR Invalid device number.
* USBD_ERR_DEV_INVALID_STATE Invalid device state.
* USBD_ERR_CFG_INVALID_NBR Invalid configuration number.
* USBD_ERR_IF_ALLOC Interfaces NOT available.
* USBD_ERR_IF_ALT_ALLOC Interface alternate settings NOT available.
*
* ---------- RETURNED BY USBD_BulkAdd() : ----------
* USBD_ERR_INVALID_ARG Invalid argument(s) passed to 'max_pkt_len'.
*
* USBD_ERR_DEV_INVALID_NBR Invalid device number.
* USBD_ERR_CFG_INVALID_NBR Invalid configuration number.
* USBD_ERR_IF_INVALID_NBR Invalid interface number.
* USBD_ERR_EP_NONE_AVAIL Physical endpoint NOT available.
* USBD_ERR_EP_ALLOC Endpoints NOT available.
*
* Return(s) : DEF_YES, if MSC instance added to USB device configuration successfully.
*
* DEF_NO, otherwise
*
* Note(s) : (1) USBD_MSC_CfgAdd() basically adds an Interface descriptor and its associated Endpoint
* descriptor(s) to the Configuration descriptor. One call to USBD_MSC_CfgAdd() builds
* the Configuration descriptor corresponding to a MSC device with the following format:
*
* Configuration Descriptor
* |-- Interface Descriptor (MSC)
* |-- Endpoint Descriptor (Bulk OUT)
* |-- Endpoint Descriptor (Bulk IN)
*
* If USBD_MSC_CfgAdd() is called several times from the application, it allows to create
* multiple instances and multiple configurations. For instance, the following architecture
* could be created for an high-speed device:
*
* High-speed
* |-- Configuration 0
* |-- Interface 0 (MSC 0)
* |-- Configuration 1
* |-- Interface 0 (MSC 0)
* |-- Interface 1 (MSC 1)
*
* In that example, there are two instances of MSC: 'MSC 0' and 'MSC 1', and two possible
* configurations for the device: 'Configuration 0' and 'Configuration 1'. 'Configuration 1'
* is composed of two interfaces. Each class instance has an association with one of the
* interfaces. If 'Configuration 1' is activated by the host, it allows the host to access
* two different functionalities offered by the device.
*********************************************************************************************************
*/
CPU_BOOLEAN USBD_MSC_CfgAdd (CPU_INT08U class_nbr,
CPU_INT08U dev_nbr,
CPU_INT08U cfg_nbr,
USBD_ERR *p_err)
{
USBD_MSC_CTRL *p_ctrl;
USBD_MSC_COMM *p_comm;
USBD_DEV_CFG *p_dev_cfg;
const CPU_CHAR *p_str;
CPU_INT16U str_len;
CPU_INT08U if_nbr;
CPU_INT08U ep_addr;
CPU_INT16U comm_nbr;
CPU_SR_ALLOC();
#if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED) /* ---------------- VALIDATE ARGUMENTS ---------------- */
if (p_err == (USBD_ERR *)0) { /* Validate error ptr. */
CPU_SW_EXCEPTION(DEF_NO);
}
#endif
if (class_nbr >= USBD_MSCCtrlNbrNext) {
*p_err = USBD_ERR_CLASS_INVALID_NBR;
return (DEF_NO);
}
p_dev_cfg = USBD_DevCfgGet(dev_nbr, p_err);
if (*p_err != USBD_ERR_NONE) {
return (DEF_NO);
}
if (p_dev_cfg->SerialNbrStrPtr != (CPU_CHAR *)0) {
p_str = &p_dev_cfg->SerialNbrStrPtr[0];
str_len = 0u;
while (( p_str != (const CPU_CHAR *) 0 ) && /* Check for NULL pointer ... */
(*p_str != ( CPU_CHAR )'\0')) { /* ... or string ending found. */
if ((*p_str < '0') || /* Serial nbr must be a hex string. */
((*p_str > '9') && (*p_str < 'A')) || /* Make sure that if A-F values are present they ... */
(*p_str > 'F')) { /* ... are lower-case. */
*p_err = USBD_ERR_INVALID_ARG;
return (DEF_NO);
}
p_str++;
str_len++;
}
if (str_len < USBD_MSC_DEV_STR_LEN) {
*p_err = USBD_ERR_INVALID_ARG;
return (DEF_NO);
}
} else {
*p_err = USBD_ERR_INVALID_ARG;
return (DEF_NO);
}
p_ctrl = &USBD_MSCCtrlTbl[class_nbr];
CPU_CRITICAL_ENTER();
p_ctrl->DevNbr = dev_nbr;
comm_nbr = USBD_MSCCommNbrNext;
if (comm_nbr >= USBD_MSC_COM_NBR_MAX) {
USBD_MSCCtrlNbrNext--;
CPU_CRITICAL_EXIT();
*p_err = USBD_ERR_MSC_INSTANCE_ALLOC;
return (DEF_NO);
}
USBD_MSCCommNbrNext++;
CPU_CRITICAL_EXIT();
p_comm = &USBD_MSCCommTbl[comm_nbr];
if_nbr = USBD_IF_Add ( dev_nbr, /* Add MSC IF desc to cfg desc. */
cfg_nbr,
&USBD_MSC_Drv,
(void *)p_comm,
(void *)0,
USBD_CLASS_CODE_MASS_STORAGE,
USBD_MSC_SUBCLASS_CODE_SCSI,
USBD_MSC_PROTOCOL_CODE_BULK_ONLY,
"USB Mass Storage Interface",
p_err);
if (*p_err != USBD_ERR_NONE) {
return (DEF_NO);
}
ep_addr = USBD_BulkAdd ( dev_nbr, /* Add bulk-IN EP desc. */
cfg_nbr,
if_nbr,
0u,
DEF_YES,
0u,
p_err);
if (*p_err != USBD_ERR_NONE) {
return (DEF_NO);
}
p_comm->DataBulkInEpAddr = ep_addr; /* Store bulk-IN EP address. */
ep_addr = USBD_BulkAdd ( dev_nbr, /* Add bulk-OUT EP desc. */
cfg_nbr,
if_nbr,
0u,
DEF_NO,
0u,
p_err);
if (*p_err != USBD_ERR_NONE) {
return (DEF_NO);
}
p_comm->DataBulkOutEpAddr = ep_addr; /* Store bulk-OUT EP address. */
CPU_CRITICAL_ENTER();
p_ctrl->State = USBD_MSC_STATE_INIT; /* Set class instance to init state. */
p_ctrl->DevNbr = dev_nbr;
p_ctrl->CommPtr = (USBD_MSC_COMM *)0;
CPU_CRITICAL_EXIT();
p_comm->CtrlPtr = p_ctrl;
*p_err = USBD_ERR_NONE;
return (DEF_YES);
}
/*
*********************************************************************************************************
* USBD_MSC_LunAdd()
*
* Description : Add a logical unit number to the MSC interface.
*
* Argument(s) : p_store_name Pointer to the Logical unit driver.
*
* class_nbr MSC instance number.
*
* p_vend_id Pointer to string containing vendor id.
*
* p_prod_id Pointer to string containing product id.
*
* prod_rev_level Product revision level.
*
* rd_only Boolean specifying if logical unit is read only or not.
*
* p_err Pointer to variable that will receive the return error code from this function:
*
* USBD_ERR_NONE LUN added successful.
* USBD_ERR_INVALID_ARGS Invalid Arguments.
* USBD_ERR_MSC_MAX_LUN_EXCEED Maximum number of LUNs reached.
* ---------- RETURNED BY USBD_SCSI_Init() : -------
* USBD_ERR_SCSI_LU_NOTRDY Disk init failed.
*
* Return(s) : None.
*
* Note(s) : None.
*********************************************************************************************************
*/
void USBD_MSC_LunAdd (const CPU_CHAR *p_store_name,
CPU_INT08U class_nbr,
CPU_CHAR *p_vend_id,
CPU_CHAR *p_prod_id,
CPU_INT32U prod_rev_level,
CPU_BOOLEAN rd_only,
USBD_ERR *p_err)
{
CPU_INT08U max_lun;
USBD_MSC_CTRL *p_ctrl;
#if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED)
CPU_SIZE_T str_len;
/* ---------------- VALIDATE ARGUMENTS ---------------- */
if (p_err == (USBD_ERR *)0) { /* Validate error ptr. */
CPU_SW_EXCEPTION(;);
}
if (p_store_name == (void*)0) {
*p_err = USBD_ERR_NULL_PTR;
return;
}
str_len = Str_Len(p_vend_id);
if (str_len > USBD_MSC_DEV_MAX_VEND_ID_LEN) {
*p_err = USBD_ERR_MSC_MAX_VEN_ID_LEN_EXCEED;
return;
}
str_len = Str_Len(p_prod_id);
if (str_len > USBD_MSC_DEV_MAX_PROD_ID_LEN) {
*p_err = USBD_ERR_MSC_MAX_PROD_ID_LEN_EXCEED;
return;
}
#endif
if (class_nbr >= USBD_MSCCtrlNbrNext) {
*p_err = USBD_ERR_CLASS_INVALID_NBR;
return;
}
p_ctrl = &USBD_MSCCtrlTbl[class_nbr];
max_lun = p_ctrl->MaxLun;
if (max_lun >= USBD_MSC_CFG_MAX_LUN) {
*p_err = USBD_ERR_MSC_MAX_LUN_EXCEED;
return;
}
USBD_MSC_LunClr(&p_ctrl->Lun[max_lun]); /* Init LUN struct. */
p_ctrl->Lun[max_lun].LunNbr = max_lun;
p_ctrl->Lun[max_lun].LunArgPtr = (void *)p_store_name;
(void)Str_Copy_N((CPU_CHAR *) &(p_ctrl->Lun[max_lun].LunInfo.VendorId[0]),
p_vend_id,
USBD_MSC_DEV_MAX_VEND_ID_LEN);
(void)Str_Copy_N((CPU_CHAR *) &(p_ctrl->Lun[max_lun].LunInfo.ProdId[0]),
p_prod_id,
USBD_MSC_DEV_MAX_PROD_ID_LEN);
p_ctrl->Lun[max_lun].LunInfo.ProdRevisionLevel = prod_rev_level;
p_ctrl->Lun[max_lun].LunInfo.ReadOnly = rd_only;
USBD_SCSI_LunAdd(p_ctrl->Lun[max_lun].LunNbr,
p_ctrl->Lun[max_lun].LunArgPtr,
p_err);
if (*p_err == USBD_ERR_NONE) {
p_ctrl->MaxLun++;
}
}
/*
*********************************************************************************************************
* USBD_MSC_IsConn()
*
* Description : Get the MSC connection state of the device.
*
* Argument(s) : class_nbr MSC instance number.
*
* Return(s) : DEF_YES, if MSC class is connected.
*
* DEF_NO, otherwise.
*
* Note(s) : None.
*********************************************************************************************************
*/
CPU_BOOLEAN USBD_MSC_IsConn (CPU_INT08U class_nbr)
{
USBD_MSC_CTRL *p_ctrl;
USBD_DEV_STATE state;
USBD_ERR err;
#if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED)
if (class_nbr >= USBD_MSCCtrlNbrNext) { /* Check MSC instance nbr. */
return (DEF_NO);
}
#endif
p_ctrl = &USBD_MSCCtrlTbl[class_nbr];
if (p_ctrl->CommPtr == (USBD_MSC_COMM *)0) {
return (DEF_NO);
}
state = USBD_DevStateGet(p_ctrl->DevNbr, &err); /* Get dev state. */
if ((err == USBD_ERR_NONE ) && /* Return true if dev state is cfg & MSC state is cfg. */
(state == USBD_DEV_STATE_CONFIGURED) &&
(p_ctrl->State == USBD_MSC_STATE_CFG )) {
return (DEF_YES);
} else {
return (DEF_NO);
}
}
/*
**********************************************************************************************************
* USBD_MSC_TaskHandler()
*
* Description : This function is used to handle MSC transfers.
*
* Argument(s) : class_nbr MSC instance number.
*
* Return(s) : none.
*
* Note(s) : none.
**********************************************************************************************************
*/
void USBD_MSC_TaskHandler (CPU_INT08U class_nbr)
{
USBD_ERR err;
USBD_ERR os_err;
USBD_MSC_CTRL *p_ctrl;
USBD_MSC_COMM *p_comm;
CPU_BOOLEAN conn;
USBD_MSC_COMM_STATE comm_state;
p_ctrl = &USBD_MSCCtrlTbl[class_nbr];
while (DEF_TRUE) {
conn = USBD_MSC_IsConn(class_nbr);
if (conn != DEF_YES) {
/* Wait till MSC state and dev state is connected. */
USBD_MSC_OS_EnumSignalPend((CPU_INT16U)0,
&os_err);
}
comm_state = USBD_MSC_CommStateGet(class_nbr); /* Get MSC comm state. */
p_comm = p_ctrl->CommPtr;
if (p_comm != (USBD_MSC_COMM *)0) {
switch(comm_state) {
case USBD_MSC_COMM_STATE_CBW: /* ---------------- RECEIVE CBW STATE ----------------- */
USBD_MSC_RxCBW(p_ctrl,
p_comm,
&err);
break;
case USBD_MSC_COMM_STATE_DATA: /* --------------- DATA TRANSPORT STATE --------------- */
USBD_MSC_CMD_Process(p_ctrl,
p_comm);
break;
case USBD_MSC_COMM_STATE_CSW: /* ---------------- TRAMSIT CSW STATE ----------------- */
USBD_MSC_TxCSW(p_ctrl,
p_comm,
&err);
break;
case USBD_MSC_COMM_STATE_RESET_RECOVERY: /* ----------------RESET RECOVERY STATE --------------- */
case USBD_MSC_COMM_STATE_RESET_RECOVERY_BULK_IN_STALL:
case USBD_MSC_COMM_STATE_RESET_RECOVERY_BULK_OUT_STALL:
/* Wait on sem for Reset Recovery to complete. */
USBD_MSC_OS_CommSignalPend( class_nbr,
(CPU_INT16U)0,
&os_err);
p_comm->NextCommState = USBD_MSC_COMM_STATE_CBW;
break;
case USBD_MSC_COMM_STATE_BULK_IN_STALL: /* --------------- BULK-IN STALL STATE ---------------- */
/* Wait on sem for clear bulk-IN stall to complete. */
USBD_MSC_OS_CommSignalPend( class_nbr,
(CPU_INT16U)0,
&os_err);
p_comm->NextCommState = USBD_MSC_COMM_STATE_CSW;
break;
case USBD_MSC_COMM_STATE_BULK_OUT_STALL: /* --------------- BULK-OUT STALL STATE -------------- */
/* Wait on sem for clear bulk-OUT stall to complete. */
USBD_MSC_OS_CommSignalPend( class_nbr,
(CPU_INT16U)0,
&os_err);
p_comm->NextCommState = USBD_MSC_COMM_STATE_CSW;
break;
case USBD_MSC_COMM_STATE_NONE:
default:
break;
}
}
}
}
/*
**********************************************************************************************************
**********************************************************************************************************
* LOCAL FUNCTIONS
**********************************************************************************************************
**********************************************************************************************************
*/
/*
*********************************************************************************************************
* USBD_MSC_Conn()
*
* Description : Notify class that configuration is active.
*
* Argument(s) : dev_nbr Device number.
*
* cfg_nbr Configuration index to add the interface to.
*
* p_if_class_arg Pointer to class argument.
*
* Return(s) : None.
*
* Note(s) : None.
*********************************************************************************************************
*/
static void USBD_MSC_Conn (CPU_INT08U dev_nbr,
CPU_INT08U cfg_nbr,
void *p_if_class_arg)
{
USBD_MSC_COMM *p_comm;
USBD_ERR os_err;
CPU_INT08U lun;
CPU_SR_ALLOC();
(void)dev_nbr;
(void)cfg_nbr;
p_comm = (USBD_MSC_COMM *)p_if_class_arg;
CPU_CRITICAL_ENTER();
p_comm->CtrlPtr->CommPtr = p_comm;
p_comm->CtrlPtr->State = USBD_MSC_STATE_CFG; /* Set initial MSC state to cfg. */
p_comm->NextCommState = USBD_MSC_COMM_STATE_CBW; /* Set initial MSC comm state to rx CBW. */
CPU_CRITICAL_EXIT();
for (lun = 0 ; lun < p_comm->CtrlPtr->MaxLun; lun++){ /* Perform some SCSI operations on each logical unit. */
USBD_SCSI_Conn(&p_comm->CtrlPtr->Lun[lun]);
}
USBD_MSC_OS_EnumSignalPost(&os_err);
USBD_DBG_MSC_MSG("MSC: Conn");
}
/*
*********************************************************************************************************
* USBD_MSC_Disconn()
*
* Description : Notify class that configuration is not active.
*
* Argument(s) : dev_nbr Device number.
*
* cfg_nbr Configuration index to add the interface.
*
* p_if_class_arg Pointer to class argument.
*
* Return(s) : None.
*
* Note(s) : None.
*********************************************************************************************************
*/
static void USBD_MSC_Disconn (CPU_INT08U dev_nbr,
CPU_INT08U cfg_nbr,
void *p_if_class_arg)
{
USBD_MSC_COMM *p_comm;
USBD_MSC_COMM_STATE comm_state;
CPU_BOOLEAN post_signal;
USBD_ERR os_err;
CPU_INT08U lun_ix;
USBD_ERR err;
CPU_SR_ALLOC();
(void)dev_nbr;
(void)cfg_nbr;
p_comm = (USBD_MSC_COMM *)p_if_class_arg;
CPU_CRITICAL_ENTER();
comm_state = p_comm->NextCommState;
switch (comm_state){
case USBD_MSC_COMM_STATE_RESET_RECOVERY:
case USBD_MSC_COMM_STATE_RESET_RECOVERY_BULK_IN_STALL:
case USBD_MSC_COMM_STATE_RESET_RECOVERY_BULK_OUT_STALL:
case USBD_MSC_COMM_STATE_BULK_IN_STALL:
case USBD_MSC_COMM_STATE_BULK_OUT_STALL:
post_signal = DEF_TRUE;
break;
case USBD_MSC_COMM_STATE_NONE:
case USBD_MSC_COMM_STATE_CBW:
case USBD_MSC_COMM_STATE_DATA:
case USBD_MSC_COMM_STATE_CSW:
default:
post_signal = DEF_FALSE;
break;
}
p_comm->CtrlPtr->CommPtr = (USBD_MSC_COMM *)0;
p_comm->CtrlPtr->State = USBD_MSC_STATE_INIT; /* Set MSC state to init. */
p_comm->NextCommState = USBD_MSC_COMM_STATE_NONE; /* Set MSC comm state to none. */
CPU_CRITICAL_EXIT();
if (post_signal == DEF_TRUE) {
USBD_MSC_OS_CommSignalPost(dev_nbr, &os_err); /* Post sem to notify waiting task if comm ... */
} /* ... is in reset recovery and bulk-IN or bulk-OUT ... */
/* ... stall states. */
/* Unlock each logical unit added to configuration */
for (lun_ix = 0 ; lun_ix < p_comm->CtrlPtr->MaxLun; lun_ix++){
USBD_SCSI_Unlock(&p_comm->CtrlPtr->Lun[lun_ix], &err);
}
USBD_DBG_MSC_MSG("MSC: Disconn");
}
/*
*********************************************************************************************************
* USBD_MSC_EP_StateUpdate()
*
* Description : Notify class that endpoint state has been updated.
*
* Argument(s) : dev_nbr Device number.
*
* cfg_nbr Configuration ix to add the interface.
*
* if_nbr Interface number.
*
* if_alt_nbr Interface alternate setting number.
*
* ep_addr Endpoint address.
*
* p_if_class_arg Pointer to class argument specific to interface.
*
* p_if_alt_class_arg Pointer to class argument specific to alternate interface.
*
* Return(s) : None.
*
* Note(s) : (1) For Reset Recovery, the host shall issue:
*
* (a) a Bulk-Only Mass Storage Reset
* (b) a Clear Feature HALT to the Bulk-In endpoint or Bulk-Out endpoint.
* (c) a Clear Feature HALT to the complement Bulk-Out endpoint or Bulk-In endpoint.
*********************************************************************************************************
*/
static void USBD_MSC_EP_StateUpdate (CPU_INT08U dev_nbr,
CPU_INT08U cfg_nbr,
CPU_INT08U if_nbr,
CPU_INT08U if_alt_nbr,
CPU_INT08U ep_addr,
void *p_if_class_arg,
void *p_if_alt_class_arg)
{
USBD_MSC_COMM *p_comm;
CPU_BOOLEAN ep_is_stall;
CPU_BOOLEAN ep_in_is_stall;
CPU_BOOLEAN ep_out_is_stall;
USBD_ERR err;
USBD_ERR os_err;
(void)cfg_nbr;
(void)if_nbr;
(void)if_alt_nbr;
(void)p_if_alt_class_arg;
p_comm = (USBD_MSC_COMM *)p_if_class_arg;
err = USBD_ERR_NONE;
switch (p_comm->NextCommState) {
case USBD_MSC_COMM_STATE_BULK_IN_STALL: /* ---------------- BULK-IN STALL STATE --------------- */
if (ep_addr == p_comm->DataBulkInEpAddr) { /* Verify correct EP addr. */
ep_is_stall = USBD_EP_IsStalled(dev_nbr, ep_addr, &err);
if (ep_is_stall == DEF_FALSE) { /* Verify that EP is unstalled. */
USBD_DBG_MSC_MSG("MSC: UpdateEP Bulk IN Stall, Signal");
/* Post sem to notify waiting task. */
USBD_MSC_OS_CommSignalPost (dev_nbr, &os_err);
} else {
USBD_DBG_MSC_MSG("MSC: UpdateEp Bulk In Stall, EP not stalled");
}
} else {
USBD_DBG_MSC_MSG("MSC: UpdateEP Bulk IN Stall, invalid endpoint");
}
break;
case USBD_MSC_COMM_STATE_BULK_OUT_STALL: /* --------------- BULK-OUT STALL STATE --------------- */
if (ep_addr == p_comm->DataBulkOutEpAddr) { /* Verify correct EP addr. */
ep_is_stall = USBD_EP_IsStalled(dev_nbr, ep_addr, &err);
if (ep_is_stall == DEF_FALSE){ /* Verify that EP is unstalled. */
USBD_DBG_MSC_MSG("MSC: UpdateEP Bulk OUT Stall, Signal");
/* Post sem to notify waiting task. */
USBD_MSC_OS_CommSignalPost (dev_nbr, &os_err);
} else {
USBD_DBG_MSC_MSG("MSC: UpdateEP Bulk OUT Stall, EP not stalled");
}
} else {
USBD_DBG_MSC_MSG("MSC: UpdateEP Bulk OUT Stall, invalid endpoint");
}
break;
case USBD_MSC_COMM_STATE_CBW: /* -------------- RX CBW / TX CSW STATE --------------- */
case USBD_MSC_COMM_STATE_CSW:
case USBD_MSC_COMM_STATE_DATA:
USBD_DBG_MSC_MSG("MSC: UpdateEP Rx CBW / Process Data / Tx CSW, skip");
break;
case USBD_MSC_COMM_STATE_RESET_RECOVERY_BULK_IN_STALL: /* ---------- RESET RECOVERY BULK-IN STATE ------------ */
if (ep_addr == p_comm->DataBulkInEpAddr) { /* Verify correct EP addr. */
ep_is_stall = USBD_EP_IsStalled(dev_nbr, ep_addr, &err);
if (ep_is_stall == DEF_FALSE) { /* Verify that EP is unstalled. */
USBD_DBG_MSC_MSG("MSC: UpdateEP Reset Recovery, EP In Cleared, Stall again");
USBD_EP_Stall(dev_nbr, p_comm->DataBulkInEpAddr, DEF_SET, &err);
} else {
USBD_DBG_MSC_MSG("MSC: UpdateEP Reset Recovery, EP In Stalled");
}
} else {
USBD_DBG_MSC_MSG("MSC: UpdateEP Reset Recovery, invalid endpoint");
}
p_comm->NextCommState = USBD_MSC_COMM_STATE_RESET_RECOVERY_BULK_OUT_STALL;
break;
case USBD_MSC_COMM_STATE_RESET_RECOVERY_BULK_OUT_STALL: /* ---------- RESET RECOVERY BULK-OUT STATE ----------- */
if (ep_addr == p_comm->DataBulkOutEpAddr) { /* Verify correct EP addr. */
ep_is_stall = USBD_EP_IsStalled(dev_nbr, ep_addr, &err);
if (ep_is_stall == DEF_FALSE) { /* Verify that EP is unstalled. */
USBD_DBG_MSC_MSG("MSC: UpdateEP Reset Recovery, EP OUT Cleared, Stall again");
USBD_EP_Stall(dev_nbr, p_comm->DataBulkOutEpAddr, DEF_SET, &err);
} else {
USBD_DBG_MSC_MSG("MSC: UpdateEP Reset Recovery, EP OUT Stalled");
}
} else {
USBD_DBG_MSC_MSG("MSC: UpdateEP Reset Recovery, invalid endpoint");
}
p_comm->NextCommState = USBD_MSC_COMM_STATE_RESET_RECOVERY;
break;
case USBD_MSC_COMM_STATE_RESET_RECOVERY: /* -------- RESET RECOVERY STATE (see Notes #1) ------- */
ep_in_is_stall = USBD_EP_IsStalled(dev_nbr, p_comm->DataBulkInEpAddr, &err);
if (err != USBD_ERR_NONE){ /* Check stall condition of bulk-IN EP. */
USBD_DBG_MSC_MSG("MSC: UpdateEP Reset Recovery, IsStalled failed");
}
ep_out_is_stall = USBD_EP_IsStalled(dev_nbr, p_comm->DataBulkOutEpAddr, &err);
if (err != USBD_ERR_NONE){ /* Check stall condition of bulk-OUT EP. */
USBD_DBG_MSC_MSG("MSC: UpdateEP Reset Recovery, IsStalled failed");
}
if ((ep_in_is_stall == DEF_FALSE) && (ep_out_is_stall == DEF_FALSE)){
USBD_DBG_MSC_MSG("MSC: UpdateEP Reset Recovery, Signal");
USBD_MSC_OS_CommSignalPost(dev_nbr, &os_err);
} else {
USBD_DBG_MSC_MSG("MSC: UpdateEP Reset Recovery, MSC Reset, Clear Stalled");
}
break;
case USBD_MSC_COMM_STATE_NONE:
default:
USBD_DBG_MSC_MSG("MSC: UpdateEP Invalid State, Stall IN/OUT");
/* Clr stall unexpected here. */
USBD_EP_Stall(dev_nbr, p_comm->DataBulkInEpAddr, DEF_CLR, &err);
USBD_EP_Stall(dev_nbr, p_comm->DataBulkOutEpAddr, DEF_CLR, &err);
break;
}
}
/*
*********************************************************************************************************
* USBD_MSC_ClassReq()
*
* Description : Process class-specific request.
*
* Argument(s) : dev_nbr Device number.
*
* p_setup_req Pointer to SETUP request structure.
*
* p_if_class_arg Pointer to class argument provided when calling USBD_IF_Add().
*
*
* Return(s) : DEF_YES, if class request was successful
*
* DEF_NO, otherwise.
*
* Note(s) : (1) The Mass Storage Reset class request is used to reset the device and its associated
* interface. This request readies the device for the next CBW from the host. The host
* sends this request via the control endpoint to the device. The device shall preserve
* the value of its bulk data toggle bits and endpoint stall conditions despite the
* Bulk-Only Mass Storage Reset. The device shall NAK the status stage of the device
* request until the Bulk-Only Mass Storage Reset is complete.
*
* (2) The Get Max LUN class request is used to determine the number of logical units supported
* by the device. The device shall return one byte of data that contains the maximum LUN
* supported by the device.
*********************************************************************************************************
*/
static CPU_BOOLEAN USBD_MSC_ClassReq ( CPU_INT08U dev_nbr,
const USBD_SETUP_REQ *p_setup_req,
void *p_if_class_arg)
{
CPU_INT08U request;
CPU_BOOLEAN valid;
USBD_MSC_CTRL *p_ctrl;
USBD_MSC_COMM *p_comm;
USBD_ERR err;
(void)dev_nbr;
request = p_setup_req->bRequest;
p_comm = (USBD_MSC_COMM *)p_if_class_arg;
p_ctrl = p_comm->CtrlPtr;
valid = DEF_NO;
switch(request) {
case USBD_MSC_REQ_MASS_STORAGE_RESET: /* -------- MASS STORAGE RESET (see Notes #1) --------- */
if ((p_setup_req->wValue == 0) &&
(p_setup_req->wLength == 0)) {
USBD_DBG_MSC_MSG("MSC: Class Mass Storage Reset, Stall IN/OUT");
USBD_EP_Abort(p_comm->CtrlPtr->DevNbr, p_comm->DataBulkInEpAddr, &err);
if (err != USBD_ERR_NONE) {
USBD_DBG_MSC_MSG("MSC: Class Mass Storage Reset, EP IN Abort failed");
}
USBD_EP_Abort(p_comm->CtrlPtr->DevNbr, p_comm->DataBulkOutEpAddr, &err);
if (err != USBD_ERR_NONE) {
USBD_DBG_MSC_MSG("MSC: Class Mass Storage Reset, EP OUT Abort failed");
}
USBD_SCSI_Reset(); /* Reset the SCSI. */
if (err == USBD_ERR_NONE){
valid = DEF_YES;
}
}
break;
case USBD_MSC_REQ_GET_MAX_LUN: /* ------------ GET MAX LUN (see Notes #2) ------------ */
if ((p_setup_req->wValue == 0) &&
(p_setup_req->wLength == 1)) {
USBD_DBG_MSC_MSG("MSC: Class Get Max Lun");
if (p_comm->CtrlPtr->MaxLun > 0) {
/* Store max LUN info. */
p_ctrl->CtrlStatusBufPtr[0] = p_comm->CtrlPtr->MaxLun - 1;
(void)USBD_CtrlTx( p_ctrl->DevNbr,/* Tx max LUN info through ctrl EP. */
(void *)&p_ctrl->CtrlStatusBufPtr[0],
1u,
USBD_MSC_CTRL_REQ_TIMEOUT_mS,
DEF_NO,
&err);
if ((err == USBD_ERR_NONE) &&
(p_comm->CtrlPtr->State == USBD_MSC_STATE_CFG)) {
valid = DEF_YES;
}
}
}
break;
default:
break;
}
return (valid);
}
/*
*********************************************************************************************************
* USBD_MSC_CommStateGet()
*
* Description : Get the MSC class communication state.
*
* Argument(s) : class_nbr MSC instance number.
*
* Return(s) : The communication state of the MSC device.
*
* Note(s) : None.
*********************************************************************************************************
*/
static USBD_MSC_COMM_STATE USBD_MSC_CommStateGet (CPU_INT08U class_nbr)
{
USBD_MSC_CTRL *p_ctrl;
if (class_nbr >= USBD_MSCCtrlNbrNext){ /* --------------------- CHECK ARG -------------------- */
return (USBD_MSC_COMM_STATE_NONE);
}
p_ctrl = &USBD_MSCCtrlTbl[class_nbr];
if (p_ctrl->CommPtr == (USBD_MSC_COMM *)0 ){
return (USBD_MSC_COMM_STATE_NONE);
}
return (p_ctrl->CommPtr->NextCommState);
}
/*
**********************************************************************************************************
* USBD_MSC_CBW_Verify()
*
* Description : Verify the CBW sent by the Host.
*
* Argument(s) : class_nbr MSC instance number.
*
* p_cbw_buf Pointer to the raw cbw buffer.
*
* cbw_len Length of the raw cbw buffer.
*
* p_err Pointer to variable that will receive the return error code from this function :
*
* USBD_ERR_NONE Valid CBW.
* USBD_ERR_MSC_INVALID_CBW Unvalid CBW.
*
* Return(s) : None.
*
* Note(s) : (1) The device performs two verifications on every CBW received. First is that the CBW
* is valid. Second is that the CBW is meaningful.
*
* (a) The device shall consider a CBW valid when:
* (1) The CBW was received after the device had sent a CSW or after a reset.
* (2) The CBW is 31 (1Fh) bytes in length.
* (3) The dCBWSignature is equal to 43425355h.
*
* (b) The device shall consider a CBW meaningful when:
* (1) No reserve bits are set.
* (2) bCBWLUN contains a valid LUN supported by the device.
* (3) both cCBWCBLength and the content of the CBWCB are in accordance with
* bInterfaceSubClass
*
**********************************************************************************************************
*/
static void USBD_MSC_CBW_Verify (const USBD_MSC_CTRL *p_ctrl,
USBD_MSC_COMM *p_comm,
void *p_cbw_buf,
CPU_INT32U cbw_len,
USBD_ERR *p_err)
{
*p_err = USBD_ERR_NONE;
if (cbw_len != USBD_MSC_LEN_CBW) { /* See note #1a2. */
USBD_DBG_MSC_MSG("MSC: Verify CBW, invalid length");
*p_err = USBD_ERR_MSC_INVALID_CBW;
}
USBD_MSC_CBW_Parse(&p_comm->CBW, p_cbw_buf); /* Parse the raw buffer into CBW struct. */
/* See note #1a3. */
if (p_comm->CBW.dCBWSignature != USBD_MSC_SIG_CBW) {
USBD_DBG_MSC_MSG("MSC: Verify CBW, invalid signature");
*p_err = USBD_ERR_MSC_INVALID_CBW;
}
/* See note #2b. */
if (((p_comm->CBW.bCBWLUN & 0xF0) > 0) ||
((p_comm->CBW.bCBWCBLength & 0xE0) > 0) ||
(p_comm->CBW.bCBWLUN >= p_ctrl->MaxLun)) {
USBD_DBG_MSC_MSG("MSC: Verify CBW, invalid LUN/reserved bits");
*p_err = USBD_ERR_MSC_INVALID_CBW;
}
}
/*
**********************************************************************************************************
* USBD_MSC_RespVerify()
*
* Description : Check the data transfer conditions of host and device and set the CSW status field.
*
* Argument(s) : class_nbr MSC instance number.
*
* scsi_data_len The length of the response the device intends to transfer.
*
* scsi_data_dir The data transfer direction.
*
* p_err Pointer to variable that will receive the return error code from this function :
* USBD_ERR_NONE CBW satisfies one of the thirteen cases.
* USBD_MSC_ERR_INVALID_DIR Mismatch between direction indicated by CBW and
* SCSI command.
*
* Return(s) : None.
*
* Note(s) : (1) "USB Mass Storage Class - Bulk Only Transport", Revision 1.0, Section 6.7, lists
* the thirteen cases of host expectation & device intent with descriptions of the
* appropriate action.
**********************************************************************************************************
*/
static void USBD_MSC_RespVerify (USBD_MSC_COMM *p_comm,
CPU_INT32U scsi_data_len,
CPU_INT08U scsi_data_dir,
USBD_ERR *p_err)
{
CPU_SR_ALLOC();
*p_err = USBD_ERR_NONE;
CPU_CRITICAL_ENTER();
p_comm->Stall = DEF_FALSE;
/* Check for host and SCSI dirs. */
if ((scsi_data_len) &&
(p_comm->CBW.bmCBWFlags != scsi_data_dir)) {
/* Case 8: Hi <> Do || 10: Hn <> Di. */
p_comm->CSW.bCSWStatus = USBD_MSC_BCSWSTATUS_PHASE_ERROR;
CPU_CRITICAL_EXIT();
*p_err = USBD_ERR_MSC_INVALID_DIR;
return;
}
if (p_comm->CBW.dCBWDataTransferLength == 0) { /* -------------------- Hn CASES: --------------------- */
if (scsi_data_len == 0) { /* Case 1: Hn = Dn. */
p_comm->CSW.bCSWStatus = USBD_MSC_BCSWSTATUS_CMD_PASSED;
} else { /* Case 2: Hn < Di || 3: Hn < Do. */
p_comm->CSW.bCSWStatus = USBD_MSC_BCSWSTATUS_PHASE_ERROR;
}
CPU_CRITICAL_EXIT();
*p_err = USBD_ERR_NONE;
return;
}
/* -------------------- Hi CASES: --------------------- */
if (p_comm->CBW.bmCBWFlags == USBD_MSC_BMCBWFLAGS_DIR_DEVICE_TO_HOST) {
if (scsi_data_len == 0) { /* Case 4: Hi > Dn. */
p_comm->CSW.bCSWStatus = USBD_MSC_BCSWSTATUS_CMD_FAILED;
p_comm->Stall = DEF_TRUE;
/* Case 5: Hi > Di. */
} else if (p_comm->CBW.dCBWDataTransferLength > scsi_data_len) {
p_comm->CSW.bCSWStatus = USBD_MSC_BCSWSTATUS_CMD_PASSED;
p_comm->Stall = DEF_TRUE;
/* Case 7: Hi < Di. */
} else if (p_comm->CBW.dCBWDataTransferLength < scsi_data_len) {
p_comm->CSW.bCSWStatus = USBD_MSC_BCSWSTATUS_PHASE_ERROR;
} else { /* Case 6: Hi == Di. */
p_comm->CSW.bCSWStatus = USBD_MSC_BCSWSTATUS_CMD_PASSED;
}
CPU_CRITICAL_EXIT();
*p_err = USBD_ERR_NONE;
return;
}
/* -------------------- Ho CASES: --------------------- */
if (p_comm->CBW.bmCBWFlags == USBD_MSC_BMCBWFLAGS_DIR_HOST_TO_DEVICE) {
/* Case 9: Ho > Dn || 11: Ho > Do. */
if (p_comm->CBW.dCBWDataTransferLength > scsi_data_len) {
p_comm->CSW.bCSWStatus = USBD_MSC_BCSWSTATUS_CMD_PASSED;
p_comm->Stall = DEF_TRUE;
/* Case 13: Ho < Do. */
} else if (p_comm->CBW.dCBWDataTransferLength < scsi_data_len) {
p_comm->CSW.bCSWStatus = USBD_MSC_BCSWSTATUS_PHASE_ERROR;
} else { /* Case 12: Ho == Do. */
p_comm->CSW.bCSWStatus = USBD_MSC_BCSWSTATUS_CMD_PASSED;
}
CPU_CRITICAL_EXIT();
*p_err = USBD_ERR_NONE;
return;
}
CPU_CRITICAL_EXIT();
}
/*
**********************************************************************************************************
* USBD_MSC_CMD_Process()
*
* Description : Process the CBW sent by the host.
*
* Argument(s) : class_nbr MSC instance number.
*
* Return(s) : None.
*
* Note(s) : None.
**********************************************************************************************************
*/
static void USBD_MSC_CMD_Process (USBD_MSC_CTRL *p_ctrl,
USBD_MSC_COMM *p_comm)
{
USBD_ERR err;
USBD_ERR stall_err;
CPU_INT08U lun;
CPU_SR_ALLOC();
lun = p_comm->CBW.bCBWLUN;
USBD_SCSI_CmdProcess(&p_ctrl->Lun[lun], /* Send the CBWCB to SCSI dev. */
p_comm->CBW.CBWCB,
&(p_ctrl->USBD_MSC_SCSI_Data_Len),
&(p_ctrl->USBD_MSC_SCSI_Data_Dir),
&err);
if (err == USBD_ERR_NONE) { /* Verify data xfer conditions. */
USBD_MSC_RespVerify(p_comm, p_ctrl->USBD_MSC_SCSI_Data_Len, p_ctrl->USBD_MSC_SCSI_Data_Dir, &err);
} else { /* Set CSW field in preparation to be returned to host. */
p_comm->CSW.bCSWStatus = USBD_MSC_BCSWSTATUS_CMD_FAILED;
}
if (err == USBD_ERR_NONE) { /* SCSI command success. */
p_comm->BytesToXfer = DEF_MIN(p_comm->CBW.dCBWDataTransferLength, p_ctrl->USBD_MSC_SCSI_Data_Len);
if (p_comm->BytesToXfer > 0) { /* Host expects data and device has data. */
if (p_comm->CBW.bmCBWFlags == USBD_MSC_BMCBWFLAGS_DIR_HOST_TO_DEVICE) {
USBD_MSC_SCSI_RxData(p_ctrl, p_comm); /* Rx data from host on bulk-OUT. */
} else {
USBD_MSC_SCSI_TxData(p_ctrl, p_comm); /* Tx data to host on bulk-IN. */
}
} else {
if (p_comm->Stall) { /* Host expects data and but dev has NO data. */
if (p_comm->CBW.bmCBWFlags == USBD_MSC_BMCBWFLAGS_DIR_HOST_TO_DEVICE) {
CPU_CRITICAL_ENTER();
p_comm->NextCommState = USBD_MSC_COMM_STATE_BULK_OUT_STALL;
CPU_CRITICAL_EXIT();
USBD_EP_Stall(p_ctrl->DevNbr, p_comm->DataBulkOutEpAddr, DEF_SET, &stall_err);
} else { /* Direction from dev to the host. */
CPU_CRITICAL_ENTER();
p_comm->NextCommState = USBD_MSC_COMM_STATE_BULK_IN_STALL;
CPU_CRITICAL_EXIT();
USBD_EP_Stall(p_ctrl->DevNbr, p_comm->DataBulkInEpAddr, DEF_SET, &stall_err);
}
} else { /* Host expects NO data. */
CPU_CRITICAL_ENTER();
p_comm->NextCommState = USBD_MSC_COMM_STATE_CSW;
CPU_CRITICAL_EXIT();
}
}
} else { /* SCSI cmd failed. */
if (p_comm->CBW.dCBWDataTransferLength == 0) { /* If no data stage send CSW to host. */
CPU_CRITICAL_ENTER();
p_comm->NextCommState = USBD_MSC_COMM_STATE_CSW;
CPU_CRITICAL_EXIT();
/* If data stage is dost to dev, ... */
/* ... stall OUT pipe and send CSW. */
} else if (p_comm->CBW.bmCBWFlags == USBD_MSC_BMCBWFLAGS_DIR_HOST_TO_DEVICE) {
CPU_CRITICAL_ENTER();
p_comm->NextCommState = USBD_MSC_COMM_STATE_BULK_OUT_STALL;
CPU_CRITICAL_EXIT();
USBD_EP_Stall(p_ctrl->DevNbr, p_comm->DataBulkOutEpAddr, DEF_SET, &stall_err);
} else { /* If data stage is dev to host, ... */
/* ... stall IN pipe and wait for clear stall. */
CPU_CRITICAL_ENTER();
p_comm->NextCommState = USBD_MSC_COMM_STATE_BULK_IN_STALL;
CPU_CRITICAL_EXIT();
USBD_EP_Stall(p_ctrl->DevNbr, p_comm->DataBulkInEpAddr, DEF_SET, &stall_err);
}
}
}
/*
**********************************************************************************************************
* USBD_MSC_SCSI_TxData()
*
* Description : Reads data from the SCSI.
*
* Argument(s) : class_nbr MSC instance number.
*
* Return(s) : None.
*
* Note(s) : None.
**********************************************************************************************************
*/
static void USBD_MSC_SCSI_TxData (USBD_MSC_CTRL *p_ctrl,
USBD_MSC_COMM *p_comm)
{
CPU_INT32U scsi_buf_len;
USBD_ERR stall_err;
CPU_SR_ALLOC();
scsi_buf_len = DEF_MIN(p_comm->BytesToXfer, USBD_MSC_CFG_DATA_LEN);
while (scsi_buf_len > 0) {
USBD_MSC_SCSI_Rd(p_ctrl, p_comm);
p_comm->BytesToXfer -= scsi_buf_len; /* Update remaining bytes to transmit. */
p_comm->CSW.dCSWDataResidue -= scsi_buf_len; /* Update CSW data residue field. */
scsi_buf_len = DEF_MIN(p_comm->BytesToXfer, USBD_MSC_CFG_DATA_LEN);
}
if (p_comm->Stall == DEF_TRUE) {
p_comm->Stall = DEF_FALSE;
CPU_CRITICAL_ENTER(); /* Set the next state to bulk-IN stall. */
p_comm->NextCommState = USBD_MSC_COMM_STATE_BULK_IN_STALL;
CPU_CRITICAL_EXIT();
USBD_EP_Stall(p_ctrl->DevNbr, p_comm->DataBulkInEpAddr, DEF_SET, &stall_err);
} else {
CPU_CRITICAL_ENTER(); /* Set the next state to tx CSW. */
p_comm->NextCommState = USBD_MSC_COMM_STATE_CSW;
CPU_CRITICAL_EXIT();
}
}
/*
**********************************************************************************************************
* USBD_MSC_SCSI_Rd()
*
* Description : Reads data from the SCSI and transmits data to the host. CSW will be transmitted
* after the data completion stage.
*
* Argument(s) : class_nbr MSC instance number.
*
* Return(s) : None.
*
* Note(s) : None.
**********************************************************************************************************
*/
static void USBD_MSC_SCSI_Rd (USBD_MSC_CTRL *p_ctrl,
USBD_MSC_COMM *p_comm)
{
CPU_INT32U scsi_ret_len;
CPU_INT32U scsi_buf_len;
CPU_INT08U lun;
USBD_ERR err;
USBD_ERR stall_err;
CPU_SR_ALLOC();
CPU_CRITICAL_ENTER();
scsi_buf_len = DEF_MIN(p_comm->BytesToXfer, USBD_MSC_CFG_DATA_LEN);
lun = p_comm->CBW.bCBWLUN;
CPU_CRITICAL_EXIT();
USBD_SCSI_DataRd(&p_ctrl->Lun[lun], /* Rd data from the SCSI. */
p_comm->CBW.CBWCB[0],
p_ctrl->DataBufPtr,
scsi_buf_len,
&scsi_ret_len,
&err);
if ((err != USBD_ERR_NONE) &&
(err != USBD_ERR_SCSI_MORE_DATA)) {
CPU_CRITICAL_ENTER();
p_comm->NextCommState = USBD_MSC_COMM_STATE_BULK_IN_STALL;
p_comm->CSW.bCSWStatus = (CPU_INT08U)USBD_MSC_BCSWSTATUS_CMD_FAILED;
CPU_CRITICAL_EXIT();
USBD_EP_Stall( p_ctrl->DevNbr, p_comm->DataBulkInEpAddr, DEF_SET, &err);
} else {
(void)USBD_BulkTx(p_ctrl->DevNbr, /* Tx data to the host. */
p_comm->DataBulkInEpAddr,
p_ctrl->DataBufPtr,
scsi_ret_len,
0,
DEF_NO,
&err);
if (err != USBD_ERR_NONE) {
CPU_CRITICAL_ENTER(); /* Enter reset recovery state if tx err. */
p_comm->NextCommState = USBD_MSC_COMM_STATE_BULK_IN_STALL;
CPU_CRITICAL_EXIT();
USBD_EP_Stall (p_ctrl->DevNbr, p_comm->DataBulkInEpAddr, DEF_SET, &stall_err);
}
}
}
/*
**********************************************************************************************************
* USBD_MSC_SCSI_Wr()
*
* Description : Save the buffer and buffer length and write data to the SCSI.
*
* Argument(s) : class_nbr MSC instance number.
*
* p_buf Pointer to the data buffer.
*
* xfer_len Length of the data buffer.
*
* Return(s) : None.
*
* Note(s) : None.
**********************************************************************************************************
*/
static void USBD_MSC_SCSI_Wr (const USBD_MSC_CTRL *p_ctrl,
USBD_MSC_COMM *p_comm,
void *p_buf,
CPU_INT32U xfer_len)
{
USBD_ERR err;
USBD_ERR stall_err;
CPU_INT08U lun;
CPU_SR_ALLOC();
CPU_CRITICAL_ENTER();
p_comm->SCSIWrBufPtr = p_buf;
p_comm->SCSIWrBuflen = xfer_len;
CPU_CRITICAL_EXIT();
lun = p_comm->CBW.bCBWLUN;
USBD_SCSI_DataWr(&p_ctrl->Lun[lun], /* Wr data to SCSI sto. */
p_comm->CBW.CBWCB[0],
p_comm->SCSIWrBufPtr,
p_comm->SCSIWrBuflen,
&err);
if ((err != USBD_ERR_NONE) &&
(err != USBD_ERR_SCSI_MORE_DATA)) {
CPU_CRITICAL_ENTER(); /* Enter bulk-OUT stall state. */
p_comm->CSW.bCSWStatus = USBD_MSC_BCSWSTATUS_CMD_FAILED;
p_comm->NextCommState = USBD_MSC_COMM_STATE_BULK_OUT_STALL;
CPU_CRITICAL_EXIT();
USBD_DBG_MSC_MSG("MSC: SCSI Wr, Stall OUT");
USBD_EP_Stall(p_ctrl->DevNbr, p_comm->DataBulkOutEpAddr, DEF_SET, &stall_err);
}
}
/*
**********************************************************************************************************
* USBD_MSC_SCSI_RxData()
*
* Description : It receives data from the host. After the data stage is complete, CSW is sent.
*
* Argument(s) : class_nbr MSC instance number.
*
* Return(s) : None.
*
* Note(s) : None.
**********************************************************************************************************
*/
static void USBD_MSC_SCSI_RxData (USBD_MSC_CTRL *p_ctrl,
USBD_MSC_COMM *p_comm)
{
CPU_INT32U scsi_buf_len;
CPU_INT32U xfer_len;
USBD_ERR err;
USBD_ERR stall_err;
CPU_SR_ALLOC();
CPU_CRITICAL_ENTER();
scsi_buf_len = DEF_MIN(p_comm->BytesToXfer, USBD_MSC_CFG_DATA_LEN);
CPU_CRITICAL_EXIT();
while (scsi_buf_len > 0){
USBD_DBG_MSC_ARG("MSC: Rx Data Len:", scsi_buf_len);
xfer_len = USBD_BulkRx(p_ctrl->DevNbr, /* Rx data from host on bulk-OUT pipe */
p_comm->DataBulkOutEpAddr,
p_ctrl->DataBufPtr,
scsi_buf_len,
0,
&err);
if (err != USBD_ERR_NONE){
CPU_CRITICAL_ENTER(); /* Enter reset recovery state if err. */
p_comm->NextCommState = USBD_MSC_COMM_STATE_BULK_OUT_STALL;
CPU_CRITICAL_EXIT();
USBD_DBG_MSC_MSG("MSC: Rx Data, Stall OUT");
USBD_EP_Stall(p_ctrl->DevNbr, p_comm->DataBulkOutEpAddr, DEF_SET, &stall_err);
return;
} else {
/* Process rx data if no err. */
USBD_MSC_SCSI_Wr(p_ctrl, p_comm, p_ctrl->DataBufPtr, xfer_len);
p_comm->BytesToXfer -= xfer_len;
p_comm->CSW.dCSWDataResidue -= xfer_len;
scsi_buf_len = DEF_MIN(p_comm->BytesToXfer, USBD_MSC_CFG_DATA_LEN);
}
}
if (p_comm->Stall){
CPU_CRITICAL_ENTER();
p_comm->Stall = DEF_FALSE; /* Enter bulk-OUT stall state. */
p_comm->NextCommState = USBD_MSC_COMM_STATE_BULK_OUT_STALL;
CPU_CRITICAL_EXIT();
USBD_DBG_MSC_MSG("MSC: Rx Data, Stall OUT");
USBD_EP_Stall(p_ctrl->DevNbr, p_comm->DataBulkOutEpAddr, DEF_SET, &stall_err);
return;
}
CPU_CRITICAL_ENTER(); /* Enter tx CSW state. */
p_comm->NextCommState = USBD_MSC_COMM_STATE_CSW;
CPU_CRITICAL_EXIT();
}
/*
**********************************************************************************************************
* USBD_MSC_TxCSW()
*
* Description : Send status CSW to the host.
*
* Argument(s) : class_nbr MSC instance number.
*
* p_err Pointer to variable that will receive the return error code from this function :
*
* USBD_ERR_NONE, if device sends status successfully.
*
* ------ RETURNED BY USBD_BulkTx() : ------
* USBD_ERR_DEV_INVALID_NBR Invalid device number.
* USBD_ERR_EP_INVALID_NBR Invalid endpoint number.
* USBD_ERR_DEV_INVALID_STATE Bulk transfer can ONLY be used after the
* device is in configured state.
* USBD_ERR_EP_INVALID_TYPE Invalid endpoint type.
*
* Return(s) : None.
*
* Note(s) : None.
**********************************************************************************************************
*/
static void USBD_MSC_TxCSW (USBD_MSC_CTRL *p_ctrl,
USBD_MSC_COMM *p_comm,
USBD_ERR *p_err)
{
USBD_ERR stall_err;
CPU_SR_ALLOC();
CPU_CRITICAL_ENTER();
p_comm->CSW.dCSWSignature = USBD_MSC_SIG_CSW; /* Set CSW signature and rcvd tag from CBW. */
p_comm->CSW.dCSWTag = p_comm->CBW.dCBWTag;
USBD_MSC_CSW_Fmt( &p_comm->CSW, /* Wr CSW to raw buf. */
(void *) p_ctrl->CSW_BufPtr);
CPU_CRITICAL_EXIT();
/* Tx CSW to host through bulk-IN pipe. */
(void)USBD_BulkTx(p_ctrl->DevNbr,
p_comm->DataBulkInEpAddr,
p_ctrl->CSW_BufPtr,
USBD_MSC_LEN_CSW,
0,
DEF_NO,
p_err);
if (*p_err != USBD_ERR_NONE){ /* Enter reset recovery state. */
CPU_CRITICAL_ENTER();
p_comm->NextCommState = USBD_MSC_COMM_STATE_BULK_IN_STALL;
CPU_CRITICAL_EXIT();
USBD_DBG_MSC_MSG("MSC: TxCSW, Stall IN");
USBD_EP_Stall(p_ctrl->DevNbr, p_comm->DataBulkInEpAddr, DEF_SET, &stall_err);
} else {
if (p_comm->CSW.bCSWStatus == USBD_MSC_BCSWSTATUS_PHASE_ERROR){
CPU_CRITICAL_ENTER();
p_comm->NextCommState = USBD_MSC_COMM_STATE_RESET_RECOVERY;
CPU_CRITICAL_EXIT();
} else {
CPU_CRITICAL_ENTER(); /* Enter rx CBW state. */
p_comm->NextCommState = USBD_MSC_COMM_STATE_CBW;
CPU_CRITICAL_EXIT();
}
}
}
/*
**********************************************************************************************************
* USBD_MSC_RxCBW()
*
* Description : Receive CBW from the host.
*
* Argument(s) : class_nbr MSC instance number.
*
* p_err Pointer to variable that will receive the return error code from this function :
* USBD_ERR_NONE CBW successfully received from host
* ------ RETURNED BY USBD_BulkRx() : ------
* USBD_ERR_DEV_INVALID_NBR Invalid device number.
* USBD_ERR_EP_INVALID_NBR Invalid endpoint number.
* USBD_ERR_DEV_INVALID_STATE Bulk transfer can ONLY be used after the
* device is in configured state.
* USBD_ERR_EP_INVALID_TYPE Invalid endpoint type.
*
* Return(s) : None.
*
* Note(s) : None.
**********************************************************************************************************
*/
static void USBD_MSC_RxCBW (USBD_MSC_CTRL *p_ctrl,
USBD_MSC_COMM *p_comm,
USBD_ERR *p_err)
{
USBD_ERR err;
USBD_ERR stall_err;
CPU_INT32U xfer_len;
CPU_SR_ALLOC();
xfer_len = USBD_BulkRx (p_ctrl->DevNbr, /*Rx CBW and returns xfer_len upon success. */
p_comm->DataBulkOutEpAddr,
p_ctrl->CBW_BufPtr,
USBD_MSC_LEN_CBW,
0,
p_err);
USBD_DBG_MSC_ARG("MSC: RxCBW", xfer_len);
switch (*p_err) {
case USBD_ERR_NONE:
break;
case USBD_ERR_DEV_INVALID_NBR:
case USBD_ERR_DEV_INVALID_STATE:
case USBD_ERR_OS_ABORT:
case USBD_ERR_EP_INVALID_ADDR:
case USBD_ERR_EP_INVALID_STATE:
case USBD_ERR_EP_INVALID_TYPE:
CPU_CRITICAL_ENTER();
if (p_ctrl->State == USBD_MSC_STATE_CFG){
p_comm->NextCommState = USBD_MSC_COMM_STATE_CBW;
}
CPU_CRITICAL_EXIT();
USBD_DBG_MSC_MSG("MSC: RxCBW, OS Abort");
break;
case USBD_ERR_OS_TIMEOUT:
CPU_CRITICAL_ENTER();
if (p_ctrl->State == USBD_MSC_STATE_CFG){
p_comm->NextCommState = USBD_MSC_COMM_STATE_CBW;
}
CPU_CRITICAL_EXIT();
USBD_DBG_MSC_MSG("MSC: RxCBW, OS Timeout");
break;
case USBD_ERR_EP_ABORT:
case USBD_ERR_RX:
case USBD_ERR_DRV_BUF_OVERFLOW:
CPU_CRITICAL_ENTER();
p_comm->NextCommState = USBD_MSC_COMM_STATE_RESET_RECOVERY_BULK_IN_STALL;
CPU_CRITICAL_EXIT();
USBD_DBG_MSC_MSG("MSC: RxCBW, Stall IN/OUT");
USBD_EP_Stall(p_ctrl->DevNbr, p_comm->DataBulkInEpAddr, DEF_SET, &stall_err);
USBD_EP_Stall(p_ctrl->DevNbr, p_comm->DataBulkOutEpAddr, DEF_SET, &stall_err);
break;
case USBD_ERR_OS_FAIL:
case USBD_ERR_NULL_PTR:
case USBD_ERR_EP_IO_PENDING:
case USBD_ERR_EP_QUEUING:
case USBD_ERR_DRV_INVALID_PKT:
default:
CPU_CRITICAL_ENTER();
p_comm->NextCommState = USBD_MSC_COMM_STATE_BULK_OUT_STALL;
CPU_CRITICAL_EXIT();
USBD_DBG_MSC_MSG("MSC: RxCBW, Stall OUT");
USBD_EP_Stall(p_ctrl->DevNbr, p_comm->DataBulkOutEpAddr, DEF_SET, &stall_err);
break;
}
if (*p_err == USBD_ERR_NONE) {
/* Verify that CBW is valid and meaningful. */
USBD_MSC_CBW_Verify(p_ctrl, p_comm, p_ctrl->CBW_BufPtr, xfer_len, &err);
if (err != USBD_ERR_NONE) { /* Enter reset recovery state. */
CPU_CRITICAL_ENTER();
p_comm->NextCommState = USBD_MSC_COMM_STATE_RESET_RECOVERY_BULK_IN_STALL;
CPU_CRITICAL_EXIT();
USBD_DBG_MSC_MSG("MSC: RxCBW, Stall IN/OUT");
USBD_EP_Stall(p_ctrl->DevNbr, p_comm->DataBulkInEpAddr, DEF_SET, &stall_err);
USBD_EP_Stall(p_ctrl->DevNbr, p_comm->DataBulkOutEpAddr, DEF_SET, &stall_err);
return;
}
CPU_CRITICAL_ENTER();
/* Host expected transfer length. */
p_comm->CSW.dCSWDataResidue = p_comm->CBW.dCBWDataTransferLength;
p_comm->BytesToXfer = 0;
p_comm->NextCommState = USBD_MSC_COMM_STATE_DATA; /* Enter data transport state. */
CPU_CRITICAL_EXIT();
}
}
/*
*********************************************************************************************************
* USBD_MSC_LunClr()
*
* Description : Initialize all logical unit fields.
*
* Argument(s) : p_lun Pointer to Logical Unit information.
*
* Return(s) : None.
*
* Note(s) : None.
*********************************************************************************************************
*/
static void USBD_MSC_LunClr (USBD_MSC_LUN_CTRL *p_lun)
{
p_lun->LunNbr = 0;
p_lun->LunInfo.ProdRevisionLevel = 0;
p_lun->LunInfo.ReadOnly = DEF_FALSE;
p_lun->NbrBlocks = 0;
p_lun->BlockSize = 0;
Mem_Clr((void *)p_lun->LunInfo.VendorId,
(CPU_SIZE_T)8);
Mem_Clr((void *)p_lun->LunInfo.ProdId,
(CPU_SIZE_T)16);
p_lun->LunArgPtr = (void *)0;
}
/*
*********************************************************************************************************
* USBD_MSC_CBW_Parse()
*
* Description : Parse CBW into CBW structure.
*
* Argument(s) : p_cbw Variable that will hold the CBW parsed in this function.
*
* p_buf_src Pointer to buffer that holds CBW.
*
* Return(s) : None.
*
* Note(s) : None.
*********************************************************************************************************
*/
static void USBD_MSC_CBW_Parse (USBD_MSC_CBW *p_cbw,
void *p_buf_src)
{
CPU_INT08U *p_buf_src_08;
p_buf_src_08 = (CPU_INT08U *)p_buf_src;
p_cbw->dCBWSignature = MEM_VAL_GET_INT32U_LITTLE((void *)((CPU_INT08U *)p_buf_src_08 + 0u));
p_cbw->dCBWTag = MEM_VAL_GET_INT32U_LITTLE((void *)((CPU_INT08U *)p_buf_src_08 + 4u));
p_cbw->dCBWDataTransferLength = MEM_VAL_GET_INT32U_LITTLE((void *)((CPU_INT08U *)p_buf_src_08 + 8u));
p_cbw->bmCBWFlags = p_buf_src_08[12];
p_cbw->bCBWLUN = p_buf_src_08[13];
p_cbw->bCBWCBLength = p_buf_src_08[14];
Mem_Copy((void *)&p_cbw->CBWCB[0],
(void *)&p_buf_src_08[15],
(CPU_SIZE_T) 16);
}
/*
*********************************************************************************************************
* USBD_MSC_CSW_Fmt()
*
* Description : Format CSW from CSW structure.
*
* Argument(s) : p_csw Variable holds the CSW information.
*
* p_buf_dest Pointer to buffer that will hold CSW.
*
* Return(s) : None.
*
* Note(s) : None.
*********************************************************************************************************
*/
static void USBD_MSC_CSW_Fmt (const USBD_MSC_CSW *p_csw,
void *p_buf_dest)
{
CPU_INT08U *p_buf_dest_08;
p_buf_dest_08 = (CPU_INT08U *)p_buf_dest;
MEM_VAL_SET_INT32U_LITTLE((void *)((CPU_INT08U *)p_buf_dest_08 + 0u), p_csw->dCSWSignature);
MEM_VAL_SET_INT32U_LITTLE((void *)((CPU_INT08U *)p_buf_dest_08 + 4u), p_csw->dCSWTag);
MEM_VAL_SET_INT32U_LITTLE((void *)((CPU_INT08U *)p_buf_dest_08 + 8u), p_csw->dCSWDataResidue);
p_buf_dest_08[12] = p_csw->bCSWStatus;
}