omap3530/beagle_drivers/wb/api/src/cyasdma.c
author arunabha
Thu, 16 Sep 2010 14:35:26 +0100
changeset 70 ed23a53a5ee2
parent 23 117faf51deac
permissions -rw-r--r--
Fixing issues in base_beagle.iby which had references to DEBUG_DIR

/* 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 ;
}