This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

TM4C1294NCPDT: Data corruption on isochronous EP1 with SW-TM4C-2.1.0.12573, but not isochronous EP2

Part Number: TM4C1294NCPDT
Other Parts Discussed in Thread: EK-TM4C1294XL, TMS320VC5506

I'm working on the EK-TM4C1294XL connected LaunchPad with the USB library in Host only mode (eUSBModeForceHost). I have two identical endpoints, #1 and #2, but I'm seeing data corruption on EP#1 every 45 to 49 ms, and always around the 256th byte of a 386 byte packet. I'm using SW-TM4C-2.1.0.12573 because that's what I started with years ago.

The other end of my USB connection is a TMS320VC5506 that has a custom class with a non-default configuration containing two endpoints, #1 and #2. Both are IN endpoints marked with a length of 388 bytes, and which always transfer 386 bytes exactly (or 0 bytes in the rare case that the 976.5625 Hz data update rate falls short of the 1000 Hz USB frame rate). The TMS320 firmware is proven, and I've also hooked up a Total Phase Beagle USB 480 Power analyzer to confirm that the data is not corrupt on the wire. The isochronous packets have 384 bytes of data followed by a 16-bit sequence number, which allows me to match packets seen on the Beagle USB analyzer with packets seen on the ARM chip. The Device is Full Speed, and the custom class protocol uses at least 75% of the available bandwidth.

My TM4C1294 code starts by scheduling both endpoints, and then scheduling continued streaming within the callback as each packet arrives. This seems to work perfectly for endpoint 2, but perhaps there is a timing issue. Looking at my sequence numbers, the bad packets are 45 sequence numbers apart, sometimes 49. I have a pair of circular buffers for the two endpoint streams, and those are 4 packets deep, so the same memory is being reused every 4th packet. If there were any memory corruption errors in my firmware, it would seem that they should occur more often than once every 45 or so buffer pairs (meaning 45 packets each from the 2 endpoints).

How well proven is the TM4C USB peripheral in isochronous streaming mode? How many people are using multiple endpoints? How many people have packet sizes greater than 256 bytes? I'm not sure where to look, but it seems like any of these details could be unique to my situation. I'm sure that folks have been streaming audio with this chip, but what about a couple of large, isochronous endpoints that consume nearly all of the full speed bandwidth?

I've looked through the errata, and see no USB problems that would affect this. Are there bug fixes in the USB library that are documented?

Is it possible that the USB library or USB peripheral are not properly reporting the 0-byte packets? I always skip advancing the circular queue pointer when the size returned in the callback is 0. If my math is right, the 976.5625 Hz update rate should result in an empty packet once every 41 or 42 frames, which seems suspiciously close to the error rate at 45 to 49 sequence numbers apart.

Brian Willoughby

Madrona Labs

  • Hello Brian,

    Brian Willoughby said:

    How well proven is the TM4C USB peripheral in isochronous streaming mode?

    Not as well as other modes from what I can see, it looks like very people are using this mode overall. Either that or it's so amazingly good almost no one has come to the E2E forums to ask for questions. Though as much as I like our products and TivaWare, I don't think the latter option would explain the lack of questions RE: isochronous mode.

    Brian Willoughby said:

    How many people are using multiple endpoints?

    Quite a number of users are using multiple endpoints.

    Brian Willoughby said:

    How many people have packet sizes greater than 256 bytes?

    This occurs a lot, though there is an issue which has cropped up with it that I will touch on below. Based on the symptoms you describe, I am not so sure it would affect you as the issue presents typically as locking up USB and not just corrupting a packet.

    Brian Willoughby said:

    I've looked through the errata, and see no USB problems that would affect this. Are there bug fixes in the USB library that are documented?

    For one, based on your TivaWare, you should read the latest release notes to track all USB library changes from 2.1.0 to 2.1.4.

    Another issue that has come up concerns the USBHCDPipeWrite API. The details and proper solution can be read in this post: e2e.ti.com/.../2131893

    Brian Willoughby said:
    Is it possible that the USB library or USB peripheral are not properly reporting the 0-byte packets?

    I need to look to see if I can track down an issue I helped with concerning such packets, I don't recall if it was a failure to report/respond, but I have faint recollection that an issue which concerned a 0 byte packet has cropped up on the forums in the past few months. I will try and circle back with you tomorrow on that topic.

  • Ralph Jacobi said:
    Is it possible that the USB library or USB peripheral are not properly reporting the 0-byte packets?

    I need to look to see if I can track down an issue I helped with concerning such packets, I don't recall if it was a failure to report/respond, but I have faint recollection that an issue which concerned a 0 byte packet has cropped up on the forums in the past few months. I will try and circle back with you tomorrow on that topic.

    [/quote]

    After posting this question, I went back to my firmware and altered the code to check for 0-byte packets in the callbacks. It looks like the USB library is reporting lots of these, so it's not completely broken. I need to do some measurements to see whether the number of 0-byte packets matches expectations, and I also want to investigate whether there might be some correlation in timing between the 0-byte packets and the corrupted packet data. For one thing, I am reusing the same buffer whenever the USB callback indicates 0 bytes, because I want my queue to be full of useful data, and perhaps there could be an issue with registering the same address twice in a row - it's doubtful that would cause a problem, but it's something that I can check while I am waiting for more information.

  • Ralph Jacobi said:

    Another issue that has come up concerns the USBHCDPipeWrite API. The details and proper solution can be read in this post: e2e.ti.com/.../2131893

    Thank you. I will look into this. However, since my endpoints are both IN only, I never call USBHCDPipeWrite() in my firmware. I did a quick search, and it doesn't seem to be used. But it's still worth reading the post to see if there is some other related aspect.

  • Hello Brian,

    I found the post I had recalled but again I am not sure if it would be applicable for your situation: e2e.ti.com/.../2279503

    The post concerns USB device mode, not USB host mode, though it does have the usage of two endpoints as well. It wasn't really resolved either, though back then I knew less about the USB library than I do now.

    What's the latest results of the investigates you were planning to make?
  • Ralph Jacobi said:
    What's the latest results of the investigates you were planning to make?

    I took at look at USBHCDPipeRead() and it seems like it might have similar issues to those reported against USBHCDPipeWrite().

    Therefore, I believe that I have a few tasks:

    A) It seems that the uDMA option does not have this issue, at least for the USBHCDPipeWrite() case. I should try turning on uDMA to see whether that solves my problem.

    B) If uDMA does not solve the issue, then I need to finish reviewing the code in USBHCDPipeRead() and try making changes to fix it, but...

    C) Since I am compiling against the 2.1.0.12573 release of the libraries, I feel like I really should upgrade to 2.1.4.178 before making any changes to the library. The problem with this is that when I switch all libraries to 2.1.4 (including usblib) my TM4C firmware stops recognizing my USB Device when it is attached. It might take some significant debugging to find out why my firmware works just fine with 2.1.0 (except for the corrupted data, which seems common to both releases), but cannot see my USB Device with 2.1.4

    Are there any known issues with 2.1.0 versus 2.1.4 that might explain (C) above? I do not actually see any Release Notes. I have both 2.1.0 and 2.1.4 installed.

  • Ralph Jacobi said:
    I found the post I had recalled but again I am not sure if it would be applicable for your situation: e2e.ti.com/.../2279503

    I found that thread to be particularly confusing, so I couldn't make total sense of it. However, the key points seem to be unexpected 0-byte packets in a design where variable packet sizes are expected, and possibly some errors on the USB transactions. It seemed that the errors might have been cleared up by changing the firmware design, but I can't be sure.

    In my case, the packet size is not variable. It is always 386 bytes on endpoints marked for 388 byte lengths (so as to avoid the expectation of packet continuation when the packet size matches the endpoint size exactly - something I learned well when operating my USB Device under Mac OS X, which has an excellent USB implementation). The 0-byte packets are expected because the Device packet rate is 976.5625 Hz while the USB Host frame rate is nominally 1000 Hz, and each side (Host v. Device) has a different crystal clock source. It is expected that some frames will not have data ready. More importantly, the Total Phase Beagle USB analyzer does not report any USB transaction errors at all, not even near the 0-byte endpoint transactions. The USB communications seem to be occurring exactly as expected, without any errors.

    Thanks for pointing to this other thread. It looks like I'll have to investigate elsewhere.

  • Brian Willoughby said:
    Are there any known issues with 2.1.0 versus 2.1.4 that might explain (C) above? I do not actually see any Release Notes. I have both 2.1.0 and 2.1.4 installed.

    I found the Release Notes in a separate PDF (SPMU299E) from the install directory. Turns out I'd already downloaded the PDF and it got lost in the 112 other Texas Instruments files that I have.

    It looks like there is a lot of information in the Release Notes, and potentially some sections that might help me successfully port from 2.1.0 to 2.1.4 - then I can look at making my own improvements to the library.

  • Ralph Jacobi said:
    What's the latest results of the investigates you were planning to make?

    Update: After careful examination of the data captured by the Beagle USB analyzer, I find that there are actually data errors in both endpoints. So, the title of my thread is now technically inaccurate. The data corruption in EP1 is large enough to detect easily, but the corruption on EP2 happens to be within range of normal data yet still incorrect.

    I wanted to clarify that there seems to be no significant difference between EP1 and EP2. It's not like EP2 is perfect, either.

    Continuing the research...

  • Hi Brian,

    I was not around prior to 2.1.4, so if it's not in the Release Notes then I am not aware of any known issues from 2.1.0.

    I can edit the title of the post if you don't mind.

    Reading through your posts again, I noticed you mentioned the issue is usually around the 256th byte, it made me wonder for a moment if maybe there is some sort of buffer issue depending on the size of the buffers. That may be another area to look at, especially if ring buffers are used. Just some additional food for thought.
  • Hi Ralph,
    The first issue that you referred to occurs at multiples of 64 bytes, and 256 fits that description. I haven't checked the exact offsets of the other corruption, but it could be at 64 and 128 or 192.
  • Looks like 2.1.4 is now working well enough for the TM4C Host to see my attached Device. I couldn't try 2.1.4 before because I was missing one line. Adding:

    SysCtlVCOGet(SYSCTL_XTAL_25MHZ, &ui32PLLRate);
    ... was all that was needed. I found this by doing a diff on the usb_host_audio_in example and seeing that was one of only two code changes.

    I also discovered the INCLUDE_DEBUG_OUTPUT define that helped me narrow down where 2.1.4 was falling down.

    Note: I spoke too soon. The original issue is not resolved yet. Although I've successfully upgraded to 2.1.4 and my Device is detected by this Host firmware, further testing shows that the data corruption is still present.

    I'll continue investigating tomorrow, but at least I'm working from the latest 2.1.4 sources instead of the ancient 2.1.0 release.

    P.S. Is there a way to remove the "Resolved" flag on this thread until we actually have a full solution?

  • Hello Brian,

    Thank you for the update, sorry to hear that the issue has reappeared. I don't have a way to remove the Resolved flag unfortunately, but I can assure you I am keeping this thread open on my end as you continue to work through this issue.

    Regarding your prior comment about 64 byte being a multiple of 256 in relation to the issue with the PipeWrite I brought up, I think that is nothing beyond coincidence as the issue with the PipeWrite function is specifically due to checking against 64 bytes. So anything over 64 bytes would trigger the issue.
  • Looking deeper, I see that I am using uDMA. Inspired by the usb_host_audio_in example, my firmware calls USBHCDPipeAllocSize(0, USBHCD_PIPE_ISOC_IN_DMA, ...) and then uses USBHCDPipeSchedule() to request that data be written to the desired queue. The isochronous stream is maintained by calling USBHCDPipeSchedule() again within each callback.

    It would seem that this avoids the special 64-byte size handling in USBHCDPipeRead()
    As far as I see, the uDMA should be loading all 386 bytes in one transfer, but I am seeing corrupted bytes.
    Any hints as to why isochronous transfers via uDMA would suffer from data corruption?

  • Hello Brian,

    Thanks for sharing about uDMA, I looked further into this but still haven't landed on anything concrete. I am discussing this with another USB expert and we wanted to get clarity about where the corruption is viewed. Is the corruption occurring only on the TM4C side within the buffers stored on the TM4C? That is my understanding based on how I am interpreting the discussion but I didn't see it explicitly stated...

  • Ralph Jacobi said:
    I am discussing this with another USB expert and we wanted to get clarity about where the corruption is viewed. Is the corruption occurring only on the TM4C side within the buffers stored on the TM4C? That is my understanding based on how I am interpreting the discussion but I didn't see it explicitly stated...

    Not sure I understand the question exactly, but I have used a USB analyzer to confirm that the data is correct on the wire, and corrupt only within the TM4C memory space. I'm not aware of any way to check the data within the USB peripheral itself. The USBHCDPipeSchedule() routine takes an address that I provide, and I see corruption in the data written there, even though no USB packet checksum error is reported. I'm checking the data at the earliest opportunity - during the completion callback. I moved my queue to a separate part of SRAM just to confirm that it wasn't a random memory corruption from some other code (there isn't much else). My queue has enough room for 4 packets, but I've also made it large enough for 40 packets with the same results.

    As I mentioned, it may have some correlation to the 0-length packets that are expected on a regular basis due to clock mismatches between Device and Host.

    FYI: I briefly tried changing the USBHCDPipeAllocSize() parameter from USBHCD_PIPE_ISOC_IN_DMA to USBHCD_PIPE_ISOC_IN, assuming that would disable uDMA, but it had no effect. I see that the library code does fall back to non-uDMA operations if it cannot allocate DMA channels, so perhaps I should confirm that it's actually using uDMA.

  • Hello Brian,

    Thank you, that answered our question well even though I was a bit unclear on what I was asking.

    There was a recent bug discovered for the usbdma and while resolving that a more robust solution was put together for the PipeWrite issue as well. I am attaching the files which cover both of those fixes. Please update your usblib with these files (make sure to recompile usblib itself if you are linking the .lib into your project) and see if there is any improvement.

    If there is a correlation to the 0-byte packets, then the timing should align on the dot, not just be nearby. How have you calculated the 41-42 frames for when you are expecting the 0-byte packet? Have you measured that the 0-byte packets are appearing exactly as expected from your calculations? If the 0-byte packets really are at the 41-42 frames mark, and the error occurs in the 45-49 frame range, then we don't believe there is any correlation as that gap is too wide to explain.

    You mention the TMS320 uses a custom class, does that differ from say, an audio device? While the issue isn't observed on that end, maybe better understanding how the device is setup could help us as well.

    usbdma file: 

    usbdma.c
    //*****************************************************************************
    //
    // usbdma.c - USB Library DMA handling functions.
    //
    // Copyright (c) 2012-2017 Texas Instruments Incorporated.  All rights reserved.
    // Software License Agreement
    // 
    // Texas Instruments (TI) is supplying this software for use solely and
    // exclusively on TI's microcontroller products. The software is owned by
    // TI and/or its suppliers, and is protected under applicable copyright
    // laws. You may not combine this software with "viral" open-source
    // software in order to form a larger program.
    // 
    // THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS.
    // NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT
    // NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    // A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY
    // CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
    // DAMAGES, FOR ANY REASON WHATSOEVER.
    // 
    // This is part of revision 2.1.4.178 of the Tiva USB Library.
    //
    //*****************************************************************************
    
    #include <stdbool.h>
    #include <stdint.h>
    #include "inc/hw_memmap.h"
    #include "inc/hw_types.h"
    #include "inc/hw_ints.h"
    #include "inc/hw_sysctl.h"
    #include "inc/hw_udma.h"
    #include "driverlib/debug.h"
    #include "driverlib/interrupt.h"
    #include "driverlib/rtos_bindings.h"
    #include "driverlib/usb.h"
    #include "driverlib/rom.h"
    #include "driverlib/rom_map.h"
    #include "driverlib/udma.h"
    #include "usblib/usblib.h"
    #include "usblib/usblibpriv.h"
    
    //*****************************************************************************
    //
    //! \addtogroup usblib_dma_api Internal USB DMA functions
    //! @{
    //
    //*****************************************************************************
    
    //*****************************************************************************
    //
    // There are two sets of functions in this file, one is used with uDMA while
    // the other is used with USB controllers with an integrated DMA controller.
    // The functions with the IDMA prefix are for the integrated DMA controller and
    // the functions that are specific to the uDMA controller are prefixed with
    // uDMA.  Any common functions are have just the DMA prefix.
    //
    //*****************************************************************************
    static tUSBDMAInstance g_psUSBDMAInst[1];
    
    //*****************************************************************************
    //
    // Macros used to determine if a uDMA endpoint configuration is used for
    // receive or transmit.
    //
    //*****************************************************************************
    #define UDMAConfigIsRx(ui32Config)                                            \
            ((ui32Config & UDMA_SRC_INC_NONE) == UDMA_SRC_INC_NONE)
    #define UDMAConfigIsTx(ui32Config)                                            \
            ((ui32Config & UDMA_DEST_INC_NONE) == UDMA_DEST_INC_NONE)
    
    //*****************************************************************************
    //
    // USBLibDMAChannelStatus() for USB controllers that use the uDMA for DMA.
    //
    //*****************************************************************************
    static uint32_t
    uDMAUSBChannelStatus(tUSBDMAInstance *psUSBDMAInst, uint32_t ui32Channel)
    {
        uint32_t ui32Status;
    
        //
        // Initialize the current status to no events.
        //
        ui32Status = USBLIBSTATUS_DMA_IDLE;
    
        //
        // Check if there is a pending DMA transfer.
        //
        if(psUSBDMAInst->ui32Complete & (1 << (ui32Channel - 1)))
        {
            //
            // Return that the DMA transfer has completed and clear the
            // DMA pending flag.
            //
            ui32Status = USBLIBSTATUS_DMA_COMPLETE;
        }
        else if(psUSBDMAInst->ui32Pending & (1 << (ui32Channel - 1)))
        {
            //
            // DMA transfer is still pending.
            //
            ui32Status = USBLIBSTATUS_DMA_PENDING;
        }
        else
        {
            //
            // DMA transfer is still pending.
            //
            ui32Status = USBLIBSTATUS_DMA_IDLE;
        }
    
        return(ui32Status);
    }
    
    //*****************************************************************************
    //
    // USBLibDMAChannelStatus() for USB controllers with an integrated DMA
    // controller.
    //
    //*****************************************************************************
    static uint32_t
    iDMAUSBChannelStatus(tUSBDMAInstance *psUSBDMAInst, uint32_t ui32Channel)
    {
        uint32_t ui32Status;
    
        //
        // Initialize the current status to no events.
        //
        ui32Status = USBLIBSTATUS_DMA_IDLE;
    
        //
        // Check if an error has occurred.
        //
        if(USBDMAChannelStatus(psUSBDMAInst->ui32Base, ui32Channel) ==
           USB_DMA_STATUS_ERROR)
        {
            ui32Status = USBLIBSTATUS_DMA_ERROR;
        }
        //
        // Otherwise check if there a pending DMA transfer has completed.
        //
        else if(psUSBDMAInst->ui32Complete & (1 << (ui32Channel - 1)))
        {
            //
            // Return that the DMA transfer has completed and clear the
            // DMA pending flag.
            //
            ui32Status = USBLIBSTATUS_DMA_COMPLETE;
        }
        else if(psUSBDMAInst->ui32Pending & (1 << (ui32Channel - 1)))
        {
            //
            // DMA transfer is still pending.
            //
            ui32Status = USBLIBSTATUS_DMA_PENDING;
        }
        else
        {
            //
            // DMA Channel is idle.
            //
            ui32Status = USBLIBSTATUS_DMA_IDLE;
        }
    
        return(ui32Status);
    }
    
    //*****************************************************************************
    //
    // USBLibDMAIntStatus() for USB controllers that use uDMA.
    //
    //*****************************************************************************
    static uint32_t
    uDMAUSBIntStatus(tUSBDMAInstance *psUSBDMAInst)
    {
        uint32_t ui32Status, ui32Pending;
        int32_t i32Channel;
    
        //
        // Initialize the current status to no events.
        //
        ui32Status = 0;
    
        //
        // No pending interrupts by default.
        //
        ui32Status = 0;
    
        //
        // Save the pending channels.
        //
        ui32Pending = psUSBDMAInst->ui32Pending;
    
        //
        // Loop through channels to find out if any pending DMA transfers have
        // completed.
        //
        for(i32Channel = 0; i32Channel < USB_MAX_DMA_CHANNELS; i32Channel++)
        {
            //
            // If pending and stopped then the DMA completed.
            //
            if((ui32Pending & 1) &&
               (MAP_uDMAChannelModeGet(i32Channel) == UDMA_MODE_STOP))
            {
                ui32Status |= (1 << i32Channel);
            }
            ui32Pending >>= 1;
    
            //
            // Done if this is zero.
            //
            if(ui32Pending == 0)
            {
                break;
            }
        }
    
        return(ui32Status);
    }
    
    //*****************************************************************************
    //
    // USBLibDMAIntStatus() for USB controllers with an integrated DMA controller.
    //
    //*****************************************************************************
    static uint32_t
    iDMAUSBIntStatus(tUSBDMAInstance *psUSBDMAInst)
    {
        //
        // Read the current DMA status, unfortunately this clears the
        // pending interrupt status.
        //
        return(USBDMAChannelIntStatus(psUSBDMAInst->ui32Base));
    }
    
    //*****************************************************************************
    //
    // USBLibDMAIntStatusClear() for USB controllers that use uDMA for DMA or have
    // an integrated DMA controller.
    //
    //*****************************************************************************
    static void
    DMAUSBIntStatusClear(tUSBDMAInstance *psUSBDMAInst, uint32_t ui32Status)
    {
        //
        // Clear out the requested interrupts.  Since the USB interface does not
        // have a true interrupt clear, this clears the current completed
        // status for the requested channels.
        //
        psUSBDMAInst->ui32Complete &= ~ui32Status;
    
        return;
    }
    
    //*****************************************************************************
    //
    // USBLibDMAIntHandler() for USB controllers that use uDMA for DMA or have an
    // integrated DMA controller.
    //
    //*****************************************************************************
    static void
    DMAUSBIntHandler(tUSBDMAInstance *psUSBDMAInst, uint32_t ui32DMAIntStatus)
    {
        uint32_t ui32Channel;
    
        if(ui32DMAIntStatus == 0)
        {
            return;
        }
    
        //
        // Determine if the uDMA is used or the USB DMA controller.
        //
        for(ui32Channel = 0; ui32Channel < USB_MAX_DMA_CHANNELS; ui32Channel++)
        {
            //
            // Mark any pending interrupts as completed.
            //
            if(ui32DMAIntStatus & 1)
            {
                psUSBDMAInst->ui32Pending &= ~(1 << ui32Channel);
                psUSBDMAInst->ui32Complete |= (1 << ui32Channel);
            }
    
            //
            // Check the next channel.
            //
            ui32DMAIntStatus >>= 1;
    
            //
            // Break if there are no more pending DMA interrupts.
            //
            if(ui32DMAIntStatus == 0)
            {
                break;
            }
        }
    }
    
    //*****************************************************************************
    //
    // USBLibDMAChannelEnable() for USB controllers that use uDMA.
    //
    //*****************************************************************************
    static void
    uDMAUSBChannelEnable(tUSBDMAInstance *psUSBDMAInst, uint32_t ui32Channel)
    {
        uint32_t ui32IntEnabled;
    
        //
        // Save if the interrupt was enabled or not.
        //
        ui32IntEnabled = IntIsEnabled(psUSBDMAInst->ui32IntNum);
    
        //
        // Disable the USB interrupt if it was enabled.
        //
        if(ui32IntEnabled)
        {
            OS_INT_DISABLE(psUSBDMAInst->ui32IntNum);
        }
    
        //
        // Mark this channel as pending and not complete.
        //
        psUSBDMAInst->ui32Pending |= (1 << (ui32Channel - 1));
        psUSBDMAInst->ui32Complete &= ~(1 << (ui32Channel - 1));
    
        //
        // Enable DMA for the endpoint.
        //
        if(UDMAConfigIsRx(psUSBDMAInst->pui32Config[ui32Channel - 1]))
        {
            MAP_USBEndpointDMAEnable(psUSBDMAInst->ui32Base,
                                     psUSBDMAInst->pui8Endpoint[ui32Channel - 1],
                                     USB_EP_DEV_OUT | USB_EP_HOST_IN);
        }
        else
        {
            MAP_USBEndpointDMAEnable(psUSBDMAInst->ui32Base,
                                     psUSBDMAInst->pui8Endpoint[ui32Channel - 1],
                                     USB_EP_DEV_IN | USB_EP_HOST_OUT);
        }
    
        //
        // Enable the DMA in the uDMA controller.
        //
        MAP_uDMAChannelEnable(ui32Channel - 1);
    
        //
        // Enable the USB interrupt if it was enabled before.
        //
        if(ui32IntEnabled)
        {
            OS_INT_ENABLE(psUSBDMAInst->ui32IntNum);
        }
    }
    
    //*****************************************************************************
    //
    // USBLibDMAChannelEnable() for USB controllers with an integrated DMA
    // controller.
    //
    //*****************************************************************************
    static void
    iDMAUSBChannelEnable(tUSBDMAInstance *psUSBDMAInst, uint32_t ui32Channel)
    {
        uint32_t ui32IntEnabled;
    
        //
        // Save if the interrupt was enabled or not.
        //
        ui32IntEnabled = IntIsEnabled(psUSBDMAInst->ui32IntNum);
    
        //
        // Disable the USB interrupt if it was enabled.
        //
        if(ui32IntEnabled)
        {
            OS_INT_DISABLE(psUSBDMAInst->ui32IntNum);
        }
    
        //
        // Mark this channel as pending and not complete.
        //
        psUSBDMAInst->ui32Pending |= (1 << (ui32Channel - 1));
        psUSBDMAInst->ui32Complete &= ~(1 << (ui32Channel - 1));
    
        //
        // Enable the interrupt for this DMA channel.
        //
        USBDMAChannelIntEnable(psUSBDMAInst->ui32Base, ui32Channel - 1);
    
        //
        // Enable the DMA channel.
        //
        USBDMAChannelEnable(psUSBDMAInst->ui32Base, ui32Channel - 1);
    
        //
        // Enable the USB interrupt if it was enabled before.
        //
        if(ui32IntEnabled)
        {
            OS_INT_ENABLE(psUSBDMAInst->ui32IntNum);
        }
    }
    
    //*****************************************************************************
    //
    // USBLibDMAChannelDisable() for USB controllers that use uDMA.
    //
    //*****************************************************************************
    static void
    uDMAUSBChannelDisable(tUSBDMAInstance *psUSBDMAInst, uint32_t ui32Channel)
    {
        //
        // Disable DMA for the endpoint.
        //
        if(UDMAConfigIsRx(psUSBDMAInst->pui32Config[ui32Channel - 1]))
        {
            MAP_USBEndpointDMADisable(psUSBDMAInst->ui32Base,
                                      psUSBDMAInst->pui8Endpoint[ui32Channel - 1],
                                      USB_EP_DEV_OUT);
        }
        else
        {
            MAP_USBEndpointDMADisable(psUSBDMAInst->ui32Base,
                                      psUSBDMAInst->pui8Endpoint[ui32Channel - 1],
                                      USB_EP_DEV_IN);
        }
    
        //
        // Disable the DMA channel in the uDMA controller.
        //
        MAP_uDMAChannelDisable(ui32Channel - 1);
    
        //
        // Clear out any pending or complete flag set for this DMA channel.
        //
        psUSBDMAInst->ui32Pending &= ~(1 << (ui32Channel - 1));
        psUSBDMAInst->ui32Complete &= ~(1 << (ui32Channel - 1));
    }
    
    //*****************************************************************************
    //
    // USBLibDMAChannelDisable() for USB controllers with an integrated DMA
    // controller.
    //
    //*****************************************************************************
    static void
    iDMAUSBChannelDisable(tUSBDMAInstance *psUSBDMAInst, uint32_t ui32Channel)
    {
        //
        // Disable the DMA channel.
        //
        USBDMAChannelDisable(psUSBDMAInst->ui32Base, ui32Channel - 1);
    
        //
        // Disable the interrupt for this DMA channel.
        //
        USBDMAChannelIntDisable(psUSBDMAInst->ui32Base, ui32Channel - 1);
    
        //
        // Clear out any pending or complete flag set for this DMA channel.
        //
        psUSBDMAInst->ui32Pending &= ~(1 << (ui32Channel - 1));
        psUSBDMAInst->ui32Complete &= ~(1 << (ui32Channel - 1));
    }
    
    //*****************************************************************************
    //
    // USBLibDMAChannelIntEnable() for USB controllers that use uDMA.
    //
    //*****************************************************************************
    static void
    uDMAUSBChannelIntEnable(tUSBDMAInstance *psUSBDMAInst, uint32_t ui32Channel)
    {
        //
        // There is no way to Enable channel interrupts when using uDMA.
        //
    }
    
    //*****************************************************************************
    //
    // USBLibDMAChannelIntEnable() for USB controllers with an integrated DMA
    // controller.
    //
    //*****************************************************************************
    static void
    iDMAUSBChannelIntEnable(tUSBDMAInstance *psUSBDMAInst, uint32_t ui32Channel)
    {
        //
        // Enable the interrupt for this DMA channel.
        //
        USBDMAChannelIntEnable(psUSBDMAInst->ui32Base, ui32Channel - 1);
    }
    
    //*****************************************************************************
    //
    // USBLibDMAChannelIntDisable() for USB controllers that use uDMA.
    //
    //*****************************************************************************
    static void
    uDMAUSBChannelIntDisable(tUSBDMAInstance *psUSBDMAInst, uint32_t ui32Channel)
    {
        //
        // There is no way to Disable channel interrupts when using uDMA.
        //
    }
    
    //*****************************************************************************
    //
    // USBLibDMAChannelIntDisable() for USB controllers with an integrated DMA
    // controller.
    //
    //*****************************************************************************
    static void
    iDMAUSBChannelIntDisable(tUSBDMAInstance *psUSBDMAInst, uint32_t ui32Channel)
    {
        //
        // Disable the interrupt for this DMA channel.
        //
        USBDMAChannelIntDisable(psUSBDMAInst->ui32Base, ui32Channel - 1);
    }
    
    //*****************************************************************************
    //
    // USBLibDMATransfer() for USB controllers that use the uDMA controller.
    //
    //*****************************************************************************
    static uint32_t
    uDMAUSBTransfer(tUSBDMAInstance *psUSBDMAInst, uint32_t ui32Channel,
                    void *pvBuffer, uint32_t ui32Size)
    {
        void *pvFIFO;
        uint32_t uluDMAChannel;
        uint32_t ui32PacketCount;
        uint32_t ui32TransferCount;
    
        if((ui32Size < 64) || ((uint32_t)pvBuffer & 0x3))
        {
            return(0);
        }
    
        //
        // Mark this channel as pending and not complete.
        //
        psUSBDMAInst->ui32Pending |= (1 << (ui32Channel - 1));
        psUSBDMAInst->ui32Complete &= ~(1 << (ui32Channel - 1));
    
        //
        // Save the pointer to the data and the byte count.
        //
        psUSBDMAInst->ppui32Data[ui32Channel - 1] = pvBuffer;
        psUSBDMAInst->pui32Count[ui32Channel - 1] = ui32Size;
    
        //
        // Need the address of the FIFO.
        //
        pvFIFO = (void *)USBFIFOAddrGet(psUSBDMAInst->ui32Base,
                                    psUSBDMAInst->pui8Endpoint[ui32Channel - 1]);
    
        //
        // Calculate the uDMA channel for this RX channel.
        //
        uluDMAChannel = UDMA_CHANNEL_USBEP1RX + ui32Channel - 1;
    
        ui32TransferCount = ui32Size;
    
        if((psUSBDMAInst->pui32Config[ui32Channel - 1] & UDMA_SIZE_32) ==
           UDMA_SIZE_32)
        {
            ui32TransferCount >>= 2;
        }
        else if((psUSBDMAInst->pui32Config[ui32Channel - 1] & UDMA_SIZE_32) ==
                UDMA_SIZE_32)
        {
            ui32TransferCount >>= 1;
        }
    
        //
        // If source increment is none this is an RX transfer.
        //
        if(UDMAConfigIsRx(psUSBDMAInst->pui32Config[ui32Channel - 1]))
        {
            MAP_uDMAChannelTransferSet(uluDMAChannel, UDMA_MODE_BASIC, pvFIFO,
                                       pvBuffer, ui32TransferCount);
        }
        else
        {
            MAP_uDMAChannelTransferSet(uluDMAChannel, UDMA_MODE_BASIC, pvBuffer,
                                       pvFIFO, ui32TransferCount);
        }
    
        //
        // Set the mode based on the size of the transfer.  More than one
        // packet requires mode 1.
        //
        if(ui32Size > psUSBDMAInst->pui32MaxPacketSize[ui32Channel - 1])
        {
            //
            // Calculate the number of packets required for this transfer.
            //
            ui32PacketCount = ((ui32Size /
                               psUSBDMAInst->pui32MaxPacketSize[ui32Channel - 1]));
    
            //
            // Set the packet count so that the last packet does not generate
            // another IN request.
            //
            USBEndpointPacketCountSet(psUSBDMAInst->ui32Base,
                                      psUSBDMAInst->pui8Endpoint[ui32Channel - 1],
                                      ui32PacketCount);
    
            //
            // Configure the USB endpoint in mode 1 for this DMA transfer.
            //
            USBEndpointDMAConfigSet(psUSBDMAInst->ui32Base,
                                    psUSBDMAInst->pui8Endpoint[ui32Channel - 1],
                                    psUSBDMAInst->pui32EPDMAMode1[ui32Channel - 1]);
        }
        else
        {
            //
            // Configure the USB endpoint in mode 0 for this DMA transfer.
            //
            USBEndpointDMAConfigSet(psUSBDMAInst->ui32Base,
                                    psUSBDMAInst->pui8Endpoint[ui32Channel - 1],
                                    psUSBDMAInst->pui32EPDMAMode0[ui32Channel -1]);
        }
    
        //
        // Enable the uDMA channel to start the transfer
        //
        uDMAUSBChannelEnable(psUSBDMAInst, ui32Channel);
    
        return(ui32Size);
    }
    
    //*****************************************************************************
    //
    // USBLibDMATransfer() for USB controllers with an integrated DMA controller.
    //
    //*****************************************************************************
    static uint32_t
    iDMAUSBTransfer(tUSBDMAInstance *psUSBDMAInst, uint32_t ui32Channel,
                    void *pvBuffer, uint32_t ui32Size)
    {
        uint32_t ui32PacketCount;
    
        if((uint32_t)pvBuffer & 0x3)
        {
            return(0);
        }
    
        //
        // Mark this channel as pending and not complete.
        //
        psUSBDMAInst->ui32Pending |= (1 << (ui32Channel - 1));
        psUSBDMAInst->ui32Complete &= ~(1 << (ui32Channel - 1));
    
        //
        // Save the pointer to the data and the byte count.
        //
        psUSBDMAInst->ppui32Data[ui32Channel - 1] = pvBuffer;
        psUSBDMAInst->pui32Count[ui32Channel - 1] = ui32Size;
    
        //
        // Set the address.
        //
        USBDMAChannelAddressSet(psUSBDMAInst->ui32Base, ui32Channel - 1, pvBuffer);
    
        //
        // Set the number of transfers.
        //
        USBDMAChannelCountSet(psUSBDMAInst->ui32Base, ui32Channel - 1, ui32Size);
    
        //
        // Set the mode based on the size of the transfer.  More than one
        // packet requires mode 1.
        //
        if(ui32Size >= psUSBDMAInst->pui32MaxPacketSize[ui32Channel - 1])
        {
            //
            // Calculate the number of packets required for this transfer.
            //
            ui32PacketCount = ui32Size /
                              psUSBDMAInst->pui32MaxPacketSize[ui32Channel - 1];
    
            if(ui32Size % psUSBDMAInst->pui32MaxPacketSize[ui32Channel - 1])
            {
                ui32PacketCount += 1;
            }
    
            USBEndpointPacketCountSet(psUSBDMAInst->ui32Base,
                                      psUSBDMAInst->pui8Endpoint[ui32Channel - 1],
                                      ui32PacketCount);
    
            //
            // Configure the USB DMA controller for mode 1.
            //
            USBEndpointDMAConfigSet(psUSBDMAInst->ui32Base,
                                    psUSBDMAInst->pui8Endpoint[ui32Channel - 1],
                                    psUSBDMAInst->pui32EPDMAMode1[ui32Channel - 1]);
    
            USBDMAChannelConfigSet(psUSBDMAInst->ui32Base, ui32Channel - 1,
                                   psUSBDMAInst->pui8Endpoint[ui32Channel - 1],
                                   psUSBDMAInst->pui32Config[ui32Channel - 1] |
                                   USB_DMA_CFG_MODE_1);
    
            //
            // Enable DMA on the endpoint.
            //
            if(psUSBDMAInst->pui32Config[ui32Channel - 1] & USB_DMA_CFG_DIR_TX)
            {
                //
                // Make sure that DMA is enabled on the endpoint.
                //
                MAP_USBEndpointDMAEnable(
                                    psUSBDMAInst->ui32Base,
                                    psUSBDMAInst->pui8Endpoint[ui32Channel - 1],
                                    USB_EP_HOST_OUT);
            }
            else
            {
                //
                // Make sure that DMA is enabled on the endpoint.
                //
                MAP_USBEndpointDMAEnable(
                                       psUSBDMAInst->ui32Base,
                                       psUSBDMAInst->pui8Endpoint[ui32Channel - 1],
                                       USB_EP_HOST_IN);
            }
    
            //
            // Enable the DMA channel.
            //
            USBDMAChannelEnable(psUSBDMAInst->ui32Base, ui32Channel - 1);
        }
        else
        {
            //
            // Configure the USB DMA controller for mode 0.
            //
            USBEndpointDMAConfigSet(psUSBDMAInst->ui32Base,
                                    psUSBDMAInst->pui8Endpoint[ui32Channel - 1],
                                    psUSBDMAInst->pui32EPDMAMode0[ui32Channel -1]);
    
            USBDMAChannelConfigSet(psUSBDMAInst->ui32Base, ui32Channel -1,
                                   psUSBDMAInst->pui8Endpoint[ui32Channel - 1],
                                   psUSBDMAInst->pui32Config[ui32Channel - 1] |
                                   USB_DMA_CFG_MODE_0);
    
            //
            // In mode 0 only enable DMA transfer for mode 0.
            //
            if(psUSBDMAInst->pui32Config[ui32Channel - 1] & USB_DMA_CFG_DIR_TX)
            {
                //
                // Make sure that DMA is enabled on the endpoint.
                //
                MAP_USBEndpointDMAEnable(
                                    psUSBDMAInst->ui32Base,
                                    psUSBDMAInst->pui8Endpoint[ui32Channel - 1],
                                    USB_EP_HOST_OUT);
                //
                // Enable the DMA channel.
                //
                USBDMAChannelEnable(psUSBDMAInst->ui32Base, ui32Channel - 1);
            }
            else
            {
                //
                // Make sure that DMA is disabled on the endpoint, it will
                // be enabled when the endpoint interrupt occurs.
                //
                MAP_USBEndpointDMADisable(
                                       psUSBDMAInst->ui32Base,
                                       psUSBDMAInst->pui8Endpoint[ui32Channel - 1],
                                       USB_EP_HOST_IN);
    
                //
                // Returns 0 so that when DMA is not used, the bUseDMA flag in the calling function
                // is set to false.
                //
                return(0);
    
            }
        }
    
        return(ui32Size);
    }
    
    //*****************************************************************************
    //
    // USBLibDMAChannelAllocate() for USB controllers that use uDMA for DMA.
    //
    //*****************************************************************************
    static uint32_t
    uDMAUSBChannelAllocate(tUSBDMAInstance *psUSBDMAInst, uint8_t ui8Endpoint,
                           uint32_t ui32MaxPacketSize, uint32_t ui32Config)
    {
        uint32_t ui32Channel;
    
        //
        // The DMA channels are organized in pairs on this controller and the
        // transmit channels are 1, 3, and 5 while receive are 0, 2, and 4.
        //
        if(ui32Config & USB_DMA_EP_RX)
        {
            ui32Channel = 0;
        }
        else
        {
            ui32Channel = 1;
        }
    
        //
        // Search for an available DMA channel to use.
        //
        for(; ui32Channel < USB_MAX_DMA_CHANNELS_0; ui32Channel += 2)
        {
            //
            // If the current endpoint value is zero then this channel is
            // available.
            //
            if(psUSBDMAInst->pui8Endpoint[ui32Channel] == 0)
            {
                //
                // Save the endpoint for this DMA channel.
                //
                psUSBDMAInst->pui8Endpoint[ui32Channel] = ui8Endpoint;
    
                //
                // Save the maximum packet size for the endpoint.
                //
                psUSBDMAInst->pui32MaxPacketSize[ui32Channel] = ui32MaxPacketSize;
    
                //
                // Set the channel configuration based on the direction.
                //
                if(ui32Config & USB_DMA_EP_RX)
                {
                    psUSBDMAInst->pui32Config[ui32Channel] =
                            UDMA_SIZE_8 | UDMA_SRC_INC_NONE | UDMA_DST_INC_8 |
                            UDMA_ARB_64;
    
                    //
                    // If in device mode and Isochronous.
                    //
                    if(((ui32Config & USB_DMA_EP_HOST) == 0) &&
                       ((ui32Config & USB_DMA_EP_TYPE_M) == USB_DMA_EP_TYPE_ISOC))
                    {
                        //
                        // USB_EP_AUTO_REQUEST is required for device
                        // Isochronous endpoints.
                        //
                        psUSBDMAInst->pui32EPDMAMode0[ui32Channel] =
                                                        USB_EP_DMA_MODE_0 |
                                                        USB_EP_AUTO_REQUEST |
                                                        USB_EP_HOST_IN;
                    }
                    else
                    {
                        psUSBDMAInst->pui32EPDMAMode0[ui32Channel] =
                                                        USB_EP_DMA_MODE_0 |
                                                        USB_EP_AUTO_CLEAR |
                                                        USB_EP_HOST_IN;
                    }
    
                    //
                    // Do not set auto request in device mode unless it is an
                    // isochronous endpoint.
                    //
                    if(((ui32Config & USB_DMA_EP_HOST) == 0) &&
                       ((ui32Config & USB_DMA_EP_TYPE_M) != USB_DMA_EP_TYPE_ISOC))
                    {
                        psUSBDMAInst->pui32EPDMAMode1[ui32Channel] =
                                                    USB_EP_DMA_MODE_1 |
                                                    USB_EP_HOST_IN |
                                                    USB_EP_AUTO_CLEAR;
                    }
                    else
                    {
                        psUSBDMAInst->pui32EPDMAMode1[ui32Channel] =
                                                    USB_EP_DMA_MODE_1 |
                                                    USB_EP_HOST_IN |
                                                    USB_EP_AUTO_REQUEST |
                                                    USB_EP_AUTO_CLEAR;
                    }
                }
                else
                {
                    psUSBDMAInst->pui32Config[ui32Channel] =
                            UDMA_SIZE_8 | UDMA_SRC_INC_8 | UDMA_DST_INC_NONE |
                            UDMA_ARB_64;
    
                    psUSBDMAInst->pui32EPDMAMode0[ui32Channel] = USB_EP_DMA_MODE_0 |
                                                                 USB_EP_HOST_OUT;
                    psUSBDMAInst->pui32EPDMAMode1[ui32Channel] = USB_EP_DMA_MODE_1 |
                                                                 USB_EP_HOST_OUT |
                                                                 USB_EP_AUTO_SET;
                }
    
                //
                // Map the uDMA channel to the given endpoint.
                //
                MAP_USBEndpointDMAChannel(psUSBDMAInst->ui32Base, ui8Endpoint,
                                          ui32Channel);
    
                //
                // Clear out the attributes on this channel.
                //
                MAP_uDMAChannelAttributeDisable(ui32Channel, UDMA_ATTR_ALL);
    
                //
                // Configure the uDMA channel for the pipe
                //
                MAP_uDMAChannelControlSet(ui32Channel,
                                          psUSBDMAInst->pui32Config[ui32Channel]);
    
                if(ui32Config & USB_DMA_EP_RX)
                {
                    MAP_USBEndpointDMADisable(psUSBDMAInst->ui32Base, ui8Endpoint,
                                              USB_EP_DEV_OUT);
                }
                else
                {
                    MAP_USBEndpointDMADisable(psUSBDMAInst->ui32Base, ui8Endpoint,
                                              USB_EP_DEV_IN);
                }
    
                //
                // Outside of this function all channels are 1 based as
                // zero is not a valid channel.
                //
                return(ui32Channel + 1);
            }
        }
        return(0);
    }
    
    //*****************************************************************************
    //
    // USBLibDMAChannelAllocate() for USB controllers with an integrated DMA
    // controller.
    //
    //*****************************************************************************
    static uint32_t
    iDMAUSBChannelAllocate(tUSBDMAInstance *psUSBDMAInst, uint8_t ui8Endpoint,
                           uint32_t ui32MaxPacketSize, uint32_t ui32Config)
    {
        uint32_t ui32Channel;
    
        //
        // Search for an available DMA channel to use.
        //
        for(ui32Channel = 0; ui32Channel < USB_MAX_DMA_CHANNELS_0; ui32Channel++)
        {
            //
            // If the current endpoint value is zero then this channel is
            // available.
            //
            if(psUSBDMAInst->pui8Endpoint[ui32Channel] == 0)
            {
                //
                // Clear out the attributes on this channel.
                //
                USBDMAChannelDisable(psUSBDMAInst->ui32Base, ui32Channel);
    
                //
                // Save the endpoint for this DMA channel.
                //
                psUSBDMAInst->pui8Endpoint[ui32Channel] = ui8Endpoint;
    
                //
                // Save the maximum packet size for the endpoint.
                //
                psUSBDMAInst->pui32MaxPacketSize[ui32Channel] = ui32MaxPacketSize;
    
                //
                // Assign the endpoint to the channel and set the direction.
                //
                if(ui32Config & USB_DMA_EP_RX)
                {
                    psUSBDMAInst->pui32Config[ui32Channel] =
                                            USB_DMA_CFG_DIR_RX |
                                            USB_DMA_CFG_BURST_NONE |
                                            USB_DMA_CFG_INT_EN;
    
                    //
                    // If in device mode and Isochronous.
                    //
                    if(((ui32Config & USB_DMA_EP_HOST) == 0) &&
                       ((ui32Config & USB_DMA_EP_TYPE_M) == USB_DMA_EP_TYPE_ISOC))
                    {
                        //
                        // USB_EP_AUTO_REQUEST is required for device
                        // Isochronous endpoints.
                        //
                        psUSBDMAInst->pui32EPDMAMode0[ui32Channel] =
                                                        USB_EP_DMA_MODE_0 |
                                                        USB_EP_AUTO_REQUEST |
                                                        USB_EP_HOST_IN;
                    }
                    else
                    {
                        psUSBDMAInst->pui32EPDMAMode0[ui32Channel] =
                                                        USB_EP_DMA_MODE_0 |
                                                        USB_EP_AUTO_CLEAR |
                                                        USB_EP_HOST_IN;
                    }
    
                    //
                    // Do not set auto request in device mode unless it is an
                    // isochronous endpoint.
                    //
                    if(((ui32Config & USB_DMA_EP_HOST) == 0) &&
                       ((ui32Config & USB_DMA_EP_TYPE_M) != USB_DMA_EP_TYPE_ISOC))
                    {
                        psUSBDMAInst->pui32EPDMAMode1[ui32Channel] =
                                                    USB_EP_DMA_MODE_1 |
                                                    USB_EP_HOST_IN |
                                                    USB_EP_AUTO_CLEAR;
                    }
                    else
                    {
                        psUSBDMAInst->pui32EPDMAMode1[ui32Channel] =
                                                    USB_EP_DMA_MODE_1 |
                                                    USB_EP_HOST_IN |
                                                    USB_EP_AUTO_REQUEST |
                                                    USB_EP_AUTO_CLEAR;
                    }
                }
                else
                {
                    psUSBDMAInst->pui32Config[ui32Channel] =
                                                    USB_DMA_CFG_DIR_TX |
                                                    USB_DMA_CFG_BURST_NONE |
                                                    USB_DMA_CFG_INT_EN;
    
                    psUSBDMAInst->pui32EPDMAMode0[ui32Channel] =
                                                    USB_EP_DMA_MODE_0 |
                                                    USB_EP_HOST_OUT;
                    psUSBDMAInst->pui32EPDMAMode1[ui32Channel] =
                                                    USB_EP_DMA_MODE_1 |
                                                    USB_EP_HOST_OUT |
                                                    USB_EP_AUTO_SET;
                }
    
                //
                // Outside of this function all channels are 1 based as
                // zero is not a valid channel.
                //
                return(ui32Channel + 1);
            }
        }
        return(0);
    }
    
    //*****************************************************************************
    //
    // USBLibDMAChannelRelease() for USB controllers that use uDMA for DMA.
    //
    //*****************************************************************************
    static void
    uDMAUSBChannelRelease(tUSBDMAInstance *psUSBDMAInst, uint8_t ui32Channel)
    {
        ASSERT(ui32Channel < USB_MAX_DMA_CHANNELS_0);
    
        //
        // Clear out the attributes on this channel.
        //
        MAP_uDMAChannelAttributeDisable(ui32Channel - 1, UDMA_ATTR_ALL);
    
        if(psUSBDMAInst->pui8Endpoint[ui32Channel] & USB_DMA_EP_RX)
        {
            MAP_USBEndpointDMADisable(psUSBDMAInst->ui32Base,
                    (psUSBDMAInst->pui8Endpoint[ui32Channel] & ~USB_DMA_EP_RX),
                    USB_EP_DEV_OUT);
        }
        else
        {
            MAP_USBEndpointDMADisable(psUSBDMAInst->ui32Base,
                    (psUSBDMAInst->pui8Endpoint[ui32Channel] & ~USB_DMA_EP_RX),
                    USB_EP_DEV_IN);
        }
    
        //
        // Clear out the state for this endpoint.
        //
        psUSBDMAInst->pui8Endpoint[ui32Channel - 1] = 0;
        psUSBDMAInst->pui32Config[ui32Channel - 1] = 0;
        psUSBDMAInst->ui32Pending &= ~(1 << (ui32Channel - 1));
        psUSBDMAInst->ui32Complete &= ~(1 << (ui32Channel - 1));
        psUSBDMAInst->pui32MaxPacketSize[ui32Channel - 1] = 0;
    }
    
    //*****************************************************************************
    //
    // DMAChannelRelease() for USB controllers with an integrated DMA controller.
    //
    //*****************************************************************************
    static void
    iDMAUSBChannelRelease(tUSBDMAInstance *psUSBDMAInst, uint8_t ui32Channel)
    {
        ASSERT(ui32Channel < USB_MAX_DMA_CHANNELS);
    
        //
        // Clear out the attributes on this channel.
        //
        USBDMAChannelDisable(psUSBDMAInst->ui32Base, ui32Channel);
    
        //
        // Clear out the state for this endpoint.
        //
        psUSBDMAInst->pui8Endpoint[ui32Channel - 1] = 0;
        psUSBDMAInst->pui32Config[ui32Channel - 1] = 0;
        psUSBDMAInst->ui32Pending &= ~(1 << (ui32Channel - 1));
        psUSBDMAInst->ui32Complete &= ~(1 << (ui32Channel - 1));
        psUSBDMAInst->pui32MaxPacketSize[ui32Channel - 1] = 0;
    }
    
    //*****************************************************************************
    //
    // USBLibDMAUnitSizeSet() for USB controllers that use uDMA for DMA.
    //
    //*****************************************************************************
    static void
    uDMAUSBUnitSizeSet(tUSBDMAInstance *psUSBDMAInst, uint32_t ui32Channel,
                       uint32_t ui32BitSize)
    {
        uint32_t ui32Value;
    
        ASSERT((ui32BitSize == 8) || (ui32BitSize == 16) || (ui32BitSize == 32));
    
        ASSERT(ui32Channel < USB_MAX_DMA_CHANNELS_0);
    
        if(ui32BitSize == 8)
        {
            ui32Value = UDMA_SIZE_8;
    
            if(UDMAConfigIsRx(psUSBDMAInst->pui32Config[ui32Channel - 1]))
            {
                //
                // Receive increments destination and not source.
                //
                ui32Value |= UDMA_DST_INC_8 | UDMA_SRC_INC_NONE;
            }
            else
            {
                //
                // Transmit increments source and not destination.
                //
                ui32Value |= UDMA_SRC_INC_8 | UDMA_DST_INC_NONE;
            }
        }
        else if(ui32BitSize == 16)
        {
            ui32Value = UDMA_SIZE_16;
    
            if(UDMAConfigIsRx(psUSBDMAInst->pui32Config[ui32Channel - 1]))
            {
                //
                // Receive increments destination and not source.
                //
                ui32Value |= UDMA_DST_INC_16 | UDMA_SRC_INC_NONE;
            }
            else
            {
                //
                // Transmit increments source and not destination.
                //
                ui32Value |= UDMA_SRC_INC_16 | UDMA_DST_INC_NONE;
            }
        }
        else
        {
            ui32Value = UDMA_SIZE_32;
    
            if(UDMAConfigIsRx(psUSBDMAInst->pui32Config[ui32Channel - 1]))
            {
                //
                // Receive increments destination and not source.
                //
                ui32Value |= (UDMA_DST_INC_32 | UDMA_SRC_INC_NONE);
            }
            else
            {
                //
                // Transmit increments source and not destination.
                //
                ui32Value |= (UDMA_SRC_INC_32 | UDMA_DST_INC_NONE);
            }
        }
    
        //
        // Keep the current arbitration size and or in the size.
        //
        psUSBDMAInst->pui32Config[ui32Channel - 1] &= 0x00ffffff;
        psUSBDMAInst->pui32Config[ui32Channel - 1] |= ui32Value;
        MAP_uDMAChannelControlSet(ui32Channel - 1,
                                  psUSBDMAInst->pui32Config[ui32Channel - 1]);
    }
    
    //*****************************************************************************
    //
    // USBLibDMAUnitSizeSet() for USB controllers that have an integrated DMA
    // controller.
    //
    //*****************************************************************************
    static void
    iDMAUSBUnitSizeSet(tUSBDMAInstance *psUSBDMAInst, uint32_t ui32Channel,
                       uint32_t ui32BitSize)
    {
    }
    
    //*****************************************************************************
    //
    // USBLibDMAArbSizeSet() for USB controllers that use uDMA for DMA.
    //
    //*****************************************************************************
    static void
    uDMAUSBArbSizeSet(tUSBDMAInstance *psUSBDMAInst, uint32_t ui32Channel,
                      uint32_t ui32ArbSize)
    {
        uint32_t ui32Value;
    
        ASSERT(ui32Channel < USB_MAX_DMA_CHANNELS_0);
    
        //
        // Get the arbitration size value.
        //
        if(ui32ArbSize == 2)
        {
            ui32Value = UDMA_ARB_2;
        }
        else if(ui32ArbSize == 4)
        {
            ui32Value = UDMA_ARB_4;
        }
        else if(ui32ArbSize == 8)
        {
            ui32Value = UDMA_ARB_8;
        }
        else if(ui32ArbSize == 16)
        {
            ui32Value = UDMA_ARB_16;
        }
        else if(ui32ArbSize == 32)
        {
            ui32Value = UDMA_ARB_32;
        }
        else if(ui32ArbSize == 64)
        {
            ui32Value = UDMA_ARB_64;
        }
        else if(ui32ArbSize == 128)
        {
            ui32Value = UDMA_ARB_128;
        }
        else if(ui32ArbSize == 256)
        {
            ui32Value = UDMA_ARB_256;
        }
        else
        {
            //
            // Default to arbitration size of 1.
            //
            ui32Value = UDMA_ARB_1;
        }
    
        //
        // Keep the current size and or in the new arbitration size.
        //
        psUSBDMAInst->pui32Config[ui32Channel - 1] &= 0xff000000;
        psUSBDMAInst->pui32Config[ui32Channel - 1] |= ui32Value;
    
        //
        // Set the uDMA channel control, remember its channel starts at 0 and
        // not 1.
        //
        MAP_uDMAChannelControlSet(ui32Channel - 1,
                                  psUSBDMAInst->pui32Config[ui32Channel - 1]);
    }
    
    //*****************************************************************************
    //
    // USBLibDMAArbSizeSet() for USB controllers that have an integrated DMA
    // controller.
    //
    //*****************************************************************************
    static void
    iDMAUSBArbSizeSet(tUSBDMAInstance *psUSBDMAInst, uint32_t ui32Channel,
                      uint32_t ui32ArbSize)
    {
    }
    
    //*****************************************************************************
    //
    // USBLibDMAStatus() for USB controllers that use uDMA for DMA or have an
    // integrated DMA controller.
    //
    //*****************************************************************************
    static uint32_t
    DMAUSBStatus(tUSBDMAInstance *psUSBDMAInst)
    {
        return(0);
    }
    
    //*****************************************************************************
    //
    //! This function is used to return the current DMA pointer for a given
    //! DMA channel.
    //!
    //! \param psUSBDMAInst is a generic instance pointer that can be used to
    //! distinguish between different hardware instances.
    //! \param ui32Channel is the DMA channel number for this request.
    //!
    //! This function returns the address that is in use by the DMA channel passed
    //! in via the \e ui32Channel parameter.  This is not the real-time pointer,
    //! but the starting address of the DMA transfer for this DMA channel.
    //!
    //! \return The current DMA address for the given DMA channel.
    //
    //*****************************************************************************
    void *
    USBLibDMAAddrGet(tUSBDMAInstance *psUSBDMAInst, uint32_t ui32Channel)
    {
        return(psUSBDMAInst->ppui32Data[ui32Channel - 1]);
    }
    
    //*****************************************************************************
    //
    //! This function is used to return the current DMA transfer size for a given
    //! DMA channel.
    //!
    //! \param psUSBDMAInst is a generic instance pointer that can be used to
    //! distinguish between different hardware instances.
    //! \param ui32Channel is the DMA channel number for this request.
    //!
    //! This function returns the DMA transfer size that is in use by the DMA
    //! channel passed in via the \e ui32Channel parameter.
    //!
    //! \return The current DMA transfer size for the given DMA channel.
    //
    //*****************************************************************************
    uint32_t
    USBLibDMASizeGet(tUSBDMAInstance *psUSBDMAInst, uint32_t ui32Channel)
    {
        return(psUSBDMAInst->pui32Count[ui32Channel - 1]);
    }
    
    //*****************************************************************************
    //
    //! This function is used to initialize the DMA interface for a USB instance.
    //!
    //! \param ui32Index is the index of the USB controller for this instance.
    //!
    //! This function performs any initialization and configuration of the DMA
    //! portions of the USB controller.  This function returns a pointer that
    //! is used with the remaining USBLibDMA APIs or the function returns zero
    //! if the requested controller cannot support DMA.  If this function is called
    //! when already initialized it will not reinitialize the DMA controller and
    //! will instead return the previously initialized DMA instance.
    //!
    //! \return A pointer to use with USBLibDMA APIs.
    //
    //*****************************************************************************
    tUSBDMAInstance *
    USBLibDMAInit(uint32_t ui32Index)
    {
        uint32_t ui32Channel;
    
        ASSERT(ui32Index == 0);
    
        //
        // Make sure that the DMA has not already been initialized.
        //
        if(g_psUSBDMAInst[0].ui32Base == USB0_BASE)
        {
            return(&g_psUSBDMAInst[0]);
        }
    
        //
        // Save the base address of the USB controller.
        //
        g_psUSBDMAInst[0].ui32Base = USB0_BASE;
    
        //
        // Save the interrupt number for the USB controller.
        //
        g_psUSBDMAInst[0].ui32IntNum = INT_USB0_TM4C123;
    
        //
        // Initialize the function pointers.
        //
        g_psUSBDMAInst[0].pfnArbSizeSet = uDMAUSBArbSizeSet;
        g_psUSBDMAInst[0].pfnChannelAllocate = uDMAUSBChannelAllocate;
        g_psUSBDMAInst[0].pfnChannelDisable = uDMAUSBChannelDisable;
        g_psUSBDMAInst[0].pfnChannelEnable = uDMAUSBChannelEnable;
        g_psUSBDMAInst[0].pfnChannelIntEnable = uDMAUSBChannelIntEnable;
        g_psUSBDMAInst[0].pfnChannelIntDisable = uDMAUSBChannelIntDisable;
        g_psUSBDMAInst[0].pfnChannelRelease = uDMAUSBChannelRelease;
        g_psUSBDMAInst[0].pfnChannelStatus = uDMAUSBChannelStatus;
        g_psUSBDMAInst[0].pfnIntHandler = DMAUSBIntHandler;
        g_psUSBDMAInst[0].pfnIntStatus = uDMAUSBIntStatus;
        g_psUSBDMAInst[0].pfnIntStatusClear = DMAUSBIntStatusClear;
        g_psUSBDMAInst[0].pfnStatus = DMAUSBStatus;
        g_psUSBDMAInst[0].pfnTransfer = uDMAUSBTransfer;
        g_psUSBDMAInst[0].pfnUnitSizeSet = uDMAUSBUnitSizeSet;
    
        //
        // These devices have a different USB interrupt number.
        //
        if(CLASS_IS_TM4C129)
        {
            g_psUSBDMAInst[0].ui32IntNum = INT_USB0_TM4C129;
        }
    
        //
        // Initialize the function pointers for the integrated USB DMA controller.
        //
        if(USBControllerVersion(g_psUSBDMAInst[0].ui32Base) == USB_CONTROLLER_VER_1)
        {
            g_psUSBDMAInst[0].pfnArbSizeSet = iDMAUSBArbSizeSet;
            g_psUSBDMAInst[0].pfnChannelAllocate = iDMAUSBChannelAllocate;
            g_psUSBDMAInst[0].pfnChannelStatus = iDMAUSBChannelStatus;
            g_psUSBDMAInst[0].pfnIntStatus = iDMAUSBIntStatus;
            g_psUSBDMAInst[0].pfnChannelIntEnable = iDMAUSBChannelIntEnable;
            g_psUSBDMAInst[0].pfnChannelIntDisable = iDMAUSBChannelIntDisable;
            g_psUSBDMAInst[0].pfnTransfer = iDMAUSBTransfer;
            g_psUSBDMAInst[0].pfnChannelRelease = iDMAUSBChannelRelease;
            g_psUSBDMAInst[0].pfnChannelEnable = iDMAUSBChannelEnable;
            g_psUSBDMAInst[0].pfnChannelDisable = iDMAUSBChannelDisable;
            g_psUSBDMAInst[0].pfnUnitSizeSet = iDMAUSBUnitSizeSet;
        }
    
        //
        // Clear out the endpoint and the current configuration.
        //
        for(ui32Channel = 0; ui32Channel < USB_MAX_DMA_CHANNELS; ui32Channel++)
        {
            g_psUSBDMAInst[0].pui8Endpoint[ui32Channel] = 0;
            g_psUSBDMAInst[0].pui32Config[ui32Channel] = 0;
            g_psUSBDMAInst[0].ui32Pending = 0;
            g_psUSBDMAInst[0].ui32Complete = 0;
        }
        return(&g_psUSBDMAInst[0]);
    }
    
    //*****************************************************************************
    //
    // Close the Doxygen group.
    //! @}
    //
    //*****************************************************************************
    

    usbhostenum file: 1541.usbhostenum.c

  • Ralph Jacobi said:
    There was a recent bug discovered for the usbdma and while resolving that a more robust solution was put together for the PipeWrite issue as well. I am attaching the files which cover both of those fixes. Please update your usblib with these files (make sure to recompile usblib itself if you are linking the .lib into your project) and see if there is any improvement.

    These fixes did improve the situation, but did not completely solve the issue. Thanks for providing the files, and my apologies for the long delay before getting back to you.

    It looks like I still need further work before the data path is as reliable as a macOS USB Host. I glanced at the changes in the files that you provided, but I did not fully review them for remaining bugs.

    If there is a correlation to the 0-byte packets, then the timing should align on the dot, not just be nearby. How have you calculated the 41-42 frames for when you are expecting the 0-byte packet? Have you measured that the 0-byte packets are appearing exactly as expected from your calculations? If the 0-byte packets really are at the 41-42 frames mark, and the error occurs in the 45-49 frame range, then we don't believe there is any correlation as that gap is too wide to explain.

    I have not measured the errors in a while, and certainly not with the new code. If you think that will help narrow in on a fix, I'll certainly take a look.

    The 41-42 frames was calculated mathematically, by comparing the data production rate of 976.5625 frames per second with the USB rate of 1000 frames per second. My estimate is that at 42.666 frames, there would be no data by the end of that frame. Of course, the exact rate of 0-byte packets would depend on the differences between the crystals on the USB Device versus the TM4C Host and the timing of interrupts.

    Another detail is that the USB Device has two endpoints, each producing packets at a rate of 976.5625 Hz. So, each USB frame normally has two isochronous packets, but every once in while (41 or 42 packets), the USB frame only has data in one endpoint packet, and the other endpoint has a 0-byte packet. Over several long runs with the Total Phase USB analyzer, I never saw any USB frame with two 0-byte packets. During the period of a USB frame, which is probably slightly less than 1 ms, the USB Device always produces at least one non-zero packet.

    What happens is that every 41 or 42 cycles, one endpoint will produce a 0-byte packet. Then, some number of frames later, the other endpoint will produce a 0-byte packet and the two endpoints will be back on the same sequence number. Sometimes the two endpoints miss a packet on consecutive USB frames, but that's actually rare. More often, it might be 1, 3, 6, 8, 11 or 12 frames before the other endpoint skips a packet. This latter effect could explain why I see 45-49 frames between errors. I'm thinking that it could be related to the 0-byte packets after all.

    You mention the TMS320 uses a custom class, does that differ from say, an audio device? While the issue isn't observed on that end, maybe better understanding how the device is setup could help us as well.

    I would say that it differs from a USB Audio Class Device greatly, because the descriptors are completely different, and packets are either 384 bytes or 0 bytes. But in the sense that it has isochronous endpoints, the USB Custom Class Device is closest to an audio or video device, or perhaps a USB ISDN telephone terminal. A big difference from a USB Audio Class Device is that audio has a nominal packet size based on the sample rate, and differences between the clocks will only vary the packet size by the size of one set of samples from all channels. For a stereo, 16-bit audio device, the packet size would only vary by 4 bytes. For an 8-channel, 24-bit audio device, the packet size would vary by 24 bytes, but that's not enough to drop the packet size all the way to 0 bytes. The TMS320 Audio Device is probably closest to a USB Microphone, which only has isochronous IN endpoints but no OUT endpoints. Apart from having large packet size variations rather than relatively small packet size variations, that might be the closest example. It's certainly true that my TM4C firmware started with the USB microphone example and then altered the code to adapt to the custom class.