omap3530/beagle_drivers/wb/api/src/cyasdma.c
changeset 27 117faf51deac
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/omap3530/beagle_drivers/wb/api/src/cyasdma.c	Wed Mar 03 13:10:32 2010 +0000
@@ -0,0 +1,1054 @@
+/* Cypress West Bridge API source file (cyasdma.c)
+## ===========================
+##
+##  Copyright Cypress Semiconductor Corporation, 2006-2009,
+##  All Rights Reserved
+##  UNPUBLISHED, LICENSED SOFTWARE.
+##
+##  CONFIDENTIAL AND PROPRIETARY INFORMATION
+##  WHICH IS THE PROPERTY OF CYPRESS.
+##
+##  Use of this file is governed
+##  by the license agreement included in the file
+##
+##     <install>/license/license.txt
+##
+##  where <install> is the Cypress software
+##  installation root directory path.
+##
+## ===========================
+*/
+
+#include "cyashal.h"
+#include "cyasdma.h"
+#include "cyaslowlevel.h"
+#include "cyaserr.h"
+#include "cyasregs.h"
+
+/*
+ * Add the DMA queue entry to the free list to be re-used later
+ */
+static void
+CyAsDmaAddRequestToFreeQueue(CyAsDevice *dev_p, CyAsDmaQueueEntry *req_p)
+{
+    uint32_t imask ;
+    imask = CyAsHalDisableInterrupts() ;
+
+    req_p->next_p = dev_p->dma_freelist_p ;
+    dev_p->dma_freelist_p = req_p ;
+
+    CyAsHalEnableInterrupts(imask) ;
+}
+
+/*
+ * Get a DMA queue entry from the free list.
+ */
+static CyAsDmaQueueEntry *
+CyAsDmaGetDmaQueueEntry(CyAsDevice *dev_p)
+{
+    CyAsDmaQueueEntry *req_p ;
+    uint32_t imask ;
+
+    CyAsHalAssert(dev_p->dma_freelist_p != 0) ;
+
+    imask = CyAsHalDisableInterrupts() ;
+    req_p = dev_p->dma_freelist_p ;
+    dev_p->dma_freelist_p = req_p->next_p ;
+    CyAsHalEnableInterrupts(imask) ;
+
+    return req_p ;
+}
+
+/*
+ * Set the maximum size that the West Bridge hardware can handle in a single DMA operation.  This size
+ * may change for the P <-> U endpoints as a function of the endpoint type and whether we are running
+ * at full speed or high speed.
+ */
+CyAsReturnStatus_t
+CyAsDmaSetMaxDmaSize(CyAsDevice *dev_p, CyAsEndPointNumber_t ep, uint32_t size)
+{
+    /* In MTP mode, EP2 is allowed to have all max sizes. */
+    if ((!dev_p->is_mtp_firmware) || (ep != 0x02))
+    {
+        if (size < 64 || size > 1024)
+            return CY_AS_ERROR_INVALID_SIZE ;
+    }
+
+    CY_AS_NUM_EP(dev_p, ep)->maxhwdata = (uint16_t)size ;
+    return CY_AS_ERROR_SUCCESS ;
+}
+
+/*
+ * The callback for requests sent to West Bridge to relay endpoint data.  Endpoint
+ * data for EP0 and EP1 are sent using mailbox requests.  This is the callback that
+ * is called when a response to a mailbox request to send data is received.
+ */
+static void
+CyAsDmaRequestCallback(
+    CyAsDevice *dev_p,
+    uint8_t context,
+    CyAsLLRequestResponse *req_p,
+    CyAsLLRequestResponse *resp_p,
+    CyAsReturnStatus_t ret)
+{
+    uint16_t v ;
+    uint16_t datacnt ;
+    CyAsEndPointNumber_t ep ;
+
+    (void)context ;
+
+    CyAsLogDebugMessage(5, "CyAsDmaRequestCallback called") ;
+
+    /*
+     * Extract the return code from the firmware
+     */
+    if (ret == CY_AS_ERROR_SUCCESS)
+    {
+        if (CyAsLLRequestResponse_GetCode(resp_p) != CY_RESP_SUCCESS_FAILURE)
+            ret = CY_AS_ERROR_INVALID_RESPONSE ;
+        else
+            ret = CyAsLLRequestResponse_GetWord(resp_p, 0) ;
+    }
+
+    /*
+     * Extract the endpoint number and the transferred byte count
+     * from the request.
+     */
+    v = CyAsLLRequestResponse_GetWord(req_p, 0) ;
+    ep = (CyAsEndPointNumber_t)((v >> 13) & 0x01) ;
+
+    if (ret == CY_AS_ERROR_SUCCESS)
+    {
+        /*
+         * If the firmware returns success, all of the data requested was
+         * transferred.  There are no partial transfers.
+         */
+        datacnt = v & 0x3FF ;
+    }
+    else
+    {
+        /*
+         * If the firmware returned an error, no data was transferred.
+         */
+        datacnt = 0 ;
+    }
+
+    /*
+     * Queue the request and response data structures for use with the
+     * next EP0 or EP1 request.
+     */
+    if (ep == 0)
+    {
+        dev_p->usb_ep0_dma_req = req_p ;
+        dev_p->usb_ep0_dma_resp = resp_p ;
+    }
+    else
+    {
+        dev_p->usb_ep1_dma_req = req_p ;
+        dev_p->usb_ep1_dma_resp = resp_p ;
+    }
+
+    /*
+     * Call the DMA complete function so we can signal that this portion of the
+     * transfer has completed.  If the low level request was canceled, we do not
+     * need to signal the completed function as the only way a cancel can happen
+     * is via the DMA cancel function.
+     */
+    if (ret != CY_AS_ERROR_CANCELED)
+        CyAsDmaCompletedCallback(dev_p->tag, ep, datacnt, ret) ;
+}
+
+/*
+ * Set the DRQ mask register for the given endpoint number.  If state is
+ * CyTrue, the DRQ interrupt for the given endpoint is enabled, otherwise
+ * it is disabled.
+ */
+static void
+CyAsDmaSetDrq(CyAsDevice *dev_p, CyAsEndPointNumber_t ep, CyBool state)
+{
+    uint16_t mask ;
+    uint16_t v ;
+    uint32_t intval ;
+
+    /*
+     * There are not DRQ register bits for EP0 and EP1
+     */
+    if (ep == 0 || ep == 1)
+        return ;
+
+    /*
+     * Disable interrupts while we do this to be sure the state of the
+     * DRQ mask register is always well defined.
+     */
+    intval = CyAsHalDisableInterrupts() ;
+
+    /*
+     * Set the DRQ bit to the given state for the ep given
+     */
+    mask = (1 << ep) ;
+    v = CyAsHalReadRegister(dev_p->tag, CY_AS_MEM_P0_DRQ_MASK) ;
+
+    if (state)
+        v |= mask ;
+    else
+        v &= ~mask ;
+
+    CyAsHalWriteRegister(dev_p->tag, CY_AS_MEM_P0_DRQ_MASK, v) ;
+    CyAsHalEnableInterrupts(intval) ;
+}
+
+/*
+* Send the next DMA request for the endpoint given
+*/
+static void
+CyAsDmaSendNextDmaRequest(CyAsDevice *dev_p, CyAsDmaEndPoint *ep_p)
+{
+    uint32_t datacnt ;
+    void *buf_p ;
+    CyAsDmaQueueEntry *dma_p ;
+
+    CyAsLogDebugMessage(6, "CyAsDmaSendNextDmaRequest called") ;
+
+    /* If the queue is empty, nothing to do */
+    dma_p = ep_p->queue_p ;
+    if (dma_p == 0)
+    {
+        /*
+         * There are not pending DMA requests for this endpoint.  Disable
+         * the DRQ mask bits to insure no interrupts will be triggered by this
+         * endpoint until someone is interested in the data.
+         */
+        CyAsDmaSetDrq(dev_p, ep_p->ep, CyFalse) ;
+        return ;
+    }
+
+    CyAsDmaEndPointSetRunning(ep_p) ;
+
+    /*
+     * Get the number of words that still need to be xferred in
+     * this request.
+     */
+    datacnt = dma_p->size - dma_p->offset ;
+    CyAsHalAssert(datacnt >= 0) ;
+
+    /*
+     * The HAL layer should never limit the size of the transfer to
+     * something less than the maxhwdata otherwise, the data will be
+     * sent in packets that are not correct in size.
+     */
+    CyAsHalAssert(ep_p->maxhaldata == CY_AS_DMA_MAX_SIZE_HW_SIZE || ep_p->maxhaldata >= ep_p->maxhwdata) ;
+
+    /*
+     * Update the number of words that need to be xferred yet
+     * based on the limits of the HAL layer.
+     */
+    if (ep_p->maxhaldata == CY_AS_DMA_MAX_SIZE_HW_SIZE)
+    {
+        if (datacnt > ep_p->maxhwdata)
+            datacnt = ep_p->maxhwdata ;
+    }
+    else
+    {
+        if (datacnt > ep_p->maxhaldata)
+            datacnt = ep_p->maxhaldata ;
+    }
+
+    /*
+     * Find a pointer to the data that needs to be transferred
+     */
+    buf_p = (((char *)dma_p->buf_p) + dma_p->offset);
+
+    /*
+     * Mark a request in transit
+     */
+    CyAsDmaEndPointSetInTransit(ep_p) ;
+
+    if (ep_p->ep == 0 || ep_p->ep == 1)
+    {
+        /*
+         * If this is a WRITE request on EP0 and EP1, we write the data via an EP_DATA request
+         * to West Bridge via the mailbox registers.  If this is a READ request, we do nothing and the data will
+         * arrive via an EP_DATA request from West Bridge.  In the request handler for the USB context we will pass
+         * the data back into the DMA module.
+         */
+        if (dma_p->readreq == CyFalse)
+        {
+            uint16_t v ;
+            uint16_t len ;
+            CyAsLLRequestResponse *resp_p ;
+            CyAsLLRequestResponse *req_p ;
+            CyAsReturnStatus_t ret ;
+
+            len = (uint16_t)(datacnt / 2) ;
+            if (datacnt % 2)
+                len++ ;
+
+            len++ ;
+
+            if (ep_p->ep == 0)
+            {
+                req_p = dev_p->usb_ep0_dma_req ;
+                resp_p = dev_p->usb_ep0_dma_resp ;
+                dev_p->usb_ep0_dma_req = 0 ;
+                dev_p->usb_ep0_dma_resp = 0 ;
+            }
+            else
+            {
+                req_p = dev_p->usb_ep1_dma_req ;
+                resp_p = dev_p->usb_ep1_dma_resp ;
+                dev_p->usb_ep1_dma_req = 0 ;
+                dev_p->usb_ep1_dma_resp = 0 ;
+            }
+
+            CyAsHalAssert(req_p != 0) ;
+            CyAsHalAssert(resp_p != 0) ;
+            CyAsHalAssert(len <= 64) ;
+
+            CyAsLLInitRequest(req_p, CY_RQT_USB_EP_DATA, CY_RQT_USB_RQT_CONTEXT, len) ;
+
+            v = (uint16_t)(datacnt | (ep_p->ep << 13) | (1 << 14)) ;
+            if (dma_p->offset == 0)
+                v |= (1 << 12) ;            /* Set the first packet bit */
+            if (dma_p->offset + datacnt == dma_p->size)
+                v |= (1 << 11) ;            /* Set the last packet bit */
+
+            CyAsLLRequestResponse_SetWord(req_p, 0, v) ;
+            CyAsLLRequestResponse_Pack(req_p, 1, datacnt, buf_p) ;
+
+            CyAsLLInitResponse(resp_p, 1) ;
+
+            ret = CyAsLLSendRequest(dev_p, req_p, resp_p, CyFalse, CyAsDmaRequestCallback) ;
+            if (ret == CY_AS_ERROR_SUCCESS)
+                CyAsLogDebugMessage(5, "+++ Send EP 0/1 data via mailbox registers") ;
+            else
+                CyAsLogDebugMessage(5, "+++ Error Sending EP 0/1 data via mailbox registers - CY_AS_ERROR_TIMEOUT") ;
+
+            if (ret != CY_AS_ERROR_SUCCESS)
+                CyAsDmaCompletedCallback(dev_p->tag, ep_p->ep, 0, ret) ;
+        }
+    }
+    else
+    {
+        /*
+         * This is a DMA request on an endpoint that is accessible via the P port.  Ask the
+         * HAL DMA capabilities to perform this.  The amount of data sent is limited by the
+         * HAL max size as well as what we need to send.  If the ep_p->maxhaldata is set to
+         * a value larger than the endpoint buffer size, then we will pass more than a single
+         * buffer worth of data to the HAL layer and expect the HAL layer to divide the data
+         * into packets.  The last parameter here (ep_p->maxhwdata) gives the packet size for
+         * the data so the HAL layer knows what the packet size should be.
+         */
+        if (CyAsDmaEndPointIsDirectionIn(ep_p))
+            CyAsHalDmaSetupWrite(dev_p->tag, ep_p->ep, buf_p, datacnt, ep_p->maxhwdata) ;
+        else
+            CyAsHalDmaSetupRead(dev_p->tag, ep_p->ep, buf_p, datacnt, ep_p->maxhwdata) ;
+
+        /*
+         * The DRQ interrupt for this endpoint should be enabled so that the data
+         * transfer progresses at interrupt time.
+         */
+        CyAsDmaSetDrq(dev_p, ep_p->ep, CyTrue) ;
+    }
+}
+
+/*
+ * This function is called when the HAL layer has completed the last requested DMA
+ * operation.  This function sends/receives the next batch of data associated with the
+ * current DMA request, or it is is complete, moves to the next DMA request.
+ */
+void
+CyAsDmaCompletedCallback(CyAsHalDeviceTag tag, CyAsEndPointNumber_t ep, uint32_t cnt, CyAsReturnStatus_t status)
+{
+    uint32_t mask ;
+    CyAsDmaQueueEntry *req_p ;
+    CyAsDmaEndPoint *ep_p ;
+    CyAsDevice *dev_p = CyAsDeviceFindFromTag(tag) ;
+
+    /* Make sure the HAL layer gave us good parameters */
+    CyAsHalAssert(dev_p != 0) ;
+    CyAsHalAssert(dev_p->sig == CY_AS_DEVICE_HANDLE_SIGNATURE) ;
+    CyAsHalAssert(ep < 16) ;
+
+
+    /* Get the endpoint ptr */
+    ep_p = CY_AS_NUM_EP(dev_p, ep) ;
+    CyAsHalAssert(ep_p->queue_p != 0) ;
+
+    /* Get a pointer to the current entry in the queue */
+    mask = CyAsHalDisableInterrupts() ;
+    req_p = ep_p->queue_p ;
+
+    /* Update the offset to reflect the data actually received or sent */
+    req_p->offset += cnt ;
+
+    /*
+     * If we are still sending/receiving the current packet, send/receive the next chunk
+     * Basically we keep going if we have not sent/received enough data, and we are not doing
+     * a packet operation, and the last packet sent or received was a full sized packet.  In
+     * other words, when we are NOT doing a packet operation, a less than full size packet
+     * (a short packet) will terminate the operation.
+     *
+     * Note: If this is EP1 request and the request has timed out, it means the buffer is not free.
+     * We have to resend the data.
+     *
+     * Note: For the MTP data transfers, the DMA transfer for the next packet can only be started
+     * asynchronously, after a firmware event notifies that the device is ready.
+     */
+    if (((req_p->offset != req_p->size) && (req_p->packet == CyFalse) && ((cnt == ep_p->maxhaldata) ||
+                    ((cnt == ep_p->maxhwdata) && ((ep != CY_AS_MTP_READ_ENDPOINT) || (cnt == dev_p->usb_max_tx_size)))))
+            || ((ep == 1) && (status == CY_AS_ERROR_TIMEOUT)))
+    {
+        CyAsHalEnableInterrupts(mask) ;
+
+        /*
+         * And send the request again to send the next block of data. Special handling for
+         * MTP transfers on EPs 2 and 6. The SendNextRequest will be processed based on the
+         * event sent by the firmware.
+         */
+        if ((ep == CY_AS_MTP_WRITE_ENDPOINT) || (
+                    (ep == CY_AS_MTP_READ_ENDPOINT) && (!CyAsDmaEndPointIsDirectionIn (ep_p))))
+            CyAsDmaEndPointSetStopped(ep_p) ;
+        else
+            CyAsDmaSendNextDmaRequest(dev_p, ep_p) ;
+    }
+    else
+    {
+        /*
+         * We get here if ...
+         *    we have sent or received all of the data
+         *         or
+         *    we are doing a packet operation
+         *         or
+         *    we receive a short packet
+         */
+
+        /*
+         * Remove this entry from the DMA queue for this endpoint.
+         */
+        CyAsDmaEndPointClearInTransit(ep_p) ;
+        ep_p->queue_p = req_p->next_p ;
+        if (ep_p->last_p == req_p)
+        {
+            /*
+             * We have removed the last packet from the DMA queue, disable the
+             * interrupt associated with this interrupt.
+             */
+            ep_p->last_p = 0 ;
+            CyAsHalEnableInterrupts(mask) ;
+            CyAsDmaSetDrq(dev_p, ep, CyFalse) ;
+        }
+        else
+            CyAsHalEnableInterrupts(mask) ;
+
+        if (req_p->cb)
+        {
+            /*
+             * If the request has a callback associated with it, call the callback
+             * to tell the interested party that this DMA request has completed.
+             *
+             * Note, we set the InCallback bit to insure that we cannot recursively
+             * call an API function that is synchronous only from a callback.
+             */
+            CyAsDeviceSetInCallback(dev_p) ;
+            (*req_p->cb)(dev_p, ep, req_p->buf_p, req_p->offset, status) ;
+            CyAsDeviceClearInCallback(dev_p) ;
+        }
+
+        /*
+         * We are done with this request, put it on the freelist to be
+         * reused at a later time.
+         */
+        CyAsDmaAddRequestToFreeQueue(dev_p, req_p) ;
+
+        if (ep_p->queue_p == 0)
+        {
+            /*
+             * If the endpoint is out of DMA entries, set it the endpoint as
+             * stopped.
+             */
+            CyAsDmaEndPointSetStopped(ep_p) ;
+
+            /*
+             * The DMA queue is empty, wake any task waiting on the QUEUE to
+             * drain.
+             */
+            if (CyAsDmaEndPointIsSleeping(ep_p))
+            {
+                CyAsDmaEndPointSetWakeState(ep_p) ;
+                CyAsHalWake(&ep_p->channel) ;
+            }
+        }
+        else
+        {
+            /*
+             * If the queued operation is a MTP transfer, wait until firmware event
+             * before sending down the next DMA request.
+             */
+            if ((ep == CY_AS_MTP_WRITE_ENDPOINT) || (
+                        (ep == CY_AS_MTP_READ_ENDPOINT) && (!CyAsDmaEndPointIsDirectionIn (ep_p))))
+                CyAsDmaEndPointSetStopped(ep_p) ;
+            else
+                CyAsDmaSendNextDmaRequest(dev_p, ep_p) ;
+        }
+    }
+}
+
+/*
+* This function is used to kick start DMA on a given channel.  If DMA is already running
+* on the given endpoint, nothing happens.  If DMA is not running, the first entry is pulled
+* from the DMA queue and sent/recevied to/from the West Bridge device.
+*/
+CyAsReturnStatus_t
+CyAsDmaKickStart(CyAsDevice *dev_p, CyAsEndPointNumber_t ep)
+{
+    CyAsDmaEndPoint *ep_p ;
+    CyAsHalAssert(dev_p->sig == CY_AS_DEVICE_HANDLE_SIGNATURE) ;
+
+    ep_p = CY_AS_NUM_EP(dev_p, ep) ;
+
+    /* We are already running */
+    if (CyAsDmaEndPointIsRunning(ep_p))
+        return CY_AS_ERROR_SUCCESS ;
+
+    CyAsDmaSendNextDmaRequest(dev_p, ep_p);
+    return CY_AS_ERROR_SUCCESS ;
+}
+
+/*
+ * This function stops the given endpoint.  Stopping and endpoint cancels
+ * any pending DMA operations and frees all resources associated with the
+ * given endpoint.
+ */
+static CyAsReturnStatus_t
+CyAsDmaStopEndPoint(CyAsDevice *dev_p, CyAsEndPointNumber_t ep)
+{
+    CyAsReturnStatus_t ret ;
+    CyAsDmaEndPoint *ep_p = CY_AS_NUM_EP(dev_p, ep) ;
+
+    /*
+     * Cancel any pending DMA requests associated with this endpoint.  This
+     * cancels any DMA requests at the HAL layer as well as dequeues any request
+     * that is currently pending.
+     */
+    ret = CyAsDmaCancel(dev_p, ep, CY_AS_ERROR_CANCELED) ;
+    if (ret != CY_AS_ERROR_SUCCESS)
+        return ret ;
+
+    /*
+     * Destroy the sleep channel
+     */
+    if (!CyAsHalDestroySleepChannel(&ep_p->channel) && ret == CY_AS_ERROR_SUCCESS)
+        ret = CY_AS_ERROR_DESTROY_SLEEP_CHANNEL_FAILED ;
+
+    /*
+     * Free the memory associated with this endpoint
+     */
+    CyAsHalFree(ep_p) ;
+
+    /*
+     * Set the data structure ptr to something sane since the
+     * previous pointer is now free.
+     */
+    dev_p->endp[ep] = 0 ;
+
+    return ret ;
+}
+
+/*
+ * This method stops the USB stack.  This is an internal function that does
+ * all of the work of destroying the USB stack without the protections that
+ * we provide to the API (i.e. stopping at stack that is not running).
+ */
+static CyAsReturnStatus_t
+CyAsDmaStopInternal(CyAsDevice *dev_p)
+{
+    CyAsReturnStatus_t ret = CY_AS_ERROR_SUCCESS ;
+    CyAsReturnStatus_t lret ;
+    CyAsEndPointNumber_t i ;
+
+    /*
+     * Stop all of the endpoints.  This cancels all DMA requests, and
+     * frees all resources associated with each endpoint.
+     */
+    for(i = 0 ; i < sizeof(dev_p->endp)/(sizeof(dev_p->endp[0])) ; i++)
+    {
+        lret = CyAsDmaStopEndPoint(dev_p, i) ;
+        if (lret != CY_AS_ERROR_SUCCESS && ret == CY_AS_ERROR_SUCCESS)
+            ret = lret ;
+    }
+
+    /*
+     * Now, free the list of DMA requests structures that we use to manage
+     * DMA requests.
+     */
+    while (dev_p->dma_freelist_p)
+    {
+        CyAsDmaQueueEntry *req_p ;
+        uint32_t imask = CyAsHalDisableInterrupts() ;
+
+        req_p = dev_p->dma_freelist_p ;
+        dev_p->dma_freelist_p = req_p->next_p ;
+
+        CyAsHalEnableInterrupts(imask) ;
+
+        CyAsHalFree(req_p) ;
+    }
+
+    CyAsLLDestroyRequest(dev_p, dev_p->usb_ep0_dma_req) ;
+    CyAsLLDestroyRequest(dev_p, dev_p->usb_ep1_dma_req) ;
+    CyAsLLDestroyResponse(dev_p, dev_p->usb_ep0_dma_resp) ;
+    CyAsLLDestroyResponse(dev_p, dev_p->usb_ep1_dma_resp) ;
+
+    return ret ;
+}
+
+
+/*
+ * CyAsDmaStop()
+ *
+ * This function shuts down the DMA module.  All resources associated with the DMA module
+ * will be freed.  This routine is the API stop function.  It insures that we are stopping
+ * a stack that is actually running and then calls the internal function to do the work.
+ */
+CyAsReturnStatus_t
+CyAsDmaStop(CyAsDevice *dev_p)
+{
+    CyAsReturnStatus_t ret ;
+
+    ret = CyAsDmaStopInternal(dev_p) ;
+    CyAsDeviceSetDmaStopped(dev_p) ;
+
+    return ret ;
+}
+
+/*
+ * CyAsDmaStart()
+ *
+ * This function intializes the DMA module to insure it is up and running.
+ */
+CyAsReturnStatus_t
+CyAsDmaStart(CyAsDevice *dev_p)
+{
+    CyAsEndPointNumber_t i ;
+    uint16_t cnt ;
+
+    if (CyAsDeviceIsDmaRunning(dev_p))
+        return CY_AS_ERROR_ALREADY_RUNNING ;
+
+    /*
+     * Pre-allocate DMA Queue structures to be used in the interrupt context
+     */
+    for(cnt = 0 ; cnt < 32 ; cnt++)
+    {
+        CyAsDmaQueueEntry *entry_p = (CyAsDmaQueueEntry *)CyAsHalAlloc(sizeof(CyAsDmaQueueEntry)) ;
+        if (entry_p == 0)
+        {
+            CyAsDmaStopInternal(dev_p) ;
+            return CY_AS_ERROR_OUT_OF_MEMORY ;
+        }
+        CyAsDmaAddRequestToFreeQueue(dev_p, entry_p) ;
+    }
+
+    /*
+     * Pre-allocate the DMA requests for sending EP0 and EP1 data to West Bridge
+     */
+    dev_p->usb_ep0_dma_req = CyAsLLCreateRequest(dev_p, CY_RQT_USB_EP_DATA, CY_RQT_USB_RQT_CONTEXT, 64) ;
+    dev_p->usb_ep1_dma_req = CyAsLLCreateRequest(dev_p, CY_RQT_USB_EP_DATA, CY_RQT_USB_RQT_CONTEXT, 64) ;
+    if (dev_p->usb_ep0_dma_req == 0 || dev_p->usb_ep1_dma_req == 0)
+    {
+        CyAsDmaStopInternal(dev_p) ;
+        return CY_AS_ERROR_OUT_OF_MEMORY ;
+    }
+    dev_p->usb_ep0_dma_req_save = dev_p->usb_ep0_dma_req ;
+
+    dev_p->usb_ep0_dma_resp = CyAsLLCreateResponse(dev_p, 1) ;
+    dev_p->usb_ep1_dma_resp = CyAsLLCreateResponse(dev_p, 1) ;
+    if (dev_p->usb_ep0_dma_resp == 0 || dev_p->usb_ep1_dma_resp == 0)
+    {
+        CyAsDmaStopInternal(dev_p) ;
+        return CY_AS_ERROR_OUT_OF_MEMORY ;
+    }
+    dev_p->usb_ep0_dma_resp_save = dev_p->usb_ep0_dma_resp ;
+
+    /*
+     * Set the dev_p->endp to all zeros to insure cleanup is possible if
+     * an error occurs during initialization.
+     */
+    CyAsHalMemSet(dev_p->endp, 0, sizeof(dev_p->endp)) ;
+
+    /*
+     * Now, iterate through each of the endpoints and initialize each
+     * one.
+     */
+    for(i = 0 ; i < sizeof(dev_p->endp)/sizeof(dev_p->endp[0]) ; i++)
+    {
+        dev_p->endp[i] = (CyAsDmaEndPoint *)CyAsHalAlloc(sizeof(CyAsDmaEndPoint)) ;
+        if (dev_p->endp[i] == 0)
+        {
+            CyAsDmaStopInternal(dev_p) ;
+            return CY_AS_ERROR_OUT_OF_MEMORY ;
+        }
+        CyAsHalMemSet(dev_p->endp[i], 0, sizeof(CyAsDmaEndPoint)) ;
+
+        dev_p->endp[i]->ep = i ;
+        dev_p->endp[i]->queue_p = 0 ;
+        dev_p->endp[i]->last_p = 0 ;
+
+        CyAsDmaSetDrq(dev_p, i, CyFalse) ;
+
+        if (!CyAsHalCreateSleepChannel(&dev_p->endp[i]->channel))
+            return CY_AS_ERROR_CREATE_SLEEP_CHANNEL_FAILED ;
+    }
+
+    /*
+     * Tell the HAL layer who to call when the HAL layer completes a DMA request
+     */
+    CyAsHalDmaRegisterCallback(dev_p->tag, CyAsDmaCompletedCallback) ;
+
+    /*
+     * Mark DMA as up and running on this device
+     */
+    CyAsDeviceSetDmaRunning(dev_p) ;
+
+    return CY_AS_ERROR_SUCCESS ;
+}
+
+/*
+* Wait for all entries in the DMA queue associated the given endpoint to be drained.  This
+* function will not return until all the DMA data has been transferred.
+*/
+CyAsReturnStatus_t
+CyAsDmaDrainQueue(CyAsDevice *dev_p, CyAsEndPointNumber_t ep, CyBool kickstart)
+{
+    CyAsDmaEndPoint *ep_p ;
+    int loopcount = 1000 ;
+    uint32_t mask ;
+
+    /*
+    * Make sure the endpoint is valid
+    */
+    if (ep >= sizeof(dev_p->endp)/sizeof(dev_p->endp[0]))
+        return CY_AS_ERROR_INVALID_ENDPOINT ;
+
+    /* Get the endpoint pointer based on the endpoint number */
+    ep_p = CY_AS_NUM_EP(dev_p, ep) ;
+
+    /*
+    * If the endpoint is empty of traffic, we return
+    * with success immediately
+    */
+    mask = CyAsHalDisableInterrupts() ;
+    if (ep_p->queue_p == 0)
+    {
+        CyAsHalEnableInterrupts(mask) ;
+        return CY_AS_ERROR_SUCCESS ;
+    }
+    else
+    {
+        /*
+         * Add 10 seconds to the time out value for each 64 KB segment
+         * of data to be transferred.
+         */
+        if (ep_p->queue_p->size > 0x10000)
+            loopcount += ((ep_p->queue_p->size / 0x10000) * 1000) ;
+    }
+    CyAsHalEnableInterrupts(mask) ;
+
+    /* If we are already sleeping on this endpoint, it is an error */
+    if (CyAsDmaEndPointIsSleeping(ep_p))
+        return CY_AS_ERROR_NESTED_SLEEP ;
+
+    /*
+    * We disable the endpoint while the queue drains to
+    * prevent any additional requests from being queued while we are waiting
+    */
+    CyAsDmaEnableEndPoint(dev_p, ep, CyFalse, CyAsDirectionDontChange) ;
+
+    if (kickstart)
+    {
+        /*
+        * Now, kick start the DMA if necessary
+        */
+        CyAsDmaKickStart(dev_p, ep) ;
+    }
+
+    /*
+    * Check one last time before we begin sleeping to see if the
+    * queue is drained.
+    */
+    if (ep_p->queue_p == 0)
+    {
+        CyAsDmaEnableEndPoint(dev_p, ep, CyTrue, CyAsDirectionDontChange) ;
+        return CY_AS_ERROR_SUCCESS ;
+    }
+
+    while (loopcount-- > 0)
+    {
+        /*
+         * Sleep for 10 ms maximum (per loop) while waiting for the transfer
+         * to complete.
+         */
+        CyAsDmaEndPointSetSleepState(ep_p) ;
+        CyAsHalSleepOn(&ep_p->channel, 10) ;
+
+        /* If we timed out, the sleep bit will still be set */
+        CyAsDmaEndPointSetWakeState(ep_p) ;
+
+        /* Check the queue to see if is drained */
+        if (ep_p->queue_p == 0)
+        {
+            /*
+             * Clear the endpoint running and in transit flags for the endpoint,
+             * now that its DMA queue is empty.
+             */
+            CyAsDmaEndPointClearInTransit(ep_p) ;
+            CyAsDmaEndPointSetStopped(ep_p) ;
+
+            CyAsDmaEnableEndPoint(dev_p, ep, CyTrue, CyAsDirectionDontChange) ;
+            return CY_AS_ERROR_SUCCESS ;
+        }
+    }
+
+    /*
+     * The DMA operation that has timed out can be cancelled, so that later
+     * operations on this queue can proceed.
+     */
+    CyAsDmaCancel(dev_p, ep, CY_AS_ERROR_TIMEOUT) ;
+    CyAsDmaEnableEndPoint(dev_p, ep, CyTrue, CyAsDirectionDontChange) ;
+    return CY_AS_ERROR_TIMEOUT ;
+}
+
+/*
+* This function queues a write request in the DMA queue for a given endpoint.  The direction of the
+* entry will be inferred from the endpoint direction.
+*/
+CyAsReturnStatus_t
+CyAsDmaQueueRequest(CyAsDevice *dev_p, CyAsEndPointNumber_t ep, void *mem_p, uint32_t size, CyBool pkt, CyBool readreq, CyAsDmaCallback cb)
+{
+    uint32_t mask ;
+    CyAsDmaQueueEntry *entry_p ;
+    CyAsDmaEndPoint *ep_p ;
+
+    /*
+    * Make sure the endpoint is valid
+    */
+    if (ep >= sizeof(dev_p->endp)/sizeof(dev_p->endp[0]))
+        return CY_AS_ERROR_INVALID_ENDPOINT ;
+
+    /* Get the endpoint pointer based on the endpoint number */
+    ep_p = CY_AS_NUM_EP(dev_p, ep) ;
+
+    if (!CyAsDmaEndPointIsEnabled(ep_p))
+        return CY_AS_ERROR_ENDPOINT_DISABLED ;
+
+    entry_p = CyAsDmaGetDmaQueueEntry(dev_p) ;
+
+    entry_p->buf_p = mem_p ;
+    entry_p->cb = cb ;
+    entry_p->size = size ;
+    entry_p->offset = 0 ;
+    entry_p->packet = pkt ;
+    entry_p->readreq = readreq ;
+
+    mask = CyAsHalDisableInterrupts() ;
+    entry_p->next_p = 0 ;
+    if (ep_p->last_p)
+        ep_p->last_p->next_p = entry_p ;
+    ep_p->last_p = entry_p ;
+    if (ep_p->queue_p == 0)
+        ep_p->queue_p = entry_p ;
+    CyAsHalEnableInterrupts(mask) ;
+
+    return CY_AS_ERROR_SUCCESS ;
+}
+
+/*
+* This function enables or disables and endpoint for DMA queueing.  If an endpoint is disabled, any queued
+* requests continue to be processed, but no new requests can be queued.
+*/
+CyAsReturnStatus_t
+CyAsDmaEnableEndPoint(CyAsDevice *dev_p, CyAsEndPointNumber_t ep, CyBool enable, CyAsDmaDirection dir)
+{
+    CyAsDmaEndPoint *ep_p ;
+
+    /*
+    * Make sure the endpoint is valid
+    */
+    if (ep >= sizeof(dev_p->endp)/sizeof(dev_p->endp[0]))
+        return CY_AS_ERROR_INVALID_ENDPOINT ;
+
+    /* Get the endpoint pointer based on the endpoint number */
+    ep_p = CY_AS_NUM_EP(dev_p, ep) ;
+
+    if (dir == CyAsDirectionOut)
+        CyAsDmaEndPointSetDirectionOut(ep_p) ;
+    else if (dir == CyAsDirectionIn)
+        CyAsDmaEndPointSetDirectionIn(ep_p) ;
+
+    /*
+    * Get the maximum size of data buffer the HAL layer can accept.  This is used when
+    * the DMA module is sending DMA requests to the HAL.  The DMA module will never send
+    * down a request that is greater than this value.
+    *
+    * For EP0 and EP1, we can send no more than 64 bytes of data at one time as this is the
+    * maximum size of a packet that can be sent via these endpoints.
+    */
+    if (ep == 0 || ep == 1)
+        ep_p->maxhaldata = 64 ;
+    else
+        ep_p->maxhaldata = CyAsHalDmaMaxRequestSize(dev_p->tag, ep) ;
+
+    if (enable)
+        CyAsDmaEndPointEnable(ep_p) ;
+    else
+        CyAsDmaEndPointDisable(ep_p) ;
+
+    return CY_AS_ERROR_SUCCESS ;
+}
+
+/*
+ * This function cancels any DMA operations pending with the HAL layer as well
+ * as any DMA operation queued on the endpoint.
+ */
+CyAsReturnStatus_t
+CyAsDmaCancel(
+    CyAsDevice *dev_p,
+    CyAsEndPointNumber_t ep,
+    CyAsReturnStatus_t err)
+{
+    uint32_t mask ;
+    CyAsDmaEndPoint *ep_p ;
+    CyAsDmaQueueEntry *entry_p ;
+    CyBool epstate ;
+
+    /*
+     * Make sure the endpoint is valid
+     */
+    if (ep >= sizeof(dev_p->endp)/sizeof(dev_p->endp[0]))
+        return CY_AS_ERROR_INVALID_ENDPOINT ;
+
+    /* Get the endpoint pointer based on the endpoint number */
+    ep_p = CY_AS_NUM_EP(dev_p, ep) ;
+
+    if (ep_p)
+    {
+        /* Remember the state of the endpoint */
+        epstate = CyAsDmaEndPointIsEnabled(ep_p) ;
+
+        /*
+         * Disable the endpoint so no more DMA packets can be
+         * queued.
+         */
+        CyAsDmaEnableEndPoint(dev_p, ep, CyFalse, CyAsDirectionDontChange) ;
+
+        /*
+         * Don't allow any interrupts from this endpoint while we get the
+         * most current request off of the queue.
+         */
+        CyAsDmaSetDrq(dev_p, ep, CyFalse) ;
+
+        /*
+         * Cancel any pending request queued in the HAL layer
+         */
+        if (CyAsDmaEndPointInTransit(ep_p))
+            CyAsHalDmaCancelRequest(dev_p->tag, ep_p->ep) ;
+
+        /*
+         * Shutdown the DMA for this endpoint so no more data is transferred
+         */
+        CyAsDmaEndPointSetStopped(ep_p) ;
+
+        /*
+         * Mark the endpoint as not in transit, because we are going to consume
+         * any queued requests
+         */
+        CyAsDmaEndPointClearInTransit(ep_p) ;
+
+        /*
+         * Now, remove each entry in the queue and call the associated callback
+         * stating that the request was canceled.
+         */
+        ep_p->last_p = 0 ;
+        while (ep_p->queue_p != 0)
+        {
+            /* Disable interrupts to manipulate the queue */
+            mask = CyAsHalDisableInterrupts() ;
+
+            /* Remove an entry from the queue */
+            entry_p = ep_p->queue_p ;
+            ep_p->queue_p = entry_p->next_p ;
+
+            /* Ok, the queue has been updated, we can turn interrupts back on */
+            CyAsHalEnableInterrupts(mask) ;
+
+            /* Call the callback indicating we have canceled the DMA */
+            if (entry_p->cb)
+                entry_p->cb(dev_p, ep, entry_p->buf_p, entry_p->size, err) ;
+
+            CyAsDmaAddRequestToFreeQueue(dev_p, entry_p) ;
+        }
+
+        if (ep == 0 || ep == 1)
+        {
+            /*
+             * If this endpoint is zero or one, we need to clear the queue of any pending
+             * CY_RQT_USB_EP_DATA requests as these are pending requests to send data to
+             * the West Bridge device.
+             */
+            CyAsLLRemoveEpDataRequests(dev_p, ep) ;
+        }
+
+        if (epstate)
+        {
+            /*
+             * The endpoint started out enabled, so we re-enable the endpoint here.
+             */
+            CyAsDmaEnableEndPoint(dev_p, ep, CyTrue, CyAsDirectionDontChange) ;
+        }
+    }
+
+    return CY_AS_ERROR_SUCCESS ;
+}
+
+CyAsReturnStatus_t
+CyAsDmaReceivedData(CyAsDevice *dev_p, CyAsEndPointNumber_t ep, uint32_t dsize, void *data)
+{
+    CyAsDmaQueueEntry *dma_p ;
+    uint8_t *src_p, *dest_p ;
+    CyAsDmaEndPoint *ep_p ;
+    uint32_t xfersize ;
+
+    /*
+     * Make sure the endpoint is valid
+     */
+    if (ep != 0 && ep != 1)
+        return CY_AS_ERROR_INVALID_ENDPOINT ;
+
+    /* Get the endpoint pointer based on the endpoint number */
+    ep_p = CY_AS_NUM_EP(dev_p, ep) ;
+    dma_p = ep_p->queue_p ;
+    if (dma_p == 0)
+        return CY_AS_ERROR_SUCCESS ;
+
+    /*
+     * If the data received exceeds the size of the DMA buffer, clip the data to the size
+     * of the buffer.  This can lead to loosing some data, but is not different than doing
+     * non-packet reads on the other endpoints.
+     */
+    if (dsize > dma_p->size - dma_p->offset)
+        dsize = dma_p->size - dma_p->offset ;
+
+    /*
+     * Copy the data from the request packet to the DMA buffer for the endpoint
+     */
+    src_p = (uint8_t *)data ;
+    dest_p = ((uint8_t *)(dma_p->buf_p)) + dma_p->offset ;
+    xfersize = dsize ;
+    while (xfersize-- > 0)
+        *dest_p++ = *src_p++ ;
+
+    /* Signal the DMA module that we have received data for this EP request */
+    CyAsDmaCompletedCallback(dev_p->tag, ep, dsize, CY_AS_ERROR_SUCCESS) ;
+
+    return CY_AS_ERROR_SUCCESS ;
+}