/****************************************************************************** * * Copyright (C) 2010 - 2014 Xilinx, Inc. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * Use of the Software is limited solely to applications: * (a) running on a Xilinx device, or * (b) that interact with a Xilinx device through a bus or interconnect. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * XILINX CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * Except as contained in this notice, the name of the Xilinx shall not be used * in advertising or otherwise to promote the sale, use or other dealings in * this Software without prior written authorization from Xilinx. * ******************************************************************************/ /*****************************************************************************/ /** * @file xusbps_class_storage.c * * This file contains the implementation of the storage class code for the * example. * *
 * MODIFICATION HISTORY:
 *
 * Ver   Who  Date     Changes
 * ----- ---- -------- ---------------------------------------------------------
 * 1.00a wgr  10/10/10 First release
 * 2.1   kpc  4/28/14  Align DMA buffers to cache line boundary
 *
******************************************************************************/ /***************************** Include Files *********************************/ #include #include "xusbps.h" /* USB controller driver */ #include "xusbps_ch9_storage.h" #include "xusbps_ch9.h" #include "xusbps_class_storage.h" #include "xil_printf.h" /* #define CLASS_STORAGE_DEBUG */ #ifdef CLASS_STORAGE_DEBUG #define printf xil_printf #endif /************************** Constant Definitions *****************************/ /************************** Function Prototypes ******************************/ /************************** Variable Definitions *****************************/ /* Pre-manufactured response to the SCSI Inquirey command. */ const static SCSI_INQUIRY scsiInquiry ALIGNMENT_CACHELINE = { 0x00, 0x80, 0x00, 0x01, 0x1f, 0x00, 0x00, 0x00, {"Xilinx "}, /* Vendor ID: must be 8 characters long. */ {"PS USB VirtDisk"}, /* Product ID: must be 16 characters long. */ {"1.00"} /* Revision: must be 4 characters long. */ }; static u8 MaxLUN ALIGNMENT_CACHELINE = 0; /* Buffer for virtual flash disk space. */ static u8 VirtFlash[VFLASH_SIZE] ALIGNMENT_CACHELINE; static USB_CBW lastCBW ALIGNMENT_CACHELINE; /* Local transmit buffer for simple replies. */ static u8 txBuffer[128] ALIGNMENT_CACHELINE; /*****************************************************************************/ /** * This function handles Reduced Block Command (RBC) requests from the host. * * @param InstancePtr is a pointer to XUsbPs instance of the controller. * @param EpNum is the number of the endpoint on which the RBC was received. * @param BufferPtr is the data buffer containing the RBC or data. * @param BufferLen is the length of the data buffer. * * @return None. * * @note None. * ******************************************************************************/ void XUsbPs_HandleStorageReq(XUsbPs *InstancePtr, u8 EpNum, u8 *BufferPtr, u32 BufferLen) { USB_CBW *CBW; u32 Offset; static u8 *VirtFlashWritePointer = VirtFlash; /* Static variables used for data transfers.*/ static int rxBytesLeft; /* Current SCSI machine state. */ static int phase = USB_EP_STATE_COMMAND; /* COMMAND phase. */ if (USB_EP_STATE_COMMAND == phase) { CBW = (USB_CBW *) BufferPtr; switch (CBW->CBWCB[0]) { case USB_RBC_INQUIRY: #ifdef CLASS_STORAGE_DEBUG printf("SCSI: INQUIRY\n"); #endif XUsbPs_EpBufferSend(InstancePtr, 1, (void *) &scsiInquiry, sizeof(scsiInquiry)); /* Send Success Status */ CBW->dCBWSignature = 0x55534253; CBW->dCBWDataTransferLength = 0; CBW->bmCBWFlags = 0; XUsbPs_EpBufferSend(InstancePtr, 1, (void *) CBW, 13); break; case USB_UFI_GET_CAP_LIST: { SCSI_CAP_LIST *CapList; CapList = (SCSI_CAP_LIST *) txBuffer; #ifdef CLASS_STORAGE_DEBUG printf("SCSI: CAPLIST\n"); #endif CapList->listLength = 8; CapList->descCode = 3; CapList->numBlocks = htonl(VFLASH_NUM_BLOCKS); CapList->blockLength = htons(VFLASH_BLOCK_SIZE); XUsbPs_EpBufferSend(InstancePtr, 1, txBuffer, sizeof(SCSI_CAP_LIST)); /* Send Success Status */ CBW->dCBWSignature = 0x55534253; CBW->dCBWDataTransferLength = be2le(be2le(CBW->dCBWDataTransferLength) - sizeof(SCSI_CAP_LIST)); CBW->bmCBWFlags = 0; XUsbPs_EpBufferSend(InstancePtr, 1, (u8 *) CBW, 13); break; } case USB_RBC_READ_CAP: { SCSI_READ_CAPACITY *Cap; Cap = (SCSI_READ_CAPACITY *) txBuffer; #ifdef CLASS_STORAGE_DEBUG printf("SCSI: READCAP\n"); #endif Cap->numBlocks = htonl(VFLASH_NUM_BLOCKS - 1); Cap->blockSize = htonl(VFLASH_BLOCK_SIZE); XUsbPs_EpBufferSend(InstancePtr, 1, txBuffer, sizeof(SCSI_READ_CAPACITY)); /* Send Success Status */ CBW->dCBWSignature = 0x55534253; CBW->dCBWDataTransferLength = 0; CBW->bmCBWFlags = 0; XUsbPs_EpBufferSend(InstancePtr, 1, (u8 *) CBW, 13); break; } case USB_RBC_READ: Offset = htonl(((SCSI_READ_WRITE *) CBW->CBWCB)-> block) * VFLASH_BLOCK_SIZE; #ifdef CLASS_STORAGE_DEBUG printf("SCSI: READ Offset 0x%08x\n", (int) Offset); #endif XUsbPs_EpBufferSend(InstancePtr, 1, &VirtFlash[Offset], htons(((SCSI_READ_WRITE *) CBW->CBWCB)-> length) * VFLASH_BLOCK_SIZE); /* Send Success Status */ CBW->dCBWSignature = 0x55534253; CBW->dCBWDataTransferLength = 0; CBW->bmCBWFlags = 0; XUsbPs_EpBufferSend(InstancePtr, 1, (u8 *) CBW, 13); break; case USB_RBC_MODE_SENSE: #ifdef CLASS_STORAGE_DEBUG printf("SCSI: MODE SENSE\n"); #endif XUsbPs_EpBufferSend(InstancePtr, 1, (u8 *) "\003\000\000\000", 4); /* Send Success Status */ CBW->dCBWSignature = 0x55534253; CBW->dCBWDataTransferLength = be2le(be2le(CBW->dCBWDataTransferLength) - 4); CBW->bmCBWFlags = 0; XUsbPs_EpBufferSend(InstancePtr, 1, (u8 *) CBW, 13); break; case USB_RBC_TEST_UNIT_READY: case USB_RBC_MEDIUM_REMOVAL: case USB_RBC_VERIFY: #ifdef CLASS_STORAGE_DEBUG printf("SCSI: TEST UNIT READY\n"); #endif /* Send Success Status */ CBW->dCBWSignature = 0x55534253; CBW->dCBWDataTransferLength = 0; CBW->bmCBWFlags = 0; XUsbPs_EpBufferSend(InstancePtr, 1, (u8 *) CBW, 13); break; case USB_RBC_WRITE: Offset = htonl(((SCSI_READ_WRITE *) CBW->CBWCB)-> block) * VFLASH_BLOCK_SIZE; #ifdef CLASS_STORAGE_DEBUG printf("SCSI: WRITE Offset 0x%08x\n", (int) Offset); #endif VirtFlashWritePointer = &VirtFlash[Offset]; /* Save the CBW for the DATA and STATUS phases. */ lastCBW = *CBW; rxBytesLeft = htons(((SCSI_READ_WRITE *) CBW->CBWCB)->length) * VFLASH_BLOCK_SIZE; phase = USB_EP_STATE_DATA; break; case USB_RBC_STARTSTOP_UNIT: { u8 immed; immed = ((SCSI_START_STOP *) CBW->CBWCB)->immed; #ifdef CLASS_STORAGE_DEBUG printf("SCSI: START/STOP unit: immed %02x\n", immed); #endif /* If the immediate bit is 0 we are supposed to send * a success status. */ if (0 == (immed & 0x01)) { /* Send Success Status */ CBW->dCBWSignature = 0x55534253; CBW->dCBWDataTransferLength = 0; CBW->bmCBWFlags = 0; XUsbPs_EpBufferSend(InstancePtr, 1, (u8 *) CBW, 13); } break; } /* Commands that we do not support for this example. */ case 0x04: /* Format Unit */ case 0x15: /* Mode Select */ case 0x5e: /* Persistent Reserve In */ case 0x5f: /* Persistent Reserve Out */ case 0x17: /* Release */ case 0x03: /* Request Sense */ case 0x16: /* Reserve */ case 0x35: /* Sync Cache */ case 0x3b: /* Write Buffer */ #ifdef CLASS_STORAGE_DEBUG printf("SCSI: Got unhandled command %02x\n", CBW->CBWCB[0]); #endif default: break; } } /* DATA phase. */ else if (USB_EP_STATE_DATA == phase) { switch (lastCBW.CBWCB[0]) { case USB_RBC_WRITE: /* Copy the data we just read into the VirtFlash buffer. */ memcpy(VirtFlashWritePointer, BufferPtr, BufferLen); VirtFlashWritePointer += BufferLen; rxBytesLeft -= BufferLen; if (rxBytesLeft <= 0) { /* Send Success Status */ lastCBW.dCBWSignature = 0x55534253; lastCBW.dCBWDataTransferLength = 0; lastCBW.bmCBWFlags = 0; XUsbPs_EpBufferSend(InstancePtr, 1, (void *) &lastCBW, 13); phase = USB_EP_STATE_COMMAND; } break; } } } /*****************************************************************************/ /** * This function handles a Storage Class Setup request from the host. * * @param InstancePtr is a pointer to XUsbPs instance of the controller. * @param SetupData is the setup data structure containing the setup * request. * * @return None. * * @note None. * ******************************************************************************/ void XUsbPs_ClassReq(XUsbPs *InstancePtr, XUsbPs_SetupData *SetupData) { Xil_AssertVoid(InstancePtr != NULL); Xil_AssertVoid(SetupData != NULL); switch (SetupData->bRequest) { case XUSBPS_CLASSREQ_MASS_STORAGE_RESET: XUsbPs_EpBufferSend(InstancePtr, 0, NULL, 0); break; case XUSBPS_CLASSREQ_GET_MAX_LUN: XUsbPs_EpBufferSend(InstancePtr, 0, &MaxLUN, 1); break; default: XUsbPs_EpStall(InstancePtr, 0, XUSBPS_EP_DIRECTION_IN); break; } }