USBHost_t36 USB Mass Storage Driver Experiments

Both teensy devices should be running the same sketch included below. If the test is performed without the second teensy connected to the USB host port, it will complete successfully. If the second teensy is connected to the USB host port, there is a very high probability that it will fail. Even reducing the frequency that the second teensy sends HID packets to 1 packet / 500 ms still has a high probability of causing the driver to fail.

Tried with a different USB hub and it seems to be working fine.
 
Here is a minimal setup to cause the MassStorageDriver to fail.

Both teensy devices should be running the same sketch included below. If the test is performed without the second teensy connected to the USB host port, it will complete successfully. If the second teensy is connected to the USB host port, there is a very high probability that it will fail. Even reducing the frequency that the second teensy sends HID packets to 1 packet / 500 ms still has a high probability of causing the driver to fail.

View attachment 30762

Code:
// Make sure to compile for RawHID USB type. 
// This sketch assumes you have migrated the latest changes to the MassStorageDriver

#include "USBHost_t36.h"

USBHost myusb;
USBHub hub1(myusb);
USBDrive usbDrive(myusb);
USBFilesystem usbPart(myusb);
USBHIDParser hid1(myusb);
RawHIDController rawHID1(myusb);

#define LEDPIN 13

void setup() {

    Serial.begin(9600);
    myusb.begin();

    pinMode(LEDPIN, OUTPUT);

    rawHID1.attachReceive(onReceiveHidData1);
    Serial.print("Insert a USB Drive to begin test.\n");
}

bool testHasRun = false;
bool rawHIDConnected = false;

elapsedMillis testTime;
elapsedMillis ledTime;
elapsedMillis rawHIDTimer;



bool ledOn = false;

uint8_t buffer[64];

void loop() {

    myusb.Task();


    if (rawHIDTimer > 50) {
        rawHIDTimer = 0;
        RawHID.send(&buffer, 0);
    }

    if (rawHID1 != rawHIDConnected) {
        rawHIDConnected = !rawHIDConnected;

        if (rawHIDConnected)
            Serial.print("Host port rawHID1 is connected\n");
        else
            Serial.print("Host port rawHID1 is disconnected\n");
    }


    if (ledTime > 100) {
        ledTime = 0;
        ledOn = !ledOn;
        digitalWrite(LEDPIN, ledOn);
    }

    
    if (usbPart) {

        if (!testHasRun) {
            testHasRun = true;
            performTest();
        }
    }

    else {
        testHasRun = false;
    }
}



void performTest() {

    Serial.println("Starting Tests");
    testTime = 0;
  
    Serial.print("Creating /testDir...");
    if (!usbPart.mkdir("/testDir")) {
        Serial.print("FAILED\n");
        return;
    }

    Serial.print("SUCCESS\n");

    File testFile[9];

    for (int i = 0; i < 9; i++) {
        char filename[50];
        sprintf(filename, "/testDir/file_%1d.txt", i);

        testFile[i] = usbPart.open(filename, FILE_WRITE_BEGIN);

        if (!testFile[i]) {
            Serial.print("Failed to create ");
            Serial.println(filename);
        }
        
        if (testFile[i]) {
            Serial.print("Writing to ");
            Serial.println(filename);
            testFile[i].write("Testing");
            testFile[i].close();

            
            if (!usbPart.remove(filename)) {
                Serial.print("Failed to remove ");
                Serial.println(filename);
            }
        }
    }
    
    Serial.print("Removing /testDir...");
    if (!usbPart.rmdir("/testDir")) {
        Serial.print("FAILED\n");
        return;
    }

    Serial.print("SUCCESS\n");

    Serial.print("Test completed in ");
    Serial.print(testTime);
    Serial.println(" miliseconds.");
    Serial.println("Remove and reinsert media to test again.");
}

bool onReceiveHidData1(uint32_t usage, const uint8_t* data, uint32_t len) {
  
    return true;
}

Not sure that would ever work the way you have it set up. As far I as know you are only going to be able to access the mscDrive if its hooked up via the USBHost port not the serial port. Running the same sketch on the Teensy attached to the Hub the second teensy will never see the mscDrive and you may run into issues
 
That is not the serial port, and it's just an illustration to get the idea across. The first teensy is of course attached to a computer. It looks like the real culprit is the MIDI driver. I'll post an updated test.
 
Last edited:
Here's a sketch to show that the USB Host MIDI driver is causing the Mass Storage driver to fail.

Both teensy devices should be running the same sketch included below. If the test is performed without the second teensy connected to the USB host port, it will complete successfully. If the second teensy is connected to the USB host port, starting the file system will fail.

The output from the debugger is included below the sketch.

deviceSetup.jpg

Code:
// Make sure to compile for MIDI USB type. 
// This sketch assumes you have migrated the latest changes to the MassStorageDriver

#include "USBHost_t36.h"

USBHost myusb;
USBHub hub1(myusb);
USBDrive usbDrive(myusb);
USBFilesystem usbPart(myusb);
MIDIDevice_BigBuffer midi1(myusb);

#define LEDPIN 13

void setup() {

    Serial.begin(9600);
    myusb.begin();

    pinMode(LEDPIN, OUTPUT);

    Serial.print("Insert a USB Drive to begin test.\n");
}

bool ledOn = false;
bool testHasRun = false;
bool MIDIConnected = false;

elapsedMillis testTime;
elapsedMillis ledTime;

void loop() {

    myusb.Task();

    if (midi1 != MIDIConnected) {
        MIDIConnected = !MIDIConnected;
        
        if (MIDIConnected)
            Serial.println("MIDI Device connected");
        else
            Serial.println("MIDI Device disconnected");
    }

    if (midi1) {
        midi1.read();
    }


    if (ledTime > 100) {
        ledTime = 0;
        ledOn = !ledOn;
        digitalWrite(LEDPIN, ledOn);
    }

    
    if (usbPart) {

        if (!testHasRun) {
            testHasRun = true;
            performTest();
        }
    }

    else {
        testHasRun = false;
    }
}

void performTest() {

    Serial.println("Starting Tests");
    testTime = 0;
  
    Serial.print("Creating /testDir...");
    if (!usbPart.mkdir("/testDir")) {
        Serial.print("FAILED\n");
        return;
    }

    Serial.print("SUCCESS\n");

    File testFile[9];

    for (int i = 0; i < 9; i++) {
        char filename[50];
        sprintf(filename, "/testDir/file_%d.txt", i);

        testFile[i] = usbPart.open(filename, FILE_WRITE_BEGIN);

        if (!testFile[i]) {
            Serial.print("Failed to create ");
            Serial.println(filename);
        }
        
        if (testFile[i]) {
            Serial.print("Writing to ");
            Serial.println(filename);
            testFile[i].write("Testing");
            testFile[i].close();

            
            if (!usbPart.remove(filename)) {
                Serial.print("Failed to remove ");
                Serial.println(filename);
            }
        }
    }
    
    Serial.print("Removing /testDir...");
    if (!usbPart.rmdir("/testDir")) {
        Serial.print("FAILED\n");
        return;
    }

    Serial.print("SUCCESS\n");

    Serial.print("Test completed in ");
    Serial.print(testTime);
    Serial.println(" miliseconds.");
    Serial.println("Remove and reinsert media to test again.");
}

Here's the debugging output after the USB drive is connected:
Code:
 === Task() Drive 0x1fff3540 connected ===
>> USBDrive::startFilesystems called 0x1fff3540
mscIint()
msGetMaxLun()
queue_Control_Transfer
queue_Transfer
add_to_async_followup_list
Async Followup
followup_Transfer callback
control CallbackIn (USBDrive)
00 00 00 00 00 00 00 00 
ISR: USBHS_USBSTS_UAI OUT
msStartStopUnit()
queue_Data_Transfer
queue_Transfer
add_to_async_followup_list
Async Followup
followup_Transfer callback
USBDrive CallbackOut (static)
transfer->qtd.token = 1
USBDrive dataOut (static)31
55 53 42 43 01 00 00 00 00 00 00 00 80 00 06 1B 01 00 00 01 00 00 00 00 00 00 00 00 00 00 00 
ISR: USBHS_USBSTS_UAI OUT
msGetCSW()
queue_Data_Transfer
queue_Transfer
add_to_async_followup_list
 
@yeahtuna Quick thing on your sketch, which may or may not help.

On the second teensy, it logically looks like it boils down to:
Code:
void loop() {

    myusb.Task();


    if (rawHIDTimer > 50) {
        rawHIDTimer = 0;
        RawHID.send(&buffer, 0);
    }
But there are some more things involved. When you compile a teensy as raw hid,
It generates TWO interfaces:
Code:
#elif defined(USB_RAWHID)
  #define VENDOR_ID		0x16C0
  #define PRODUCT_ID		0x0486
  #define RAWHID_USAGE_PAGE	0xFFAB  // recommended: 0xFF00 to 0xFFFF
  #define RAWHID_USAGE		0x0200  // recommended: 0x0100 to 0xFFFF
  #define MANUFACTURER_NAME	{'T','e','e','n','s','y','d','u','i','n','o'}
  #define MANUFACTURER_NAME_LEN	11
  #define PRODUCT_NAME		{'T','e','e','n','s','y','d','u','i','n','o',' ','R','a','w','H','I','D'}
  #define PRODUCT_NAME_LEN	18
  #define EP0_SIZE		64
  #define NUM_ENDPOINTS         4
  #define NUM_INTERFACE		2
  #define RAWHID_INTERFACE      0	// RawHID
  #define RAWHID_TX_ENDPOINT    3
  #define RAWHID_TX_SIZE        64
  #define RAWHID_TX_INTERVAL    1	 // TODO: is this ok for 480 Mbit speed
  #define RAWHID_RX_ENDPOINT    4
  #define RAWHID_RX_SIZE        64
  #define RAWHID_RX_INTERVAL    1	 // TODO: is this ok for 480 Mbit speed
  #define SEREMU_INTERFACE      1	// Serial emulation
  #define SEREMU_TX_ENDPOINT    2
  #define SEREMU_TX_SIZE        64
  #define SEREMU_TX_INTERVAL    1	 // TODO: is this ok for 480 Mbit speed
  #define SEREMU_RX_ENDPOINT    2
  #define SEREMU_RX_SIZE        32
  #define SEREMU_RX_INTERVAL    2	 // TODO: is this ok for 480 Mbit speed
  #define ENDPOINT2_CONFIG	ENDPOINT_RECEIVE_INTERRUPT + ENDPOINT_TRANSMIT_INTERRUPT
  #define ENDPOINT3_CONFIG	ENDPOINT_RECEIVE_UNUSED + ENDPOINT_TRANSMIT_INTERRUPT
  #define ENDPOINT4_CONFIG	ENDPOINT_RECEIVE_INTERRUPT + ENDPOINT_TRANSMIT_UNUSED
The Rawhid as you mentioned, but also a Serial Emulation HID object.

In debug you will probably see it trying to connect the second interface looking for a free USBHIDParser which does not exist... So I don't remember what happens with to those messages on your seond Teensy that you do: Serial.print... with. For example could they be queuing up and then cause the issue?

You could try adding a seond one of the USBHIDParser objects, to allow it to connect. To be able to communicate over that Serial object you could then add a
USBSerialEmu object.

HUB - Not all hubs are created equal. As I understand it from stuff Paul has posted before, there are some different HUB chips which only handle lets say 4 connections. So some of the HUB units you purchase that have more the 4 connections, actually are multiple physical hubs in the topology. For example an 8 port one may be made up of 3 hub chips, one that may only connect to the two other chips or maybe handles a port or two and the other two the connect to the other ports... So in these cases you need more than one USBHub object.


RAWHID code - a while ago, I was experimenting with RAWHID being able to transfer 512 byte packets.
The code is still up in my fork in a branch never merged: Main part is in:
https://github.com/KurtE/USBHost_t36/blob/FS_Integration_MSC/src/rawhid.cpp
Would also need some from USBHost_t36.h
Actually probably also changes in USBHIDParser,

What I found back then, was RAWHID could run into problems and do things like hang, as it might run out of resources. That is for example if one side is sending data faster than the other side can process, and the original rawhid code only maybe setup one or two buffers to receive data. The version I mentioned allowed me to define multiple buffers and in the case of the 512 byte packets, large enough to handle those. I believe I set it up there for up to 4 in both directions.
There is another thread on this. https://forum.pjrc.com/threads/6862...es-sense-to-add-to-system?highlight=rawhid512

Probably enough here for one post
 
Thanks for the reply, KurtE. The RawHID was a red herring. I know all hubs are not created equal, and I tested with a few more hubs and could not reproduce the issue. The second test with the devices set to USB MIDI, outlined in post 729, fails on all my hubs.
 
Thanks for the reply, KurtE. The RawHID was a red herring. I know all hubs are not created equal, and I tested with a few more hubs and could not reproduce the issue. The second test with the devices set to USB MIDI, outlined in post 729, fails on all my hubs.

What USB type are you building for?
 
@KurtE - Had a chance to play with the MTP sketch. Some interesting results when playing with write sizes.

Kingston DT50 32G, 512 bytes at a time:
Code:
Start write file: write_test_file.txt length:1048576 Write Size:512
cb_write = 512
Time to write: 1642

Now with 16384 bytes at a time:
Code:
Start write file: write_test_file.txt length:1048576 Write Size:16384
cb_write = 16384
Time to write: 235

Then the SanDisk Ultra DualDrive 32G, 512 bytes at a time:
Code:
Start write file: write_test_file.txt length:1048576 Write Size:512
cb_write = 512
Time to write: 3049

Then with 16384 bytes at a time:
Code:
Start write file: write_test_file.txt length:1048576 Write Size:16384
cb_write = 16384
Time to write: 98

Tried a few others as well. Results varied a lot. But the newer SanDisk device seemed to benefit most from the larger write size. Increasing the write size to 32768 bytes did not improve the write time much more. This kind of makes sense as the USB host uses a 16384 byte buffer size I think. Write times also vary depending on the devices internal buffering size. The smaller the internal buffer the more often the device has to stall the pipe till it is finished flushing it's internal buffers. On some of my older flash drives it almost seems like a hang. Very slow...
 
If lines 194 and 195 in midi.cpp are commented out, then the test completes.

https://github.com/PaulStoffregen/U...5b028699b47910aa5cd6a0823de5f69/midi.cpp#L194

So, correct me if I'm wrong, but it seems that MIDI devices queue transfers that only complete once a MIDI event is received. So any other transfers that are queue by other drivers won't get called until after a MIDI event has been received.

Code:
// if an IN endpoint was found, create its pipe
	if (rx_ep && rx_size <= max_packet_size) {
		rxpipe = new_Pipe(dev, rx_ep_type, rx_ep, 1, rx_size);
		if (rxpipe) {
			rxpipe->callback_function = rx_callback;
			queue_Data_Transfer(rxpipe, rx_buffer, rx_size, this);
			rx_packet_queued = true;
		}
	}

I modified my initial sketch to send a control change message every 500ms, and sure enough the test completes without issue. This also explains why unplugging a MIDI device from a hub can cause the hub to hang, as I've documented in another thread.

Is there a way we could force a transfer to queue in front of any transfer owned by MIDI devices instead of at the end as is currently implemented:

Code:
static void add_to_async_followup_list(Transfer_t *first, Transfer_t *last)
{
	last->next_followup = NULL; // always add to end of list

	if (async_followup_last == NULL) {
		
		first->prev_followup = NULL;
		async_followup_first = first;
		
	} else {

		first->prev_followup = async_followup_last;
		async_followup_last->next_followup = first;
	}
	async_followup_last = last;
}

Perhaps we can implement it such that if a transfer hasn't been completed within a certain amount of time, it gets pushed to the end of the transfer queue list.

Another idea might be to have a separate queue for MIDI devices so that they can't block transfers for other drivers, although if you had multiple MIDI drivers, they would still block each other.
 
Last edited:
@yeahtuna regarding your midi sketch. Been looking at on and off most of the day and there has been something that has been bothering me about especially since I dont play with MIDI for any reason. I guess the thing that bothers me the most is that you are putting the same sketch on both Teensies, both are just doing midi.read's, and not defining and callback functions, etc so not sure its a fair test of the midi interface. So spent the day playing to see if I could come up with a better test.

On a Teensy Micromod with the hub and a Teensy 4.1 attached along with a Sansdisk dual ultra 32Gb drive I loaded the following sketches:

Micromod:
Code:
//https://forum.pjrc.com/threads/70227-USBhost_t36-MIDI-losing-midi-note-on-off-events-(Teensy-3-6)-during-modwheel-change?highlight=usbhost+midi+examples

#include <USBHost_t36.h>

USBHost myusb;
USBHub hub1(myusb);
USBHub hub2(myusb);
USBHub hub3(myusb);
MIDIDevice_BigBuffer midi1(myusb); 
USBDrive usbDrive(myusb);
USBFilesystem usbPart(myusb);

#define LEDPIN 13
bool ledOn = false;
bool testHasRun = false;
bool MIDIConnected = false;

elapsedMillis testTime;
elapsedMillis ledTime;

void setup() {
  Serial.begin(115200);
  delay(1500);
  myusb.begin();
  pinMode(LEDPIN, OUTPUT);

  midi1.setHandleNoteOn(myNoteOn);
  midi1.setHandleNoteOff(myNoteOff); 
  midi1.setHandleControlChange(myControlChange); 
}

int NumNotes=0;

void loop() {
  myusb.Task();
     if (midi1 != MIDIConnected) {
        MIDIConnected = !MIDIConnected;
        
        if (MIDIConnected)
            Serial.println("MIDI Device connected");
        else
            Serial.println("MIDI Device disconnected");
    }
    if (midi1) {
        midi1.read();
    }

    if (ledTime > 100) {
        ledTime = 0;
        ledOn = !ledOn;
        digitalWrite(LEDPIN, ledOn);
    }

    if (usbPart) {
        if (!testHasRun) {
            testHasRun = true;
            performTest();
        }
    } else {
        testHasRun = false;
        delay(1500);
    }
}


void myNoteOn(byte channel, byte note, byte velocity) {
  Serial.print("Note On, ch=");
  Serial.print(channel, DEC);
  Serial.print(", note=");
  Serial.print(note, DEC);
  Serial.print(", velocity=");
  Serial.println(velocity, DEC);
  
  Serial.print(">>>>>>>> ");
  if(velocity>0)
    Serial.println(++NumNotes);
  else
    Serial.println(--NumNotes);  
}

void myNoteOff(byte channel, byte note, byte velocity) {
  Serial.print("Note Off, ch=");
  Serial.print(channel, DEC);
  Serial.print(", note=");
  Serial.print(note, DEC);
  Serial.print(", velocity=");
  Serial.println(velocity, DEC);  
  Serial.print(">>>>>>>> ");
  Serial.println(--NumNotes);
}


void myControlChange(byte channel, byte control, byte value) {

}

void performTest() {

    Serial.println("Starting Tests");
    testTime = 0;
  
    Serial.print("Creating /testDir...");
    if (!usbPart.mkdir("/testDir")) {
        Serial.print("FAILED\n");
        return;
    }

    Serial.print("SUCCESS\n");

    File testFile[9];

    for (int i = 0; i < 9; i++) {
        char filename[50];
        sprintf(filename, "/testDir/file_%d.txt", i);

        testFile[i] = usbPart.open(filename, FILE_WRITE_BEGIN);

        if (!testFile[i]) {
            Serial.print("Failed to create ");
            Serial.println(filename);
        }
        
        if (testFile[i]) {
            Serial.print("Writing to ");
            Serial.println(filename);
            testFile[i].write("Testing");
            testFile[i].close();

            
            if (!usbPart.remove(filename)) {
                Serial.print("Failed to remove ");
                Serial.println(filename);
            }
        }
    }
    
    Serial.print("Removing /testDir...");
    if (!usbPart.rmdir("/testDir")) {
        Serial.print("FAILED\n");
        return;
    }

    Serial.print("SUCCESS\n");

    Serial.print("Test completed in ");
    Serial.print(testTime);
    Serial.println(" miliseconds.");
    Serial.println("Remove and reinsert media to test again.");
}
on the Teensy 4,
Code:
#include <Bounce.h>  // Bounce library makes button change detection easy
const int channel = 1;

Bounce button1 = Bounce(1, 5);  // 5 = 5 ms debounce time
Bounce button2 = Bounce(2, 5);  // which is appropriate for good
Bounce button3 = Bounce(3, 5);  // quality mechanical pushbuttons
void setup() {
  pinMode(1, INPUT_PULLUP);
  pinMode(2, INPUT_PULLUP);
  pinMode(3, INPUT_PULLUP);
  pinMode(13, OUTPUT);
  digitalWrite(13, HIGH);
}

void loop() {
  button1.update();
  button2.update();
  button3.update();
  // Note On messages when each button is pressed
  if (button1.fallingEdge()) {
    usbMIDI.sendNoteOn(60, 99, channel);  // 60 = C4
  }
  if (button2.fallingEdge()) {
    usbMIDI.sendNoteOn(61, 99, channel);  // 61 = C#4
  }
  if (button3.fallingEdge()) {
    usbMIDI.sendNoteOn(62, 99, channel);  // 62 = D4
  }
  // Note Off messages when each button is released
  if (button1.risingEdge()) {
    usbMIDI.sendNoteOff(60, 0, channel);  // 60 = C4
  }
  if (button2.risingEdge()) {
    usbMIDI.sendNoteOff(61, 0, channel);  // 61 = C#4
  }
  if (button3.risingEdge()) {
    usbMIDI.sendNoteOff(62, 0, channel);  // 62 = D4
  }

  // MIDI Controllers should discard incoming MIDI messages.
  while (usbMIDI.read()) {
  }
}

I compiled with Serial+MIDI since I am printing to Serial as well:
Code:
=== Task() Drive 0x20005260 connected ===
>> USBDrive::startFilesystems called 0x20005260
	>>Partition 1 VT:1 T:12 32 60062468
	>>USBFilesystem::claimPartition 0x1 called >>USBFilesystem::check_voltype_guid(1, 0x20067fa0)
+ Claimed

Try Partition list>>USBDrive::printPartionTable

Partition Table
	part,boot,bgnCHS[3],type,endCHS[3],start,length
FAT32:	1,0,0x0,0x21,0x0,0xC,0xFE,0xFF,0xFF,32,60062468
pt_#0:	2,0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0,0
pt_#0:	3,0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0,0
pt_#0:	4,0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0,0
MIDI Device connected
Starting Tests
Creating /testDir...SUCCESS
Writing to /testDir/file_0.txt
Writing to /testDir/file_1.txt
Writing to /testDir/file_2.txt
Writing to /testDir/file_3.txt
Writing to /testDir/file_4.txt
Writing to /testDir/file_5.txt
Writing to /testDir/file_6.txt
Writing to /testDir/file_7.txt
Writing to /testDir/file_8.txt
Removing /testDir...SUCCESS
Test completed in 198 miliseconds.
Remove and reinsert media to test again.
no hangs and your test ran.

Only issue was if I removed and reinsert media it would hang the TMM. But that I think is a MSC issue.
 
So, correct me if I'm wrong, but it seems that MIDI devices queue transfers that only complete once a MIDI event is received. So any other transfers that are queue by other drivers won't get called until after a MIDI event has been received.

No. If an endpoint (pipe) blocks, the host simply moves on to the next round-robin style. If a single transfer blocked everything else until it completed it would even block new devices from being enumerated.
Any active transfers should be cancelled/cleaned up when a device is removed. The code is definitely there to do that but I can't say for certain that it works 100%.
 
Thanks for taking the time to test. I appreciate others taking this issue seriously. The reason your test doesn't hang is because your MIDI device connected to the hub is constantly sending MIDI. The Mass Storage driver will only hang if MIDI event are not being sent.

Have a look at my last post for a clear explanation.
 
No. If an endpoint (pipe) blocks, the host simply moves on to the next round-robin style. If a single transfer blocked everything else until it completed it would even block new devices from being enumerated.
Any active transfers should be cancelled/cleaned up when a device is removed. The code is definitely there to do that but I can't say for certain that it works 100%.

You're talking about this code?

Code:
if (stat & USBHS_USBSTS_UAI) { // completed qTD(s) from the async schedule
		println("Async Followup");
		//print(async_followup_first, async_followup_last);
		Transfer_t *p = async_followup_first;
		while (p) {

			//println("ISR: transfer USBHS_USBSTS_UAI");
			if (followup_Transfer(p)) {
				// transfer completed
				Transfer_t *next = p->next_followup;
				remove_from_async_followup_list(p);
				free_Transfer(p);
				p = next;
			} else {
				// transfer still pending
				println("ISR: USBHS_USBSTS_UAI PENDING");
				p = p->next_followup;
			}
		}
		println("ISR: USBHS_USBSTS_UAI OUT");
		//print(async_followup_first, async_followup_last);
	}

If the code was working properly, then we should see "ISR: USBHS_USBSTS_UAI PENDING" in my debugging output from the MIDI test sketch I provided, but we don't.

Update: I was referencing the wrong code.
 
Last edited:
Thanks for taking the time to test. I appreciate others taking this issue seriously. The reason your test doesn't hang is because your MIDI device connected to the hub is constantly sending MIDI. The Mass Storage driver will only hang if MIDI event are not being sent.

Have a look at my last post for a clear explanation.

Actually I don;t believe that is the case. Take a closer look at the sketch. Only time a Midi message is sent is when one of the 3 buttons are pressed. and also look at the last line
Code:
  // MIDI Controllers should discard incoming MIDI messages.
  while (usbMIDI.read()) {
  }
I didn;t make this up this is from https://www.pjrc.com/teensy/td_midi.html

and I didn;t press any buttons to send messages until the end. The lights continued to blink throughout.
 
You're talking about this code?

Code:
if (stat & USBHS_USBSTS_UPI) { // completed qTD(s) from the periodic schedule
		println("Periodic Followup");
		Transfer_t *p = periodic_followup_first;
		while (p) {
			//println("ISR: transfer USBHS_USBSTS_UPI");

			if (followup_Transfer(p)) {
				// transfer completed
				Transfer_t *next = p->next_followup;
				remove_from_periodic_followup_list(p);
				free_Transfer(p);
				p = next;
			} else {
				println("ISR: USBHS_USBSTS_UPI PENDING");
				// transfer still pending
				p = p->next_followup;
			}
		}
		println("ISR: USBHS_USBSTS_UPI OUT");
	}

If the code was working properly, then we should see "ISR: USBHS_USBSTS_UPI PENDING" in my debugging output from the MIDI test sketch I provided, but we don't.

No, I'm talking about the inherent operation of the host controller. Look at the new_Pipe() function, you can see how the queue heads for all async (bulk and control) endpoints are arranged as a circular list. The host controller rotates between them on each micro-frame, trying to fill any pending transfers.

It's not clear if the midi device you're referencing has a bulk or interrupt endpoint; interrupts use periodic transfers rather than async and definitely wouldn't be blocking the mass storage because they use a different queue.
 
@mjs513

I performed your test as best I could---I was still using T3.6 devices. I performed the test 10 times, each time I power cycled the teensy connected to the computer, leaving the hub, flash drive, and second teensy connected. Out of the ten tests, only once did it complete.

Code:
while (usbMIDI.read()) {
  }

I'm aware of this issue. I had added the call myself after I posted, just in case that was causing the problem.
 
Alright. I've found something else. If I reduce the speed of the T3.6 connected to the computer from 180 Mhz down to 96 Mhz, the test always completes.
 
I am setup for testing now. @mjs513 I am getting the same results as you. Everything seems to be working except un-plugging and replugging the flash drive. I can't remember, but has a powered hub been tried?
 
I am setup for testing now. @mjs513 I am getting the same results as you. Everything seems to be working except un-plugging and replugging the flash drive. I can't remember, but has a powered hub been tried?

Can you confirm that the device connected to your computer is T3.6 and the speed is set to 180 Mhz? No, I'm not testing with a powered hub at the moment.
 
I am setup for testing now. @mjs513 I am getting the same results as you. Everything seems to be working except un-plugging and replugging the flash drive. I can't remember, but has a powered hub been tried?

I suspect if you comment out the lines that queue a transfer in claim() midi.cpp, the hub will denounce properly when you remove the drive.

Code:
// if an IN endpoint was found, create its pipe
	if (rx_ep && rx_size <= max_packet_size) {
		rxpipe = new_Pipe(dev, rx_ep_type, rx_ep, 1, rx_size);
		if (rxpipe) {
			rxpipe->callback_function = rx_callback;
			//queue_Data_Transfer(rxpipe, rx_buffer, rx_size, this);
			//rx_packet_queued = true;
		}
	} else {
		rxpipe = NULL;
	}
 
I've found something. If a delay is added before a transfer is removed from the async followup list, the test completes reliably.

Sounds like the issue is might be that we are calling queue_Data_Transfer() while the completed transfer is still being removed? And adding the delay ensures that the call to queue_Data_Transfer() commpletes before re removed completed transfer from the the async followup list?

Code:
if (stat & USBHS_USBSTS_UAI) { // completed qTD(s) from the async schedule
		//println("Async Followup");
		//print(async_followup_first, async_followup_last);
		Transfer_t *p = async_followup_first;
		while (p) {
			if (followup_Transfer(p)) {
				// transfer completed

				delay(10); // Help prevent issues with USB MassStorageDriver

				Transfer_t *next = p->next_followup;
				remove_from_async_followup_list(p);
				free_Transfer(p);
				p = next;
			} else {
				// transfer still pending
				p = p->next_followup;
			}
		}
		//print(async_followup_first, async_followup_last);
	}
 
Last edited:
Back
Top