-/* Copyright (c) 2010-2011 mbed.org, MIT License\r
-*\r
-* Permission is hereby granted, free of charge, to any person obtaining a copy of this software\r
-* and associated documentation files (the "Software"), to deal in the Software without\r
-* restriction, including without limitation the rights to use, copy, modify, merge, publish,\r
-* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the\r
-* Software is furnished to do so, subject to the following conditions:\r
-*\r
-* The above copyright notice and this permission notice shall be included in all copies or\r
-* substantial portions of the Software.\r
-*\r
-* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING\r
-* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
-* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,\r
-* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r
-* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
-*/\r
-\r
-#include <stdint.h>\r
-#include <stdlib.h>\r
-#include <cstring>\r
-#include <cstdio>\r
-\r
-#include "USBMSD.h"\r
-\r
-#include "descriptor_msc.h"\r
-\r
-#include "Kernel.h"\r
-\r
-#include "platform_memory.h"\r
-\r
-#define DISK_OK 0x00\r
-#define NO_INIT 0x01\r
-#define NO_DISK 0x02\r
-#define WRITE_PROTECT 0x04\r
-\r
-#define CBW_Signature 0x43425355\r
-#define CSW_Signature 0x53425355\r
-\r
-// SCSI Commands\r
-#define TEST_UNIT_READY 0x00\r
-#define REQUEST_SENSE 0x03\r
-#define FORMAT_UNIT 0x04\r
-#define INQUIRY 0x12\r
-#define MODE_SELECT6 0x15\r
-#define MODE_SENSE6 0x1A\r
-#define START_STOP_UNIT 0x1B\r
-#define MEDIA_REMOVAL 0x1E\r
-#define READ_FORMAT_CAPACITIES 0x23\r
-#define READ_CAPACITY 0x25\r
-#define READ10 0x28\r
-#define WRITE10 0x2A\r
-#define VERIFY10 0x2F\r
-#define READ12 0xA8\r
-#define WRITE12 0xAA\r
-#define MODE_SELECT10 0x55\r
-#define MODE_SENSE10 0x5A\r
-\r
-// MSC class specific requests\r
-#define MSC_REQUEST_RESET 0xFF\r
-#define MSC_REQUEST_GET_MAX_LUN 0xFE\r
-\r
-#define STARTSTOP_STOPMOTOR 0x0\r
-#define STARTSTOP_STARTMOTOR 0x1\r
-#define STARTSTOP_EJECT 0x2\r
-#define STARTSTOP_LOAD 0x3\r
-\r
-#define DEFAULT_CONFIGURATION (1)\r
-\r
-// max packet size\r
-#define MAX_PACKET MAX_PACKET_SIZE_EPBULK\r
-\r
-// #define iprintf(...) THEKERNEL->streams->printf(__VA_ARGS__)\r
-#define iprintf(...) do { } while (0)\r
-\r
-// CSW Status\r
-enum Status {\r
- CSW_PASSED,\r
- CSW_FAILED,\r
- CSW_ERROR,\r
-};\r
-\r
-USBMSD::USBMSD(USB *u, MSD_Disk *d) {\r
- this->usb = u;\r
- this->disk = d;\r
-\r
- usbdesc_interface i = {\r
- DL_INTERFACE, // bLength\r
- DT_INTERFACE, // bDescType\r
- 0, // bInterfaceNumber - filled out by USB during attach()\r
- 0, // bAlternateSetting\r
- 2, // bNumEndpoints\r
- UC_MASS_STORAGE, // bInterfaceClass\r
- MSC_SUBCLASS_SCSI, // bInterfaceSubClass\r
- MSC_PROTOCOL_BULK_ONLY, // bInterfaceProtocol\r
- 0, // iInterface\r
- 0, 0, 0, // dummy padding\r
- this, // callback\r
- };\r
- memcpy(&MSC_Interface, &i, sizeof(MSC_Interface));\r
-\r
- usbdesc_endpoint j = {\r
- DL_ENDPOINT, // bLength\r
- DT_ENDPOINT, // bDescType\r
- EP_DIR_IN, // bEndpointAddress - we provide direction, index is filled out by USB during attach()\r
- EA_BULK, // bmAttributes\r
- MAX_PACKET_SIZE_EPBULK, // wMaxPacketSize\r
- 0, // bInterval\r
- 0, // dummy padding\r
- this, // endpoint callback\r
- };\r
- memcpy(&MSC_BulkIn, &j, sizeof(MSC_BulkIn));\r
-\r
- usbdesc_endpoint k = {\r
- DL_ENDPOINT, // bLength\r
- DT_ENDPOINT, // bDescType\r
- EP_DIR_OUT, // bEndpointAddress - we provide direction, index is filled out by USB during attach()\r
- EA_BULK, // bmAttributes\r
- MAX_PACKET_SIZE_EPBULK, // wMaxPacketSize\r
- 0, // bInterval\r
- 0, // dummy padding\r
- this, // endpoint callback\r
- };\r
- memcpy(&MSC_BulkOut, &k, sizeof(MSC_BulkOut));\r
-\r
- // because gcc-4.6 won't let us simply do MSC_Description = usbstring("Smoothie MSD")\r
- usbdesc_string_l(13) us = usbstring("Smoothie MSD");\r
- memcpy(&MSC_Description, &us, sizeof(MSC_Description));\r
-\r
- usb->addInterface(&MSC_Interface);\r
-\r
- usb->addEndpoint(&MSC_BulkIn);\r
- usb->addEndpoint(&MSC_BulkOut);\r
-\r
- MSC_Interface.iInterface =\r
- usb->addString(&MSC_Description);\r
-}\r
-\r
-// Called in ISR context to process a class specific request\r
-bool USBMSD::USBEvent_Request(CONTROL_TRANSFER &transfer)\r
-{\r
- iprintf("MSD:Control: ");\r
- bool success = false;\r
-// CONTROL_TRANSFER * transfer = getTransferPtr();\r
- static uint8_t maxLUN[1] = {0};\r
-\r
- if (transfer.setup.bmRequestType.Type == CLASS_TYPE) {\r
- switch (transfer.setup.bRequest) {\r
- case MSC_REQUEST_RESET:\r
-// iprintf("MSC:Req Reset\n");\r
- reset();\r
- success = true;\r
- break;\r
- case MSC_REQUEST_GET_MAX_LUN:\r
-// iprintf("MSC:Req Get_Max_Lun\n");\r
- transfer.remaining = 1;\r
- transfer.ptr = maxLUN;\r
- transfer.direction = DEVICE_TO_HOST;\r
- success = true;\r
- break;\r
- default:\r
- break;\r
- }\r
- }\r
- iprintf("%d\n", success?1:0);\r
-\r
- return success;\r
-}\r
-\r
-bool USBMSD::USBEvent_RequestComplete(CONTROL_TRANSFER &transfer, uint8_t *buf, uint32_t length)\r
-{\r
- return true;\r
-}\r
-\r
-bool USBMSD::connect()\r
-{\r
- BlockCount = 0;\r
-\r
- //disk initialization\r
- if (disk->disk_status() & NO_INIT) {\r
- if (disk->disk_initialize()) {\r
- return false;\r
- }\r
- }\r
-\r
- // get number of blocks\r
- BlockCount = disk->disk_sectors();\r
-\r
- // get memory size\r
-// MemorySize = disk->disk_size();\r
- BlockSize = disk->disk_blocksize();\r
-\r
- if ((BlockCount > 0) && (BlockSize != 0)) {\r
- page = (uint8_t*) AHB0.alloc(BlockSize);\r
- if (page == NULL)\r
- return false;\r
- } else {\r
- return false;\r
- }\r
-\r
- return true;\r
-}\r
-\r
-\r
-void USBMSD::reset() {\r
- stage = READ_CBW;\r
- usb->endpointSetInterrupt(MSC_BulkOut.bEndpointAddress, true);\r
- usb->endpointSetInterrupt(MSC_BulkIn.bEndpointAddress, false);\r
-}\r
-\r
-\r
-// Called in ISR context called when a data is received\r
-// bool USBMSD::EP2_OUT_callback() {\r
-bool USBMSD::USBEvent_EPOut(uint8_t bEP, uint8_t bEPStatus) {\r
- uint32_t size = 0;\r
-// uint8_t buf[MAX_PACKET_SIZE_EPBULK];\r
- usb->readEP(MSC_BulkOut.bEndpointAddress, buffer, &size, MAX_PACKET_SIZE_EPBULK);\r
- iprintf("MSD:EPOut:Read %lu\n", size);\r
- switch (stage) {\r
- // the device has to decode the CBW received\r
- case READ_CBW:\r
- CBWDecode(buffer, size);\r
- break;\r
-\r
- // the device has to receive data from the host\r
- case PROCESS_CBW:\r
- switch (cbw.CB[0]) {\r
- case WRITE10:\r
- case WRITE12:\r
- memoryWrite(buffer, size);\r
- break;\r
- case VERIFY10:\r
- memoryVerify(buffer, size);\r
- break;\r
- }\r
- break;\r
-\r
- // an error has occured: stall endpoint and send CSW\r
- default:\r
- usb->stallEndpoint(MSC_BulkOut.bEndpointAddress);\r
- csw.Status = CSW_ERROR;\r
- sendCSW();\r
- break;\r
- }\r
-\r
- //reactivate readings on the OUT bulk endpoint\r
- usb->readStart(MSC_BulkOut.bEndpointAddress, MAX_PACKET_SIZE_EPBULK);\r
- return true;\r
-}\r
-\r
-// Called in ISR context when a data has been transferred\r
-// bool USBMSD::EP2_IN_callback() {\r
-bool USBMSD::USBEvent_EPIn(uint8_t bEP, uint8_t bEPStatus) {\r
- uint8_t _stage = stage;\r
- if (stage != 2)\r
- iprintf("MSD:In:S%d,G:", stage);\r
-\r
- bool gotMoreData = false;\r
-\r
- switch (stage) {\r
- // not sure\r
- case READ_CBW: // stage 0\r
- gotMoreData = false;\r
- break;\r
-\r
- // the device has to send data to the host\r
- case PROCESS_CBW: // stage 2\r
- gotMoreData = false;\r
- switch (cbw.CB[0]) {\r
- case READ10:\r
- case READ12:\r
- memoryRead();\r
- gotMoreData = true;\r
- break;\r
- }\r
- break;\r
-\r
- //the device has to send a CSW\r
- case SEND_CSW: // stage 3\r
- sendCSW();\r
- gotMoreData = true;\r
- break;\r
-\r
- // an error has occured\r
- case ERROR: // stage 1\r
- usb->stallEndpoint(MSC_BulkIn.bEndpointAddress);\r
- sendCSW();\r
- gotMoreData = false;\r
- break;\r
-\r
- // the host has received the CSW -> we wait a CBW\r
- case WAIT_CSW: // stage 4\r
- stage = READ_CBW;\r
- gotMoreData = false;\r
- break;\r
- }\r
-\r
- if (_stage != 2)\r
- iprintf("%d\n", gotMoreData?1:0);\r
-\r
- return gotMoreData;\r
-}\r
-\r
-void USBMSD::memoryWrite (uint8_t * buf, uint16_t size) {\r
-\r
- if (lba > BlockCount) {\r
- size = (BlockCount - lba) * BlockSize + addr_in_block;\r
- stage = ERROR;\r
- usb->stallEndpoint(MSC_BulkOut.bEndpointAddress);\r
- }\r
-\r
- // we fill an array in RAM of 1 block before writing it in memory\r
- for (int i = 0; i < size; i++)\r
- page[addr_in_block + i] = buf[i];\r
-\r
- // if the array is filled, write it in memory\r
- if ((addr_in_block + size) >= BlockSize) {\r
- if (!(disk->disk_status() & WRITE_PROTECT)) {\r
- disk->disk_write((const char *)page, lba);\r
- }\r
- }\r
-\r
- addr_in_block += size;\r
- length -= size;\r
- csw.DataResidue -= size;\r
- if (addr_in_block >= BlockSize)\r
- {\r
- addr_in_block = 0;\r
- lba++;\r
- }\r
-\r
- if ((!length) || (stage != PROCESS_CBW)) {\r
- csw.Status = (stage == ERROR) ? CSW_FAILED : CSW_PASSED;\r
- sendCSW();\r
- }\r
-}\r
-\r
-void USBMSD::memoryVerify (uint8_t * buf, uint16_t size) {\r
- uint32_t n;\r
-\r
- if (lba > BlockCount) {\r
- size = (BlockCount - lba) * BlockSize + addr_in_block;\r
- stage = ERROR;\r
- usb->stallEndpoint(MSC_BulkOut.bEndpointAddress);\r
- }\r
-\r
- // beginning of a new block -> load a whole block in RAM\r
- if (addr_in_block == 0)\r
- disk->disk_read((char *)page, lba);\r
-\r
- // info are in RAM -> no need to re-read memory\r
- for (n = 0; n < size; n++) {\r
- if (page[addr_in_block + n] != buf[n]) {\r
- memOK = false;\r
- break;\r
- }\r
- }\r
-\r
- addr_in_block += size;\r
- length -= size;\r
- csw.DataResidue -= size;\r
-\r
- if (addr_in_block >= BlockSize)\r
- {\r
- addr_in_block = 0;\r
- lba++;\r
- }\r
-\r
- if ( !length || (stage != PROCESS_CBW)) {\r
- csw.Status = (memOK && (stage == PROCESS_CBW)) ? CSW_PASSED : CSW_FAILED;\r
- sendCSW();\r
- }\r
-}\r
-\r
-\r
-bool USBMSD::inquiryRequest (void) {\r
- uint8_t inquiry[] = { 0x00, 0x80, 0x00, 0x01,\r
- 36 - 4, 0x00, 0x00, 0x01,\r
- 'M', 'B', 'E', 'D', '.', 'O', 'R', 'G',\r
- 'M', 'B', 'E', 'D', ' ', 'U', 'S', 'B', ' ', 'D', 'I', 'S', 'K', ' ', ' ', ' ',\r
- '1', '.', '0', ' ',\r
- };\r
-\r
- if (BlockCount == 0)\r
- inquiry[0] = 0x20; // PERIPHERAL_QUALIFIER = 1 : "A peripheral device is not connected, however usually we do support this type of peripheral"\r
-\r
- if (!write(inquiry, sizeof(inquiry))) {\r
- return false;\r
- }\r
- return true;\r
-}\r
-\r
-\r
-bool USBMSD::readFormatCapacity() {\r
- uint8_t capacity[] = { 0x00, 0x00, 0x00, 0x08,\r
- (uint8_t) ((BlockCount >> 24) & 0xff),\r
- (uint8_t) ((BlockCount >> 16) & 0xff),\r
- (uint8_t) ((BlockCount >> 8) & 0xff),\r
- (uint8_t) ((BlockCount >> 0) & 0xff),\r
-\r
- 0x02,\r
- (uint8_t) ((BlockSize >> 16) & 0xff),\r
- (uint8_t) ((BlockSize >> 8) & 0xff),\r
- (uint8_t) ((BlockSize >> 0) & 0xff),\r
- };\r
- if (!write(capacity, sizeof(capacity))) {\r
- return false;\r
- }\r
- return true;\r
-}\r
-\r
-\r
-bool USBMSD::readCapacity (void) {\r
- uint8_t capacity[] = {\r
- (uint8_t) (((BlockCount - 1) >> 24) & 0xff),\r
- (uint8_t) (((BlockCount - 1) >> 16) & 0xff),\r
- (uint8_t) (((BlockCount - 1) >> 8) & 0xff),\r
- (uint8_t) (((BlockCount - 1) >> 0) & 0xff),\r
-\r
- (uint8_t) ((BlockSize >> 24) & 0xff),\r
- (uint8_t) ((BlockSize >> 16) & 0xff),\r
- (uint8_t) ((BlockSize >> 8) & 0xff),\r
- (uint8_t) ((BlockSize >> 0) & 0xff),\r
- };\r
- if (!write(capacity, sizeof(capacity))) {\r
- return false;\r
- }\r
- return true;\r
-}\r
-\r
-bool USBMSD::write (uint8_t * buf, uint16_t size) {\r
- if (size >= cbw.DataLength) {\r
- size = cbw.DataLength;\r
- }\r
- stage = SEND_CSW;\r
-\r
-// iprintf("MSD:write: %u bytes\n", size);\r
-\r
- if (!usb->writeNB(MSC_BulkIn.bEndpointAddress, buf, size, MAX_PACKET_SIZE_EPBULK)) {\r
- return false;\r
- }\r
-\r
-// iprintf("MSD:write OK, sending CSW\n");\r
-\r
- csw.DataResidue -= size;\r
- csw.Status = CSW_PASSED;\r
-\r
- usb->endpointSetInterrupt(MSC_BulkIn.bEndpointAddress, true);\r
-\r
- return true;\r
-}\r
-\r
-\r
-bool USBMSD::modeSense6 (void) {\r
- uint8_t sense6[] = { 0x03, 0x00, 0x00, 0x00 };\r
- if (!write(sense6, sizeof(sense6))) {\r
- return false;\r
- }\r
- return true;\r
-}\r
-\r
-void USBMSD::sendCSW() {\r
- csw.Signature = CSW_Signature;\r
-// iprintf("MSD:SendCSW:\n\tSignature : %lu\n\tTag : %lu\n\tDataResidue: %lu\n\tStatus : %u\n", csw.Signature, csw.Tag, csw.DataResidue, csw.Status);\r
- usb->writeNB(MSC_BulkIn.bEndpointAddress, (uint8_t *)&csw, sizeof(CSW), MAX_PACKET_SIZE_EPBULK);\r
- stage = WAIT_CSW;\r
- usb->endpointSetInterrupt(MSC_BulkIn.bEndpointAddress, true);\r
-}\r
-\r
-bool USBMSD::requestSense (void) {\r
- uint8_t request_sense[] = {\r
- 0x70,\r
- 0x00,\r
- 0x05, // Sense Key: illegal request\r
- 0x00,\r
- 0x00,\r
- 0x00,\r
- 0x00,\r
- 0x0A,\r
- 0x00,\r
- 0x00,\r
- 0x00,\r
- 0x00,\r
- 0x30,\r
- 0x01,\r
- 0x00,\r
- 0x00,\r
- 0x00,\r
- 0x00,\r
- };\r
-\r
- if (BlockCount == 0)\r
- {\r
- request_sense[ 2] = 0x02; // Not Ready\r
- request_sense[12] = 0x3A; // Medium not present\r
- request_sense[13] = 0x00; // No known reason\r
- }\r
-\r
- if (!write(request_sense, sizeof(request_sense))) {\r
- return false;\r
- }\r
-\r
- return true;\r
-}\r
-\r
-void USBMSD::fail() {\r
- csw.Status = CSW_FAILED;\r
- sendCSW();\r
-}\r
-\r
-void USBMSD::CBWDecode(uint8_t * buf, uint16_t size) {\r
- if (size == sizeof(cbw)) {\r
- memcpy((uint8_t *)&cbw, buf, size);\r
- if (cbw.Signature == CBW_Signature) {\r
- csw.Tag = cbw.Tag;\r
- csw.DataResidue = cbw.DataLength;\r
- if ((cbw.CBLength < 1) || (cbw.CBLength > 16) ) {\r
- iprintf("MSD:Got CBW 0x%02X with invalid CBLength\n", cbw.CB[0]);\r
- fail();\r
- } else {\r
- iprintf("MSD:Got CBW 0x%02X with datalength %lu\n", cbw.CB[0], cbw.DataLength);\r
- switch (cbw.CB[0]) {\r
- case TEST_UNIT_READY:\r
- testUnitReady();\r
- break;\r
- case REQUEST_SENSE:\r
- requestSense();\r
- break;\r
- case INQUIRY:\r
- inquiryRequest();\r
- break;\r
- case MODE_SENSE6:\r
- modeSense6();\r
- break;\r
- case READ_FORMAT_CAPACITIES:\r
- readFormatCapacity();\r
- break;\r
- case READ_CAPACITY:\r
- readCapacity();\r
- break;\r
- case READ10:\r
- case READ12:\r
-// iprintf("MSD:READ10\n");\r
- if (infoTransfer()) {\r
- if ((cbw.Flags & 0x80)) {\r
- iprintf("MSD: Read %lu blocks from LBA %lu\n", blocks, lba);\r
- stage = PROCESS_CBW;\r
-// memoryRead();\r
- usb->endpointSetInterrupt(MSC_BulkIn.bEndpointAddress, true);\r
- } else {\r
- usb->stallEndpoint(MSC_BulkOut.bEndpointAddress);\r
- csw.Status = CSW_ERROR;\r
- sendCSW();\r
- }\r
- }\r
- break;\r
- case WRITE10:\r
- case WRITE12:\r
- if (infoTransfer()) {\r
- if (!(cbw.Flags & 0x80)) {\r
- iprintf("MSD: Write %lu blocks from LBA %lu\n", blocks, lba);\r
- stage = PROCESS_CBW;\r
- } else {\r
- usb->stallEndpoint(MSC_BulkIn.bEndpointAddress);\r
- csw.Status = CSW_ERROR;\r
- sendCSW();\r
- }\r
- }\r
- break;\r
- case VERIFY10:\r
- if (!(cbw.CB[1] & 0x02)) {\r
- csw.Status = CSW_PASSED;\r
- sendCSW();\r
- break;\r
- }\r
- if (infoTransfer()) {\r
- if (!(cbw.Flags & 0x80)) {\r
- iprintf("MSD: Verify %lu blocks from LBA %lu\n", blocks, lba);\r
- stage = PROCESS_CBW;\r
- memOK = true;\r
- } else {\r
- usb->stallEndpoint(MSC_BulkIn.bEndpointAddress);\r
- csw.Status = CSW_ERROR;\r
- sendCSW();\r
- }\r
- }\r
- break;\r
- case START_STOP_UNIT:\r
- {\r
- switch (cbw.CB[4] & 0x03)\r
- {\r
- case STARTSTOP_STOPMOTOR:\r
- break;\r
- case STARTSTOP_STARTMOTOR:\r
- break;\r
- case STARTSTOP_EJECT:\r
- break;\r
- case STARTSTOP_LOAD:\r
- break;\r
- }\r
- csw.Status = CSW_PASSED;\r
- sendCSW();\r
- break;\r
- }\r
- default:\r
- iprintf("MSD: Unhandled SCSI CBW 0x%02X\n", cbw.CB[0]);\r
- fail();\r
- break;\r
- }\r
- }\r
- }\r
- else {\r
- iprintf("MSD:Got CBW 0x%02X with bad signature\n", cbw.CB[0]);\r
- }\r
- }\r
- else {\r
- iprintf("MSD:Got CBW 0x%02X with bad length: %u (%u)\n", cbw.CB[0], size, sizeof(cbw));\r
- }\r
-}\r
-\r
-void USBMSD::testUnitReady (void) {\r
-\r
- if (cbw.DataLength != 0) {\r
- if ((cbw.Flags & 0x80) != 0) {\r
- usb->stallEndpoint(MSC_BulkIn.bEndpointAddress);\r
- } else {\r
- usb->stallEndpoint(MSC_BulkOut.bEndpointAddress);\r
- }\r
- }\r
-\r
- if (BlockCount > 0)\r
- csw.Status = CSW_PASSED;\r
- else\r
- csw.Status = CSW_ERROR;\r
-\r
- sendCSW();\r
-}\r
-\r
-void USBMSD::memoryRead (void) {\r
- uint32_t n;\r
-\r
- n = (length > MAX_PACKET_SIZE_EPBULK) ? MAX_PACKET_SIZE_EPBULK : length;\r
-\r
- if (lba > BlockCount) {\r
- iprintf("MSD:Attemt to read beyond end of disk! Read LBA %lu > Disk LBAs %lu\n", lba, BlockCount);\r
- n = (BlockCount - lba) * BlockSize + addr_in_block;\r
- stage = ERROR;\r
- }\r
-\r
- // we read an entire block\r
- if (addr_in_block == 0)\r
- {\r
- iprintf("MSD:LBA %lu:", lba);\r
- disk->disk_read((char *)page, lba);\r
- }\r
-\r
- iprintf(" %u", addr_in_block / MAX_PACKET_SIZE_EPBULK);\r
-\r
- // write data which are in RAM\r
- usb->writeNB(MSC_BulkIn.bEndpointAddress, &page[addr_in_block], n, MAX_PACKET_SIZE_EPBULK);\r
-\r
- addr_in_block += n;\r
-\r
- length -= n;\r
- csw.DataResidue -= n;\r
-\r
- if (addr_in_block >= BlockSize)\r
- {\r
- iprintf("\n");\r
- addr_in_block = 0;\r
- lba++;\r
- }\r
-\r
- if ( !length || (stage != PROCESS_CBW)) {\r
- csw.Status = (stage == PROCESS_CBW) ? CSW_PASSED : CSW_FAILED;\r
- stage = (stage == PROCESS_CBW) ? SEND_CSW : stage;\r
- }\r
- usb->endpointSetInterrupt(MSC_BulkIn.bEndpointAddress, true);\r
-}\r
-\r
-bool USBMSD::infoTransfer (void) {\r
- // Logical Block Address of First Block\r
- lba = (cbw.CB[2] << 24) | (cbw.CB[3] << 16) | (cbw.CB[4] << 8) | (cbw.CB[5] << 0);\r
-\r
-// addr = lba * BlockSize;\r
-\r
- // Number of Blocks to transfer\r
- switch (cbw.CB[0]) {\r
- case READ10:\r
- case WRITE10:\r
- case VERIFY10:\r
- blocks = (cbw.CB[7] << 8) | (cbw.CB[8] << 0);\r
- break;\r
-\r
- case READ12:\r
- case WRITE12:\r
- blocks = (cbw.CB[6] << 24) | (cbw.CB[7] << 16) | (cbw.CB[8] << 8) | (cbw.CB[9] << 0);\r
- break;\r
- }\r
-\r
- if ((lba + blocks) > BlockCount)\r
- {\r
- csw.Status = CSW_FAILED;\r
- sendCSW();\r
- return false;\r
- }\r
-\r
- length = blocks * BlockSize;\r
-\r
- if (!cbw.DataLength) { // host requests no data\r
- csw.Status = CSW_FAILED;\r
- sendCSW();\r
- return false;\r
- }\r
-\r
- if (cbw.DataLength != length) {\r
- if ((cbw.Flags & 0x80) != 0) {\r
- usb->stallEndpoint(MSC_BulkIn.bEndpointAddress);\r
- } else {\r
- usb->stallEndpoint(MSC_BulkOut.bEndpointAddress);\r
- }\r
-\r
- csw.Status = CSW_FAILED;\r
- sendCSW();\r
- return false;\r
- }\r
-\r
- addr_in_block = 0;\r
-\r
-// iprintf("MSD:transferring %lu blocks from LBA %lu.\n", blocks, lba);\r
-\r
- return true;\r
-}\r
-\r
-void USBMSD::on_module_loaded()\r
-{\r
- connect();\r
-}\r
-\r
-bool USBMSD::USBEvent_busReset(void)\r
-{\r
- return true;\r
-}\r
-\r
-bool USBMSD::USBEvent_connectStateChanged(bool connected)\r
-{\r
- return true;\r
-}\r
-\r
-bool USBMSD::USBEvent_suspendStateChanged(bool suspended)\r
-{\r
- return true;\r
-}\r
+/* Copyright (c) 2010-2011 mbed.org, MIT License
+*
+* 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.
+*
+* 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 AUTHORS OR COPYRIGHT HOLDERS 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.
+*/
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <cstring>
+#include <cstdio>
+
+#include "USBMSD.h"
+
+#include "descriptor_msc.h"
+
+#include "Kernel.h"
+
+#include "platform_memory.h"
+
+#define DISK_OK 0x00
+#define NO_INIT 0x01
+#define NO_DISK 0x02
+#define WRITE_PROTECT 0x04
+
+#define CBW_Signature 0x43425355
+#define CSW_Signature 0x53425355
+
+// SCSI Commands
+#define TEST_UNIT_READY 0x00
+#define REQUEST_SENSE 0x03
+#define FORMAT_UNIT 0x04
+#define INQUIRY 0x12
+#define MODE_SELECT6 0x15
+#define MODE_SENSE6 0x1A
+#define START_STOP_UNIT 0x1B
+#define MEDIA_REMOVAL 0x1E
+#define READ_FORMAT_CAPACITIES 0x23
+#define READ_CAPACITY 0x25
+#define READ10 0x28
+#define WRITE10 0x2A
+#define VERIFY10 0x2F
+#define READ12 0xA8
+#define WRITE12 0xAA
+#define MODE_SELECT10 0x55
+#define MODE_SENSE10 0x5A
+
+// MSC class specific requests
+#define MSC_REQUEST_RESET 0xFF
+#define MSC_REQUEST_GET_MAX_LUN 0xFE
+
+#define STARTSTOP_STOPMOTOR 0x0
+#define STARTSTOP_STARTMOTOR 0x1
+#define STARTSTOP_EJECT 0x2
+#define STARTSTOP_LOAD 0x3
+
+#define DEFAULT_CONFIGURATION (1)
+
+// max packet size
+#define MAX_PACKET MAX_PACKET_SIZE_EPBULK
+
+// #define iprintf(...) THEKERNEL->streams->printf(__VA_ARGS__)
+#define iprintf(...) do { } while (0)
+
+// CSW Status
+enum Status {
+ CSW_PASSED,
+ CSW_FAILED,
+ CSW_ERROR,
+};
+
+USBMSD::USBMSD(USB *u, MSD_Disk *d) {
+ this->usb = u;
+ this->disk = d;
+
+ usbdesc_interface i = {
+ DL_INTERFACE, // bLength
+ DT_INTERFACE, // bDescType
+ 0, // bInterfaceNumber - filled out by USB during attach()
+ 0, // bAlternateSetting
+ 2, // bNumEndpoints
+ UC_MASS_STORAGE, // bInterfaceClass
+ MSC_SUBCLASS_SCSI, // bInterfaceSubClass
+ MSC_PROTOCOL_BULK_ONLY, // bInterfaceProtocol
+ 0, // iInterface
+ 0, 0, 0, // dummy padding
+ this, // callback
+ };
+ memcpy(&MSC_Interface, &i, sizeof(MSC_Interface));
+
+ usbdesc_endpoint j = {
+ DL_ENDPOINT, // bLength
+ DT_ENDPOINT, // bDescType
+ EP_DIR_IN, // bEndpointAddress - we provide direction, index is filled out by USB during attach()
+ EA_BULK, // bmAttributes
+ MAX_PACKET_SIZE_EPBULK, // wMaxPacketSize
+ 0, // bInterval
+ 0, // dummy padding
+ this, // endpoint callback
+ };
+ memcpy(&MSC_BulkIn, &j, sizeof(MSC_BulkIn));
+
+ usbdesc_endpoint k = {
+ DL_ENDPOINT, // bLength
+ DT_ENDPOINT, // bDescType
+ EP_DIR_OUT, // bEndpointAddress - we provide direction, index is filled out by USB during attach()
+ EA_BULK, // bmAttributes
+ MAX_PACKET_SIZE_EPBULK, // wMaxPacketSize
+ 0, // bInterval
+ 0, // dummy padding
+ this, // endpoint callback
+ };
+ memcpy(&MSC_BulkOut, &k, sizeof(MSC_BulkOut));
+
+ // because gcc-4.6 won't let us simply do MSC_Description = usbstring("Smoothie MSD")
+ usbdesc_string_l(13) us = usbstring("Smoothie MSD");
+ memcpy(&MSC_Description, &us, sizeof(MSC_Description));
+
+ usb->addInterface(&MSC_Interface);
+
+ usb->addEndpoint(&MSC_BulkIn);
+ usb->addEndpoint(&MSC_BulkOut);
+
+ MSC_Interface.iInterface =
+ usb->addString(&MSC_Description);
+}
+
+// Called in ISR context to process a class specific request
+bool USBMSD::USBEvent_Request(CONTROL_TRANSFER &transfer)
+{
+ iprintf("MSD:Control: ");
+ bool success = false;
+// CONTROL_TRANSFER * transfer = getTransferPtr();
+ static uint8_t maxLUN[1] = {0};
+
+ if (transfer.setup.bmRequestType.Type == CLASS_TYPE) {
+ switch (transfer.setup.bRequest) {
+ case MSC_REQUEST_RESET:
+// iprintf("MSC:Req Reset\n");
+ reset();
+ success = true;
+ break;
+ case MSC_REQUEST_GET_MAX_LUN:
+// iprintf("MSC:Req Get_Max_Lun\n");
+ transfer.remaining = 1;
+ transfer.ptr = maxLUN;
+ transfer.direction = DEVICE_TO_HOST;
+ success = true;
+ break;
+ default:
+ break;
+ }
+ }
+ iprintf("%d\n", success?1:0);
+
+ return success;
+}
+
+bool USBMSD::USBEvent_RequestComplete(CONTROL_TRANSFER &transfer, uint8_t *buf, uint32_t length)
+{
+ return true;
+}
+
+bool USBMSD::connect()
+{
+ BlockCount = 0;
+
+ //disk initialization
+ if (disk->disk_status() & NO_INIT) {
+ if (disk->disk_initialize()) {
+ return false;
+ }
+ }
+
+ // get number of blocks
+ BlockCount = disk->disk_sectors();
+
+ // get memory size
+// MemorySize = disk->disk_size();
+ BlockSize = disk->disk_blocksize();
+
+ if ((BlockCount > 0) && (BlockSize != 0)) {
+ page = (uint8_t*) AHB0.alloc(BlockSize);
+ if (page == NULL)
+ return false;
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
+
+void USBMSD::reset() {
+ stage = READ_CBW;
+ usb->endpointSetInterrupt(MSC_BulkOut.bEndpointAddress, true);
+ usb->endpointSetInterrupt(MSC_BulkIn.bEndpointAddress, false);
+}
+
+
+// Called in ISR context called when a data is received
+// bool USBMSD::EP2_OUT_callback() {
+bool USBMSD::USBEvent_EPOut(uint8_t bEP, uint8_t bEPStatus) {
+ uint32_t size = 0;
+// uint8_t buf[MAX_PACKET_SIZE_EPBULK];
+ usb->readEP(MSC_BulkOut.bEndpointAddress, buffer, &size, MAX_PACKET_SIZE_EPBULK);
+ iprintf("MSD:EPOut:Read %lu\n", size);
+ switch (stage) {
+ // the device has to decode the CBW received
+ case READ_CBW:
+ CBWDecode(buffer, size);
+ break;
+
+ // the device has to receive data from the host
+ case PROCESS_CBW:
+ switch (cbw.CB[0]) {
+ case WRITE10:
+ case WRITE12:
+ memoryWrite(buffer, size);
+ break;
+ case VERIFY10:
+ memoryVerify(buffer, size);
+ break;
+ }
+ break;
+
+ // an error has occured: stall endpoint and send CSW
+ default:
+ usb->stallEndpoint(MSC_BulkOut.bEndpointAddress);
+ csw.Status = CSW_ERROR;
+ sendCSW();
+ break;
+ }
+
+ //reactivate readings on the OUT bulk endpoint
+ usb->readStart(MSC_BulkOut.bEndpointAddress, MAX_PACKET_SIZE_EPBULK);
+ return true;
+}
+
+// Called in ISR context when a data has been transferred
+// bool USBMSD::EP2_IN_callback() {
+bool USBMSD::USBEvent_EPIn(uint8_t bEP, uint8_t bEPStatus) {
+ uint8_t _stage = stage;
+ if (stage != 2)
+ iprintf("MSD:In:S%d,G:", stage);
+
+ bool gotMoreData = false;
+
+ switch (stage) {
+ // not sure
+ case READ_CBW: // stage 0
+ gotMoreData = false;
+ break;
+
+ // the device has to send data to the host
+ case PROCESS_CBW: // stage 2
+ gotMoreData = false;
+ switch (cbw.CB[0]) {
+ case READ10:
+ case READ12:
+ memoryRead();
+ gotMoreData = true;
+ break;
+ }
+ break;
+
+ //the device has to send a CSW
+ case SEND_CSW: // stage 3
+ sendCSW();
+ gotMoreData = true;
+ break;
+
+ // an error has occured
+ case ERROR: // stage 1
+ usb->stallEndpoint(MSC_BulkIn.bEndpointAddress);
+ sendCSW();
+ gotMoreData = false;
+ break;
+
+ // the host has received the CSW -> we wait a CBW
+ case WAIT_CSW: // stage 4
+ stage = READ_CBW;
+ gotMoreData = false;
+ break;
+ }
+
+ if (_stage != 2)
+ iprintf("%d\n", gotMoreData?1:0);
+
+ return gotMoreData;
+}
+
+void USBMSD::memoryWrite (uint8_t * buf, uint16_t size) {
+
+ if (lba > BlockCount) {
+ size = (BlockCount - lba) * BlockSize + addr_in_block;
+ stage = ERROR;
+ usb->stallEndpoint(MSC_BulkOut.bEndpointAddress);
+ }
+
+ // we fill an array in RAM of 1 block before writing it in memory
+ for (int i = 0; i < size; i++)
+ page[addr_in_block + i] = buf[i];
+
+ // if the array is filled, write it in memory
+ if ((addr_in_block + size) >= BlockSize) {
+ if (!(disk->disk_status() & WRITE_PROTECT)) {
+ disk->disk_write((const char *)page, lba);
+ }
+ }
+
+ addr_in_block += size;
+ length -= size;
+ csw.DataResidue -= size;
+ if (addr_in_block >= BlockSize)
+ {
+ addr_in_block = 0;
+ lba++;
+ }
+
+ if ((!length) || (stage != PROCESS_CBW)) {
+ csw.Status = (stage == ERROR) ? CSW_FAILED : CSW_PASSED;
+ sendCSW();
+ }
+}
+
+void USBMSD::memoryVerify (uint8_t * buf, uint16_t size) {
+ uint32_t n;
+
+ if (lba > BlockCount) {
+ size = (BlockCount - lba) * BlockSize + addr_in_block;
+ stage = ERROR;
+ usb->stallEndpoint(MSC_BulkOut.bEndpointAddress);
+ }
+
+ // beginning of a new block -> load a whole block in RAM
+ if (addr_in_block == 0)
+ disk->disk_read((char *)page, lba);
+
+ // info are in RAM -> no need to re-read memory
+ for (n = 0; n < size; n++) {
+ if (page[addr_in_block + n] != buf[n]) {
+ memOK = false;
+ break;
+ }
+ }
+
+ addr_in_block += size;
+ length -= size;
+ csw.DataResidue -= size;
+
+ if (addr_in_block >= BlockSize)
+ {
+ addr_in_block = 0;
+ lba++;
+ }
+
+ if ( !length || (stage != PROCESS_CBW)) {
+ csw.Status = (memOK && (stage == PROCESS_CBW)) ? CSW_PASSED : CSW_FAILED;
+ sendCSW();
+ }
+}
+
+
+bool USBMSD::inquiryRequest (void) {
+ uint8_t inquiry[] = { 0x00, 0x80, 0x00, 0x01,
+ 36 - 4, 0x00, 0x00, 0x01,
+ 'M', 'B', 'E', 'D', '.', 'O', 'R', 'G',
+ 'M', 'B', 'E', 'D', ' ', 'U', 'S', 'B', ' ', 'D', 'I', 'S', 'K', ' ', ' ', ' ',
+ '1', '.', '0', ' ',
+ };
+
+ if (BlockCount == 0)
+ inquiry[0] = 0x20; // PERIPHERAL_QUALIFIER = 1 : "A peripheral device is not connected, however usually we do support this type of peripheral"
+
+ if (!write(inquiry, sizeof(inquiry))) {
+ return false;
+ }
+ return true;
+}
+
+
+bool USBMSD::readFormatCapacity() {
+ uint8_t capacity[] = { 0x00, 0x00, 0x00, 0x08,
+ (uint8_t) ((BlockCount >> 24) & 0xff),
+ (uint8_t) ((BlockCount >> 16) & 0xff),
+ (uint8_t) ((BlockCount >> 8) & 0xff),
+ (uint8_t) ((BlockCount >> 0) & 0xff),
+
+ 0x02,
+ (uint8_t) ((BlockSize >> 16) & 0xff),
+ (uint8_t) ((BlockSize >> 8) & 0xff),
+ (uint8_t) ((BlockSize >> 0) & 0xff),
+ };
+ if (!write(capacity, sizeof(capacity))) {
+ return false;
+ }
+ return true;
+}
+
+
+bool USBMSD::readCapacity (void) {
+ uint8_t capacity[] = {
+ (uint8_t) (((BlockCount - 1) >> 24) & 0xff),
+ (uint8_t) (((BlockCount - 1) >> 16) & 0xff),
+ (uint8_t) (((BlockCount - 1) >> 8) & 0xff),
+ (uint8_t) (((BlockCount - 1) >> 0) & 0xff),
+
+ (uint8_t) ((BlockSize >> 24) & 0xff),
+ (uint8_t) ((BlockSize >> 16) & 0xff),
+ (uint8_t) ((BlockSize >> 8) & 0xff),
+ (uint8_t) ((BlockSize >> 0) & 0xff),
+ };
+ if (!write(capacity, sizeof(capacity))) {
+ return false;
+ }
+ return true;
+}
+
+bool USBMSD::write (uint8_t * buf, uint16_t size) {
+ if (size >= cbw.DataLength) {
+ size = cbw.DataLength;
+ }
+ stage = SEND_CSW;
+
+// iprintf("MSD:write: %u bytes\n", size);
+
+ if (!usb->writeNB(MSC_BulkIn.bEndpointAddress, buf, size, MAX_PACKET_SIZE_EPBULK)) {
+ return false;
+ }
+
+// iprintf("MSD:write OK, sending CSW\n");
+
+ csw.DataResidue -= size;
+ csw.Status = CSW_PASSED;
+
+ usb->endpointSetInterrupt(MSC_BulkIn.bEndpointAddress, true);
+
+ return true;
+}
+
+
+bool USBMSD::modeSense6 (void) {
+ uint8_t sense6[] = { 0x03, 0x00, 0x00, 0x00 };
+ if (!write(sense6, sizeof(sense6))) {
+ return false;
+ }
+ return true;
+}
+
+void USBMSD::sendCSW() {
+ csw.Signature = CSW_Signature;
+// iprintf("MSD:SendCSW:\n\tSignature : %lu\n\tTag : %lu\n\tDataResidue: %lu\n\tStatus : %u\n", csw.Signature, csw.Tag, csw.DataResidue, csw.Status);
+ usb->writeNB(MSC_BulkIn.bEndpointAddress, (uint8_t *)&csw, sizeof(CSW), MAX_PACKET_SIZE_EPBULK);
+ stage = WAIT_CSW;
+ usb->endpointSetInterrupt(MSC_BulkIn.bEndpointAddress, true);
+}
+
+bool USBMSD::requestSense (void) {
+ uint8_t request_sense[] = {
+ 0x70,
+ 0x00,
+ 0x05, // Sense Key: illegal request
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x0A,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x30,
+ 0x01,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ };
+
+ if (BlockCount == 0)
+ {
+ request_sense[ 2] = 0x02; // Not Ready
+ request_sense[12] = 0x3A; // Medium not present
+ request_sense[13] = 0x00; // No known reason
+ }
+
+ if (!write(request_sense, sizeof(request_sense))) {
+ return false;
+ }
+
+ return true;
+}
+
+void USBMSD::fail() {
+ csw.Status = CSW_FAILED;
+ sendCSW();
+}
+
+void USBMSD::CBWDecode(uint8_t * buf, uint16_t size) {
+ if (size == sizeof(cbw)) {
+ memcpy((uint8_t *)&cbw, buf, size);
+ if (cbw.Signature == CBW_Signature) {
+ csw.Tag = cbw.Tag;
+ csw.DataResidue = cbw.DataLength;
+ if ((cbw.CBLength < 1) || (cbw.CBLength > 16) ) {
+ iprintf("MSD:Got CBW 0x%02X with invalid CBLength\n", cbw.CB[0]);
+ fail();
+ } else {
+ iprintf("MSD:Got CBW 0x%02X with datalength %lu\n", cbw.CB[0], cbw.DataLength);
+ switch (cbw.CB[0]) {
+ case TEST_UNIT_READY:
+ testUnitReady();
+ break;
+ case REQUEST_SENSE:
+ requestSense();
+ break;
+ case INQUIRY:
+ inquiryRequest();
+ break;
+ case MODE_SENSE6:
+ modeSense6();
+ break;
+ case READ_FORMAT_CAPACITIES:
+ readFormatCapacity();
+ break;
+ case READ_CAPACITY:
+ readCapacity();
+ break;
+ case READ10:
+ case READ12:
+// iprintf("MSD:READ10\n");
+ if (infoTransfer()) {
+ if ((cbw.Flags & 0x80)) {
+ iprintf("MSD: Read %lu blocks from LBA %lu\n", blocks, lba);
+ stage = PROCESS_CBW;
+// memoryRead();
+ usb->endpointSetInterrupt(MSC_BulkIn.bEndpointAddress, true);
+ } else {
+ usb->stallEndpoint(MSC_BulkOut.bEndpointAddress);
+ csw.Status = CSW_ERROR;
+ sendCSW();
+ }
+ }
+ break;
+ case WRITE10:
+ case WRITE12:
+ if (infoTransfer()) {
+ if (!(cbw.Flags & 0x80)) {
+ iprintf("MSD: Write %lu blocks from LBA %lu\n", blocks, lba);
+ stage = PROCESS_CBW;
+ } else {
+ usb->stallEndpoint(MSC_BulkIn.bEndpointAddress);
+ csw.Status = CSW_ERROR;
+ sendCSW();
+ }
+ }
+ break;
+ case VERIFY10:
+ if (!(cbw.CB[1] & 0x02)) {
+ csw.Status = CSW_PASSED;
+ sendCSW();
+ break;
+ }
+ if (infoTransfer()) {
+ if (!(cbw.Flags & 0x80)) {
+ iprintf("MSD: Verify %lu blocks from LBA %lu\n", blocks, lba);
+ stage = PROCESS_CBW;
+ memOK = true;
+ } else {
+ usb->stallEndpoint(MSC_BulkIn.bEndpointAddress);
+ csw.Status = CSW_ERROR;
+ sendCSW();
+ }
+ }
+ break;
+ case START_STOP_UNIT:
+ {
+ switch (cbw.CB[4] & 0x03)
+ {
+ case STARTSTOP_STOPMOTOR:
+ break;
+ case STARTSTOP_STARTMOTOR:
+ break;
+ case STARTSTOP_EJECT:
+ break;
+ case STARTSTOP_LOAD:
+ break;
+ }
+ csw.Status = CSW_PASSED;
+ sendCSW();
+ break;
+ }
+ default:
+ iprintf("MSD: Unhandled SCSI CBW 0x%02X\n", cbw.CB[0]);
+ fail();
+ break;
+ }
+ }
+ }
+ else {
+ iprintf("MSD:Got CBW 0x%02X with bad signature\n", cbw.CB[0]);
+ }
+ }
+ else {
+ iprintf("MSD:Got CBW 0x%02X with bad length: %u (%u)\n", cbw.CB[0], size, sizeof(cbw));
+ }
+}
+
+void USBMSD::testUnitReady (void) {
+
+ if (cbw.DataLength != 0) {
+ if ((cbw.Flags & 0x80) != 0) {
+ usb->stallEndpoint(MSC_BulkIn.bEndpointAddress);
+ } else {
+ usb->stallEndpoint(MSC_BulkOut.bEndpointAddress);
+ }
+ }
+
+ if (BlockCount > 0)
+ csw.Status = CSW_PASSED;
+ else
+ csw.Status = CSW_ERROR;
+
+ sendCSW();
+}
+
+void USBMSD::memoryRead (void) {
+ uint32_t n;
+
+ n = (length > MAX_PACKET_SIZE_EPBULK) ? MAX_PACKET_SIZE_EPBULK : length;
+
+ if (lba > BlockCount) {
+ iprintf("MSD:Attemt to read beyond end of disk! Read LBA %lu > Disk LBAs %lu\n", lba, BlockCount);
+ n = (BlockCount - lba) * BlockSize + addr_in_block;
+ stage = ERROR;
+ }
+
+ // we read an entire block
+ if (addr_in_block == 0)
+ {
+ iprintf("MSD:LBA %lu:", lba);
+ disk->disk_read((char *)page, lba);
+ }
+
+ iprintf(" %u", addr_in_block / MAX_PACKET_SIZE_EPBULK);
+
+ // write data which are in RAM
+ usb->writeNB(MSC_BulkIn.bEndpointAddress, &page[addr_in_block], n, MAX_PACKET_SIZE_EPBULK);
+
+ addr_in_block += n;
+
+ length -= n;
+ csw.DataResidue -= n;
+
+ if (addr_in_block >= BlockSize)
+ {
+ iprintf("\n");
+ addr_in_block = 0;
+ lba++;
+ }
+
+ if ( !length || (stage != PROCESS_CBW)) {
+ csw.Status = (stage == PROCESS_CBW) ? CSW_PASSED : CSW_FAILED;
+ stage = (stage == PROCESS_CBW) ? SEND_CSW : stage;
+ }
+ usb->endpointSetInterrupt(MSC_BulkIn.bEndpointAddress, true);
+}
+
+bool USBMSD::infoTransfer (void) {
+ // Logical Block Address of First Block
+ lba = (cbw.CB[2] << 24) | (cbw.CB[3] << 16) | (cbw.CB[4] << 8) | (cbw.CB[5] << 0);
+
+// addr = lba * BlockSize;
+
+ // Number of Blocks to transfer
+ switch (cbw.CB[0]) {
+ case READ10:
+ case WRITE10:
+ case VERIFY10:
+ blocks = (cbw.CB[7] << 8) | (cbw.CB[8] << 0);
+ break;
+
+ case READ12:
+ case WRITE12:
+ blocks = (cbw.CB[6] << 24) | (cbw.CB[7] << 16) | (cbw.CB[8] << 8) | (cbw.CB[9] << 0);
+ break;
+ }
+
+ if ((lba + blocks) > BlockCount)
+ {
+ csw.Status = CSW_FAILED;
+ sendCSW();
+ return false;
+ }
+
+ length = blocks * BlockSize;
+
+ if (!cbw.DataLength) { // host requests no data
+ csw.Status = CSW_FAILED;
+ sendCSW();
+ return false;
+ }
+
+ if (cbw.DataLength != length) {
+ if ((cbw.Flags & 0x80) != 0) {
+ usb->stallEndpoint(MSC_BulkIn.bEndpointAddress);
+ } else {
+ usb->stallEndpoint(MSC_BulkOut.bEndpointAddress);
+ }
+
+ csw.Status = CSW_FAILED;
+ sendCSW();
+ return false;
+ }
+
+ addr_in_block = 0;
+
+// iprintf("MSD:transferring %lu blocks from LBA %lu.\n", blocks, lba);
+
+ return true;
+}
+
+void USBMSD::on_module_loaded()
+{
+ connect();
+}
+
+bool USBMSD::USBEvent_busReset(void)
+{
+ return true;
+}
+
+bool USBMSD::USBEvent_connectStateChanged(bool connected)
+{
+ return true;
+}
+
+bool USBMSD::USBEvent_suspendStateChanged(bool suspended)
+{
+ return true;
+}