/*---------------------------------------------------------------------------- * U S B - K e r n e l *---------------------------------------------------------------------------- * Name: mscuser.c * Purpose: Mass Storage Class Custom User Module * Version: V1.20 *---------------------------------------------------------------------------- * This software is supplied "AS IS" without any warranties, express, * implied or statutory, including but not limited to the implied * warranties of fitness for purpose, satisfactory quality and * noninfringement. Keil extends you a royalty-free right to reproduce * and distribute executable files created using this software for use * on NXP Semiconductors LPC microcontroller devices only. Nothing else * gives you the right to use this software. * * Copyright (c) 2009 Keil - An ARM Company. All rights reserved. *---------------------------------------------------------------------------- * History: * V1.20 Added SCSI_READ12, SCSI_WRITE12 * V1.00 Initial Version *---------------------------------------------------------------------------*/ #include "type.h" #include "usb.h" #include "msc.h" #include "usbcfg.h" #include "usbhw.h" #include "usbcore.h" #include "mscuser.h" #include "memory.h" uint8_t Memory[MSC_MemorySize]; /* MSC RAM */ uint32_t MemOK; /* Memory OK */ uint32_t Offset; /* R/W Offset */ uint32_t Length; /* R/W Length */ uint8_t BulkStage = MSC_BS_CBW; /* Bulk Stage */ uint8_t BulkBuf[MSC_MAX_PACKET] __attribute__ ((aligned (4))); /* Bulk In/Out Buffer */ uint8_t BulkLen; /* Bulk In/Out Length */ MSC_CBW CBW; /* Command Block Wrapper */ MSC_CSW CSW; /* Command Status Wrapper */ /* * Set Stall for MSC Endpoint * Parameters: EPNum: Endpoint Number * EPNum.0..3: Address * EPNum.7: Dir * Return Value: None */ void MSC_SetStallEP (uint32_t EPNum) { /* set EP halt status according stall status */ USB_SetStallEP (EPNum); USB_EndPointHalt |= (EPNum & 0x80) ? ((1 << 16) << (EPNum & 0x0F)) : (1 << EPNum); } /* * MSC Mass Storage Reset Request Callback * Called automatically on Mass Storage Reset Request * Parameters: None (global SetupPacket and EP0Buf) * Return Value: TRUE - Success, FALSE - Error */ uint32_t MSC_Reset (void) { USB_EndPointStall = 0x00000000; /* EP must stay stalled */ CSW.dSignature = 0; /* invalid signature */ BulkStage = MSC_BS_CBW; return (TRUE); } /* * MSC Get Max LUN Request Callback * Called automatically on Get Max LUN Request * Parameters: None (global SetupPacket and EP0Buf) * Return Value: TRUE - Success, FALSE - Error */ uint32_t MSC_GetMaxLUN (void) { EP0Buf[0] = 0; /* No LUN associated with this device */ return (TRUE); } /* * MSC Memory Read Callback * Called automatically on Memory Read Event * Parameters: None (global variables) * Return Value: None */ void MSC_MemoryRead (void) { uint32_t n; if (Length > MSC_MAX_PACKET) { n = MSC_MAX_PACKET; } else { n = Length; } if ((Offset + n) > MSC_MemorySize) { n = MSC_MemorySize - Offset; BulkStage = MSC_BS_DATA_IN_LAST_STALL; } USB_WriteEP (MSC_EP_IN, &Memory[Offset], n); Offset += n; Length -= n; CSW.dDataResidue -= n; if (Length == 0) { BulkStage = MSC_BS_DATA_IN_LAST; } if (BulkStage != MSC_BS_DATA_IN) { CSW.bStatus = CSW_CMD_PASSED; } } /* * MSC Memory Write Callback * Called automatically on Memory Write Event * Parameters: None (global variables) * Return Value: None */ void MSC_MemoryWrite (void) { uint32_t n; if ((Offset + BulkLen) > MSC_MemorySize) { BulkLen = MSC_MemorySize - Offset; BulkStage = MSC_BS_CSW; MSC_SetStallEP (MSC_EP_OUT); } for (n = 0; n < BulkLen; n++) { Memory[Offset + n] = BulkBuf[n]; } Offset += BulkLen; Length -= BulkLen; CSW.dDataResidue -= BulkLen; if ((Length == 0) || (BulkStage == MSC_BS_CSW)) { CSW.bStatus = CSW_CMD_PASSED; MSC_SetCSW (); } } /* * MSC Memory Verify Callback * Called automatically on Memory Verify Event * Parameters: None (global variables) * Return Value: None */ void MSC_MemoryVerify (void) { uint32_t n; if ((Offset + BulkLen) > MSC_MemorySize) { BulkLen = MSC_MemorySize - Offset; BulkStage = MSC_BS_CSW; MSC_SetStallEP (MSC_EP_OUT); } for (n = 0; n < BulkLen; n++) { if (Memory[Offset + n] != BulkBuf[n]) { MemOK = FALSE; break; } } Offset += BulkLen; Length -= BulkLen; CSW.dDataResidue -= BulkLen; if ((Length == 0) || (BulkStage == MSC_BS_CSW)) { CSW.bStatus = (MemOK) ? CSW_CMD_PASSED : CSW_CMD_FAILED; MSC_SetCSW (); } } /* * MSC SCSI Read/Write Setup Callback * Parameters: None (global variables) * Return Value: TRUE - Success, FALSE - Error */ uint32_t MSC_RWSetup (void) { uint32_t n; /* Logical Block Address of First Block */ n = (CBW.CB[2] << 24) | (CBW.CB[3] << 16) | (CBW.CB[4] << 8) | (CBW.CB[5] << 0); Offset = n * MSC_BlockSize; /* Number of Blocks to transfer */ switch (CBW.CB[0]) { case SCSI_READ10: case SCSI_WRITE10: case SCSI_VERIFY10: n = (CBW.CB[7] << 8) | (CBW.CB[8] << 0); break; case SCSI_READ12: case SCSI_WRITE12: n = (CBW.CB[6] << 24) | (CBW.CB[7] << 16) | (CBW.CB[8] << 8) | (CBW.CB[9] << 0); break; } Length = n * MSC_BlockSize; if (CBW.dDataLength == 0) { /* host requests no data */ CSW.bStatus = CSW_CMD_FAILED; MSC_SetCSW (); return (FALSE); } if (CBW.dDataLength != Length) { if ((CBW.bmFlags & 0x80) != 0) { /* stall appropriate EP */ MSC_SetStallEP (MSC_EP_IN); } else { MSC_SetStallEP (MSC_EP_OUT); } CSW.bStatus = CSW_CMD_FAILED; MSC_SetCSW (); return (FALSE); } return (TRUE); } /* * Check Data IN Format * Parameters: None (global variables) * Return Value: TRUE - Success, FALSE - Error */ uint32_t DataInFormat (void) { if (CBW.dDataLength == 0) { CSW.bStatus = CSW_PHASE_ERROR; MSC_SetCSW (); return (FALSE); } if ((CBW.bmFlags & 0x80) == 0) { MSC_SetStallEP (MSC_EP_OUT); CSW.bStatus = CSW_PHASE_ERROR; MSC_SetCSW (); return (FALSE); } return (TRUE); } /* * Perform Data IN Transfer * Parameters: None (global variables) * Return Value: TRUE - Success, FALSE - Error */ void DataInTransfer (void) { if (BulkLen >= CBW.dDataLength) BulkLen = CBW.dDataLength; BulkStage = MSC_BS_DATA_IN_LAST; USB_WriteEP (MSC_EP_IN, BulkBuf, BulkLen); CSW.dDataResidue -= BulkLen; CSW.bStatus = CSW_CMD_PASSED; } /* * MSC SCSI Test Unit Ready Callback * Parameters: None (global variables) * Return Value: None */ void MSC_TestUnitReady (void) { if (CBW.dDataLength != 0) { if ((CBW.bmFlags & 0x80) != 0) { MSC_SetStallEP (MSC_EP_IN); } else { MSC_SetStallEP (MSC_EP_OUT); } } CSW.bStatus = CSW_CMD_PASSED; MSC_SetCSW (); } /* * MSC SCSI Request Sense Callback * Parameters: None (global variables) * Return Value: None */ void MSC_RequestSense (void) { if (!DataInFormat ()) return; BulkBuf[0] = 0x70; /* Response Code */ BulkBuf[1] = 0x00; BulkBuf[2] = 0x02; /* Sense Key */ BulkBuf[3] = 0x00; BulkBuf[4] = 0x00; BulkBuf[5] = 0x00; BulkBuf[6] = 0x00; BulkBuf[7] = 0x0A; /* Additional Length */ BulkBuf[8] = 0x00; BulkBuf[9] = 0x00; BulkBuf[10] = 0x00; BulkBuf[11] = 0x00; BulkBuf[12] = 0x30; /* ASC */ BulkBuf[13] = 0x01; /* ASCQ */ BulkBuf[14] = 0x00; BulkBuf[15] = 0x00; BulkBuf[16] = 0x00; BulkBuf[17] = 0x00; BulkLen = 18; DataInTransfer (); } /* * MSC SCSI Inquiry Callback * Parameters: None (global variables) * Return Value: None */ void MSC_Inquiry (void) { if (!DataInFormat ()) return; BulkBuf[0] = 0x00; /* Direct Access Device */ BulkBuf[1] = 0x80; /* RMB = 1: Removable Medium */ BulkBuf[2] = 0x00; /* Version: No conformance claim to standard */ BulkBuf[3] = 0x01; BulkBuf[4] = 36 - 4; /* Additional Length */ BulkBuf[5] = 0x80; /* SCCS = 1: Storage Controller Component */ BulkBuf[6] = 0x00; BulkBuf[7] = 0x00; BulkBuf[8] = 'K'; /* Vendor Identification */ BulkBuf[9] = 'e'; BulkBuf[10] = 'i'; BulkBuf[11] = 'l'; BulkBuf[12] = ' '; BulkBuf[13] = ' '; BulkBuf[14] = ' '; BulkBuf[15] = ' '; BulkBuf[16] = 'L'; /* Product Identification */ BulkBuf[17] = 'P'; BulkBuf[18] = 'C'; BulkBuf[19] = '1'; BulkBuf[20] = '3'; BulkBuf[21] = '4'; BulkBuf[22] = 'x'; BulkBuf[23] = ' '; BulkBuf[24] = 'D'; BulkBuf[25] = 'i'; BulkBuf[26] = 's'; BulkBuf[27] = 'k'; BulkBuf[28] = ' '; BulkBuf[29] = ' '; BulkBuf[30] = ' '; BulkBuf[31] = ' '; BulkBuf[32] = '1'; /* Product Revision Level */ BulkBuf[33] = '.'; BulkBuf[34] = '0'; BulkBuf[35] = ' '; BulkLen = 36; DataInTransfer (); } /* * MSC SCSI Mode Sense (6-Byte) Callback * Parameters: None (global variables) * Return Value: None */ void MSC_ModeSense6 (void) { if (!DataInFormat ()) return; BulkBuf[0] = 0x03; BulkBuf[1] = 0x00; BulkBuf[2] = 0x00; BulkBuf[3] = 0x00; BulkLen = 4; DataInTransfer (); } /* * MSC SCSI Mode Sense (10-Byte) Callback * Parameters: None (global variables) * Return Value: None */ void MSC_ModeSense10 (void) { if (!DataInFormat ()) return; BulkBuf[0] = 0x00; BulkBuf[1] = 0x06; BulkBuf[2] = 0x00; BulkBuf[3] = 0x00; BulkBuf[4] = 0x00; BulkBuf[5] = 0x00; BulkBuf[6] = 0x00; BulkBuf[7] = 0x00; BulkLen = 8; DataInTransfer (); } /* * MSC SCSI Read Capacity Callback * Parameters: None (global variables) * Return Value: None */ void MSC_ReadCapacity (void) { if (!DataInFormat ()) return; /* Last Logical Block */ BulkBuf[0] = ((MSC_BlockCount - 1) >> 24) & 0xFF; BulkBuf[1] = ((MSC_BlockCount - 1) >> 16) & 0xFF; BulkBuf[2] = ((MSC_BlockCount - 1) >> 8) & 0xFF; BulkBuf[3] = ((MSC_BlockCount - 1) >> 0) & 0xFF; /* Block Length */ BulkBuf[4] = (MSC_BlockSize >> 24) & 0xFF; BulkBuf[5] = (MSC_BlockSize >> 16) & 0xFF; BulkBuf[6] = (MSC_BlockSize >> 8) & 0xFF; BulkBuf[7] = (MSC_BlockSize >> 0) & 0xFF; BulkLen = 8; DataInTransfer (); } /* * MSC SCSI Read Format Capacity Callback * Parameters: None (global variables) * Return Value: None */ void MSC_ReadFormatCapacity (void) { if (!DataInFormat ()) return; BulkBuf[0] = 0x00; BulkBuf[1] = 0x00; BulkBuf[2] = 0x00; BulkBuf[3] = 0x08; /* Capacity List Length */ /* Block Count */ BulkBuf[4] = (MSC_BlockCount >> 24) & 0xFF; BulkBuf[5] = (MSC_BlockCount >> 16) & 0xFF; BulkBuf[6] = (MSC_BlockCount >> 8) & 0xFF; BulkBuf[7] = (MSC_BlockCount >> 0) & 0xFF; /* Block Length */ BulkBuf[8] = 0x02; /* Descriptor Code: Formatted Media */ BulkBuf[9] = (MSC_BlockSize >> 16) & 0xFF; BulkBuf[10] = (MSC_BlockSize >> 8) & 0xFF; BulkBuf[11] = (MSC_BlockSize >> 0) & 0xFF; BulkLen = 12; DataInTransfer (); } /* * MSC Get Command Block Wrapper Callback * Parameters: None (global variables) * Return Value: None */ void MSC_GetCBW (void) { uint32_t n; for (n = 0; n < BulkLen; n++) { *((uint8_t *) & CBW + n) = BulkBuf[n]; } if ((BulkLen == sizeof (CBW)) && (CBW.dSignature == MSC_CBW_Signature)) { /* Valid CBW */ CSW.dTag = CBW.dTag; CSW.dDataResidue = CBW.dDataLength; if ((CBW.bLUN != 0) || (CBW.bCBLength < 1) || (CBW.bCBLength > 16)) { fail: CSW.bStatus = CSW_CMD_FAILED; MSC_SetCSW (); } else { switch (CBW.CB[0]) { case SCSI_TEST_UNIT_READY: MSC_TestUnitReady (); break; case SCSI_REQUEST_SENSE: MSC_RequestSense (); break; case SCSI_FORMAT_UNIT: goto fail; case SCSI_INQUIRY: MSC_Inquiry (); break; case SCSI_START_STOP_UNIT: goto fail; case SCSI_MEDIA_REMOVAL: goto fail; case SCSI_MODE_SELECT6: goto fail; case SCSI_MODE_SENSE6: MSC_ModeSense6 (); break; case SCSI_MODE_SELECT10: goto fail; case SCSI_MODE_SENSE10: MSC_ModeSense10 (); break; case SCSI_READ_FORMAT_CAPACITIES: MSC_ReadFormatCapacity (); break; case SCSI_READ_CAPACITY: MSC_ReadCapacity (); break; case SCSI_READ10: case SCSI_READ12: if (MSC_RWSetup ()) { if ((CBW.bmFlags & 0x80) != 0) { BulkStage = MSC_BS_DATA_IN; MSC_MemoryRead (); } else { /* direction mismatch */ MSC_SetStallEP (MSC_EP_OUT); CSW.bStatus = CSW_PHASE_ERROR; MSC_SetCSW (); } } break; case SCSI_WRITE10: case SCSI_WRITE12: if (MSC_RWSetup ()) { if ((CBW.bmFlags & 0x80) == 0) { BulkStage = MSC_BS_DATA_OUT; } else { /* direction mismatch */ MSC_SetStallEP (MSC_EP_IN); CSW.bStatus = CSW_PHASE_ERROR; MSC_SetCSW (); } } break; case SCSI_VERIFY10: if ((CBW.CB[1] & 0x02) == 0) { // BYTCHK = 0 -> CRC Check (not implemented) CSW.bStatus = CSW_CMD_PASSED; MSC_SetCSW (); break; } if (MSC_RWSetup ()) { if ((CBW.bmFlags & 0x80) == 0) { BulkStage = MSC_BS_DATA_OUT; MemOK = TRUE; } else { MSC_SetStallEP (MSC_EP_IN); CSW.bStatus = CSW_PHASE_ERROR; MSC_SetCSW (); } } break; default: goto fail; } } } else { /* Invalid CBW */ MSC_SetStallEP (MSC_EP_IN); /* set EP to stay stalled */ USB_EndPointStall |= (MSC_EP_IN & 0x80) ? ((1 << 16) << (MSC_EP_IN & 0x0F)) : (1 << MSC_EP_IN); MSC_SetStallEP (MSC_EP_OUT); /* set EP to stay stalled */ USB_EndPointStall |= (MSC_EP_OUT & 0x80) ? ((1 << 16) << (MSC_EP_OUT & 0x0F)) : (1 << MSC_EP_OUT); BulkStage = MSC_BS_ERROR; } } /* * MSC Set Command Status Wrapper Callback * Parameters: None (global variables) * Return Value: None */ void MSC_SetCSW (void) { CSW.dSignature = MSC_CSW_Signature; USB_WriteEP (MSC_EP_IN, (uint8_t *) & CSW, sizeof (CSW)); BulkStage = MSC_BS_CSW; } /* * MSC Bulk In Callback * Parameters: None (global variables) * Return Value: None */ void MSC_BulkIn (void) { switch (BulkStage) { case MSC_BS_DATA_IN: switch (CBW.CB[0]) { case SCSI_READ10: case SCSI_READ12: MSC_MemoryRead (); break; } break; case MSC_BS_DATA_IN_LAST: MSC_SetCSW (); break; case MSC_BS_DATA_IN_LAST_STALL: MSC_SetStallEP (MSC_EP_IN); MSC_SetCSW (); break; case MSC_BS_CSW: BulkStage = MSC_BS_CBW; break; } } /* * MSC Bulk Out Callback * Parameters: None (global variables) * Return Value: None */ void MSC_BulkOut (void) { BulkLen = USB_ReadEP (MSC_EP_OUT, BulkBuf); switch (BulkStage) { case MSC_BS_CBW: MSC_GetCBW (); break; case MSC_BS_DATA_OUT: switch (CBW.CB[0]) { case SCSI_WRITE10: case SCSI_WRITE12: MSC_MemoryWrite (); break; case SCSI_VERIFY10: MSC_MemoryVerify (); break; } break; default: MSC_SetStallEP (MSC_EP_OUT); CSW.bStatus = CSW_PHASE_ERROR; MSC_SetCSW (); break; } }