#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <io.h>
#include "alt_types.h" 
#include "sys/alt_irq.h"  
#include "system.h"
#include "isp1761_device_register.h"
#include "usb_protocol.h"


typedef int bool;
#define TRUE    1
#define FALSE   0
#define MIN(x,y) ((x) < (y) ? (x) : (y))
#define MAX(x,y) ((x) > (y) ? (x) : (y))

#define MAGIC_HEADER 0xBEADED00

#define PORTS_S2F_FIFO_DATA 0x0
#define PORTS_S2F_FIFO_STATUS 0x4
#define PORTS_F2S_FIFO_DATA 0x8
#define PORTS_F2S_FIFO_STATUS 0xC
#define PORTS_FIFO_EMPTYFLAG 0x80000000

#if 0
#define DEBUG_ISR DEBUG_OUT
#define DEBUG_OUT(x)  {printf x;}
#else
#define DEBUG_OUT(x)
#define DEBUG_ISR
#endif

#define DC_USB_VID   0x09FB
#define DC_USB_PID   0x1337

struct
{
    alt_u32 bytes_received;
    alt_u32 bytes_sent;
    alt_u32 ports_s2f_fifo_size;
    alt_u32 ports_f2s_fifo_size;
    bool    data_phase;
    alt_u32 ports_f2s_remaining;
} g_transfer_status;

#pragma pack(1)   

/* Device descriptor */
unsigned char gDeviceDesc[] = 
{
    DC_DEV_DESC_LEN,  /* length of this desc. */
    DC_DEV_DESC_TYPE, /* DEVICE descriptor */
    0x10,0x01,         /* spec rev level (BCD) */ //Complient to USB2.0 FullSpeed
    0xFF, //USBCS_VENDER_SPECIFIC ,  richard 0x00,              /* device class */
    
    0x00,              /* device subclass */
    0x00,              /* device protocol */  
    0x40,      /* max packet size */
    (DC_USB_VID & 0xFF),
    (DC_USB_VID >> 8),
    (DC_USB_PID & 0xFF),
    (DC_USB_PID >> 8),
    0x00,0x01,         /* 1761's revision ID */  
    1,                 /* index of manuf. string */   
    2,                 /* index of prod.  string */  
    3,                 /* index of ser. # string */   
    0x01 /* number of configs. */ 
};

/* configuration descriptor */
unsigned char gConfigDesc[] =
{
    DC_CONFIG_DESC_LEN,                /* length of this desc. */ 
    DC_CONFIG_DESC_TYPE,       /* CONFIGURATION descriptor */  
    0x20, 0x00,    /* total length returned */ 
    
    0x01,           /* number of interfaces */ 
    0x01,                               /* number of this config */ 
    0x00,                       /* index of config. string */  
    0xC0,                       /* attr.: self powered */   
    0x00, //0x01,                       /* we take no bus power */  

    DC_INTF_DESC_LEN,          /* length of this desc. */  
    DC_INTF_DESC_TYPE,         /* INTERFACE descriptor */  
    0x00,                       /* interface number */
    0x00,                       /* alternate setting */  
  
    0x02,                       /* # of (non 0) endpoints */ 
    0xFF, // richard 0x08,                       /* interface class (Mass Storage)*/
    0xFF, // richard 0x06,                       /* interface subclass (SCSI Transparent) */  
    0xFF, // richard 0x50,                       /* interface protocol (BOT Protocol) */  

    0x00,                       /* index of intf. string */ 

    /* Pipe 0 */
    DC_EP_DESC_LEN,            /* length of this desc. */   
    DC_EP_DESC_TYPE,           /* ENDPOINT descriptor */ 
    0x81,                        /* address (IN) */  
    0x02,                       /* attributes  (BULK) */    
    0x40,                       /* max packet size */
    0x00,
    0,                          /* interval (ms) */

    /* Pipe 1 */ 
    DC_EP_DESC_LEN,            /* length of this desc. */   
    DC_EP_DESC_TYPE,           /* ENDPOINT descriptor*/ 
    0x01,                       /* address (OUT) */  
    0x02,                       /* attributes  (BULK) */    
    0x40,                       /* max packet size */
    0x00,
    0                                   /* interval (ms) */
};

/* Unicode descriptors for our device description */
unsigned char gUnicodeString[]= { 
    0x04,0x03,
    0x09,0x04           /* We offer only one language: 0409, US English */
};

unsigned char gMfgString[] =  
{ 10,3,
      'E',0, 'E',0, 'C',0, 'G',0
};

unsigned char gProductString[] =  
{
    56,3,
    'D',0, 'E',0, '3',0, ' ',0, 'P',0, 'o',0, 'r',0, 't',0, 's',0, ' ',0,
    'P',0, 'a',0, 'c',0, 'k',0, 'a',0, 'g',0, 'e',0, ' ',0, 'I',0, 'n',0,
    't',0, 'e',0, 'r',0, 'f',0, 'a',0, 'c',0, 'e',0
};

unsigned char gSerialString[] =
{
    0x0A, 0x03, '1', 0x00, '.',0x00, '0', 0x00, '0', 0x00
};

#pragma pack()


void portsProcessEvent(void);

bool dcInit(void);
void dcReset(void);
void dcSetupEndpoints(bool bHighSpeed);
bool dcIsEndpointEmpty(alt_u8 Endpoint);
int dcGetBufferLength(alt_u8 Endpoint);
int dcReadEndpoint(alt_u8 Endpoint, alt_u8 *pBuffer, int BufLen);
void dcWriteEndpoint(alt_u8 Endpoint, alt_u8 *pData, int DataLen);
void dcRxCommand(bool bSetupToken);
void dcTxCommand(void);
void dcRxData(alt_u8 endpoint);
void dcTxData(alt_u8 endpoint);
void dcStallEndpoint(alt_u8 endpoint);
void dcAckEndpoint(alt_u8 endpoint);
void dcProcessEvent(void);

void HAL1761_RegWrite32(alt_u32 Addr, alt_u32 data32);
alt_u32 HAL1761_RegRead32(alt_u32 Addr);
void HAL1761_DeviceDataRead(alt_u32 DataPort, alt_u8 szData[], alt_u32 ByteLen);
void HAL1761_DeviceDataWrite(alt_u32 DataPort, alt_u8 szData[], alt_u32 ByteLen);
 

 
int main(int argc, char** argv)
{
    if (!dcInit())
    {
        DEBUG_OUT(("Could not dcInit()\n"));
        return -1;
    }
    DEBUG_OUT(("NIOS program is running\n"));
    
    g_transfer_status.bytes_received = 0;
    g_transfer_status.bytes_sent = 0;
    g_transfer_status.ports_f2s_fifo_size = (IORD(PORTS_BASE, PORTS_F2S_FIFO_STATUS) >> 16) & 0x7FFF;
    g_transfer_status.ports_s2f_fifo_size = (IORD(PORTS_BASE, PORTS_S2F_FIFO_STATUS) >> 16) & 0xFFFF;
    g_transfer_status.data_phase = FALSE;
    
    //alt_irq_register(ISP1761_IRQ, 0, isr);    
    
    while(TRUE)
    {
        dcProcessEvent();
        portsProcessEvent();
    }
    
    return 0;
}

//void isr(void* context, alt_u32 id)
//{
    //dcProcessEvent();
//}
 
void portsProcessEvent()
{
    int max_packet_size;
    int fifo_bytes;
    int bytes_to_xfer;
    int words_to_xfer;
    int i;
    
    max_packet_size = (int) *((alt_u16*)&gConfigDesc[22]);
    
    // Send any pending FPGA->USB traffic first
    fifo_bytes = IORD(PORTS_BASE, PORTS_F2S_FIFO_STATUS);
    if (!(fifo_bytes & PORTS_FIFO_EMPTYFLAG))
    {
    	fifo_bytes &= 0xFFFF;

    	// When the fifo is close to being empty, do one word at a time
    	fifo_bytes -= 5;
    	fifo_bytes = MAX(fifo_bytes, 1);

        // First data word from the fifo indicates total transaction size in bytes
        if (g_transfer_status.data_phase == FALSE)
        {
            g_transfer_status.ports_f2s_remaining = IORD(PORTS_BASE, PORTS_F2S_FIFO_DATA);
            g_transfer_status.data_phase = TRUE;
        }
        else
        {        
            fifo_bytes <<= 2;
            bytes_to_xfer = MIN(fifo_bytes, (max_packet_size - g_transfer_status.bytes_sent));
            bytes_to_xfer = MIN(bytes_to_xfer, g_transfer_status.ports_f2s_remaining);
            words_to_xfer = (bytes_to_xfer+3) >> 2;
        
            if (bytes_to_xfer > 0)
            {
            	// DEBUG_OUT(("fpga to usb traffic of %d bytes  bytes_to_xfer %d  remaining %d\n", fifo_bytes, bytes_to_xfer, g_transfer_status.ports_f2s_remaining));

                HAL1761_RegWrite32(DC_EP_INDEX, EP1TX);
                HAL1761_RegWrite32(DC_BUF_LEN, max_packet_size);

                // Unroll by 4
                for (i = words_to_xfer; i > 3; i-=4)
                {
                    alt_u32 word;
                    word = IORD(PORTS_BASE, PORTS_F2S_FIFO_DATA);
                    IOWR(ISP1761_BASE, DC_DATA_PORT, word);
                    word = IORD(PORTS_BASE, PORTS_F2S_FIFO_DATA);
                    IOWR(ISP1761_BASE, DC_DATA_PORT, word);
                    word = IORD(PORTS_BASE, PORTS_F2S_FIFO_DATA);
                    IOWR(ISP1761_BASE, DC_DATA_PORT, word);
                    word = IORD(PORTS_BASE, PORTS_F2S_FIFO_DATA);
                    IOWR(ISP1761_BASE, DC_DATA_PORT, word);
                }
                
                for ( ; i > 0; i--)
                {
                    alt_u32 word;
                    word = IORD(PORTS_BASE, PORTS_F2S_FIFO_DATA);
                    IOWR(ISP1761_BASE, DC_DATA_PORT, word);
                }
                
                g_transfer_status.bytes_sent += bytes_to_xfer;
                g_transfer_status.ports_f2s_remaining -= bytes_to_xfer;
                if (g_transfer_status.ports_f2s_remaining == 0)
                {
                    g_transfer_status.data_phase = FALSE;

                    // The last packet may be shorter than maxPacketSize so
                    // we have to tell the ISP1761 manually to send it instead
                    // of expecting more bytes
                    if (g_transfer_status.bytes_sent != max_packet_size)
                    {
                    	alt_u32 reg;

                    	HAL1761_RegWrite32(DC_EP_INDEX, EP1TX);
                    	reg = HAL1761_RegRead32(DC_CONTROL_FUNC);
                    	HAL1761_RegWrite32(DC_CONTROL_FUNC, reg | CONTROL_FUNC_VENDP);
                    	HAL1761_RegWrite32(DC_CONTROL_FUNC, reg);
                    }
                }
            }
        }
    }
    
    // Send USB->FPGA traffic
    if (g_transfer_status.bytes_received > 0)
    {
        fifo_bytes = IORD(PORTS_BASE, PORTS_S2F_FIFO_STATUS) & 0xFFFF;
		fifo_bytes = g_transfer_status.ports_s2f_fifo_size - fifo_bytes;

		// For safety, never use the last few words of the FIFO
		fifo_bytes -= 5;
        fifo_bytes <<= 2;
        
        bytes_to_xfer = MIN(fifo_bytes, g_transfer_status.bytes_received);
        words_to_xfer = (bytes_to_xfer+3) >> 2;
		// DEBUG_OUT(("usb to fpga traffic of %d  fifo_bytes %d  bytes_to_xfr %d\n", g_transfer_status.bytes_received, fifo_bytes, bytes_to_xfer));
        
		if (words_to_xfer > 0)
		{
            HAL1761_RegWrite32(DC_EP_INDEX, EP1RX); 
            for (i = words_to_xfer; i > 3; i -= 4)
            {
                alt_u32 word;
                word = IORD(ISP1761_BASE, DC_DATA_PORT);
                IOWR(PORTS_BASE, PORTS_S2F_FIFO_DATA, word);
                word = IORD(ISP1761_BASE, DC_DATA_PORT);
                IOWR(PORTS_BASE, PORTS_S2F_FIFO_DATA, word);
                word = IORD(ISP1761_BASE, DC_DATA_PORT);
                IOWR(PORTS_BASE, PORTS_S2F_FIFO_DATA, word);
                word = IORD(ISP1761_BASE, DC_DATA_PORT);
                IOWR(PORTS_BASE, PORTS_S2F_FIFO_DATA, word);
            }
            
            for ( ; i > 0; i--)
            {
                alt_u32 word;
                word = IORD(ISP1761_BASE, DC_DATA_PORT);
                IOWR(PORTS_BASE, PORTS_S2F_FIFO_DATA, word);
            }
            
            g_transfer_status.bytes_received -= bytes_to_xfer;
            if(g_transfer_status.bytes_received == 0) {
		// DEBUG_OUT(("s2f done last %d fifo_bytes %d\n", bytes_to_xfer, fifo_bytes));
	    }
		}
    }   
}
 
bool dcInit()
{
    alt_u32 data32, read32;
    int i;
   
    HAL1761_RegWrite32(DC_UNLOCK_DEVICE, 0xAA37);
   
    // reset
    HAL1761_RegWrite32(DC_MODE, MODE_SFRESET);
    HAL1761_RegWrite32(DC_MODE, 0);
    
    HAL1761_RegWrite32(DC_UNLOCK_DEVICE, 0xAA37);
    
    /* io test */   
    for(i=0;i<512 ;i+=13)
    { 
        data32 = i;
        HAL1761_RegWrite32(DC_SCRATCH, data32);
        read32 = HAL1761_RegRead32(DC_SCRATCH); 
    
        if (read32 != data32)
        {
            DEBUG_OUT(("dcInit: SCRATCH REGISTER Test Fail: got %x instead of %x\n", read32, data32));
            return FALSE;
        }
    }
    
    /* chip version check */   
    read32 = HAL1761_RegRead32(DC_CHIP_ID); 
    if (read32 != 0x00158210)
    {
        DEBUG_OUT(("dcInit: Invalid Chip ID: %08lXh\n", read32));
        return FALSE;
    }
    
    // config port1
    HAL1761_RegWrite32(OTG_CONTROL, 0x00060401);
   
    dcReset();
    dcSetupEndpoints(FALSE);  
        
    return TRUE;            
}

// Called at init and every bus reset
void dcReset()
{
    DEBUG_OUT(("dcReset()\n"));
    
    // Reset DC_MODE
    HAL1761_RegWrite32(DC_MODE, MODE_WKUPCS|MODE_CLKAON|MODE_GLINTENA);

    // Reset interrupt config
    HAL1761_RegWrite32(DC_INT_CONFIG, (CONFIG_INT_DBG_ACK | CONFIG_INT_DBG_IN_ACK | CONFIG_INT_DBG_OUT_ACK_NYET) | CONFIG_INTPOL_H );
    
    // Reset interrupt enable conditions
    HAL1761_RegWrite32(DC_INT_ENABLE, IE_BASIC_CTRL_EP_MASK | IE_NON_CTRL_EP_MASK); 
    
    // Reset device address and connect to the bus
    HAL1761_RegWrite32(DC_ADDRESS, 0x80);
    
    // Set DMA endpoint to unused value
    HAL1761_RegWrite32(DC_DMA_EP, EP7RX);
} 

void dcSetupEndpoints(bool bHighSpeed)
{
    alt_u16 MaxPacketSize = bHighSpeed? 512 : 64;
    
    // Clear endpoints
    HAL1761_RegWrite32(DC_EP_INDEX, EP1RX);
    HAL1761_RegWrite32(DC_CONTROL_FUNC, CONTROL_FUNC_CLBUF);        
    HAL1761_RegWrite32(DC_CONTROL_FUNC, CONTROL_FUNC_CLBUF);
    HAL1761_RegWrite32(DC_EP_INDEX, EP1TX);
    HAL1761_RegWrite32(DC_CONTROL_FUNC, CONTROL_FUNC_CLBUF);        
    HAL1761_RegWrite32(DC_CONTROL_FUNC, CONTROL_FUNC_CLBUF);
    
    // Allocate FIFOs for endpoints
    HAL1761_RegWrite32(DC_EP_INDEX, EP1RX);
    HAL1761_RegWrite32(DC_EP_MAX_PK_SIZE, MaxPacketSize);
    HAL1761_RegWrite32(DC_EP_INDEX, EP1TX);
    HAL1761_RegWrite32(DC_EP_MAX_PK_SIZE, MaxPacketSize);
    
    // Set up endpoint types and enable them
    HAL1761_RegWrite32(DC_EP_INDEX, EP1RX);
    HAL1761_RegWrite32(DC_EP_TYPE, EP_TYPE_ENDPTY_BLK|EP_TYPE_DBLBUF|EP_TYPE_ENABLE);
    HAL1761_RegWrite32(DC_EP_INDEX, EP1TX);
    HAL1761_RegWrite32(DC_EP_TYPE, EP_TYPE_ENDPTY_BLK|EP_TYPE_DBLBUF|EP_TYPE_ENABLE);
    
    // Clear endpoints
    HAL1761_RegWrite32(DC_EP_INDEX, EP1RX);
    HAL1761_RegWrite32(DC_CONTROL_FUNC, CONTROL_FUNC_CLBUF);        
    HAL1761_RegWrite32(DC_CONTROL_FUNC, CONTROL_FUNC_CLBUF);
    HAL1761_RegWrite32(DC_EP_INDEX, EP1TX);
    HAL1761_RegWrite32(DC_CONTROL_FUNC, CONTROL_FUNC_CLBUF);        
    HAL1761_RegWrite32(DC_CONTROL_FUNC, CONTROL_FUNC_CLBUF);
    
    // Update descriptors
    if (!bHighSpeed)
    {
        gDeviceDesc[2] = 0x10;
        gDeviceDesc[3] = 0x01;
        gConfigDesc[22] = 0x40;
        gConfigDesc[23] = 0x00;
        gConfigDesc[29] = 0x40;
        gConfigDesc[30] = 0x00;
    }
    else
    {
        gDeviceDesc[2] = 0x00;
        gDeviceDesc[3] = 0x02;
        gConfigDesc[22] = 0x00;
        gConfigDesc[23] = 0x02;
        gConfigDesc[29] = 0x00;
        gConfigDesc[30] = 0x02;
        
    }

    DEBUG_OUT(("dcSetupEndpoints(%s)\n", bHighSpeed? "HS" : "not HS"));
}

/* Check to see if endpoint is full */ 
int dcIsEndpointEmpty(alt_u8 Endpoint)
{
    bool bEmpty = TRUE;
    alt_32 Data32;
 
    HAL1761_RegWrite32(DC_EP_INDEX, Endpoint);
    Data32 = HAL1761_RegRead32(DC_BUF_STATUS);
    Data32 = (Data32 >> 16 ) & 0xFFFF;
    if (Data32 & 0x03)
        bEmpty = FALSE;
        
    return bEmpty;        
} 

int dcGetBufferLength(alt_u8 Endpoint)
{
    int length;
    
    HAL1761_RegWrite32(DC_EP_INDEX, Endpoint);
    length = HAL1761_RegRead32(DC_BUF_LEN);
    length &= 0xFFFF; // 2-byte
    
    return length;
}

/* Read a packet out of a FIFO */
int dcReadEndpoint(alt_u8 Endpoint, alt_u8 *pBuffer, int BufLen)
{
    int nActualReadByte;
    alt_u32 *pBuf32;

    pBuf32 = (alt_u32 *)pBuffer;

    if (dcIsEndpointEmpty(Endpoint)) 
        return(0);
        
    /* read buffer length */
    nActualReadByte = dcGetBufferLength(Endpoint);

    /* Read packet and align the bytes*/
    if (nActualReadByte > BufLen) 
        nActualReadByte = BufLen;

    /* Bug fix for zero length packets */
    if (nActualReadByte == 0)
    {
        HAL1761_RegWrite32(DC_EP_INDEX, Endpoint);
        HAL1761_RegWrite32(DC_CONTROL_FUNC, CONTROL_FUNC_CLBUF);        
        HAL1761_RegWrite32(DC_CONTROL_FUNC, CONTROL_FUNC_CLBUF);        
    }
    else
    {   
        HAL1761_DeviceDataRead(DC_DATA_PORT, (alt_u8 *)pBuf32,nActualReadByte); 
    }

    DEBUG_OUT(("dcReadEndpoint: EP0%d %d bytes\n",Endpoint,nActualReadByte));

    /* Return bytes read */
    return nActualReadByte;
}

/* Write a packet into the fifo */
void dcWriteEndpoint(alt_u8 Endpoint, alt_u8 *pData, int DataLen)
{
    alt_u32 Data32;

    DEBUG_OUT(("dcWriteEndpoint: EP0%d %d bytes\n",Endpoint,((unsigned short)DataLen)));

    /* write endpoint index */
    HAL1761_RegWrite32(DC_EP_INDEX, Endpoint);        

    if (Endpoint == EP0TX)
        HAL1761_RegWrite32(DC_CONTROL_FUNC, CONTROL_FUNC_DSEN);

    /* write buffer length to validate the buffer */
    Data32 = HAL1761_RegRead32(DC_BUF_LEN);
    Data32 &= 0xFFFF0000;
    Data32 |= (DataLen & 0xFFFF);
    HAL1761_RegWrite32(DC_BUF_LEN, Data32);

    /* Write the data  to  FIFO*/
    HAL1761_DeviceDataWrite(DC_DATA_PORT, pData, DataLen);
        
    return ;      
}

/* Deal with incoming packet on the control endpoint */
void dcRxCommand(bool bSetupToken)
{
    /* Get receiver status */    
    alt_u8      szCommand[8];
    
    /* Is this a setup packet? */  
    if (bSetupToken)
    {
        int i = dcReadEndpoint(EP0SETUP,szCommand,sizeof(szCommand));

        /* Read the packet into our buffer */
        if (i != sizeof(szCommand))
        {
            HAL1761_RegWrite32(DC_EP_INDEX, EP0TX);
            HAL1761_RegWrite32(DC_CONTROL_FUNC, CONTROL_FUNC_STALL);

            HAL1761_RegWrite32(DC_EP_INDEX, EP0RX);
            HAL1761_RegWrite32(DC_CONTROL_FUNC, CONTROL_FUNC_STALL);

            DEBUG_OUT(("dcRxCommand: Short USB control read (%d bytes)!\n",i));
            return;
        }
        else
        {
            alt_u32 Data32;
            memcpy(&Data32, szCommand, 4);
            DEBUG_OUT(("dcRxCommand: Setup Token:%08lXh\n", Data32));
            memcpy(&Data32, &szCommand[4], 4);
            DEBUG_OUT(("dcRxCommand: Setup Token:%08lXh\n", Data32));
        }
        
        switch (szCommand[1])
        {
            case GET_DESCRIPTOR:
            {
                alt_u16 wLength = szCommand[6] | (szCommand[7] << 8);
                switch (szCommand[3])
                {
                    case DC_DEV_DESC_TYPE:
                        DEBUG_OUT(("dcRxCommand: Get Device Descriptor\n"));
                        dcWriteEndpoint(EP0TX, gDeviceDesc, MIN(wLength, sizeof(gDeviceDesc)));
                        break;
                        
                    case DC_CONFIG_DESC_TYPE:
                        DEBUG_OUT(("dcRxCommand: Get Configuration Descriptor\n"));
                        dcWriteEndpoint(EP0TX, gConfigDesc, MIN(wLength, sizeof(gConfigDesc)));
                        break;
                        
                    case DC_STRING_DESC_TYPE:
                        DEBUG_OUT(("dcRxCommand: Get String(ID=%d) Descriptor\n", szCommand[2]));
                        
                        switch (szCommand[2])
                        {
                            case 0: dcWriteEndpoint(EP0TX,gUnicodeString, sizeof(gUnicodeString)); break;
                            case 1: dcWriteEndpoint(EP0TX,gMfgString, sizeof(gMfgString)); break;
                            case 2: dcWriteEndpoint(EP0TX,gProductString, sizeof(gProductString)); break;
                            case 3: dcWriteEndpoint(EP0TX,gSerialString, sizeof(gSerialString)); break;
                            default: DEBUG_OUT(("dcRxCommand: Unsupported string desc %d\n", szCommand[2])); dcStallEndpoint(EP0RX); break;
                        } 
                        break; 
                        
                    case DC_CONFIG_QUALIFIER_TYPE:
                    default:
                        DEBUG_OUT(("dcRxCommand: Unsupported: get descriptor request %d\n", szCommand[3]));
                        dcStallEndpoint(EP0TX);
                        break;
                }
            }
            break;

            case GET_STATUS:
            {
                unsigned char status[2] = { 0x01, 0x00 };    /* Self powered */
                DEBUG_OUT(("dcRxCommand: get status\n"));
                dcWriteEndpoint(EP0TX, status, sizeof(status));
                dcAckEndpoint(EP0TX);
            }
            
            case SET_CONFIGURATION:
            {
                DEBUG_OUT(("dcRxCommand: set configuration %d\n", szCommand[2]));
                dcAckEndpoint(EP0TX);
            }
            break;

            case SET_INTERFACE:
            {
                DEBUG_OUT(("dcRxCommand: set interface %d\n", szCommand[2]));
                dcAckEndpoint(EP0TX);
            }
            break;

            case SET_ADDRESS:
            {
                DEBUG_OUT(("dcRxCommand: set address %d\n", szCommand[2]));
                alt_u8 Address = szCommand[2] & 0x7F;
                HAL1761_RegWrite32(DC_ADDRESS, (0x80 | Address));
                dcAckEndpoint(EP0TX);
            }
            break;
            
            default:
            {
                DEBUG_OUT(("dcRxCommand: Unsupported: standard request %d\n", szCommand[1]));
                dcStallEndpoint(EP0TX);
            }
            break;
        }
    }
    else
    {
        dcStallEndpoint(EP0TX);
        DEBUG_OUT(("dcRxCommand: Unsupported: control OUT packet\n"));
    }
} 

void dcTxCommand()
{
    dcAckEndpoint(EP0RX);
}

void dcStallEndpoint(alt_u8 endpoint)
{
    HAL1761_RegWrite32(DC_EP_INDEX, endpoint);        
    HAL1761_RegWrite32(DC_CONTROL_FUNC, CONTROL_FUNC_STALL);
}

void dcAckEndpoint(alt_u8 endpoint)
{
    HAL1761_RegWrite32(DC_EP_INDEX, endpoint);        
    HAL1761_RegWrite32(DC_CONTROL_FUNC, CONTROL_FUNC_STATUS_EM_ACK);
}
 

void dcRxData(alt_u8 endpoint)
{   
    int new_length = dcGetBufferLength(endpoint);        

    g_transfer_status.bytes_received += new_length;

    DEBUG_ISR(("dcRxData: new %d total %d\n", new_length, g_transfer_status.bytes_received));
}


void dcTxData(alt_u8 endpoint)
{
    g_transfer_status.bytes_sent = 0;
}


void dcProcessEvent(void)
{
    alt_u32 Event, NoneCtrlEpEvent;
    
    Event = HAL1761_RegRead32(DC_INTERRUPT);
    if (!Event)
    {
        return;
    }
    HAL1761_RegWrite32(DC_INTERRUPT, Event);
            
    NoneCtrlEpEvent = Event & IE_NON_CTRL_EP_MASK;
    Event &= ~IE_NON_CTRL_EP_MASK;
    
    alt_u8 Handle = EP1RX;
    while(NoneCtrlEpEvent)
    {
        alt_u32 EpEvent = IE_EP0RX << Handle; 
        
        if (NoneCtrlEpEvent & EpEvent)
        {
            NoneCtrlEpEvent &= ~EpEvent;
            if (Handle & 0x01)
            {
                dcTxData(Handle);
            }
            else
            {
                dcRxData(Handle);
            }
        }                                    

        Handle++;
    } 

    if (Event & IE_EP0SETUP)
    {
        Event &= ~IE_EP0SETUP;
        DEBUG_ISR(("IE_EP0SETUP\n"));
        dcRxCommand(TRUE);  /* Control SETUP */
    }
    
    if (Event & IE_EP0RX)
    {
        Event &= ~IE_EP0RX;
        DEBUG_ISR(("IE_EP0RX\n"));
        dcRxCommand(FALSE);  /* Control Out */
    }

    if (Event & IE_EP0TX)
    {
        Event &= ~IE_EP0TX;
        DEBUG_ISR(("IE_EP0TX\n"));
        dcTxCommand();
    }   
    
    if (Event & IE_RST)
    {
        Event &= ~IE_RST;   
        DEBUG_ISR(("IE_RST\n"));   
               
        dcReset();
        dcSetupEndpoints(FALSE);  
    }
    
    if (Event & IE_SUSP)
    {   
        Event &= ~IE_SUSP;  
        DEBUG_ISR(("IE_SUSP\n"));        
    }      
    
    if (Event & IE_RESM)
    {   
        Event &= ~IE_RESM;
        DEBUG_ISR(("IE_RESM\n"));
        
        HAL1761_RegWrite32(DC_UNLOCK_DEVICE, 0xAA37);                            
    }         
    
    if (Event & IE_VBUS)
    {
        Event &= ~IE_VBUS;
        DEBUG_ISR(("IE_VBUS\n"));       
    }        
    
    if (Event & IE_HS_STA)
    {
        //High speed detected
        Event &= ~IE_HS_STA;
        DEBUG_ISR(("IE_HS_STA\n"));
  
        dcSetupEndpoints(TRUE);      
    }  
    
    if (Event & IE_DMA)
    {     
        DEBUG_ISR(("IE_DMA\n"));         
    }               
} 


//======== Fundametal USB Register Access Function =======

void HAL1761_RegWrite32(alt_u32 Addr, alt_u32 data32)
{
    IOWR(ISP1761_BASE, Addr, data32);  
}

alt_u32 HAL1761_RegRead32(alt_u32 Addr)
{
    alt_u32 data32;
    data32 = IORD(ISP1761_BASE, Addr);
    return data32;
}

void HAL1761_DeviceDataRead(alt_u32 DataPort, alt_u8 szData[], alt_u32 ByteLen)
{
    alt_u32 data32;
    int i= 0, k = 4;
    while(i < ByteLen)
    {
        data32 = IORD(ISP1761_BASE, DataPort);
        if (ByteLen - i < 4)
            k = ByteLen - i;
        memcpy(szData+i, &data32, k);
        i = i + 4;
    }        
}   

void HAL1761_DeviceDataWrite(alt_u32 DataPort, alt_u8 szData[], alt_u32 ByteLen)
{
    alt_u32 data32;
    int i= 0, k = 4;
    while(i < ByteLen)
    {
        if (ByteLen - i < 4)
            k = ByteLen - i;
        memcpy(&data32, szData+i, k);
        IOWR(ISP1761_BASE, DataPort, data32);
        i = i + k;
    }        
}  
