Hi, I have a Micro with the AtMega32U4 chip on it which does USB. I'm trying to make a simple USB Mass Storage. It can return nothing but random data at this stage - I'm just trying to get the device to be detected by my Mac/PC/etc.
I've read lots about LUAF and found lots of examples on the web but I'm trying to write it as a library for the Arduino. The HID, and MIDI examples are useful but have only got me so far. This wiki page is useful too but it doesn't cover the detail needed for a USB noob like myself.
From my research so far my understanding is I have a device class descriptor (provided by PluggableUSB), an interface descriptor (provided by subclass), 2 endpoint descriptors for bulk in and bulk out (provided in interface descriptor). There is also a Control Endpoint by default provided by PluggableUSB for responding to commands (I assume this is where I listen out for the SCSI commands?)
Here is my code so far
// MassStorage.h
#ifndef MSTORAGE_h
#define MSTORAGE_h
#include <stdint.h>
#include <Arduino.h>
#include "PluggableUSB.h"
#if !defined(USBCON)
#error MassStorage can only be used with an USB MCU.
#endif
#define USB_MASS_STORAGE_SUBCLASS 0x06
#define USB_MASS_STORAGE_PROTOCOL 0x50
typedef struct
{
InterfaceDescriptor id;
EndpointDescriptor in;
EndpointDescriptor out;
} MSDDescriptor;
class MassStorage_ : public PluggableUSBModule
{
private:
uint8_t epType[2];
public:
MassStorage_(void);
void start(void);
protected:
// Implementation of the PluggableUSBModule
int getInterface(uint8_t* interfaceCount);
int getDescriptor(USBSetup& setup);
bool setup(USBSetup& setup);
uint8_t getShortName(char* name);
};
extern MassStorage_ MassStorage;
#endif
// MassStorage.cpp
#include "MassStorage.h"
#define BULK_ENDPOINT_OUT pluggedEndpoint
#define BULK_ENDPOINT_IN ((uint8_t)(pluggedEndpoint+1))
MassStorage_ MassStorage;
int MassStorage_::getInterface(uint8_t* interfaceCount)
{
*interfaceCount += 1; // uses 1
MSDDescriptor msdInterface = {
D_INTERFACE(pluggedInterface, 2, USB_DEVICE_CLASS_STORAGE, USB_MASS_STORAGE_SUBCLASS, USB_MASS_STORAGE_PROTOCOL),
D_ENDPOINT(USB_ENDPOINT_IN(BULK_ENDPOINT_IN), USB_ENDPOINT_TYPE_BULK, USB_EP_SIZE, 0x00),
D_ENDPOINT(USB_ENDPOINT_OUT(BULK_ENDPOINT_OUT), USB_ENDPOINT_TYPE_BULK, USB_EP_SIZE, 0x00)
};
return USB_SendControl(0, &msdInterface, sizeof(msdInterface));
}
int MassStorage_::getDescriptor(USBSetup& setup)
{
return 0;
}
uint8_t MassStorage_::getShortName(char *name)
{
name[0] = 'M';
name[1] = 'S';
name[2] = 'D';
return 3;
}
bool MassStorage_::setup(USBSetup& setup)
{
uint8_t request = setup.bRequest;
uint8_t requestType = setup.bmRequestType;
if (requestType == REQUEST_DEVICETOHOST_CLASS_INTERFACE) {
if (request == 0xFF) { // Storage reset
return true;
} else if (request == 0xFE) { // Return Max Lun
uint8_t maxLun = 1;
USB_SendControl(0, &maxLun, sizeof(maxLun));
return true;
}
}
return false;
}
void MassStorage_::start(void)
{
}
MassStorage_::MassStorage_(void) : PluggableUSBModule(2, 2, epType)
{
epType[0] = EP_TYPE_BULK_IN; // ENDPOINT_IN
epType[1] = EP_TYPE_BULK_OUT; // ENDPOINT_OUT
PluggableUSB().plug(this);
}
// Arduino Sketch
#include "MassStorage.h"
void setup() {
MassStorage.start();
}
void loop() {
}
How my understanding of how it works is that the PluggableUSB class takes care of most stuff (device descriptor etc) and you just need to override key methods such as getInterface().
Now I believe the interface descriptor is being sent correctly and it tries to reset the USB Device because I see this in my Console logs for the laptop
default 11:23:46.587681 +0000 kernel USB device 2341803714100002 - fConsecutiveResetCount = 1.
default 11:23:46.988379 +0000 kernel USB device 2341803714100002 - will be reset!
default 11:23:47.227445 +0000 kernel USBMSC Identifier (non-unique): 0x2341 0x8037 0x100
default 11:23:47.248156 +0000 kernel USB device 2341803714100002 - fConsecutiveResetCount = 2.
default 11:23:48.549563 +0000 kernel USB device 2341803714100002 - will be reset!
default 11:23:48.786988 +0000 kernel USBMSC Identifier (non-unique): 0x2341 0x8037 0x100
default 11:23:48.804627 +0000 kernel USB device 2341803714100002 - fConsecutiveResetCount = 3.
default 11:23:50.106117 +0000 kernel USB device 2341803714100002 - will be reset!
default 11:23:50.341132 +0000 kernel USBMSC Identifier (non-unique): 0x2341 0x8037 0x100
default 11:23:51.919567 +0000 kernel USB device 2341803714100002 - fConsecutiveResetCount = 5.
default 11:23:52.920026 +0000 kernel USB device 2341803714100002 -
...
fConsecutiveResetCount > kIOUSBMassStorageMaxConsecutiveResets ( 5 ).
default 11:23:52.920059 +0000 kernel USB device 2341803714100002 - fIOUSBMassStorageDriverNub->terminate().
default 11:23:52.920432 +0000 kernel USB device 2341803714100002 - fConsecutiveResetCount = 6.
default 11:23:52.920735 +0000 kernel IOUSBMassStorageInterfaceNub: bad busy count (0,-1)
Backtrace 0xffffff8000a255a4 0xffffff7f83029e04 0xffffff8000a5582e 0xffffff8000a28839 0xffffff8000a33227 0xffffff800035b0ce 0x0
It does seem something to do with MassStorage is being detected! I believe I'm meant to intercept the "Reset" Config packet and return an acknowledgment packet, which I do by returning true in bool MassStorage_::setup(USBSetup& setup) (assuming this is how you ack from the examples).
I'm also meant to handle the SCSI commands but I don't understand how or where I listen out for them and how I return data to them?
If anyone has any guidance or more experience to point me in the right direction I'd be very grateful. I'm quite out of my comfort zone and I feel that it's something that if the right person just saw it they'd be able to tell me what I'm doing wrong in 5 minutes vs the hours and hours I've spend trying to get this far!