Dependencies: ChaNFSSD mbed ChaNFS
Diff: msc_bot.cpp
- Revision:
- 0:02c293160df3
diff -r 000000000000 -r 02c293160df3 msc_bot.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/msc_bot.cpp Wed Nov 09 17:30:56 2011 +0000 @@ -0,0 +1,400 @@ +/* + LPCUSB, an USB device driver for LPC microcontrollers + Copyright (C) 2006 Bertrik Sikken (bertrik@sikken.nl) + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/** @file + + Bulk-only-transfer layer for mass storage. + + This layers sits between the generic USB layers and the SCSI layer + and performs data transfer according to the BOT protocol. +*/ + +//#define DEBUG +#include "dbg.h" +#include "mbed.h" +#include <string.h> + +#include "msc_bot.h" +#include "msc_scsi.h" +#include "USBMSC.h" + +extern USBMSC usbdev; + +/** Command block wrapper structure */ +struct TCBW { + uint32_t dwCBWSignature; + uint32_t dwCBWTag; + uint32_t dwCBWDataTransferLength; + uint8_t bmCBWFlags; + uint8_t bCBWLun; + uint8_t bCBWCBLength; + uint8_t CBWCB[16]; + uint8_t dummy[MAX_PACKET_SIZE_EPBULK - 31]; +} __attribute__((packed)); + +/** Command status wrapper structure */ +struct TCSW { + uint32_t dwCSWSignature; + uint32_t dwCSWTag; + uint32_t dwCSWDataResidue; + uint8_t bmCSWStatus; + uint8_t dummy[MAX_PACKET_SIZE_EPBULK - 13]; +} __attribute__((packed)); + +/** States of BOT state machine */ +typedef enum { + eCBW, + eDataOut, + eDataIn, + eCSW, + eStalled +} EBotState; + + +#define CBW_SIGNATURE 0x43425355 /**< magic word in CBW */ +#define CSW_SIGNATURE 0x53425355 /**< magic word in CSW */ + +#define STATUS_PASSED 0x00 /**< successful transfer */ +#define STATUS_FAILED 0x01 /**< failed transfer */ +#define STATUS_PHASE_ERR 0x02 /**< conflict between host and device */ + +static uint32_t dwTransferSize; /**< total size of data transfer */ +static uint32_t dwOffset; /**< offset in current data transfer */ + +static TCBW CBW; +static TCSW CSW; + +static EBotState eState; + +static uint8_t *pbData; + + + +/** + Resets the BOT state machine + */ +void MSCBotReset(void) +{ + DBG("BOT reset in state %d\n", eState); + // reset BOT state + eState = eCBW; + // reset SCSI + SCSIReset(); +} + + +/** + Prepares a CSW, to be sent on next bulk-IN interrupt + + @param [in] bStatus CSW status + */ +static void SendCSW(uint8_t bStatus) +{ + int iResidue; + + iResidue = CBW.dwCBWDataTransferLength - dwTransferSize; + + // construct CSW + CSW.dwCSWSignature = CSW_SIGNATURE; + CSW.dwCSWTag = CBW.dwCBWTag; + CSW.dwCSWDataResidue = MAX(iResidue, 0); + CSW.bmCSWStatus = bStatus; + + DBG("CSW: status=%x, residue=%d\n", bStatus, CSW.dwCSWDataResidue); + + // next state + eState = eCSW; +} + + +/** + Checks if CBW is valid and meaningful + + @param [in] pCBW Command block wrapper + @param [in] iLen Length of CBW + + @return true if valid and meaningful + */ +static bool CheckCBW(TCBW *pCBW, int iLen) +{ + // CBW valid? + if (iLen != 31) { + DBG("Invalid length (%d)\n", iLen); + return false; + } + if (pCBW->dwCBWSignature != CBW_SIGNATURE) { + DBG("Invalid signature %x\n", pCBW->dwCBWSignature); + return false; + } + + // CBW meaningful? + if (pCBW->bCBWLun != 0) { + DBG("Invalid LUN %d\n", pCBW->bCBWLun); + return false; + } + if ((pCBW->bCBWCBLength < 1) || (pCBW->bCBWCBLength > 16)) { + DBG("Invalid CB len %d\n", pCBW->bCBWCBLength); + return false; + } + return true; +} + + +/************************************************************************* + BOTStall + ======== + Local function to stall ongoing transfer + + Which endpoint to stall is determined by looking at the transfer + direction intended by the host. + +**************************************************************************/ +static void BOTStall(void) +{ + if ((CBW.bmCBWFlags & 0x80) || (CBW.dwCBWDataTransferLength == 0)) { + // stall data-in or CSW + usbdev.stallEndpoint(EPBULK_IN); + } + else { + // stall data-out + usbdev.stallEndpoint(EPBULK_OUT); + } +} + + +/************************************************************************* + HandleDataIn + ============ + Handles data from device-to-host + +**************************************************************************/ +static void HandleDataIn(int io) +{ + int iChunk; + // process data for host in SCSI layer + pbData = SCSIHandleData(CBW.CBWCB, CBW.bCBWCBLength, pbData, dwOffset); + if (pbData == NULL) { + BOTStall(); + SendCSW(STATUS_FAILED); + return; + } + + // send data to host? + if (dwOffset < dwTransferSize) { + iChunk = MIN(64, dwTransferSize - dwOffset); + //USBHwEPWrite(MSC_BULK_IN_EP, pbData, iChunk); + usbdev.writeNB(EPBULK_IN, pbData, iChunk, MAX_PACKET_SIZE_EPBULK); + dwOffset += iChunk; + } + + // are we done now? + if (dwOffset == dwTransferSize) { + if (dwOffset != CBW.dwCBWDataTransferLength) { + // stall pipe + DBG("stalling DIN"); + BOTStall(); + } + // done + SendCSW(STATUS_PASSED); + } +} + + +/************************************************************************* + HandleDataOut + ============= + Handles data from host-to-device + +**************************************************************************/ +static void HandleDataOut(void) +{ + int iChunk; + + if (dwOffset < dwTransferSize) { + // get data from host +// iChunk = USBHwEPRead(MSC_BULK_OUT_EP, pbData, dwTransferSize - dwOffset); + usbdev.read(EPBULK_OUT, pbData, (uint16_t*)&iChunk, MAX_PACKET_SIZE_EPBULK); + // process data in SCSI layer + pbData = SCSIHandleData(CBW.CBWCB, CBW.bCBWCBLength, pbData, dwOffset); + if (pbData == NULL) { + BOTStall(); + SendCSW(STATUS_FAILED); + return; + } + dwOffset += iChunk; + } + + // are we done now? + if (dwOffset == dwTransferSize) { + if (dwOffset != CBW.dwCBWDataTransferLength) { + // stall pipe + DBG("stalling DOUT"); + BOTStall(); + } + SendCSW(STATUS_PASSED); + } +} + + +/** + Handles the BOT bulk OUT endpoint + + @param [in] bEP Endpoint number + @param [in] bEPStatus Endpoint status (indicates NAK, STALL, etc) + + */ +void MSCBotBulkOut() +{ + int iLen, iChunk; + bool fHostIn, fDevIn; + + switch (eState) { + + case eCBW: + //iLen = USBHwEPRead(bEP, (U8 *)&CBW, sizeof(CBW)); + usbdev.read(EPBULK_OUT, (uint8_t *)&CBW, (uint16_t*)&iLen, MAX_PACKET_SIZE_EPBULK); + + // check if we got a good CBW + if (!CheckCBW(&CBW, iLen)) { + // see 6.6.1 + usbdev.stallEndpoint(EPBULK_IN); + usbdev.stallEndpoint(EPBULK_OUT); + eState = eStalled; + break; + } + + DBG("CBW: len=%d, flags=%x, cmd=%x, cmdlen=%d\n", + CBW.dwCBWDataTransferLength, CBW.bmCBWFlags, CBW.CBWCB[0], CBW.bCBWCBLength); + + dwOffset = 0; + dwTransferSize = 0; + fHostIn = ((CBW.bmCBWFlags & 0x80) != 0); + + // verify request + pbData = SCSIHandleCmd(CBW.CBWCB, CBW.bCBWCBLength, &iLen, &fDevIn); + if (pbData == NULL) { + // unknown command + BOTStall(); + SendCSW(STATUS_FAILED); + break; + } + + // rule: if device and host disagree on direction, send CSW with status 2 + if ((iLen > 0) && + ((fHostIn && !fDevIn) || + (!fHostIn && fDevIn))) { + DBG("Host and device disagree on direction\n"); + BOTStall(); + SendCSW(STATUS_PHASE_ERR); + break; + } + + // rule: if D > H, send CSW with status 2 + if (iLen > CBW.dwCBWDataTransferLength) { + DBG("Negative residue\n"); + BOTStall(); + SendCSW(STATUS_PHASE_ERR); + break; + } + + dwTransferSize = iLen; + if ((dwTransferSize == 0) || fDevIn) { + // data from device-to-host + eState = eDataIn; + HandleDataIn(0); + } + else { + // data from host-to-device + eState = eDataOut; + } + break; + + case eDataOut: + HandleDataOut(); + break; + + case eDataIn: + case eCSW: +// iChunk = USBHwEPRead(bEP, NULL, 0); + uint8_t dummy[64]; + int idummy; + usbdev.read(EPBULK_OUT, dummy, (uint16_t*)&idummy, MAX_PACKET_SIZE_EPBULK); + DBG("Phase error in state %d, %d bytes\n", eState, iChunk); + eState = eCBW; + break; + + case eStalled: + // keep stalling + usbdev.stallEndpoint(EPBULK_OUT); + break; + + default: + DBG("Invalid state %d\n", eState); + break; + } +} + + +/** + Handles the BOT bulk IN endpoint + + @param [in] bEP Endpoint number + @param [in] bEPStatus Endpoint status (indicates NAK, STALL, etc) + + */ +void MSCBotBulkIn() +{ + DBG("MSCBotBulkIn: %d\r\n", eState); + switch (eState) { + + case eCBW: + case eDataOut: + // ignore possibly old ACKs + break; + + case eDataIn: + HandleDataIn(1); + break; + + case eCSW: + // wait for an IN token, then send the CSW + //USBHwEPWrite(MSC_BULK_IN_EP, (uint8_t *)&CSW, 13); + usbdev.writeNB(EPBULK_IN, (uint8_t *)&CSW, 13, MAX_PACKET_SIZE_EPBULK); + eState = eCBW; + break; + + case eStalled: + // keep stalling + usbdev.stallEndpoint(EPBULK_IN); + break; + + default: + DBG("Invalid state %d\n", eState); + break; + } +} +