Mass Storage Device using the usb host interface

I've been working on support for usb mass storage devices using the usb host interface. It works good atm. When using inquiry command for example it returns the correct data. But when using read10 command it returns some weird random data. It returns the same data for all sectors. But when resetting the arduino the data changes. I have no idea where this behaviour comes from.

I will upload the code if needed.

I think you will need to upload your code for anyone to have a chance of helping.

MSD.cpp:

#include <Arduino.h>
#include <Usb.h>
#include <LiquidCrystal.h>
#include "MSD.h"

//uint32_t idxIn;
//uint32_t idxOut;

//Capacity cap;

MassStorageDevice::MassStorageDevice(){
}
MassStorageDevice::MassStorageDevice(USBHost UsbHostController, UsbDevice Device, LiquidCrystal lc)
{
usb = UsbHostController;
msc = Device;
lcd = lc;

/*ushort len[2];

usb.getConfDescr(msc->address, 0, 4, 0, (byte*)&len);

byte* data = new byte[len[1]];

usb.getConfDescr(msc->address, 0, len[1], 0, &data[0]);

USB_CONFIGURATION_DESCRIPTOR* Config = (USB_CONFIGURATION_DESCRIPTOR*)data;

data += CONF_DESCR_LEN;

USB_INTERFACE_DESCRIPTOR* Interface0 = (USB_INTERFACE_DESCRIPTOR*)data;

data+=INTR_DESCR_LEN;

for(int i = 0; i < Interface0->bNumEndpoints; i++)
{
USB_ENDPOINT_DESCRIPTOR* Endpoint = (USB_ENDPOINT_DESCRIPTOR*)data;

if((Endpoint->bmAttributes & 3) == 2)
{
if(Endpoint->bEndpointAddress & 0x80 == 0x80)
{
//idxIn = UHD_Pipe_Alloc(msc->address, Endpoint->bEndpointAddress & 0xF, Endpoint->bmAttributes & 3, 0x80, Endpoint->wMaxPacketSize, Endpoint->bInterval, 2);
idxIn = UHD_Pipe_Alloc(msc->address, 2, 2, 0x80, 512, 0, 2);
}
else
{
//idxOut = UHD_Pipe_Alloc(msc->address, Endpoint->bEndpointAddress & 0xF, Endpoint->bmAttributes & 3, 0, Endpoint->wMaxPacketSize, Endpoint->bInterval, 2);
idxOut = UHD_Pipe_Alloc(msc->address, 2, 2, 0, 512, 0, 2);
}
}

data += EP_DESCR_LEN;
} */

idxIn = UHD_Pipe_Alloc(msc->address, 0x82, 2, 0x80, 512, 0, 2);
idxOut = UHD_Pipe_Alloc(msc->address, 0x2, 2, 0, 512, 0, 2);

InquiryResponse resp = SCSI_Inquiry();

lcd->clear();
lcd->setCursor(0, 0);
lcd->print((char*)&resp.ProductID[0]);

cap = SCSI_ReadCapacity10();

uint32_t capacity = cap.dwBlockAddress * cap.dwBlockLength;

lcd->setCursor(0, 1);
lcd->print(capacity);
lcd->print(" B");

delay(100);

SCSI_StartStop(1);

delay(150);

uint8_t buf[512];

lcd->clear();
for(int i = 0; i < 1000; i++)
{
SCSI_Read12(i, 1, &buf[0]);
lcd->setCursor(0, 0);
for(int i = 0; i < 16; i++)
{
lcd->print(buf*, HEX);*

  • }*
  • }*
  • while(true);*
    }
    uint32_t MassStorageDevice::GetBlockSize()
    {
  • return cap.dwBlockLength;*
    }
    InquiryResponse MassStorageDevice::SCSI_Inquiry()
    {
  • CommandBlockWrapper cmd;*
  • cmd.dCBWSignature = MASS_CBW_SIGNATURE;*
  • cmd.dCBWTag = 123;*
  • cmd.dCBWDataTransferLength = sizeof(InquiryResponse);*
  • cmd.bmCBWFlags = MASS_CMD_DIR_IN;*
  • cmd.bmCBWLUN = 0;*
  • cmd.bmCBWCBLength = 6;*
    for (uint8_t i = 0; i < 16; i++) cmd.CBWCB = 0;
    * cmd.CBWCB[0] = SCSI_CMD_INQUIRY;
    _
    cmd.CBWCB[1] = 0 << 5;_
    _
    cmd.CBWCB[4] = sizeof(InquiryResponse);_
    _
    InquiryResponse resp;_
    uint32_t length = sizeof(InquiryResponse);
    SendCommand(&cmd, (uint8_t)&resp, &length, 0);

    * return resp;*
    }
    void MassStorageDevice::SCSI_StartStop(uint8_t ctl)
    {
    * CommandBlockWrapper cbw;*
    * cbw.dCBWSignature = MASS_CBW_SIGNATURE;
    _
    cbw.dCBWTag = 123;_
    _
    cbw.dCBWDataTransferLength = 0;_
    cbw.bmCBWFlags = MASS_CMD_DIR_OUT;
    _
    cbw.bmCBWLUN = 0;_
    _
    cbw.bmCBWCBLength = 6;_
    for (uint8_t i = 0; i < 16; i++)
    _ cbw.CBWCB = 0;_
    cbw.CBWCB[0] = SCSI_CMD_START_STOP_UNIT;
    _ cbw.CBWCB[1] = 0 << 5;
    cbw.CBWCB[4] = ctl & 0x03;
    SendCommand(&cbw, 0, 0, -1);
    }_
    void MassStorageDevice::SCSI_Read12(uint32_t lba, uint32_t nBlocks, uint8_t buf)

    {
    * //SCSI_StartStop(1);
    _ // delay(150);
    CommandBlockWrapper cmd;_
    cmd.dCBWSignature = MASS_CBW_SIGNATURE;
    _ cmd.dCBWTag = 123;
    cmd.dCBWDataTransferLength = cap.dwBlockLength * nBlocks;_

    cmd.bmCBWFlags = MASS_CMD_DIR_IN;
    _ cmd.bmCBWLUN = 0;
    cmd.bmCBWCBLength = 10;
    for (uint8_t i = 0; i < 16; i++) cmd.CBWCB = 0;
    /*cmd.CBWCB[0] = SCSI_CMD_READ_12;
    cmd.CBWCB[1] = 0 << 5;
    cmd.CBWCB[2] = lba & 0xFF;
    cmd.CBWCB[3] = (lba >> 8) & 0xFF;
    cmd.CBWCB[4] = (lba >> 16) & 0xFF;
    cmd.CBWCB[5] = (lba >> 24) & 0xFF;
    cmd.CBWCB[6] = nBlocks & 0xFF;
    cmd.CBWCB[7] = (nBlocks >> 8) & 0xFF;
    cmd.CBWCB[8] = (nBlocks >> 16) & 0xFF;
    cmd.CBWCB[9] = (nBlocks >> 24) & 0xFF;/

    * cmd.CBWCB[0] = SCSI_CMD_READ_10;*
    * cmd.CBWCB[1] = 0 << 5;
    cmd.CBWCB[2] = ((lba >> 24) & 0xff);
    cmd.CBWCB[3] = ((lba >> 16) & 0xff);
    cmd.CBWCB[4] = ((lba >> 8) & 0xff);
    cmd.CBWCB[5] = (lba & 0xff);
    cmd.CBWCB[8] = nBlocks;_

    uint32_t length = cap.dwBlockLength * nBlocks;
    uint32_t error;
    _ error = SendCommand(&cmd, buf, &length, 0);
    if(error != 0)
    {
    lcd->clear();
    lcd->setCursor(0, 0);
    lcd->print("error");
    lcd->setCursor(0, 1);
    lcd->print(error);
    while(true);
    }
    /lcd->clear();
    for(int i = 0; i < 512; i++)

    * {
    lcd->setCursor(0, 0);
    lcd->print(buf);[/color]
    }/

    * //lcd->print(";");
    // lcd->print(buf[200]);
    /lcd->setCursor(0, 0);
    lcd->print(error);

    * lcd->setCursor(0, 1);
    lcd->print(length);/

    }
    Capacity MassStorageDevice::SCSI_ReadCapacity10()
    {
    * CommandBlockWrapper cmd;_
    cmd.dCBWSignature = MASS_CBW_SIGNATURE;
    _ cmd.dCBWTag = 123;
    cmd.dCBWDataTransferLength = sizeof(Capacity);_

    cmd.bmCBWFlags = MASS_CMD_DIR_IN;
    _ cmd.bmCBWLUN = 0;
    cmd.bmCBWCBLength = 10;_

    for (uint8_t i = 0; i < 16; i++) cmd.CBWCB = 0;
    cmd.CBWCB[0] = SCSI_CMD_READ_CAPACITY_10;
    _ cmd.CBWCB[1] = 0 << 5;
    Capacity resp;_

    uint32_t length = sizeof(Capacity);
    SendCommand(&cmd, (uint8_t)&resp, &length, 0);

    * resp.dwBlockAddress = SwapByteOrder_32(resp.dwBlockAddress);
    resp.dwBlockLength = SwapByteOrder_32(resp.dwBlockLength);
    _ return resp;
    }_

    uint32_t MassStorageDevice::SendCommand(CommandBlockWrapper cmd, uint8_t* data, uint32_t* length, int flag)

    {
    UHD_Pipe_Write(idxOut, sizeof(CommandBlockWrapper), (uint8_t*)cmd);
    * UHD_Pipe_Send(idxOut, tokOUT);
    while(!UHD_Pipe_Is_Transfer_Complete(idxOut, tokOUT));
    _ if(flag == 0)
    {_

    UHD_Pipe_Send(idxIn, tokIN);
    while(uhd_byte_count(idxIn) == 0);
    uint32_t l = uhd_byte_count(idxIn);
    UHD_Pipe_Read(idxIn, length, data);
    _ length = l;
    }

    else if(flag == 1)
    {_

    UHD_Pipe_Write(idxOut, length, data);
    UHD_Pipe_Send(idxOut, tokOUT);

    * while(!UHD_Pipe_Is_Transfer_Complete(idxOut, tokOUT));
    _ }_
    UHD_Pipe_Send(idxIn, tokIN);
    while(uhd_byte_count(idxIn) == 0);
    _ CommandStatusWrapper rest;_
    UHD_Pipe_Read(idxIn, sizeof(CommandStatusWrapper), (uint8_t)&rest);

    * if(rest.dCSWTag != 123) while(true);*
    * return rest.bCSWStatus;*
    }
    [/quote]

MSD.h:

#ifndef MSD_H
#define MSD_H

#include <Arduino.h>
#include <Usb.h>
#include <LiquidCrystal.h>

#define hrBUSY 0x01

#define bmREQ_MASSOUT USB_SETUP_HOST_TO_DEVICE|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_INTERFACE
#define bmREQ_MASSIN USB_SETUP_DEVICE_TO_HOST|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_INTERFACE

// Mass Storage Subclass Constants
#define MASS_SUBCLASS_SCSI_NOT_REPORTED 0x00 // De facto use
#define MASS_SUBCLASS_RBC 0x01
#define MASS_SUBCLASS_ATAPI 0x02 // MMC-5 (ATAPI)
#define MASS_SUBCLASS_OBSOLETE1 0x03 // Was QIC-157
#define MASS_SUBCLASS_UFI 0x04 // Specifies how to interface Floppy Disk Drives to USB
#define MASS_SUBCLASS_OBSOLETE2 0x05 // Was SFF-8070i
#define MASS_SUBCLASS_SCSI 0x06 // SCSI Transparent Command Set
#define MASS_SUBCLASS_LSDFS 0x07 // Specifies how host has to negotiate access before trying SCSI
#define MASS_SUBCLASS_IEEE1667 0x08

// Mass Storage Class Protocols
#define MASS_PROTO_CBI 0x00 // CBI (with command completion interrupt)
#define MASS_PROTO_CBI_NO_INT 0x01 // CBI (without command completion interrupt)
#define MASS_PROTO_OBSOLETE 0x02
#define MASS_PROTO_BBB 0x50 // Bulk Only Transport
#define MASS_PROTO_UAS 0x62

// Request Codes
#define MASS_REQ_ADSC 0x00
#define MASS_REQ_GET 0xFC
#define MASS_REQ_PUT 0xFD
#define MASS_REQ_GET_MAX_LUN 0xFE
#define MASS_REQ_BOMSR 0xFF // Bulk-Only Mass Storage Reset

#define MASS_CBW_SIGNATURE 0x43425355
#define MASS_CSW_SIGNATURE 0x53425355

#define MASS_CMD_DIR_OUT (0 << 7)
#define MASS_CMD_DIR_IN (1 << 7)

#define SCSI_CMD_INQUIRY 0x12
#define SCSI_CMD_REPORT_LUNS 0xA0
#define SCSI_CMD_REQUEST_SENSE 0x03
#define SCSI_CMD_FORMAT_UNIT 0x04
#define SCSI_CMD_READ_6 0x08
#define SCSI_CMD_READ_10 0x28
#define SCSI_CMD_READ_12 0xA8
#define SCSI_CMD_READ_CAPACITY_10 0x25
#define SCSI_CMD_TEST_UNIT_READY 0x00
#define SCSI_CMD_WRITE_6 0x0A
#define SCSI_CMD_WRITE_10 0x2A
#define SCSI_CMD_MODE_SENSE_6 0x1A
#define SCSI_CMD_MODE_SENSE_10 0x5A
#define SCSI_CMD_START_STOP_UNIT 0x1B
#define SCSI_CMD_PREVENT_REMOVAL 0x1E
#define SCSI_S_NOT_READY 0x02
#define SCSI_S_MEDIUM_ERROR 0x03
#define SCSI_S_ILLEGAL_REQUEST 0x05
#define SCSI_S_UNIT_ATTENTION 0x06

#define SCSI_ASC_MEDIUM_NOT_PRESENT 0x3A
#define SCSI_ASC_LBA_OUT_OF_RANGE 0x21
#define SCSI_ASC_MEDIA_CHANGED 0x28

#define MASS_ERR_SUCCESS 0x00
#define MASS_ERR_PHASE_ERROR 0x02
#define MASS_ERR_UNIT_NOT_READY 0x03
#define MASS_ERR_UNIT_BUSY 0x04
#define MASS_ERR_STALL 0x05
#define MASS_ERR_CMD_NOT_SUPPORTED 0x06
#define MASS_ERR_INVALID_CSW 0x07
#define MASS_ERR_NO_MEDIA 0x08
#define MASS_ERR_BAD_LBA 0x09
#define MASS_ERR_MEDIA_CHANGED 0x0A
#define MASS_ERR_DEVICE_DISCONNECTED 0x11
#define MASS_ERR_UNABLE_TO_RECOVER 0x12 // Reset recovery error
#define MASS_ERR_INVALID_LUN 0x13
#define MASS_ERR_WRITE_STALL 0x14
#define MASS_ERR_READ_NAKS 0x15
#define MASS_ERR_WRITE_NAKS 0x16
#define MASS_ERR_WRITE_PROTECTED 0x17
#define MASS_ERR_GENERAL_SCSI_ERROR 0xFE
#define MASS_ERR_GENERAL_USB_ERROR 0xFF
#define MASS_ERR_USER 0xA0 // For subclasses to define their own error codes

#define MASS_TRANS_FLG_CALLBACK 0x01 // Callback is involved
#define MASS_TRANS_FLG_NO_STALL_CHECK 0x02 // STALL condition is not checked
#define MASS_TRANS_FLG_NO_PHASE_CHECK 0x04 // PHASE_ERROR is not checked

#define MASS_MAX_ENDPOINTS 3

struct CommandBlockWrapperBase {
uint32_t dCBWSignature;
uint32_t dCBWTag;
uint32_t dCBWDataTransferLength;
uint8_t bmCBWFlags;
}
attribute((packed));

struct CommandBlockWrapper :
public CommandBlockWrapperBase {

struct {
uint8_t bmCBWLUN :
4;
uint8_t bmReserved1 :
4;
};

struct {
uint8_t bmCBWCBLength :
4;
uint8_t bmReserved2 :
4;
};

uint8_t CBWCB[16];
}
attribute((packed));

struct CommandStatusWrapper {
uint32_t dCSWSignature;
uint32_t dCSWTag;
uint32_t dCSWDataResidue;
uint8_t bCSWStatus;
}
attribute((packed));

struct InquiryResponse {
uint8_t DeviceType :
5;
uint8_t 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));

struct Capacity {
//uint8_t data[8];
uint32_t dwBlockAddress;
uint32_t dwBlockLength;
}
attribute((packed));

inline uint32_t SwapByteOrder_32(uint32_t value) {
uint32_t Byte0 = value & 0x000000FF;
uint32_t Byte1 = value & 0x0000FF00;
uint32_t Byte2 = value & 0x00FF0000;
uint32_t Byte3 = value & 0xFF000000;
return (Byte0 << 24) | (Byte1 << 8) | (Byte2 >> 8) | (Byte3 >> 24);
}

class MassStorageDevice
{
public**:**
MassStorageDevice(void);
MassStorageDevice(USBHost UsbHostController, UsbDevice Device, LiquidCrystal lc);
uint32_t GetBlockSize();
void SCSI_Read12(uint32_t lba, uint32_t nBlocks, uint8_t* buf);
private**:**
uint32_t idxIn;
uint32_t idxOut;
Capacity cap;
USBHost usb;
UsbDevice msc;
LiquidCrystal
lcd;
InquiryResponse SCSI_Inquiry();
void SCSI_StartStop(uint8_t ctl);
Capacity SCSI_ReadCapacity10();
uint32_t SendCommand(CommandBlockWrapper* cmd, uint8_t* data, uint32_t* length, int flag);
};

#endif

I found the problem. There is a bug in the function UHD_Pipe_Read. (As described here: Atmel SAM UHD_Pipe_Read can't handle packet size >255 bytes · Issue #82 · arduino/ArduinoCore-sam · GitHub)

I fixed it by putting the fixed version of the function with a different name in my code. Here is it if you want:

uint32_t UHD_Pipe_Read_FIX(uint32_t ul_pipe, uint32_t ul_size, uint8_t* data)
{
uint8_t *ptr_ep_data = 0;
uint16_t nb_byte_received = 0;
uint32_t ul_nb_trans = 0;

// Get information to read data
nb_byte_received = uhd_byte_count(ul_pipe);

ptr_ep_data = (uint8_t *) & uhd_get_pipe_fifo_access(ul_pipe, 8);

// Copy data from pipe to payload buffer
while (ul_size && nb_byte_received) {
*data++ = *ptr_ep_data++;
ul_nb_trans++;
ul_size--;
nb_byte_received--;
}

return ul_nb_trans;
}

Glad you managed to fix the problem.

Riva:
Glad you managed to fix the problem.

Yea, it works great (but not with all usb sticks yet). I tested it with audio streaming from the usb stick. And it sounds so great! (8bit 44100 Hz)