/*
 *
 *     Module Name:  usbDma.c
 *         Version: 1.00
 *        Language: Ansi C
 * Compile Options:
 * Compile defines:
 *       Libraries:
 *    Link Options:
 *
 *    Entry Points:
 *
 *  Copyright (c) 1996-2006 Digi International Inc., All Rights Reserved
 *
 *  This software contains proprietary and confidential information of Digi
 *  International Inc.  By accepting transfer of this copy, Recipient agrees
 *  to retain this software in confidence, to prevent disclosure to others,
 *  and to make no use of this software other than that for which it was
 *  delivered.  This is an unpublished copyrighted work of Digi International
 *  Inc.  Except as permitted by federal law, 17 USC 117, copying is strictly
 *  prohibited.
 *
 *  Restricted Rights Legend
 *
 *  Use, duplication, or disclosure by the Government is subject to
 *  restrictions set forth in sub-paragraph (c)(1)(ii) of The Rights in
 *  Technical Data and Computer Software clause at DFARS 252.227-7031 or
 *  subparagraphs (c)(1) and (2) of the Commercial Computer Software -
 *  Restricted Rights at 48 CFR 52.227-19, as applicable.
 *
 *  Digi International Inc. 11001 Bren Road East, Minnetonka, MN 55343
 *
 */

#include <stdio.h>
#include <string.h>
#include <errno.h>
#include "tx_api.h"
#include "bsp_drivers.h"
#include "usbreg_def.h"
#include "usbDef.h"
#include "nammu.h"

#if (defined BSP_USB_DEVICE_VERSION) && (BSP_USB_DEVICE_VERSION == BSP_USB_DRIVER_VERSION_1)

#define _USB_DMA_SIER_SETTING        0x1e00000 /* 0x1e00000 */
#define _USB_DMA_SIER_NRIP          (0x1 << 29)

#define _USB_DMA_USB_OVERFLOW       0x800
#define _USB_DMA_USB_TRANSFER_STATUS   0x20002000
#define _USB_DMA_USB_TRANSFER_STATUS_ERROR   0x20000000

int UsbDma_DoneDesc(NaUsbEndpoint_t * ep, int request_status)
{
    int     rc = -1;
    int     descIndex;
    int     lengthAdjust;

    unsigned int    bytesAvailable, mps;
    unsigned long   flag;

    NaUsbDeviceReq_t * req;
    physical_address_t  phy;

    USB_LOCK;

    descIndex = ep->dma_buffer_desc_process_index;

    if (ep->dma[descIndex].src_addr != 0)
    {
        req = usb_get_request(ep, &ep->dma[descIndex]);

        bytesAvailable = ep->dma[descIndex].buf_len;
    #if defined (NS9750)
        bytesAvailable = (bytesAvailable == 0)? req->length : req->length - bytesAvailable;
    #endif
        /* reset the process dma buffer descriptor value for naUsbFillDmaDesc to work*/
        lengthAdjust = 0;
        flag = ep->dma[descIndex].flag;

        if ((flag & _USB_DMA_USB_OVERFLOW) ||
            (flag & _USB_DMA_USB_TRANSFER_STATUS) == _USB_DMA_USB_TRANSFER_STATUS_ERROR)
            lengthAdjust = 1;

	    /* If the error status is found in DMA (M31 or overflow in the USB 
	     * device IP is not set), then there will be extra data in the 
	     * DMA buffer.  We need to find out the actual bytes.
         */
        if (lengthAdjust)
        {
            mps = ep->max_packet_size;
            if(bytesAvailable < mps)     
                bytesAvailable = 0;    
            else if ((bytesAvailable % mps) == 0)
                      bytesAvailable = bytesAvailable - mps;
            else
                bytesAvailable = bytesAvailable - bytesAvailable % mps;
        } 


        req->bytes_transfered = bytesAvailable;
        req->dma = NULL;

        /* This is for debug. Make sure the phy and the src_addr in DMA is ok */
        if (!NAVaToPhys(req->buf, &phy))
        { 
            USB_DEBUG(USB_DEVICE_TRACE, TRACE_CRITICAL, "process_dma_in: channel %d NAVaToPhys fails\n", ep->dma_channel);
        }
        if (ep->dma[descIndex].src_addr != phy)
        {
            USB_DEBUG(USB_DEVICE_TRACE, TRACE_CRITICAL, "process_dma_in: channel %d - src addr 0x%x and phy 0x%x\n", 
                ep->dma_channel, (unsigned)ep->dma[descIndex].src_addr, (unsigned)phy);
        }

        NAAfterDMA(NA_DEVICE_TO_MEMORY_DMA, NULL, req->buf, bytesAvailable);

        usb_ep_complete_request(ep, req, req->buf, bytesAvailable, request_status);

        // if no completion function was specified, free the buffer here
        if (req->complete == NULL)
        {
            usb_device_free_buffer(ep, req->buf, req->length);
        }

        usb_free_request(ep, req);

        ep->dma[descIndex].src_addr = 0x0;
        ep->dma[descIndex].buf_len = 0x0;
        ep->dma[descIndex].flag = 0x0;
        descIndex++;
        if (descIndex == ep->dma_buffer_desc_count)
        {
            descIndex = 0;
        }

        ep->dma_buffer_desc_process_index = descIndex;
        ep->dma_buffer_desc_active_count--;
        rc = 0;
    }  

    USB_UNLOCK;
    return rc;

}

/**
 * Process control endpoint OUT
 **/
static void process_ep0_out(NaUsbEndpoint_t *ep, unsigned length)
{
    NaUsbCtrlRequest_t      ctrl_request;
    int     descIndex;
    NaUsbDeviceReq_t * req;
    char * buffer;
    unsigned bytesAvailable;
    int rc = NA_USB_SUCCESS;

    
    descIndex = ep->dma_buffer_desc_process_index;

    buffer = (char *)ep->dma[descIndex].src_addr;

    req = usb_get_request(ep, &ep->dma[descIndex]);

#if defined (NS9750)     /* In NS9750, the status has number of empty spaces available. */
    bytesAvailable = req->length - length;               
#else                /* In NS9360, the status has bytesAvailable. */
    bytesAvailable = length;
#endif

    if (gUsbDeviceDriver->dev->ep0_callback != NULL)
    {
        rc = gUsbDeviceDriver->dev->ep0_callback(gUsbDeviceDriver->dev, 
//                            (void *)ep->request[descIndex].dma->src_addr, bytesAvailable);
                            (void *)buffer, bytesAvailable);
    }

    if (gUsbDeviceDriver->dev->ep0_callback == NULL || rc == NA_USB_NONE)
    {
        if (bytesAvailable == 8)
        {
            naUsbDevParseCtrlRequest(buffer, &ctrl_request);
            usb_device_standard_request_handle(&ctrl_request);
        }
    }
    
    usb_ep_complete_request(ep, req, req->buf, bytesAvailable, NA_USB_REQUEST_COMPLETED);

    // if no completion function was specified, free the buffer here
    if (req->complete == NULL)
    {
       usb_device_free_buffer(ep, buffer, bytesAvailable);
    }

    usb_free_request(ep, req);

    ep->dma[descIndex].src_addr = 0x0;
    ep->dma[descIndex].buf_len = 0x0;
    ep->dma[descIndex].flag = 0x0;

    descIndex++;
    if (descIndex == ep->dma_buffer_desc_count)
    {
        descIndex = 0;
    }

    ep->dma_buffer_desc_process_index = descIndex;
    ep->dma_buffer_desc_active_count--;
}

/**
 * Process DMA for endpoint IN.
 * If DMA completes data transfered, user's complete callback function will be called
 * to notify the number of bytes that has been transfered and the free memory
 * function is also called to free the memory.
 *
 **/
static int process_dma_desc_in(NaUsbEndpoint_t *ep)
{
    int     rc = -1;
    int     lengthAdjust = 0;
    int     bytesAvailable;
    int     descIndex;
#if 0
    unsigned long volatile *addr; 
    unsigned long status;
#endif

    NaUsbDeviceReq_t * req;
    physical_address_t  phy;

    USB_LOCK;
#if 0        
    addr = NA_USB_DEVICE_DMA_SIER (ep->dma_channel);
    status = *addr;
#endif

    descIndex = ep->dma_buffer_desc_process_index; 

    /* process all dma that are finished transmitting */
    while ((ep->dma[descIndex].src_addr != 0) && 
//    if ((ep->dma[descIndex].src_addr != 0) && 
           ((ep->dma[descIndex].flag & DMA_FULL_MASK) != DMA_FULL_MASK))
    {
        bytesAvailable = ep->dma[descIndex].buf_len;
        if ((ep->dma[descIndex].flag & _USB_DMA_USB_TRANSFER_STATUS) == _USB_DMA_USB_TRANSFER_STATUS_ERROR)
            lengthAdjust = 1;

    #if defined (NS9360)
        /* Because the buffer length has number of bytes transmitted, we will set it to
         *  zero so that it is consistant with the NS9750.
         */
        ep->dma[descIndex].buf_len = 0;
    #endif

        /* call the write or read call back function */
    #if defined (NS9750)
        bytesAvailable = (bytesAvailable == 0) ? ep->request[descIndex].length 
                         : ep->request[descIndex].length - bytesAvailable;
    #endif
        if (lengthAdjust == 1)
        {
            bytesAvailable = 0;
        }

        req = usb_get_request(ep, &ep->dma[descIndex]);

        req->bytes_transfered = bytesAvailable;
        req->dma = NULL;

        /* This is for debug. Make sure the phy and the src_addr in DMA is ok */
        if (!NAVaToPhys(req->buf, &phy))
        { 
            USB_DEBUG(USB_DEVICE_TRACE, TRACE_CRITICAL, "process_dma_in: channel %d NAVaToPhys fails\n", ep->dma_channel);
        }
        if (ep->dma[descIndex].src_addr != phy)
        {
            USB_DEBUG(USB_DEVICE_TRACE, TRACE_CRITICAL, "process_dma_in: channel %d - src addr 0x%x and phy 0x%x\n", 
                ep->dma_channel, (unsigned)ep->dma[descIndex].src_addr, (unsigned)phy);
        }

        NAAfterDMA(NA_MEMORY_TO_DEVICE_DMA, req->buf, NULL, bytesAvailable);

        usb_ep_complete_request(ep, req, req->buf, bytesAvailable, NA_USB_REQUEST_COMPLETED);

        // if no completion function was specified, free the buffer here
        if (req->complete == NULL)
        {
            usb_device_free_buffer(ep, req->buf, req->length);
        }

        ep->dma[descIndex].src_addr = 0x0;
        ep->dma[descIndex].buf_len = 0x0;
        ep->dma[descIndex].flag = 0x0;

        usb_free_request(ep, req);

        descIndex++;
        if (descIndex == ep->dma_buffer_desc_count)
        {
            descIndex = 0;  /* wrap back to beginning */
        }

        ep->dma_buffer_desc_process_index = descIndex;
        ep->dma_buffer_desc_active_count--;

        rc = 0;
    }

    USB_UNLOCK;

    return rc;
}


/**
 * Process DMA for endpoint OUT.
 * If DMA completes data transfered, user's complete callback function will be called
 * to notify the number of bytes that has been transfered and the free memory
 * function is also called to free the memory.
 *
 **/
static int process_dma_desc_out(NaUsbEndpoint_t *ep)
{
    int     rc = -1;
    int     descIndex;
    int     lengthAdjust;

    NaUsbDeviceReq_t * req;

    unsigned int        bytesAvailable, mps;
    unsigned long       flag;

    USB_LOCK;

    descIndex = ep->dma_buffer_desc_process_index;

    while ((ep->dma[descIndex].flag & DMA_FULL_MASK) == DMA_FULL_MASK &&
           ((ep->dma[descIndex].src_addr != 0) ||
            ((ep->dma[descIndex].flag & _USB_DMA_USB_OVERFLOW) == _USB_DMA_USB_OVERFLOW)))
    {
        req = usb_get_request(ep, &ep->dma[descIndex]);

        bytesAvailable = ep->dma[descIndex].buf_len;
    #if defined (NS9750)
        bytesAvailable = (bytesAvailable == 0)? req->length : req->length - bytesAvailable;
    #endif
        /* reset the process dma buffer descriptor value for naUsbFillDmaDesc to work*/
        lengthAdjust = 0;
        flag = ep->dma[descIndex].flag;

        if ((ep->dma[descIndex].flag & _USB_DMA_USB_OVERFLOW) ||
            (flag & _USB_DMA_USB_TRANSFER_STATUS) == _USB_DMA_USB_TRANSFER_STATUS_ERROR)
            lengthAdjust = 1;

	    /* The 0x20002000 is the error condition for DMA during USB-OUT
         * data receive.  If the DMA closed and the 0x2000, which is 
         * M31 in the USB device IP is not set, then there will be extra
         * data in the DMA buffer.  We need to subtract that out.
         */
        if (lengthAdjust)
        {
            mps = ep->max_packet_size;
            if(bytesAvailable < mps)     
                bytesAvailable = 0;    
            else if ((bytesAvailable % mps) == 0)
                      bytesAvailable = bytesAvailable - mps;
            else
                bytesAvailable = bytesAvailable - bytesAvailable % mps;
        } 

        req->bytes_transfered = bytesAvailable;
        req->dma = NULL;

        NAAfterDMA(NA_DEVICE_TO_MEMORY_DMA, NULL, req->buf, bytesAvailable);

        usb_ep_complete_request(ep, req, req->buf, bytesAvailable, NA_USB_REQUEST_COMPLETED);

        // if no completion function was specified, free the buffer here
        if (req->complete == NULL)
        {
            usb_device_free_buffer(ep, req->buf, req->length);
        }

        usb_free_request(ep, req);

        ep->dma[descIndex].src_addr = 0x0;
        ep->dma[descIndex].buf_len = 0x0;
        ep->dma[descIndex].flag = 0x0;
        descIndex++;
        if (descIndex == ep->dma_buffer_desc_count)
        {
            descIndex = 0;
        }

        ep->dma_buffer_desc_process_index = descIndex;
        ep->dma_buffer_desc_active_count--;

        rc = 0;
    }  
    USB_UNLOCK;
    return rc;

}

/**
 * Refetch the given dma channel when the dma channel encounters an error.
 *
 **/
void usbDma_Refetch(int dma_channel)
{
    unsigned long volatile *addr;

    usb_reset_fifo_error(dma_channel);   

    addr = NA_USB_DEVICE_DMA_CCR (dma_channel);
    *addr &= ~DMA_CE_MASK;  
    *addr |= DMA_BDR_MASK;
    *addr |= DMA_CE_MASK;

    addr = NA_USB_FIFOSNC_ADDRESS (dma_channel);
    narm_write_reg(NARM_USB_FIFOS_REG, addr, clr, 0);
    return;
}
#define _USB_DMA_CHANNEL_1_IRQ_BIT   (0x1 << 14)

/**
 * Process any DMA. This function is called when USB interrupt ISR
 * gets an USB interrupt signal.
 *
 * This function will check and process each dma channel. 
 *
 **/
int usbDma_Process(NaUsbEndpoint_t * ep)
{
    
    int rc = -1;
    unsigned long volatile *addr;
    unsigned long status;
    int ccode;

    addr = NA_USB_DEVICE_DMA_SIER (ep->dma_channel);
    status = *addr;

    switch (ep->dma_channel)
    {
    case 0:
#if 0
/* this may be used for debugging the DMA */
        if ((status & 0x40800000) == 0x40800000)
        {
            printf("error completion 0x%x\n", (unsigned)status);

            addr = NA_USB_DEVICE_DMA_CCR (ep->dma_channel);
            *addr |= DMA_CHANNEL_ABORT;

            addr = NA_USB_DEVICE_DMA_CCR (ep->dma_channel);
            *addr |= DMA_CHANNEL_ENABLE;

            addr = NA_USB_DEVICE_DMA_SIER (ep->dma_channel);
            *addr |= status;
        }
        else if ((status & 0x10200000) == 0x10200000 || (status & 0x08100000) == 0x08100000)
//                else if ((status & 0xB8000000) > 0)
        {
            printf("status 0x%x\n", (unsigned)status);
            if ((status & 0x10200000) > 0)
            {
                addr = NA_USB_DEVICE_DMA_CCR (ep->dma_channel);
                *addr &= ~DMA_CHANNEL_ABORT;
            }
            addr = NA_USB_DEVICE_DMA_SIER (ep->dma_channel);
            *addr |= status;    /* Clear All status bits */

            addr = NA_USB_DEVICE_DMA_CCR (ep->dma_channel);
            *addr |= DMA_CHANNEL_ENABLE;
        }
#endif

        if ((status & 0x81040000) > 0x80000000)
        {
            process_ep0_out(ep, (status & 0xffff));
            addr = NA_USB_DEVICE_DMA_SIER (ep->dma_channel);
            *addr |= status;
        }
/*        if (gUsbDeviceDriver->ep0_state != eUsbEp_Sending) */
        {
            ccode = usb_setup_ep0_out(&gUsbDeviceDriver->ep0_out);
            if (ccode == 0)
            {
                gUsbDeviceDriver->ep0_state = eUsbEp_Waiting;
            }
            else
            {
                USB_DEBUG(USB_DEVICE_TRACE, TRACE_WARNING, "usbDma_Process: setup ep0 out fails %d\n", ccode);
            }
        }
        break;
    default:
#if 0
/* This may be used for debugging DMA status */
        if ((status & 0x40800000) == 0x40800000)
        {
            printf("Error completion 0x%X = 0x%X\n", (unsigned)addr, (unsigned)status);
        }

        if ((status & 0x10200000) == 0x10200000 || (status & 0x20400000) == 0x20400000 ||
            (status & 0x08100000) == 0x08100000)
        {
            printf("Error 0x%X = 0x%X\n", (unsigned)addr, (unsigned)status);

            if ((status & 0x10200000) > 0)
            {
                addr = NA_USB_DEVICE_DMA_CCR (ep->dma_channel);
                *addr &= ~DMA_CHANNEL_ABORT;
            }
        #if defined (NS9750)
            if (status & _USB_DMA_SIER_NRIP)
            {
                addr = NA_USB_FIFOSNC_ADDRESS(ep->dma_channel);
                narm_write_reg(NARM_USB_FIFOS_REG, addr, clr, 1);  
            }
        #endif
            addr = NA_USB_DEVICE_DMA_SIER (ep->dma_channel);
            *addr |= status;
            addr = NA_USB_DEVICE_DMA_CCR (ep->dma_channel);
            *addr |= DMA_CHANNEL_ENABLE;
        }
#endif

/*        if ((status & 0x81040000) > 0x80000000) */
        {

            if (ep->dir == USB_DIR_OUT)
            {

            #if defined (NS9750)
                if (status & _USB_DMA_SIER_NRIP)
                {
                    addr = NA_USB_FIFOSNC_ADDRESS(ep->dma_channel);
                    narm_write_reg(NARM_USB_FIFOS_REG, addr, clr, 1);  
                }
            #endif

                addr = NA_USB_DEVICE_DMA_CCR (ep->dma_channel);
                *addr &= ~DMA_CHANNEL_ABORT;

                rc = process_dma_desc_out(ep);

            }
            else
            {
                rc =process_dma_desc_in(ep);
            }

            addr = NA_USB_DEVICE_DMA_SIER (ep->dma_channel);
            *addr |= status;    /* Clear All status bits */

            if (ep->dma_buffer_desc_active_count != 0)
            {
	            rc = usbDma_EnableEndpoint(ep);
            }
        }
        break;
    } /* case */

    return rc;
}

/**
 * Setup Endpoint OUT request. This function sets up a
 * DMA channel waiting for data from the host to an
 * endpoint.
 *
 **/                                             
int usbDma_SetupOutPacket(NaUsbEndpoint_t * ep, NaUsbDeviceReq_t * req)
{
    int                 rc = 0;
    physical_address_t  physicalAddr;
    int                 bufIndex;
    unsigned            flag;

    TX_INTERRUPT_SAVE_AREA 

    TX_DISABLE

    USB_LOCK;
    bufIndex = ep->dma_buffer_desc_setup_index;
    if (ep->dma[bufIndex].src_addr != 0)
    {
        rc = -EAGAIN;
        goto _ret;
    }
    if (!NAVaToPhys(req->buf, &physicalAddr))
    { 
        /* something wrong here */
        rc = -EOPNOTSUPP;
        goto _ret;
    }

    usb_set_fifo_packet_size (ep->dma_channel, ep->max_packet_size);

    NABeforeDMA(NA_DEVICE_TO_MEMORY_DMA, NULL, req->buf, req->length);

    ep->dma[bufIndex].src_addr = (unsigned long) physicalAddr;

    ep->dma[bufIndex].buf_len = req->length;
    ep->dma[bufIndex].dst_addr = 0;
    flag = DMA_INTERRUPT_MASK;

    req->dma = &ep->dma[bufIndex];

    /* Clear the full bit for receiving; but still have to set the CE bit for this channel. */
    bufIndex++; 
    if (bufIndex == ep->dma_buffer_desc_count)
    {
        ep->dma[bufIndex-1].flag = flag | DMA_WRAP_MASK;

        bufIndex = 0;
    }
    else
    {
        ep->dma[bufIndex-1].flag = flag;
    }

    ep->dma_buffer_desc_setup_index = bufIndex;


_ret:
    USB_UNLOCK;
    TX_RESTORE
    return rc;
}


/**
 * Setup Endpoint IN request. This function sets up a
 * DMA channel to send data to the host from an
 * endpoint.
 *
 **/
int usbDma_SetupInPacket(NaUsbEndpoint_t * ep, NaUsbDeviceReq_t * req)
{
    int                 rc = 0;
    physical_address_t  phy;
    int                 bufIndex;         /* buffer descriptor index to be filled first */
    unsigned long volatile *addr;
    unsigned long status;
    unsigned        length;

    TX_INTERRUPT_SAVE_AREA 

    TX_DISABLE

    USB_LOCK;
    bufIndex = ep->dma_buffer_desc_setup_index;

    addr = NA_USB_DEVICE_DMA_CCR (ep->dma_channel);       /* get DMA Chan Control Reg */
    status = *addr;
    if (status & DMA_BDR_MASK)      /* if BDR is still set, return */
    {
        USB_DEBUG(USB_DEVICE_TRACE, TRACE_WARNING, "usbDma_SetupInPacket: DMA_BDR_MASK\n");
        rc = -EAGAIN;
        goto _ret;
	}

    if (ep->dma[bufIndex].src_addr != 0)  
    {
        TX_RESTORE
        USB_DEBUG(USB_DEVICE_TRACE, TRACE_WARNING, "usbDma_SetupInPacket: busy dma_fails %d\n", ep->dma_fails);
        TX_DISABLE

        ep->dma_fails++;
        /* No dma available. Is something wrong?
         * if we wait 2 times calling this function and it still fails,
         * we reset the dma for other IN packet.
         */
        if (ep->dma_fails > 2)
        {
            usbDma_Refetch(ep->dma_channel);
#if 0
            usb_ep_complete_request(ep, req, req->buf, 0, NA_USB_REQUEST_CLOSED);

            // if no completion function was specified, free the buffer here
            if (req->complete == NULL)
            {
                usb_device_free_buffer(ep, req->buf, req->length);
            }

            ep->dma[bufIndex].src_addr = 0x0;
            ep->dma[bufIndex].buf_len = 0x0;
            ep->dma[bufIndex].flag = 0;

            usb_free_request(ep, req);
#endif

            status = FLY_BY_READ | BURST_OPERS_4 | DMA_CHANNEL_ENABLE;
            addr = NA_USB_DEVICE_DMA_CCR (ep->dma_channel);
            *addr = status;           /* start transmitting */ 
            ep->dma_fails = 0;
        }
        rc = -EAGAIN;
        goto _ret;
    }

    if (!NAVaToPhys(req->buf, &phy))
    { 
        USB_DEBUG(USB_DEVICE_TRACE, TRACE_CRITICAL, "usbDma_SetupInPacket: channel %d NAVaToPhys fails\n", ep->dma_channel);
        rc = -EOPNOTSUPP;
        goto _ret;
    }

    length = req->length;

    if (length > 0)
    {
        if (ep->dma_channel == 1 && (length % ep->max_packet_size) == 0)
        {
            length++;
        }
        NABeforeDMA(NA_MEMORY_TO_DEVICE_DMA, req->buf, NULL, length);
        usb_set_fifo_packet_size (ep->dma_channel, ep->max_packet_size);
        ep->dma[bufIndex].src_addr = phy;
    }
    else
    {
        NABeforeDMA(NA_MEMORY_TO_DEVICE_DMA, req->buf, NULL, length);
        usb_set_fifo_packet_size (ep->dma_channel, 0);
        ep->dma[bufIndex].src_addr = phy;
    }
    ep->dma[bufIndex].buf_len = length;
    ep->dma[bufIndex].dst_addr = 0x0; 
    status = DMA_FULL_MASK| DMA_INTERRUPT_MASK | DMA_LAST_MASK;

    req->dma = &ep->dma[bufIndex];
    bufIndex++;

    if (bufIndex == ep->dma_buffer_desc_count)
    {
        ep->dma[bufIndex-1].flag = DMA_WRAP_MASK | status;
        bufIndex = 0;
    }
    else
    {
        ep->dma[bufIndex-1].flag = status;
    }

    ep->dma_buffer_desc_setup_index = bufIndex;

_ret:
    USB_UNLOCK;

    TX_RESTORE
    return rc;
}

/**
 * Enable DMA for an endpoint. It enables an dma channel to an endpoint.
 *
 **/
int usbDma_EnableEndpoint(NaUsbEndpoint_t * ep)
{
    unsigned long volatile *addr;
    unsigned long value;

    TX_INTERRUPT_SAVE_AREA 

    TX_DISABLE

    if (ep->dma_channel > 1 && ep->dir == USB_DIR_IN)
    {
        usb_set_fifo_nack_irq(ep->dma_channel, USB_ENABLED);
    #if defined (NS9750)
        usb_set_fifo_error_irq(ep->dma_channel, USB_ENABLED);  
    #endif
    }

    value = narm_read_reg(NARM_USB_GIE_REG, NA_USB_GIE_ADDRESS, dma);
    value |= (1 << ep->dma_channel);
    narm_write_reg(NARM_USB_GIE_REG, NA_USB_GIE_ADDRESS, dma, value);

    addr = NA_USB_DEVICE_DMA_CCR (ep->dma_channel);
    *addr |= DMA_CHANNEL_ENABLE;

/*    narm_write_reg(NARM_USB_GIE_REG, NA_USB_GIE_ADDRESS, gbl_dma, 1); */

    TX_RESTORE
    return 0;
}

int usbDma_DisableEndpoint(NaUsbEndpoint_t * ep)
{
    unsigned long value; 
    unsigned long volatile *addr;        /* address of fifo register       */

    addr = NA_USB_DEVICE_DMA_CCR (ep->dma_channel);
    *addr |= DMA_CHANNEL_ABORT;

    *addr &= ~DMA_CHANNEL_ENABLE;

    /* read back the DMA interrupt enable field */
    value = narm_read_reg(NARM_USB_GIE_REG, NA_USB_GIE_ADDRESS, dma);

    /* disable the DMA */
    value &= ~(1 << ep->dma_channel);

    /* Write back to the Global interrupt enable reg's DMA field */
    narm_write_reg(NARM_USB_GIE_REG, NA_USB_GIE_ADDRESS, dma, value);

//    addr = NA_USB_DEVICE_DMA_BDP (netos_ep->dma_channel);     /* get DMA Buffer Desc Reg */
//    *addr = (unsigned long) 0;

    return 0;
}

/**
 * Initialize DMA to an endpoint.
 *
 **/
int usbDma_InitEndpoint(NaUsbEndpoint_t * ep)
{
    physical_address_t      phys;

    unsigned long value; 
    unsigned long volatile *addr;

    int             rc = 0;

    if (ep->dir == USB_DIR_OUT)
    {
        usb_set_fifo_packet_size (ep->dma_channel, ep->max_packet_size);
    }

    /* set up Fifo Packet Control Reg */

    if (!NAVaToPhys(ep->dma, &phys))
    {
        USB_DEBUG(USB_DEVICE_TRACE, TRACE_CRITICAL, "usbDma_InitEndpoint: NAVaToPhys fails\n");
        rc = -EOPNOTSUPP;
        goto _ret;
    }

    addr = NA_USB_DEVICE_DMA_BDP (ep->dma_channel);     /* get DMA Buffer Desc Reg */
    *addr = (unsigned long) phys;  

    addr = NA_USB_DEVICE_DMA_CCR (ep->dma_channel);
    *addr = 0;

    addr = NA_USB_FIFOSNC_ADDRESS (ep->dma_channel);
    *addr = 0;
    narm_write_reg(NARM_USB_FIFOS_REG, addr, clr, 0); 
    narm_write_reg(NARM_USB_FIFOS_REG, addr, type, ep->type);
    narm_write_reg(NARM_USB_FIFOS_REG, addr, dir, ep->dir);  /* 0 is NA_USB_DEVICE_FIFO_STATUS_DIR_OUT; */ 

    value = *addr;
    /* Enable USB DMA irq */
/*    value = narm_read_reg(NARM_USB_GIE_REG, NA_USB_GIE_ADDRESS, dma);
 *    value |= (1 << ep->dma_channel);
 *    narm_write_reg(NARM_USB_GIE_REG, NA_USB_GIE_ADDRESS, dma, value);
 */

    /* enable DMA irq status */
    addr = NA_USB_DEVICE_DMA_SIER (ep->dma_channel);
    *addr = _USB_DMA_SIER_SETTING;

    if (ep->dir == USB_DIR_OUT)
    {

#if defined (NS9360)
        addr = NA_USB_FIFOSNC_ADDRESS (ep->dma_channel);
        if (ep->dma_channel > 1 && narm_read_reg(NARM_USB_FIFOS_REG, addr, timeout_cnt) == 0)
            narm_write_reg(NARM_USB_FIFOS_REG, addr, timeout_cnt, 6); /* set it to default of 6. */ 
#endif

        value = FLY_BY_WRITE | BURST_OPERS_4;

    }
    else
    {
        value = FLY_BY_READ | BURST_OPERS_4;
    }
    addr = NA_USB_DEVICE_DMA_CCR (ep->dma_channel);
    *addr = value;
    
_ret:
    return rc;
}


#endif /* BSP_USB_DEVICE_VERSION */
