Author Topic: USB buffering problem?  (Read 14032 times)

Offline dkg

  • Newbie
  • *
  • Posts: 48
    • View Profile
USB buffering problem?
« on: March 23, 2010, 09:14:00 PM »
Hi Mark,

I am working on implementing a USB slave device for our uTasker-based product. I have defined 2 bulk endpoints with 320 byte queues. Our normal message size is approx 240-253 bytes. If the message size is below the max packet size of the full-speed endpoint (64 bytes) everything works great. The problem starts when we use larger messages. It seems the accounting of how many bytes are left to transmit back to the host is getting messed up. We get communications working for a while (20-30 seconds). But, at some point, uTasker starts belching out huge amount of bad data like there was a wrap-around of the bytes left, We monitor the USB traffic using USBlyzer (great tool worth the money). Even before this fatal situation, the packets coming back to the host don't seem to be what I would expect -- I think I should expect packets of 64 bytes until the last one but, instead, we end up getting random packet sizes until the message is complete.

I know the uTasker demo program implements a CDC class device (UART) and I am guessing that code never uses large messages. I guess it is possible this situation has never been tested? I haven't been able to place a breakpoint at the point of failure. I am slowly learning the uTasker USB driver code and was wondering if you might have some advice on what to look for.

Thanks,
Dave G.

Offline mhoneywill

  • Full Member
  • ***
  • Posts: 173
    • View Profile
Re: USB buffering problem?
« Reply #1 on: March 24, 2010, 08:45:44 AM »
Hi Dave,

I don't know anything about USB on embedded systems, but am aware of a driver stack built for the AVR called LUFA see http://www.fourwalledcubicle.com/LUFA.php

I've not looked at the code in detail so do not know how generic it is or how tightly it is integrated into the AVR, but it might be worth a look.

Cheers

Martin

Offline mark

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 3236
    • View Profile
    • uTasker
Re: USB buffering problem?
« Reply #2 on: March 24, 2010, 03:45:27 PM »
Hi Dave

Please could you try with the latest development USB driver here: http://www.uTasker.com/software/V1.4/uTaskerUSB_Driver_24-3-2010.zip

The files usb_drv.c and usb.h can be replaced but you will in addition need to add these defines to driver.h so that it compiles:

#define USB_ENDPOINT_TERMINATES 0x20
#define ENDPOINT_REQUEST_TYPE       3
#define ENDPOINT_CLEARED            4
#define TRANSPARENT_CALLBACK        7


Furthermore, ensure that the USB and Ethernet interrupts have different interrupt level/priorities - as discussed here: http://www.utasker.com/forum/index.php?topic=842.msg3690#msg3690



Now to some explanations:
1) There are no know problems with the buffer operation itself but two cases have been identified where other interrupts can cause a buffer corruption to occur. See the two patches marked with MJB_TEST_PROTECT_NEW_WRITE and MJB_WAIT_UNTIL_ALL_BUFFERS_SENT.
A third define called MJB_CATCH_ERROR catches this error by monitoring the buffer operation (this is not the actually input/output buffer used between driver and application but a ping-pong FIFO buffer used to ensure that the USB engine is fed with data to achieve decent throughput when sending). This should never occur (with the other defines set). An IN endpoint throughput of about 8MBits/s is typical.

I would hope that these resolve any transmission corruption possibilities (some intensive testing was done recently and, although not always detectable with the standard CDC operation, certain interrupts taking place in combination with burst type operation could result in some garbage transmission or the transmitter stopping further sending.

2) When data is sent via the output buffer it will be sent in packets equal to the maximum data token size until the final data block is sent. If this data block is less that the data token length (eg. 64 in most full-speed bulk cases) there is no possibility of problems. In this case the USB host recognises that the final token is the end of the 'message'. If however the final data token length happens to be equal to the maximum data token size it is not so clear - a terminal emulator will pass on the data to the terminal application and all is well, but other USB interfaces may wait for the termination of the 'message' before passing it on to the application on the PC. In the case, where the message length is equal to a multiple of the maximum data token size it can cause problems. For this reason there was a define USB_TERMINATE_ONLY_CONTROL which could be used to define whether a zero frame is sent in this case or not (the zero frame allows the USB host to known that the complete message has been sent and so ensures that it doesn't continue waiting for more without letting the application know that data is ready). In some projects it was found to be necessary, in others it was found to be not allowed - depending on the exact USB host driver...

In this version it is possible to configure this on an IN endpoint basis. I have set it as default in the CDC configuration as follows:

    {                                                                    // bulk in endpoint descriptor for the second interface
    DESCRIPTOR_TYPE_ENDPOINT_LENGTH,                                     // descriptor size in bytes (0x07)
    DESCRIPTOR_TYPE_ENDPOINT,                                            // end point descriptor (0x05)
    (IN_ENDPOINT | 0x02),                                                // direction and address of end point
    (ENDPOINT_BULK | ENDPOINT_TERMINATE_TRANSMISSION),                   // {13} endpoint attributes (ENDPOINT_TERMINATE_TRANSMISSION is a pseudo parameter configuring the endpoint to send zero frame on transmission termination)
    {LITTLE_SHORT_WORD_BYTES(64)},                                       // endpoint FIFO size (little-endian - 64 bytes)
    0                                                                    // polling interval in ms - ignored for bulk
    }


This parameter is set here but is not actually sent out during enumeration, but rather used by the driver to know how it should react in the said case.

3) Note also that the output buffer is a ring buffer. This means that it doesn't actually guaranty that "messages" are sent out as groups of 64 bytes and terminated by a shorter data token (or zero terminator). If there is a wrap-around in the buffer it will result in two 'messages' being sent. This is because transmission is directly from the buffer using DMA and so the DMA setup results in two "messages" - one up to the end of the buffer and one at the start of the buffer (after the wrap-around).
This can cause problem for certain USB protocols. For example I am working on USB mass storage and this case is not acceptable since the USB host believes that the message is corrupt since it doesn't have the fixed length. In this case it is however not a reason to avoid buffered output since it is easy to manage by giving the buffer a size equal to the standard message length (or twice so that a follow-on message can be made ready for the next transmission - which is almost always 512 bytes for mass storage media). This allows multiple mass storage data blocks to be added since they are guarantied to be transmitted as single 512 byte messages.
In between message sequences (where other types are also transmitted) an fnFlush(USBPortID_comms, FLUSH_TX); can be called to set the buffer back to the start.
Up to now I only found this to be necessary with the mass storage class operation, but, as I said, is easy to manage.

4) This version also allows both callbacks and queues to be used for OUT bulk endpoints. This I found useful for 'peeking' at incoming commands (this occurs in the interrupt routine directly) and halting the bulk endpoint where necessary, or allowing the reception to be processed via the input queue (by the task which will be delayed slightly). Probably this is not of interest at the moment but I am playing with it during the mass storage development. What you certainly can't do with the present solution (needs also a slight modification in the HW driver, which I haven't done for Coldfire yet) is stall (halt) a bulk endpoint. This is something required by mass storage but not for the CDC - if you do find it necessary, the bulk endpoint call-back can return STALL_ENDPOINT but you will need to request the HW level change from me, which I will try to test shortly.

Tell me if this solves your problem case.

Regards

Mark


Offline dkg

  • Newbie
  • *
  • Posts: 48
    • View Profile
Re: USB buffering problem?
« Reply #3 on: March 24, 2010, 05:46:25 PM »
Hi Mark,

Thanks for the detailed info. I had forgotten about the interrupt issue you found. I won't be able to get back to this issue until next week. I'll let you know what I find out.

Dave G.

Offline dkg

  • Newbie
  • *
  • Posts: 48
    • View Profile
Re: USB buffering problem?
« Reply #4 on: March 31, 2010, 04:06:59 PM »
Hi Mark,

Things are working much better now :)  I did need to make a couple other changes to make it compile.

I had to restore the lines that define _PACK (I am using GNU) and the endian macros in usb.h... maybe you moved them somewhere in your current development image?

I also added a define for vendor specified classes:
#define INTERFACE_CLASS_VENDOR_SPECIFIC      0xff

I added in my code a call to flush the transmit buffers and that cleared up a problem with the host software dealing with random size packets.

I added the use of ENDPOINT_TERMINATE_TRANSMISSION. I think you should probably change your driver to strip out that bit before it goes out on the USB interface. USBLYZER complains that this reserved bit is set when it see it in the descriptor.

Thanks for the new version of code. Next, I'll want to implement the mass storage class ;)

Dave G.

Offline mark

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 3236
    • View Profile
    • uTasker
Re: USB buffering problem?
« Reply #5 on: March 31, 2010, 08:53:48 PM »
Hi Dave

I am glad that you are making progress.

The _PACK define has been moved to m5223x.h recently (it is needed more generally).

I did note that there are defines for the vendor specific class in usb.h as follows:

#define DEVICE_CLASS_VENDOR_SPECIFIC                     0xff,0,0        // Vendor Specific Device Class

#define VENDOR_SPECIFIC_ENTRY                            0xff



This is a mistake concerning the new ENDPOINT_TERMINATE_TRANSMISSION option. It is filtered when used by the endpoint itself but I forgot that config_descriptor (the const definition of the configuration, containing this new flag) is sent out as it is in fnGetUSB_config_descriptor(). This can not generically be filtered since it would also cancel any benefits of having this defines as a const struct.

I'll have a look into another method to add such a configuration setting since this method is not acceptable at the moment.

I'll also send you my mass storage class development in a private email. I now have the generic solution to clearing a halted bulk endpoint too. The mass storage is not complete (based on SD card) but may give you some ideas in case you want to continue some work on it.

Regards

Mark


Offline dkg

  • Newbie
  • *
  • Posts: 48
    • View Profile
Re: USB buffering problem?
« Reply #6 on: March 31, 2010, 09:04:54 PM »
Oops, didn't notice VENDOR_SPECIFIC_ENTRY.

I actually don't need the mass storage USB class until I implement/test the SD card support local to the product. We want to eventually be able to pass along the SD card contents to the host computer as a removable drive.

Thanks for the help. Back to the debugging :)

Dave G.

Offline mark

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 3236
    • View Profile
    • uTasker
Re: USB buffering problem?
« Reply #7 on: March 31, 2010, 09:26:54 PM »
Hi Dave

Yes, I find the USB mass storage device a natural way to access the SD card from a PC after it has been used for data logging (etc.) by the processor (it could also be inserted into the PC's SD slot/SD card adapter, but it is nice to not have to actually handle it and leave it inside the equipment like fixed media).

Another was to do it is via FTP since the SD card is visible there too.

Thanks for the feedback.

Regards

Mark

Offline mark

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 3236
    • View Profile
    • uTasker
Re: USB buffering problem?
« Reply #8 on: March 31, 2010, 10:42:30 PM »
Hi Dave

I have implemented a different method of controlling the endpoint termination characteristic.

When opening the particular endpoint configuration it can be specified:

    tInterfaceParameters.usConfig = USB_TERMINATING_ENDPOINT;

This requires an extra endpoint variable but avoids the problem of the flag being sent in the configuration descriptor. I will send you the new code so that you can verify it.

Regards

Mark