Преглед на файлове

Merge pull request #42 from wwatson4506/master

Addition of Mass Storage Driver to this library
main
Paul Stoffregen преди 4 години
родител
ревизия
89bc6d712e
No account linked to committer's email address
променени са 4 файла, в които са добавени 941 реда и са изтрити 0 реда
  1. +596
    -0
      MassStorageDriver.cpp
  2. +79
    -0
      USBHost_t36.h
  3. +96
    -0
      examples/mscTesting/mscTesting.ino
  4. +170
    -0
      utility/msc.h

+ 596
- 0
MassStorageDriver.cpp Целия файл

@@ -0,0 +1,596 @@
/* 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;
}
}

+ 79
- 0
USBHost_t36.h Целия файл

@@ -30,6 +30,7 @@
#error "USBHost_t36 only works with Teensy 3.6 or Teensy 4.x. Please select it in Tools > Boards"
#endif
#include "utility/imxrt_usbhs.h"
#include "utility/msc.h"

// Dear inquisitive reader, USB is a complex protocol defined with
// very specific terminology. To have any chance of understand this
@@ -1958,5 +1959,83 @@ private:
Transfer_t mytransfers[7] __attribute__ ((aligned(32)));
};

//--------------------------------------------------------------------------
class msController : public USBDriver {
public:
msController(USBHost &host) { init(); }
msController(USBHost *host) { init(); }

msSCSICapacity_t msCapacity;
msInquiryResponse_t msInquiry;
msRequestSenseResponse_t msSense;
msDriveInfo_t msDriveInfo;

bool mscTransferComplete = false;
uint8_t mscInit(void);
void msReset();
uint8_t msGetMaxLun();
void msCurrentLun(uint8_t lun) {currentLUN = lun;}
uint8_t msCurrentLun() {return currentLUN;}
bool available() { delay(0); return deviceAvailable; }
uint8_t checkConnectedInitialized(void);
uint16_t getIDVendor() {return idVendor; }
uint16_t getIDProduct() {return idProduct; }
uint8_t getHubNumber() { return hubNumber; }
uint8_t getHubPort() { return hubPort; }
uint8_t getDeviceAddress() { return deviceAddress; }
uint8_t WaitMediaReady();
uint8_t msTestReady();
uint8_t msReportLUNs(uint8_t *Buffer);
uint8_t msStartStopUnit(uint8_t mode);
uint8_t msReadDeviceCapacity(msSCSICapacity_t * const Capacity);
uint8_t msDeviceInquiry(msInquiryResponse_t * const Inquiry);
uint8_t msProcessError(uint8_t msStatus);
uint8_t msRequestSense(msRequestSenseResponse_t * const Sense);
uint8_t msRequestSense(void *Sense);

uint8_t msReadBlocks(const uint32_t BlockAddress, const uint16_t Blocks,
const uint16_t BlockSize, void * sectorBuffer);
uint8_t msWriteBlocks(const uint32_t BlockAddress, const uint16_t Blocks,
const uint16_t BlockSize, const void * sectorBuffer);
protected:
virtual bool claim(Device_t *device, int type, const uint8_t *descriptors, uint32_t len);
virtual void control(const Transfer_t *transfer);
virtual void disconnect();
static void callbackIn(const Transfer_t *transfer);
static void callbackOut(const Transfer_t *transfer);
void new_dataIn(const Transfer_t *transfer);
void new_dataOut(const Transfer_t *transfer);
void init();
uint8_t msDoCommand(msCommandBlockWrapper_t *CBW, void *buffer);
uint8_t msGetCSW(void);
private:
Pipe_t mypipes[3] __attribute__ ((aligned(32)));
Transfer_t mytransfers[7] __attribute__ ((aligned(32)));
strbuf_t mystring_bufs[1];
uint32_t packetSizeIn;
uint32_t packetSizeOut;
Pipe_t *datapipeIn;
Pipe_t *datapipeOut;
uint32_t endpointIn = 0;
uint32_t endpointOut = 0;
setup_t setup;
uint8_t report[8];
uint8_t maxLUN = 0;
uint8_t currentLUN = 0;
// msSCSICapacity_t msCapacity;
// msInquiryResponse_t msInquiry;
// msRequestSenseResponse_t msSense;
uint16_t idVendor = 0;
uint16_t idProduct = 0;
uint8_t hubNumber = 0;
uint8_t hubPort = 0;
uint8_t deviceAddress = 0;
volatile bool msOutCompleted = false;
volatile bool msInCompleted = false;
volatile bool msControlCompleted = false;
uint32_t CBWTag = 0;
bool deviceAvailable = false;
};


#endif

+ 96
- 0
examples/mscTesting/mscTesting.ino Целия файл

@@ -0,0 +1,96 @@
//Copyright 2020 by Warren Watson
// Version 02-Feb-20
//
#include "Arduino.h"
#include "USBHost_t36.h"

USBHost myusb;
USBHub hub1(myusb);
USBHub hub2(myusb);
USBHub hub3(myusb);
USBHub hub4(myusb);

msController msDrive1(myusb);
msController msDrive2(myusb);

// Show USB drive information for the selected USB drive.
int showUSBDriveInfo(msController *drive) {
if(drive == &msDrive1) {
Serial.printf(F("msDrive1 is "));
} else {
Serial.printf(F("msDrive2 is "));
}
if(drive->msDriveInfo.mounted == true) {// check if mounted.
Serial.printf(F("Mounted\n\n"));
} else {
Serial.printf(F("NOT Mounted\n\n"));
}
// Now we will print out the information.
Serial.printf(F(" connected %d\n"),drive->msDriveInfo.connected);
Serial.printf(F(" initialized %d\n"),drive->msDriveInfo.initialized);
Serial.printf(F(" USB Vendor ID: %4.4x\n"),drive->msDriveInfo.idVendor);
Serial.printf(F(" USB Product ID: %4.4x\n"),drive->msDriveInfo.idProduct);
Serial.printf(F(" HUB Number: %d\n"),drive->msDriveInfo.hubNumber);
Serial.printf(F(" HUB Port: %d\n"),drive->msDriveInfo.hubPort);
Serial.printf(F(" Device Address: %d\n"),drive->msDriveInfo.deviceAddress);
Serial.printf(F("Removable Device: "));
if(drive->msDriveInfo.inquiry.Removable == 1)
Serial.printf(F("YES\n"));
else
Serial.printf(F("NO\n"));
Serial.printf(F(" VendorID: %8.8s\n"),drive->msDriveInfo.inquiry.VendorID);
Serial.printf(F(" ProductID: %16.16s\n"),drive->msDriveInfo.inquiry.ProductID);
Serial.printf(F(" RevisionID: %4.4s\n"),drive->msDriveInfo.inquiry.RevisionID);
Serial.printf(F(" Version: %d\n"),drive->msDriveInfo.inquiry.Version);
Serial.printf(F(" Sector Count: %ld\n"),drive->msDriveInfo.capacity.Blocks);
Serial.printf(F(" Sector size: %ld\n"),drive->msDriveInfo.capacity.BlockSize);
Serial.printf(F(" Disk Capacity: %.f Bytes\n\n"),(double_t)drive->msDriveInfo.capacity.Blocks *
(double_t)drive->msDriveInfo.capacity.BlockSize);
return 0;
}

static uint8_t mscError = 0;

void setup() {
while(!Serial);

Serial.printf(F("\n\nMSC TEST\n\n"));
Serial.printf(F("Initializing USB Drive(s)\n"));
myusb.begin();

if(mscError = msDrive1.mscInit())
Serial.printf(F("msDrive1 not connected: Code: %d\n\n"), mscError);
else
Serial.printf(F("msDrive1 connected\n"));
if(mscError = msDrive2.mscInit())
Serial.printf(F("msDrive2 not connected: Code: %d\n\n"), mscError);
else
Serial.printf(F("msDrive2 connected\n"));

}

void loop() {
char op = 0;
Serial.printf(F("\nPress a key to show USB drive info:\n\n"));

while(!Serial.available()) yield();
op = Serial.read();
if(Serial.available()) Serial.read(); // Get rid of CR or LF if there.

// Check if msDrive1 is plugged in and initialized
if((mscError = msDrive1.checkConnectedInitialized()) != MS_INIT_PASS) {
Serial.printf(F("msDrive1 not connected: Code: %d\n\n"), mscError);
} else {
Serial.printf(F("msDrive1 connected/initilized\n"));
showUSBDriveInfo(&msDrive1);
}
// Check if msDrive2 is plugged in and initialized
if((mscError = msDrive2.checkConnectedInitialized()) != MS_INIT_PASS) {
Serial.printf(F("msDrive2 not connected: Code: %d\n\n"), mscError);
} else {
Serial.printf(F("msDrive2 connected/initilized\n"));
showUSBDriveInfo(&msDrive2);
}
}

+ 170
- 0
utility/msc.h Целия файл

@@ -0,0 +1,170 @@
/*
* 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.
*/
// msc.h

#ifndef _MSC_H_
#define _MSC_H_

// SCSI Commands (minimal set)
#define CBW_SIGNATURE 0x43425355UL
#define CSW_SIGNATURE 0x53425355UL
#define CMD_DIR_DATA_OUT 0x00
#define CMD_DIR_DATA_IN 0x80
#define CMD_RD_CAPACITY_10 0x25
#define CMD_INQUIRY 0x12
#define CMD_RD_10 0x28
#define CMD_WR_10 0x2A
#define CMD_TEST_UNIT_READY 0x00
#define CMD_REQUEST_SENSE 0x03
#define CMD_START_STOP_UNIT 0x1B
#define CMD_REPORT_LUNS 0xA0
#define NO_RD_WR 0x00 // transfer type is not read or write blocks.

// Command Status Wrapper Error Codes
#define MS_CBW_PASS 0
#define MS_CBW_FAIL 1
#define MS_CBW_PHASE_ERROR 2
#define MS_CSW_TAG_ERROR 253
#define MS_CSW_SIG_ERROR 254
#define MS_SCSI_ERROR 255

// SCSI Sense Key codes
#define MS_NOT_READY 0x02
#define MS_MEDIUM_ERROR 0x03
#define MS_ILLEGAL_REQUEST 0x05
#define MS_UNIT_ATTENTION 0x06
#define MS_LBA_OUT_OF_RANGE 0x21
#define MS_MEDIA_CHANGED 0x28
#define MS_MEDIUM_NOT_PRESENT 0x3A

// SCSI Error Codes
#define MS_MEDIA_CHANGED_ERR 0x2A
#define MS_NO_MEDIA_ERR 0x28
#define MS_UNIT_NOT_READY 0x23
#define MS_BAD_LBA_ERR 0x29
#define MS_CMD_ERR 0x26

#define MS_INIT_PASS 0
#define MAXLUNS 16

// These two defines are timeouts for detecting a connected drive
// and waiting for it to be operational.
#define MEDIA_READY_TIMEOUT 1000
#define MSC_CONNECT_TIMEOUT 4000

// Command Block Wrapper Struct
typedef struct
{
uint32_t Signature;
uint32_t Tag;
uint32_t TransferLength;
uint8_t Flags;
uint8_t LUN;
uint8_t CommandLength;
uint8_t CommandData[16];
} __attribute__((packed)) msCommandBlockWrapper_t;

// MSC Command Status Wrapper Struct
typedef struct
{
uint32_t Signature;
uint32_t Tag;
uint32_t DataResidue;
uint8_t Status;
} __attribute__((packed)) msCommandStatusWrapper_t;

// MSC Device Capacity Struct
typedef struct
{
uint32_t Blocks;
uint32_t BlockSize;
} msSCSICapacity_t;

// MSC Inquiry Command Reponse Struct
typedef struct
{
unsigned DeviceType : 5;
unsigned PeripheralQualifier : 3;
unsigned Reserved : 7;
unsigned Removable : 1;
uint8_t Version;
unsigned ResponseDataFormat : 4;
unsigned Reserved2 : 1;
unsigned NormACA : 1;
unsigned TrmTsk : 1;
unsigned AERC : 1;
uint8_t AdditionalLength;
uint8_t Reserved3[2];
unsigned SoftReset : 1;
unsigned CmdQue : 1;
unsigned Reserved4 : 1;
unsigned Linked : 1;
unsigned Sync : 1;
unsigned WideBus16Bit : 1;
unsigned WideBus32Bit : 1;
unsigned RelAddr : 1;
uint8_t VendorID[8];
uint8_t ProductID[16];
uint8_t RevisionID[4];
} __attribute__((packed)) msInquiryResponse_t;

// Request Sense Response Struct
typedef struct
{
uint8_t ResponseCode;
uint8_t SegmentNumber;
unsigned SenseKey : 4;
unsigned Reserved : 1;
unsigned ILI : 1;
unsigned EOM : 1;
unsigned FileMark : 1;
uint8_t Information[4];
uint8_t AdditionalLength;
uint8_t CmdSpecificInformation[4];
uint8_t AdditionalSenseCode;
uint8_t AdditionalSenseQualifier;
uint8_t FieldReplaceableUnitCode;
uint8_t SenseKeySpecific[3];
uint8_t padding[234];
} __attribute__((packed)) msRequestSenseResponse_t;

// MSC Drive status/info struct
typedef struct {
bool connected = false; // Device is connected
bool initialized = false; // Device is initialized
bool mounted = false; // Device is mounted
const char * drvName = 0;
uint32_t bufferSize = 0;
uint8_t hubNumber;
uint8_t hubPort;
uint8_t deviceAddress;
uint16_t idVendor;
uint16_t idProduct;
msSCSICapacity_t capacity;
msInquiryResponse_t inquiry;
} __attribute__((packed)) msDriveInfo_t;

#endif //_MSC_H_

Loading…
Отказ
Запис