omap3530/beagle_drivers/wb/api/src/cyasdma.c
author arunabha
Wed, 03 Mar 2010 13:10:32 +0000
changeset 27 117faf51deac
permissions -rw-r--r--
Bug 1996 - Contribution for West Bridge Astoria Symbian Storage Driver this storage driver is for the West Bridge Astoria chipset. This device has a USB, SD and processor port for communication with a baseband processor. In our port, we connected this device DVK to the Beagleboard through the SPI interface of the OMAP3. After driver installation, the Symbian OS can see an external device or D: drive represented by the SD card. In this driver, the USB interface is not used directly, though this may be the subject of future contributions. The appropriate way to test the driver is to access the external volume and do file read and write to it, pretty much the same way you would test a thumb drive on your PC

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