Hi
I took a quick look at the example and the problem is that when there is no transmission after a reception the reception queue is no longer ready to accept further data, meaning that the USB host is wanting to send something but the device is refusing to receive it (since it has no where to receive it to).
However if the echo is performed the transmission somehow also sets up/frees the next reception queue and then reception is possible again.
You can see it in progress by simply setting a break point on any reception and then type in some more characters at the host (the device will block them and the host will be sending PINGs all the time to inform that it wants to sent). Then let the code continue and the transmission start and then the device accepts the data (the host sends all of the queued characters that it was wanting to send in a single packet and they all get echoed in a packet back - and it continues.
What I can't say is why a transmission is necessary to allow subsequent receptions since normally the Rx and Tx are independent (the example doesn't even share the same endpoint for Rx and Tx (it uses 2 and 3) which makes it even more surprising).
Also I didn't immediately see what the Tx queue management was doing to either setup or re-enable rx queue operation but certainly this is something in the stack. You may find that the stack disables reception by default until there is a transmission or some other API command is executed to re-enable it (check the documentation) and, since the echo example is envisaged to always echo it fails when the echo is not performed. I have heard of people needing to patch the stack to do things that examples are not testing - maybe ask Erich Styger who I understand has some CDC fixes.
When I echo in the uTasker USB task I do it like this and the Rx and Tx have their own independent queues which doesn't pose such complications.
extern void USB_stack(TTASKTABLE *ptrTaskTable) // scheduled on USB reception
{
QUEUE_TRANSFER Length;
unsigned char ucInputMessage[LARGE_MESSAGE]; // reserve space for receiving messages to
while ((Length = fnRead(USBPortID_VCOM, ucInputMessage, LARGE_MESSAGE)) != 0) { // read
fnWrite(USBPortID_VCOM, ucInputMessage, Length); // echo reception back to the same USB endpoint
}
}
I find it difficult to follow the SDK stack but if you want to dive in there you need to home in on the endpoint/asynchrous list that is used for device endpoint queue control and see why it is not ready.
Regards
Mark
uTasker project developer for Kinetis and i.MX RT]
Contact me by personal message or on the uTasker web site to discuss professional training or product development requirements