|
|
|
|
|
|
|
|
|
|
|
/* MSC Teensy36 USB Host Mass Storage library |
|
|
|
|
|
* Copyright (c) 2017-2019 Warren Watson. |
|
|
|
|
|
* |
|
|
|
|
|
* 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. |
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
|
|
//MassStorageDriver.cpp |
|
|
|
|
|
|
|
|
|
|
|
#include <Arduino.h> |
|
|
|
|
|
#include "USBHost_t36.h" // Read this header first for key info |
|
|
|
|
|
|
|
|
|
|
|
#define print USBHost::print_ |
|
|
|
|
|
#define println USBHost::println_ |
|
|
|
|
|
|
|
|
|
|
|
// Uncomment this to display function usage and sequencing. |
|
|
|
|
|
//#define DBGprint 1 |
|
|
|
|
|
|
|
|
|
|
|
// Big Endian/Little Endian |
|
|
|
|
|
#define swap32(x) ((x >> 24) & 0xff) | \ |
|
|
|
|
|
((x << 8) & 0xff0000) | \ |
|
|
|
|
|
((x >> 8) & 0xff00) | \ |
|
|
|
|
|
((x << 24) & 0xff000000) |
|
|
|
|
|
|
|
|
|
|
|
void msController::init() |
|
|
|
|
|
{ |
|
|
|
|
|
contribute_Pipes(mypipes, sizeof(mypipes)/sizeof(Pipe_t)); |
|
|
|
|
|
contribute_Transfers(mytransfers, sizeof(mytransfers)/sizeof(Transfer_t)); |
|
|
|
|
|
contribute_String_Buffers(mystring_bufs, sizeof(mystring_bufs)/sizeof(strbuf_t)); |
|
|
|
|
|
driver_ready_for_device(this); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
bool msController::claim(Device_t *dev, int type, const uint8_t *descriptors, uint32_t len) |
|
|
|
|
|
{ |
|
|
|
|
|
println("msController claim this=", (uint32_t)this, HEX); |
|
|
|
|
|
// only claim at interface level |
|
|
|
|
|
|
|
|
|
|
|
if (type != 1) return false; |
|
|
|
|
|
if (len < 9+7+7) return false; // Interface descriptor + 2 endpoint decriptors |
|
|
|
|
|
|
|
|
|
|
|
print_hexbytes(descriptors, len); |
|
|
|
|
|
|
|
|
|
|
|
uint32_t numendpoint = descriptors[4]; |
|
|
|
|
|
if (numendpoint < 1) return false; |
|
|
|
|
|
if (descriptors[5] != 8) return false; // bInterfaceClass, 8 = MASS Storage class |
|
|
|
|
|
if (descriptors[6] != 6) return false; // bInterfaceSubClass, 6 = SCSI transparent command set (SCSI Standards) |
|
|
|
|
|
if (descriptors[7] != 80) return false; // bInterfaceProtocol, 80 = BULK-ONLY TRANSPORT |
|
|
|
|
|
|
|
|
|
|
|
uint8_t desc_index = 9; |
|
|
|
|
|
uint8_t in_index = 0xff, out_index = 0xff; |
|
|
|
|
|
|
|
|
|
|
|
println("numendpoint=", numendpoint, HEX); |
|
|
|
|
|
while (numendpoint--) { |
|
|
|
|
|
if ((descriptors[desc_index] != 7) || (descriptors[desc_index+1] != 5)) return false; // not an end point |
|
|
|
|
|
if (descriptors[desc_index+3] == 2) { // Bulk end point |
|
|
|
|
|
if (descriptors[desc_index+2] & 0x80) |
|
|
|
|
|
in_index = desc_index; |
|
|
|
|
|
else |
|
|
|
|
|
out_index = desc_index; |
|
|
|
|
|
} |
|
|
|
|
|
desc_index += 7; // point to next one... |
|
|
|
|
|
} |
|
|
|
|
|
if ((in_index == 0xff) || (out_index == 0xff)) return false; // did not find end point |
|
|
|
|
|
endpointIn = descriptors[in_index+2]; // bulk-in descriptor 1 81h |
|
|
|
|
|
endpointOut = descriptors[out_index+2]; // bulk-out descriptor 2 02h |
|
|
|
|
|
|
|
|
|
|
|
println("endpointIn=", endpointIn, HEX); |
|
|
|
|
|
println("endpointOut=", endpointOut, HEX); |
|
|
|
|
|
|
|
|
|
|
|
uint32_t sizeIn = descriptors[in_index+4] | (descriptors[in_index+5] << 8); |
|
|
|
|
|
println("packet size in (msController) = ", sizeIn); |
|
|
|
|
|
|
|
|
|
|
|
uint32_t sizeOut = descriptors[out_index+4] | (descriptors[out_index+5] << 8); |
|
|
|
|
|
println("packet size out (msController) = ", sizeOut); |
|
|
|
|
|
packetSizeIn = sizeIn; |
|
|
|
|
|
packetSizeOut = sizeOut; |
|
|
|
|
|
|
|
|
|
|
|
uint32_t intervalIn = descriptors[in_index+6]; |
|
|
|
|
|
uint32_t intervalOut = descriptors[out_index+6]; |
|
|
|
|
|
|
|
|
|
|
|
println("polling intervalIn = ", intervalIn); |
|
|
|
|
|
println("polling intervalOut = ", intervalOut); |
|
|
|
|
|
datapipeIn = new_Pipe(dev, 2, endpointIn, 1, packetSizeIn, intervalIn); |
|
|
|
|
|
datapipeOut = new_Pipe(dev, 2, endpointOut, 0, packetSizeOut, intervalOut); |
|
|
|
|
|
datapipeIn->callback_function = callbackIn; |
|
|
|
|
|
datapipeOut->callback_function = callbackOut; |
|
|
|
|
|
|
|
|
|
|
|
idVendor = dev->idVendor; |
|
|
|
|
|
idProduct = dev->idProduct; |
|
|
|
|
|
hubNumber = dev->hub_address; |
|
|
|
|
|
deviceAddress = dev->address; |
|
|
|
|
|
hubPort = dev->hub_port; // Used for device ID with multiple drives. |
|
|
|
|
|
|
|
|
|
|
|
msOutCompleted = false; |
|
|
|
|
|
msInCompleted = false; |
|
|
|
|
|
msControlCompleted = false; |
|
|
|
|
|
deviceAvailable = true; |
|
|
|
|
|
msDriveInfo.initialized = false; |
|
|
|
|
|
msDriveInfo.connected = true; |
|
|
|
|
|
#ifdef DBGprint |
|
|
|
|
|
Serial.printf(" connected %d\n",msDriveInfo.connected); |
|
|
|
|
|
Serial.printf(" initialized %d\n",msDriveInfo.initialized); |
|
|
|
|
|
#endif |
|
|
|
|
|
return true; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void msController::disconnect() |
|
|
|
|
|
{ |
|
|
|
|
|
deviceAvailable = false; |
|
|
|
|
|
println("Device Disconnected..."); |
|
|
|
|
|
msDriveInfo.connected = false; |
|
|
|
|
|
msDriveInfo.initialized = false; |
|
|
|
|
|
memset(&msDriveInfo, 0, sizeof(msDriveInfo_t)); |
|
|
|
|
|
|
|
|
|
|
|
#ifdef DBGprint |
|
|
|
|
|
Serial.printf(" connected %d\n",msDriveInfo.connected); |
|
|
|
|
|
Serial.printf(" initialized %d\n",msDriveInfo.initialized); |
|
|
|
|
|
#endif |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void msController::control(const Transfer_t *transfer) |
|
|
|
|
|
{ |
|
|
|
|
|
println("control CallbackIn (msController)"); |
|
|
|
|
|
print_hexbytes(report, 8); |
|
|
|
|
|
msControlCompleted = true; |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void msController::callbackIn(const Transfer_t *transfer) |
|
|
|
|
|
{ |
|
|
|
|
|
println("msController CallbackIn (static)"); |
|
|
|
|
|
if (transfer->driver) { |
|
|
|
|
|
print("transfer->qtd.token = "); |
|
|
|
|
|
println(transfer->qtd.token & 255); |
|
|
|
|
|
((msController *)(transfer->driver))->new_dataIn(transfer); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void msController::callbackOut(const Transfer_t *transfer) |
|
|
|
|
|
{ |
|
|
|
|
|
println("msController CallbackOut (static)"); |
|
|
|
|
|
if (transfer->driver) { |
|
|
|
|
|
print("transfer->qtd.token = "); |
|
|
|
|
|
println(transfer->qtd.token & 255); |
|
|
|
|
|
((msController *)(transfer->driver))->new_dataOut(transfer); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void msController::new_dataOut(const Transfer_t *transfer) |
|
|
|
|
|
{ |
|
|
|
|
|
uint32_t len = transfer->length - ((transfer->qtd.token >> 16) & 0x7FFF); |
|
|
|
|
|
println("msController dataOut (static)", len, DEC); |
|
|
|
|
|
print_hexbytes((uint8_t*)transfer->buffer, (len < 32)? len : 32 ); |
|
|
|
|
|
msOutCompleted = true; // Last out transaction is completed. |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void msController::new_dataIn(const Transfer_t *transfer) |
|
|
|
|
|
{ |
|
|
|
|
|
uint32_t len = transfer->length - ((transfer->qtd.token >> 16) & 0x7FFF); |
|
|
|
|
|
println("msController dataIn (static): ", len, DEC); |
|
|
|
|
|
print_hexbytes((uint8_t*)transfer->buffer, (len < 32)? len : 32 ); |
|
|
|
|
|
msInCompleted = true; // Last in transaction is completed. |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Initialize Mass Storage Device |
|
|
|
|
|
uint8_t msController::mscInit(void) { |
|
|
|
|
|
#ifdef DBGprint |
|
|
|
|
|
Serial.printf("mscIint()\n"); |
|
|
|
|
|
#endif |
|
|
|
|
|
uint8_t msResult = MS_CBW_PASS; |
|
|
|
|
|
|
|
|
|
|
|
CBWTag = 0; |
|
|
|
|
|
uint32_t start = millis(); |
|
|
|
|
|
// Check if device is connected. |
|
|
|
|
|
do { |
|
|
|
|
|
if((millis() - start) >= MSC_CONNECT_TIMEOUT) { |
|
|
|
|
|
return MS_NO_MEDIA_ERR; // Not connected Error. |
|
|
|
|
|
} |
|
|
|
|
|
yield(); |
|
|
|
|
|
} while(!available()); |
|
|
|
|
|
|
|
|
|
|
|
msReset(); |
|
|
|
|
|
delay(500); |
|
|
|
|
|
maxLUN = msGetMaxLun(); |
|
|
|
|
|
// msResult = msReportLUNs(&maxLUN); |
|
|
|
|
|
//Serial.printf("maxLUN = %d\n",maxLUN); |
|
|
|
|
|
// delay(150); |
|
|
|
|
|
//------------------------------------------------------- |
|
|
|
|
|
// msResult = msStartStopUnit(1); |
|
|
|
|
|
msResult = WaitMediaReady(); |
|
|
|
|
|
if(msResult) |
|
|
|
|
|
return msResult; |
|
|
|
|
|
|
|
|
|
|
|
// Retrieve drive information. |
|
|
|
|
|
msDriveInfo.initialized = true; |
|
|
|
|
|
msDriveInfo.hubNumber = getHubNumber(); // Which HUB. |
|
|
|
|
|
msDriveInfo.hubPort = getHubPort(); // Which HUB port. |
|
|
|
|
|
msDriveInfo.deviceAddress = getDeviceAddress(); // Device addreess. |
|
|
|
|
|
msDriveInfo.idVendor = getIDVendor(); // USB Vendor ID. |
|
|
|
|
|
msDriveInfo.idProduct = getIDProduct(); // USB Product ID. |
|
|
|
|
|
msResult = msDeviceInquiry(&msInquiry); // Config Info. |
|
|
|
|
|
if(msResult) |
|
|
|
|
|
return msResult; |
|
|
|
|
|
msResult = msReadDeviceCapacity(&msCapacity); // Size Info. |
|
|
|
|
|
if(msResult) |
|
|
|
|
|
return msResult; |
|
|
|
|
|
memcpy(&msDriveInfo.inquiry, &msInquiry, sizeof(msInquiryResponse_t)); |
|
|
|
|
|
memcpy(&msDriveInfo.capacity, &msCapacity, sizeof(msSCSICapacity_t)); |
|
|
|
|
|
return msResult; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
//--------------------------------------------------------------------------- |
|
|
|
|
|
// Perform Mass Storage Reset |
|
|
|
|
|
void msController::msReset() { |
|
|
|
|
|
#ifdef DBGprint |
|
|
|
|
|
Serial.printf("msReset()\n"); |
|
|
|
|
|
#endif |
|
|
|
|
|
mk_setup(setup, 0x21, 0xff, 0, 0, 0); |
|
|
|
|
|
queue_Control_Transfer(device, &setup, NULL, this); |
|
|
|
|
|
while (!msControlCompleted) yield(); |
|
|
|
|
|
msControlCompleted = false; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
//--------------------------------------------------------------------------- |
|
|
|
|
|
// Get MAX LUN |
|
|
|
|
|
uint8_t msController::msGetMaxLun() { |
|
|
|
|
|
#ifdef DBGprint |
|
|
|
|
|
Serial.printf("msGetMaxLun()\n"); |
|
|
|
|
|
#endif |
|
|
|
|
|
report[0] = 0; |
|
|
|
|
|
mk_setup(setup, 0xa1, 0xfe, 0, 0, 1); |
|
|
|
|
|
queue_Control_Transfer(device, &setup, report, this); |
|
|
|
|
|
while (!msControlCompleted) yield(); |
|
|
|
|
|
msControlCompleted = false; |
|
|
|
|
|
maxLUN = report[0]; |
|
|
|
|
|
return maxLUN; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
uint8_t msController::WaitMediaReady() { |
|
|
|
|
|
uint8_t msResult; |
|
|
|
|
|
uint32_t start = millis(); |
|
|
|
|
|
#ifdef DBGprint |
|
|
|
|
|
Serial.printf("WaitMediaReady()\n"); |
|
|
|
|
|
#endif |
|
|
|
|
|
do { |
|
|
|
|
|
if((millis() - start) >= MEDIA_READY_TIMEOUT) { |
|
|
|
|
|
return MS_UNIT_NOT_READY; // Not Ready Error. |
|
|
|
|
|
} |
|
|
|
|
|
msResult = msTestReady(); |
|
|
|
|
|
yield(); |
|
|
|
|
|
} while(msResult == 1); |
|
|
|
|
|
return msResult; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Check if drive is connected and Initialized. |
|
|
|
|
|
uint8_t msController::checkConnectedInitialized(void) { |
|
|
|
|
|
uint8_t msResult = MS_CBW_PASS; |
|
|
|
|
|
#ifdef DBGprint |
|
|
|
|
|
Serial.printf("checkConnectedInitialized()\n"); |
|
|
|
|
|
#endif |
|
|
|
|
|
if(!msDriveInfo.connected) { |
|
|
|
|
|
return MS_NO_MEDIA_ERR; |
|
|
|
|
|
} |
|
|
|
|
|
if(!msDriveInfo.initialized) { |
|
|
|
|
|
msResult = mscInit(); |
|
|
|
|
|
if(msResult != MS_CBW_PASS) return MS_UNIT_NOT_READY; // Not Initialized |
|
|
|
|
|
} |
|
|
|
|
|
return MS_CBW_PASS; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
//--------------------------------------------------------------------------- |
|
|
|
|
|
// Send SCSI Command |
|
|
|
|
|
// Do a complete 3 stage transfer. |
|
|
|
|
|
uint8_t msController::msDoCommand(msCommandBlockWrapper_t *CBW, void *buffer) |
|
|
|
|
|
{ |
|
|
|
|
|
uint8_t CSWResult = 0; |
|
|
|
|
|
mscTransferComplete = false; |
|
|
|
|
|
#ifdef DBGprint |
|
|
|
|
|
Serial.printf("msDoCommand():\n"); |
|
|
|
|
|
#endif |
|
|
|
|
|
if(CBWTag == 0xFFFFFFFF) CBWTag = 1; |
|
|
|
|
|
queue_Data_Transfer(datapipeOut, CBW, sizeof(msCommandBlockWrapper_t), this); // Command stage. |
|
|
|
|
|
while(!msOutCompleted) yield(); |
|
|
|
|
|
msOutCompleted = false; |
|
|
|
|
|
if((CBW->Flags == CMD_DIR_DATA_IN)) { // Data stage from device. |
|
|
|
|
|
queue_Data_Transfer(datapipeIn, buffer, CBW->TransferLength, this); |
|
|
|
|
|
while(!msInCompleted) yield(); |
|
|
|
|
|
msInCompleted = false; |
|
|
|
|
|
} else { // Data stage to device. |
|
|
|
|
|
queue_Data_Transfer(datapipeOut, buffer, CBW->TransferLength, this); |
|
|
|
|
|
while(!msOutCompleted) yield(); |
|
|
|
|
|
msOutCompleted = false; |
|
|
|
|
|
} |
|
|
|
|
|
CSWResult = msGetCSW(); // Status stage. |
|
|
|
|
|
// All stages of this transfer have completed. |
|
|
|
|
|
//Check for special cases. |
|
|
|
|
|
//If test for unit ready command is given then |
|
|
|
|
|
// return the CSW status byte. |
|
|
|
|
|
//Bit 0 == 1 == not ready else |
|
|
|
|
|
//Bit 0 == 0 == ready. |
|
|
|
|
|
//And the Start/Stop Unit command as well. |
|
|
|
|
|
if((CBW->CommandData[0] == CMD_TEST_UNIT_READY) || |
|
|
|
|
|
(CBW->CommandData[0] == CMD_START_STOP_UNIT)) |
|
|
|
|
|
return CSWResult; |
|
|
|
|
|
else // Process possible SCSI errors. |
|
|
|
|
|
return msProcessError(CSWResult); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
//--------------------------------------------------------------------------- |
|
|
|
|
|
// Get Command Status Wrapper |
|
|
|
|
|
uint8_t msController::msGetCSW(void) { |
|
|
|
|
|
#ifdef DBGprint |
|
|
|
|
|
Serial.printf("msGetCSW()\n"); |
|
|
|
|
|
#endif |
|
|
|
|
|
msCommandStatusWrapper_t StatusBlockWrapper = (msCommandStatusWrapper_t) |
|
|
|
|
|
{ |
|
|
|
|
|
.Signature = CSW_SIGNATURE, |
|
|
|
|
|
.Tag = 0, |
|
|
|
|
|
.DataResidue = 0, // TODO: Proccess this if received. |
|
|
|
|
|
.Status = 0 |
|
|
|
|
|
}; |
|
|
|
|
|
queue_Data_Transfer(datapipeIn, &StatusBlockWrapper, sizeof(StatusBlockWrapper), this); |
|
|
|
|
|
while(!msInCompleted) yield(); |
|
|
|
|
|
msInCompleted = false; |
|
|
|
|
|
mscTransferComplete = true; |
|
|
|
|
|
if(StatusBlockWrapper.Signature != CSW_SIGNATURE) return msProcessError(MS_CSW_SIG_ERROR); // Signature error |
|
|
|
|
|
if(StatusBlockWrapper.Tag != CBWTag) return msProcessError(MS_CSW_TAG_ERROR); // Tag mismatch error |
|
|
|
|
|
return StatusBlockWrapper.Status; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
//--------------------------------------------------------------------------- |
|
|
|
|
|
// Test Unit Ready |
|
|
|
|
|
uint8_t msController::msTestReady() { |
|
|
|
|
|
#ifdef DBGprint |
|
|
|
|
|
Serial.printf("msTestReady()\n"); |
|
|
|
|
|
#endif |
|
|
|
|
|
msCommandBlockWrapper_t CommandBlockWrapper = (msCommandBlockWrapper_t) |
|
|
|
|
|
{ |
|
|
|
|
|
.Signature = CBW_SIGNATURE, |
|
|
|
|
|
.Tag = ++CBWTag, |
|
|
|
|
|
.TransferLength = 0, |
|
|
|
|
|
.Flags = CMD_DIR_DATA_IN, |
|
|
|
|
|
.LUN = currentLUN, |
|
|
|
|
|
.CommandLength = 6, |
|
|
|
|
|
.CommandData = {CMD_TEST_UNIT_READY, 0x00, 0x00, 0x00, 0x00, 0x00} |
|
|
|
|
|
}; |
|
|
|
|
|
queue_Data_Transfer(datapipeOut, &CommandBlockWrapper, sizeof(CommandBlockWrapper), this); |
|
|
|
|
|
while(!msOutCompleted) yield(); |
|
|
|
|
|
msOutCompleted = false; |
|
|
|
|
|
return msGetCSW(); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
//--------------------------------------------------------------------------- |
|
|
|
|
|
// Start/Stop unit |
|
|
|
|
|
uint8_t msController::msStartStopUnit(uint8_t mode) { |
|
|
|
|
|
#ifdef DBGprint |
|
|
|
|
|
Serial.printf("msStartStopUnit()\n"); |
|
|
|
|
|
#endif |
|
|
|
|
|
msCommandBlockWrapper_t CommandBlockWrapper = (msCommandBlockWrapper_t) |
|
|
|
|
|
{ |
|
|
|
|
|
.Signature = CBW_SIGNATURE, |
|
|
|
|
|
.Tag = ++CBWTag, |
|
|
|
|
|
.TransferLength = 0, |
|
|
|
|
|
.Flags = CMD_DIR_DATA_IN, |
|
|
|
|
|
.LUN = currentLUN, |
|
|
|
|
|
.CommandLength = 6, |
|
|
|
|
|
.CommandData = {CMD_START_STOP_UNIT, 0x01, 0x00, 0x00, mode, 0x00} |
|
|
|
|
|
}; |
|
|
|
|
|
queue_Data_Transfer(datapipeOut, &CommandBlockWrapper, sizeof(CommandBlockWrapper), this); |
|
|
|
|
|
while(!msOutCompleted) yield(); |
|
|
|
|
|
msOutCompleted = false; |
|
|
|
|
|
return msGetCSW(); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
//--------------------------------------------------------------------------- |
|
|
|
|
|
// Read Mass Storage Device Capacity (Number of Blocks and Block Size) |
|
|
|
|
|
uint8_t msController::msReadDeviceCapacity(msSCSICapacity_t * const Capacity) { |
|
|
|
|
|
#ifdef DBGprint |
|
|
|
|
|
Serial.printf("msReadDeviceCapacity()\n"); |
|
|
|
|
|
#endif |
|
|
|
|
|
uint8_t result = 0; |
|
|
|
|
|
msCommandBlockWrapper_t CommandBlockWrapper = (msCommandBlockWrapper_t) |
|
|
|
|
|
{ |
|
|
|
|
|
.Signature = CBW_SIGNATURE, |
|
|
|
|
|
.Tag = ++CBWTag, |
|
|
|
|
|
.TransferLength = sizeof(msSCSICapacity_t), |
|
|
|
|
|
.Flags = CMD_DIR_DATA_IN, |
|
|
|
|
|
.LUN = currentLUN, |
|
|
|
|
|
.CommandLength = 10, |
|
|
|
|
|
.CommandData = {CMD_RD_CAPACITY_10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00} |
|
|
|
|
|
}; |
|
|
|
|
|
result = msDoCommand(&CommandBlockWrapper, Capacity); |
|
|
|
|
|
Capacity->Blocks = swap32(Capacity->Blocks); |
|
|
|
|
|
Capacity->BlockSize = swap32(Capacity->BlockSize); |
|
|
|
|
|
return result; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
//--------------------------------------------------------------------------- |
|
|
|
|
|
// Do Mass Storage Device Inquiry |
|
|
|
|
|
uint8_t msController::msDeviceInquiry(msInquiryResponse_t * const Inquiry) |
|
|
|
|
|
{ |
|
|
|
|
|
#ifdef DBGprint |
|
|
|
|
|
Serial.printf("msDeviceInquiry()\n"); |
|
|
|
|
|
#endif |
|
|
|
|
|
msCommandBlockWrapper_t CommandBlockWrapper = (msCommandBlockWrapper_t) |
|
|
|
|
|
{ |
|
|
|
|
|
.Signature = CBW_SIGNATURE, |
|
|
|
|
|
.Tag = ++CBWTag, |
|
|
|
|
|
.TransferLength = sizeof(msInquiryResponse_t), |
|
|
|
|
|
.Flags = CMD_DIR_DATA_IN, |
|
|
|
|
|
.LUN = currentLUN, |
|
|
|
|
|
.CommandLength = 6, |
|
|
|
|
|
.CommandData = {CMD_INQUIRY,0x00,0x00,0x00,sizeof(msInquiryResponse_t),0x00} |
|
|
|
|
|
}; |
|
|
|
|
|
return msDoCommand(&CommandBlockWrapper, Inquiry); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
//--------------------------------------------------------------------------- |
|
|
|
|
|
// Request Sense Data |
|
|
|
|
|
uint8_t msController::msRequestSense(msRequestSenseResponse_t * const Sense) |
|
|
|
|
|
{ |
|
|
|
|
|
#ifdef DBGprint |
|
|
|
|
|
Serial.printf("msRequestSense()\n"); |
|
|
|
|
|
#endif |
|
|
|
|
|
msCommandBlockWrapper_t CommandBlockWrapper = (msCommandBlockWrapper_t) |
|
|
|
|
|
{ |
|
|
|
|
|
.Signature = CBW_SIGNATURE, |
|
|
|
|
|
.Tag = ++CBWTag, |
|
|
|
|
|
.TransferLength = sizeof(msRequestSenseResponse_t), |
|
|
|
|
|
.Flags = CMD_DIR_DATA_IN, |
|
|
|
|
|
.LUN = currentLUN, |
|
|
|
|
|
.CommandLength = 6, |
|
|
|
|
|
.CommandData = {CMD_REQUEST_SENSE, 0x00, 0x00, 0x00, sizeof(msRequestSenseResponse_t), 0x00} |
|
|
|
|
|
}; |
|
|
|
|
|
return msDoCommand(&CommandBlockWrapper, Sense); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
//--------------------------------------------------------------------------- |
|
|
|
|
|
// Report LUNs |
|
|
|
|
|
uint8_t msController::msReportLUNs(uint8_t *Buffer) |
|
|
|
|
|
{ |
|
|
|
|
|
#ifdef DBGprint |
|
|
|
|
|
Serial.printf("msReportLuns()\n"); |
|
|
|
|
|
#endif |
|
|
|
|
|
msCommandBlockWrapper_t CommandBlockWrapper = (msCommandBlockWrapper_t) |
|
|
|
|
|
{ |
|
|
|
|
|
.Signature = CBW_SIGNATURE, |
|
|
|
|
|
.Tag = ++CBWTag, |
|
|
|
|
|
.TransferLength = MAXLUNS, |
|
|
|
|
|
.Flags = CMD_DIR_DATA_IN, |
|
|
|
|
|
.LUN = currentLUN, |
|
|
|
|
|
.CommandLength = 12, |
|
|
|
|
|
.CommandData = {CMD_REPORT_LUNS, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, MAXLUNS, 0x00, 0x00} |
|
|
|
|
|
}; |
|
|
|
|
|
return msDoCommand(&CommandBlockWrapper, Buffer); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//--------------------------------------------------------------------------- |
|
|
|
|
|
// Read Sectors (Multi Sector Capable) |
|
|
|
|
|
uint8_t msController::msReadBlocks( |
|
|
|
|
|
const uint32_t BlockAddress, |
|
|
|
|
|
const uint16_t Blocks, |
|
|
|
|
|
const uint16_t BlockSize, |
|
|
|
|
|
void * sectorBuffer) |
|
|
|
|
|
{ |
|
|
|
|
|
#ifdef DBGprint |
|
|
|
|
|
Serial.printf("msReadBlocks()\n"); |
|
|
|
|
|
#endif |
|
|
|
|
|
uint8_t BlockHi = (Blocks >> 8) & 0xFF; |
|
|
|
|
|
uint8_t BlockLo = Blocks & 0xFF; |
|
|
|
|
|
msCommandBlockWrapper_t CommandBlockWrapper = (msCommandBlockWrapper_t) |
|
|
|
|
|
{ |
|
|
|
|
|
|
|
|
|
|
|
.Signature = CBW_SIGNATURE, |
|
|
|
|
|
.Tag = ++CBWTag, |
|
|
|
|
|
.TransferLength = (uint32_t)(Blocks * BlockSize), |
|
|
|
|
|
.Flags = CMD_DIR_DATA_IN, |
|
|
|
|
|
.LUN = currentLUN, |
|
|
|
|
|
.CommandLength = 10, |
|
|
|
|
|
.CommandData = {CMD_RD_10, 0x00, |
|
|
|
|
|
(uint8_t)(BlockAddress >> 24), |
|
|
|
|
|
(uint8_t)(BlockAddress >> 16), |
|
|
|
|
|
(uint8_t)(BlockAddress >> 8), |
|
|
|
|
|
(uint8_t)(BlockAddress & 0xFF), |
|
|
|
|
|
0x00, BlockHi, BlockLo, 0x00} |
|
|
|
|
|
}; |
|
|
|
|
|
return msDoCommand(&CommandBlockWrapper, sectorBuffer); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
//--------------------------------------------------------------------------- |
|
|
|
|
|
// Write Sectors (Multi Sector Capable) |
|
|
|
|
|
uint8_t msController::msWriteBlocks( |
|
|
|
|
|
const uint32_t BlockAddress, |
|
|
|
|
|
const uint16_t Blocks, |
|
|
|
|
|
const uint16_t BlockSize, |
|
|
|
|
|
const void * sectorBuffer) |
|
|
|
|
|
{ |
|
|
|
|
|
#ifdef DBGprint |
|
|
|
|
|
Serial.printf("msWriteBlocks()\n"); |
|
|
|
|
|
#endif |
|
|
|
|
|
uint8_t BlockHi = (Blocks >> 8) & 0xFF; |
|
|
|
|
|
uint8_t BlockLo = Blocks & 0xFF; |
|
|
|
|
|
msCommandBlockWrapper_t CommandBlockWrapper = (msCommandBlockWrapper_t) |
|
|
|
|
|
{ |
|
|
|
|
|
.Signature = CBW_SIGNATURE, |
|
|
|
|
|
.Tag = ++CBWTag, |
|
|
|
|
|
.TransferLength = (uint32_t)(Blocks * BlockSize), |
|
|
|
|
|
.Flags = CMD_DIR_DATA_OUT, |
|
|
|
|
|
.LUN = currentLUN, |
|
|
|
|
|
.CommandLength = 10, |
|
|
|
|
|
.CommandData = {CMD_WR_10, 0x00, |
|
|
|
|
|
(uint8_t)(BlockAddress >> 24), |
|
|
|
|
|
(uint8_t)(BlockAddress >> 16), |
|
|
|
|
|
(uint8_t)(BlockAddress >> 8), |
|
|
|
|
|
(uint8_t)(BlockAddress & 0xFF), |
|
|
|
|
|
0x00, BlockHi, BlockLo, 0x00} |
|
|
|
|
|
}; |
|
|
|
|
|
return msDoCommand(&CommandBlockWrapper, (void *)sectorBuffer); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Proccess Possible SCSI errors |
|
|
|
|
|
uint8_t msController::msProcessError(uint8_t msStatus) { |
|
|
|
|
|
#ifdef DBGprint |
|
|
|
|
|
Serial.printf("msProcessError()\n"); |
|
|
|
|
|
#endif |
|
|
|
|
|
uint8_t msResult = 0; |
|
|
|
|
|
switch(msStatus) { |
|
|
|
|
|
case MS_CBW_PASS: |
|
|
|
|
|
return MS_CBW_PASS; |
|
|
|
|
|
break; |
|
|
|
|
|
case MS_CBW_PHASE_ERROR: |
|
|
|
|
|
Serial.printf("SCSI Phase Error: %d\n",msStatus); |
|
|
|
|
|
return MS_SCSI_ERROR; |
|
|
|
|
|
break; |
|
|
|
|
|
case MS_CSW_TAG_ERROR: |
|
|
|
|
|
Serial.printf("CSW Tag Error: %d\n",MS_CSW_TAG_ERROR); |
|
|
|
|
|
return MS_CSW_TAG_ERROR; |
|
|
|
|
|
break; |
|
|
|
|
|
case MS_CSW_SIG_ERROR: |
|
|
|
|
|
Serial.printf("CSW Signature Error: %d\n",MS_CSW_SIG_ERROR); |
|
|
|
|
|
return MS_CSW_SIG_ERROR; |
|
|
|
|
|
break; |
|
|
|
|
|
case MS_CBW_FAIL: |
|
|
|
|
|
msResult = msRequestSense(&msSense); |
|
|
|
|
|
if(msResult) return msResult; |
|
|
|
|
|
switch(msSense.SenseKey) { |
|
|
|
|
|
case MS_UNIT_ATTENTION: |
|
|
|
|
|
switch(msSense.AdditionalSenseCode) { |
|
|
|
|
|
case MS_MEDIA_CHANGED: |
|
|
|
|
|
return MS_MEDIA_CHANGED_ERR; |
|
|
|
|
|
break; |
|
|
|
|
|
default: |
|
|
|
|
|
msStatus = MS_UNIT_NOT_READY; |
|
|
|
|
|
} |
|
|
|
|
|
case MS_NOT_READY: |
|
|
|
|
|
switch(msSense.AdditionalSenseCode) { |
|
|
|
|
|
case MS_MEDIUM_NOT_PRESENT: |
|
|
|
|
|
msStatus = MS_NO_MEDIA_ERR; |
|
|
|
|
|
break; |
|
|
|
|
|
default: |
|
|
|
|
|
msStatus = MS_UNIT_NOT_READY; |
|
|
|
|
|
} |
|
|
|
|
|
case MS_ILLEGAL_REQUEST: |
|
|
|
|
|
switch(msSense.AdditionalSenseCode) { |
|
|
|
|
|
case MS_LBA_OUT_OF_RANGE: |
|
|
|
|
|
msStatus = MS_BAD_LBA_ERR; |
|
|
|
|
|
break; |
|
|
|
|
|
default: |
|
|
|
|
|
msStatus = MS_CMD_ERR; |
|
|
|
|
|
} |
|
|
|
|
|
default: |
|
|
|
|
|
msStatus = MS_SCSI_ERROR; |
|
|
|
|
|
} |
|
|
|
|
|
default: |
|
|
|
|
|
Serial.printf("SCSI Error: %d\n",msStatus); |
|
|
|
|
|
return msStatus; |
|
|
|
|
|
} |
|
|
|
|
|
} |