EA BaseBoard, playing wav, PC see\'s SD-card through USB port.

Dependencies:   mbed

Revision:
0:fef366d2ed20
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/msc_scsi.cpp	Tue Nov 22 05:45:58 2011 +0000
@@ -0,0 +1,370 @@
+/*
+    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
+
+    This is the SCSI layer of the USB mass storage application example.
+    This layer depends directly on the blockdev layer.
+    
+    Windows peculiarities:
+    * Size of REQUEST SENSE CDB is 12 bytes instead of expected 6
+    * Windows requires VERIFY(10) command to do a format.
+      This command is not mandatory in the SBC/SBC-2 specification.
+*/
+/*
+ *  Modified for mbed NXP LPC1768 & LPCXpresso board
+ *  original    LPC214x USB stack beta by bertrik
+ *              target-20070727.tar.gr (119.5KB)
+ *              http://sourceforge.net/projects/lpcusb/
+ *  By Kenji Arai / JH1PJL on September 25th, 2010
+ *      October 3rd, 2010
+ */
+#define FATFS
+
+//#define DEBUG
+#include "dbg.h"
+#include "mbed.h"
+#include <string.h>        // memcpy
+
+#include "blockdev.h"
+#include "msc_scsi.h"
+
+//#include "SDFileSystem.h"
+//extern SDFileSystem sd;
+
+#define BLOCKSIZE        512
+
+// SBC2 mandatory SCSI commands
+#define    SCSI_CMD_TEST_UNIT_READY    0x00
+#define SCSI_CMD_REQUEST_SENSE        0x03
+#define SCSI_CMD_FORMAT_UNIT        0x04
+#define SCSI_CMD_READ_6                0x08    /* not implemented yet */
+#define SCSI_CMD_INQUIRY            0x12
+#define SCSI_CMD_SEND_DIAGNOSTIC    0x1D    /* not implemented yet */
+#define SCSI_CMD_READ_CAPACITY_10    0x25
+#define SCSI_CMD_READ_10            0x28
+#define SCSI_CMD_REPORT_LUNS        0xA0    /* not implemented yet */
+
+// SBC2 optional SCSI commands
+#define SCSI_CMD_WRITE_6            0x0A    /* not implemented yet */
+#define SCSI_CMD_WRITE_10            0x2A
+#define SCSI_CMD_VERIFY_10            0x2F    /* required for windows format */
+
+// sense codes
+#define WRITE_ERROR                0x030C00
+#define READ_ERROR                0x031100
+#define INVALID_CMD_OPCODE        0x052000
+#define INVALID_FIELD_IN_CDB    0x052400
+
+//    Sense code, which is set on error conditions
+static uint32_t            dwSense;    // hex: 00aabbcc, where aa=KEY, bb=ASC, cc=ASCQ
+
+static const uint8_t        abInquiry[] = {
+    0x00,        // PDT = direct-access device
+    0x80,        // removeable medium bit = set
+    0x05,        // version = complies to SPC3
+    0x02,        // response data format = SPC3
+    0x1F,        // additional length
+    0x00,
+    0x00,
+    0x00,
+    'L','P','C','U','S','B',' ',' ',    // vendor
+    'M','a','s','s',' ','s','t','o',    // product
+    'r','a','g','e',' ',' ',' ',' ',
+    '0','.','1',' '                        // revision
+};
+
+//    Data for "request sense" command. The 0xFF are filled in later
+static const uint8_t abSense[] = { 0x70, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x0A, 
+                              0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00,
+                              0x00, 0x00 };
+
+//    Buffer for holding one block of disk data
+static uint8_t abBlockBuf[512];
+
+
+struct TCDB6 {
+    uint8_t        bOperationCode;
+    uint8_t        abLBA[3];
+    uint8_t        bLength;
+    uint8_t        bControl;
+} __attribute__((packed));
+
+
+/*************************************************************************
+    SCSIReset
+    =========
+        Resets any SCSI state
+        
+**************************************************************************/
+void SCSIReset(void)
+{
+    dwSense = 0;
+}
+
+
+/*************************************************************************
+    SCSIHandleCmd
+    =============
+        Verifies a SCSI CDB and indicates the direction and amount of data
+        that the device wants to transfer.
+        
+    If this call fails, a sense code is set in dwSense.
+
+    IN        pbCDB        Command data block
+            iCDBLen        Command data block len
+    OUT        *piRspLen    Length of intended response data:
+            *pfDevIn    true if data is transferred from device-to-host
+    
+    Returns a pointer to the data exchange buffer if successful,
+    return NULL otherwise.
+**************************************************************************/
+uint8_t * SCSIHandleCmd(uint8_t *pbCDB, uint8_t iCDBLen, int *piRspLen, bool *pfDevIn)
+{
+    static const uint8_t aiCDBLen[] = {6, 10, 10, 0, 16, 12, 0, 0};
+    int        i;
+    TCDB6    *pCDB;
+    uint32_t        dwLen, dwLBA;
+    uint8_t        bGroupCode;
+    
+    pCDB = (TCDB6 *)pbCDB;
+    
+    // default direction is from device to host
+    *pfDevIn = true;
+    
+    // check CDB length
+    bGroupCode = (pCDB->bOperationCode >> 5) & 0x7;
+    if (iCDBLen < aiCDBLen[bGroupCode]) {
+        DBG("Invalid CBD len (expected %d)!\n", aiCDBLen[bGroupCode]);
+        return NULL;
+    }
+
+    switch (pCDB->bOperationCode) {
+
+    // test unit ready (6)
+    case SCSI_CMD_TEST_UNIT_READY:
+        DBG("TEST UNIT READY\n");
+        *piRspLen = 0;
+        break;
+    
+    // request sense (6)
+    case SCSI_CMD_REQUEST_SENSE:
+        DBG("REQUEST SENSE (%06X)\n", dwSense);
+        // check params
+        *piRspLen = MIN(18, pCDB->bLength);
+        break;
+    
+    case SCSI_CMD_FORMAT_UNIT:
+        DBG("FORMAT UNIT %02X\n", pbCDB[1]);
+        *piRspLen = 0;
+        break;
+    
+    // inquiry (6)
+    case SCSI_CMD_INQUIRY:
+        DBG("INQUIRY\n");
+        // see SPC3r23, 4.3.4.6
+        *piRspLen = MIN(36, pCDB->bLength);
+        break;
+        
+    // read capacity (10)
+    case SCSI_CMD_READ_CAPACITY_10:
+        DBG("READ CAPACITY\n");
+        *piRspLen = 8;
+        break;
+        
+    // read (10)
+    case SCSI_CMD_READ_10:
+        dwLBA = (pbCDB[2] << 24) | (pbCDB[3] << 16) | (pbCDB[4] << 8) | (pbCDB[5]);
+        dwLen = (pbCDB[7] << 8) | pbCDB[8];
+        DBG("READ10, LBA=%d, len=%d\n", dwLBA, dwLen);
+        *piRspLen = dwLen * BLOCKSIZE;
+        break;
+
+    // write (10)
+    case SCSI_CMD_WRITE_10:
+        dwLBA = (pbCDB[2] << 24) | (pbCDB[3] << 16) | (pbCDB[4] << 8) | (pbCDB[5]);
+        dwLen = (pbCDB[7] << 8) | pbCDB[8];
+        DBG("WRITE10, LBA=%d, len=%d\n", dwLBA, dwLen);
+        *piRspLen = dwLen * BLOCKSIZE;
+        *pfDevIn = false;
+        break;
+
+    case SCSI_CMD_VERIFY_10:
+        DBG("VERIFY10\n");
+        if ((pbCDB[1] & (1 << 1)) != 0) {
+            // we don't support BYTCHK
+            DBG("BYTCHK not supported\n");
+            return NULL;
+        }
+        *piRspLen = 0;
+        break;
+    
+    default:
+        DBG("Unhandled SCSI: ");        
+        for (i = 0; i < iCDBLen; i++) {
+            DBG(" %02X", pbCDB[i]);
+        }
+        DBG("\n");
+        // unsupported command
+        dwSense = INVALID_CMD_OPCODE;
+        *piRspLen = 0;
+        return NULL;
+    }
+    
+    
+    return abBlockBuf;
+}
+
+
+/*************************************************************************
+    SCSIHandleData
+    ==============
+        Handles a block of SCSI data.
+        
+    IN        pbCDB        Command data block
+            iCDBLen        Command data block len
+    IN/OUT    pbData        Data buffer
+    IN        dwOffset    Offset in data
+    
+    Returns a pointer to the next data to be exchanged if successful,
+    returns NULL otherwise.
+**************************************************************************/
+uint8_t * SCSIHandleData(uint8_t *pbCDB, uint8_t iCDBLen, uint8_t *pbData, uint32_t dwOffset)
+{
+    TCDB6    *pCDB;
+    uint32_t        dwLBA;
+    uint32_t        dwBufPos, dwBlockNr;
+    uint32_t        dwDevSize, dwMaxBlock;
+    
+    pCDB = (TCDB6 *)pbCDB;
+    
+    switch (pCDB->bOperationCode) {
+
+    // test unit ready
+    case SCSI_CMD_TEST_UNIT_READY:
+        if (dwSense != 0) {
+            return NULL;
+        }
+        break;
+    
+    // request sense
+    case SCSI_CMD_REQUEST_SENSE:
+        memcpy(pbData, abSense, 18);
+        // fill in KEY/ASC/ASCQ
+        pbData[2] = (dwSense >> 16) & 0xFF;
+        pbData[12] = (dwSense >> 8) & 0xFF;
+        pbData[13] = (dwSense >> 0) & 0xFF;
+        // reset sense data
+        dwSense = 0;
+        break;
+    
+    case SCSI_CMD_FORMAT_UNIT:
+        // nothing to do, ignore this command
+        break;
+    
+    // inquiry
+    case SCSI_CMD_INQUIRY:
+        memcpy(pbData, abInquiry, sizeof(abInquiry));
+        break;
+        
+    // read capacity
+    case SCSI_CMD_READ_CAPACITY_10:
+#ifdef FATFS
+        // get size of drive (bytes)
+        dwDevSize = BlockDevGetSize();
+//        dwDevSize = sd.disk_sectors() * 512;
+#else
+        // get size of drive (bytes)
+        BlockDevGetSize(&dwDevSize);
+#endif
+        // calculate highest LBA
+        dwMaxBlock = (dwDevSize - 1) / 512;
+        
+        pbData[0] = (dwMaxBlock >> 24) & 0xFF;
+        pbData[1] = (dwMaxBlock >> 16) & 0xFF;
+        pbData[2] = (dwMaxBlock >> 8) & 0xFF;
+        pbData[3] = (dwMaxBlock >> 0) & 0xFF;
+        pbData[4] = (BLOCKSIZE >> 24) & 0xFF;
+        pbData[5] = (BLOCKSIZE >> 16) & 0xFF;
+        pbData[6] = (BLOCKSIZE >> 8) & 0xFF;
+        pbData[7] = (BLOCKSIZE >> 0) & 0xFF;
+        break;
+        
+    // read10
+    case SCSI_CMD_READ_10:
+        dwLBA = (pbCDB[2] << 24) | (pbCDB[3] << 16) | (pbCDB[4] << 8) | (pbCDB[5]);
+
+        // copy data from block buffer
+        dwBufPos = (dwOffset & (BLOCKSIZE - 1));
+        if (dwBufPos == 0) {
+            // read new block
+            dwBlockNr = dwLBA + (dwOffset / BLOCKSIZE);
+            DBG("R");
+            if (BlockDevRead(dwBlockNr, abBlockBuf)) {
+//            if (sd.disk_read((char*)abBlockBuf, dwBlockNr)) {
+                dwSense = READ_ERROR;
+                DBG("BlockDevRead failed\n");
+                return NULL;
+            }
+        }
+        // return pointer to data
+        return abBlockBuf + dwBufPos;
+
+    // write10
+    case SCSI_CMD_WRITE_10:
+        dwLBA = (pbCDB[2] << 24) | (pbCDB[3] << 16) | (pbCDB[4] << 8) | (pbCDB[5]);
+        
+        // copy data to block buffer
+        dwBufPos = ((dwOffset + 64) & (BLOCKSIZE - 1));
+        if (dwBufPos == 0) {
+            // write new block
+            dwBlockNr = dwLBA + (dwOffset / BLOCKSIZE);
+            DBG("W");
+            if (BlockDevWrite(dwBlockNr, abBlockBuf)) {
+//            if (sd.disk_write((char*)abBlockBuf, dwBlockNr)) {
+                dwSense = WRITE_ERROR;
+                DBG("BlockDevWrite failed\n");
+                return NULL;
+            }
+        }
+        // return pointer to next data
+        return abBlockBuf + dwBufPos;
+        
+    case SCSI_CMD_VERIFY_10:
+        // dummy implementation
+        break;
+        
+    default:
+        // unsupported command
+        dwSense = INVALID_CMD_OPCODE;
+        return NULL;
+    }
+    
+    // default: return pointer to start of block buffer
+    return abBlockBuf;
+}