
/**
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.
Copyright (c) 2001. Samsung Electronics, co. ltd  All rights reserved.

Module Name:  

Abstract:

    S3C2440 USB function(ACTIVE SYNC) device driver (Chip Layer)

rev:
	2003.3.18	: EP3 is replaced by EP4 for DMA operation because DMA2(EP3) is used by audio. (purnnamu)
	2003.3.17	: Every USBD interrupt is followed by the dummy interrupt 
				  because USBD sub-pending interrupt is cleared after clearing INTPND register.
				  So,USBD sub-pending bit is cleared before clearing INTPND register. (purnnamu)
    2002.5.7	: Add to s3c2410_code (Seung-han, Lim)
    2002.1.22	: First release/no error recovery (kwangyoon LEE, kwangyoon@samsung.com)
Notes: 
*/

#include <windows.h>
#include <types.h>
#include <ceddk.h>
#include <memory.h>
#include <serhw.h>
#include <nkintr.h>


#include "S2440.h"
#include <SC2440_usb_hw.h>
#include <SC2440_usb_ser.h>
#undef ZONE_INIT
#include <serdbg.h>
#include <drv_glob.h> //:-)
#include <2440usb.h>  //:-)

#define USBDMSG		 DEBUGMSG  //RETAILMSG
//#define DEBUGMSG RETAILMSG 
#define ZONE_ERROR 1
#define ZONE_IR 1

PDRIVER_GLOBALS v_pDriverGlobals=NULL; //:-)
volatile USBD_GLOBALS *usbdShMem=NULL;  //:-)
volatile DMAreg *v_pDMAregs=NULL;  //:-)
volatile INTreg *v_pINTregs=NULL;  //:-)
ULONG realPhysicalAddr_UsbdRxBuf=0;

ULONG totalRxCnt=0; //debug
/*
#if (USBD_GLOBALS_BUF_SIZE!=0x4000)
	STOP_COMPILE GEN_ERROR
	//drv_glob.h should be changed correctly
#endif	
*/

// This driver defines a simple BULK FIFO device. It supports only one
// interface with one alternate setting, as shown below.  
//
//  Endpoint    Setting 0
//------------------------
//  EP0         Control
//  EP1 In      Bulk  
//  EP3 Out     Bulk   

// Steal the useless IR zone from MDD and use it as EP0 & USB specific stuff
 
#define ZONE_USB ZONE_IR

// Define the configuration descriptor length
 
#define CFGLEN  32   
#define iCONF   18
#define TLEN    (CFGLEN + 18)

// Define the configuration descriptor itself

const BYTE uStd[TLEN]=  {
	0x12,					//  0 desc size                      
	DEVICE,					//  1 desc type (DEVICE)             
	0x00,					//  2 USB release                    
	0x01,					//  3 => 1.00                        
	0xff,					//  4 class                          
	0xff,					//  5 subclass                       
	0xff,					//  6 protocol                       
	EP0_MAXP_SIZE,				//  7 max pack size                  
	0x47,					//  8 vendor ID LSB (Samsung Semi)   
	0x05,					//  9 vendor ID MSB ( 0x1419 )       
	0x20,					// 10 product ID LSB                 
	0x27,					// 11 product ID MSB                 
	0x00,					// 12 device release LSB             
	0x00,					// 13 device release MSB             
	0x00,					// 14 manufacturer string desc index 
	0x00,					// 15 product string desc index      
	0x00,					// 16 serial num string desc index   
	0x01,					// 17 num of possible configurations 

	// Configuration Descriptor 
	0x09,					//  desc size                            
	CONFIGURATION,			//  desc type (CONFIGURATION)            
	CFGLEN%256,				//  total length of data returned LSB    
	CFGLEN/256,				//  total length of data returned MSB    
	0x01,					//  num of interfaces                    
	0x01,					//  value to select config (1 for now)   
	0x00,					//  index of string desc ( 0 for now)    
	0x80,					//  bus powered                          
	25,						//  max power, 50mA for now    

	// Interface Decriptor 
	0x09,					//  desc size                            
	INTERFACE,				//  desc type (INTERFACE)                
	0x00,					//  interface index.                     
	0x00,					//  value for alternate setting          
	0x02,					//  bNumEndpoints (number endpoints used, excluding EP0)
	0xff,
	0xff,
	0xff,
	0x00,					//  string index,      

	// Endpoint descriptor (EP 1 Bulk IN) 
	0x07,					//  desc size                            
	ENDPOINT,				//  desc type (ENDPOINT)                 
	0x81,					//  endpoint address: endpoint 1, IN    
	0x02,					//  endpoint attributes: Bulk       
	EP1_IN_MAXP_SIZE,			//  max packet size LSB                  
	0x00,					//  max packet size MSB                  
	0x00,					//  polling interval (4ms/bit=time,500ms)  

#if	0
	// Endpoint descriptor (EP 2 Interrupt IN) 
	0x07,					//  desc size                            
	ENDPOINT,				//  desc type (ENDPOINT)                 
	0x82,					//  endpoint address: endpoint 2, IN     
	0x03,					//  endpoint attributes: Interrupt       
	EP2_IN_MAXP_SIZE,			//  max packet size LSB                  
	0x00,					//  max packet size MSB                  
	0xFA,					//  polling interval (4ms/bit=time,100ms)  
#endif

	// Endpoint descriptor (EP 4 Bulk OUT) 
	0x07,					//  desc size                            
	ENDPOINT,				//  desc type (ENDPOINT)                 
	0x04,					//  endpoint address: endpoint 4, OUT    
	0x02,					//  endpoint attributes: Bulk            
	EP4_OUT_MAXP_SIZE,			//  max packet size LSB                  
	0x00,					//  max packet size MSB                  
	0x00					//  polling interval (4ms/bit=time,500ms)  

#if	0
	// Endpoint descriptor (EP 4 Interrupt OUT) 
	0x07,					//  desc size                            
	ENDPOINT,				//  desc type (ENDPOINT)                 
	0x04,					//  endpoint address: endpoint 4, OUT    
	0x03,					//  endpoint attributes: Interrupt            
	EP4_OUT_MAXP_SIZE,			//  max packet size LSB                  
	0x00,					//  max packet size MSB                  
	0x00					//  polling interval (4ms/bit=time,500ms)   // $$$$
#endif
};

// Register writes need to be verified. This macro loops the write until
// the effects are visible and records the number of retries.

#define UDC_REG_WRITE(_struct,_ptr,_field,_val) IOW_REG_FIELD(_struct,_ptr,_field,_val)

#define UDC_REG_BITSET(_struct,_ptr,_field,_val) { _struct xx;														\
  					 	   *(unsigned int *)&xx = 0;										\
  						   xx._field = _val;												\
  						   IOW_REG_SET(_struct,_ptr,*(unsigned int*)&xx); }

#define UDC_REG_WRITEX(_setptr,_setval)		(_setptr) = _setval;

// Variables for EP0 resend control. Keep track of last packet sent.

static char *sendPacket;
static int  sendPacketLength;
static int  sendTotalLength;
static char *savSendPacket;
static int  savSendPacketLength;

// EP1/EP3 packet size. For polling this can not be greater than 16

static unsigned int maxPacketSize = EP1Len;  

// Read the command from the endpoint 0 FIFO 

void HW_USBClocks(PSER_INFO pHWHead);


static
int getCommand(PSER_INFO pHWHead, void *argP)
{
	unsigned char *bufP = (unsigned char*)argP;
	int length	    = pHWHead->pUSBCtrlAddr->OFCR1.out_cnt_low | pHWHead->pUSBCtrlAddr->OFCR2.out_cnt_high<<8;
	int i;

	memset(bufP, 0x55, 8);

	// All setup commands are 8 bytes in length
	if (length != 8) 
	{
		DEBUGMSG(1, (TEXT("UDC bad command length %d\n"), length));
		return 1;
	}
	
	DEBUGMSG(1, (TEXT("[GETCOMMAND : ")));

	for (i=0; i < length; i++) 
	{
		bufP[i] = (BYTE)pHWHead->pUSBCtrlAddr->EP0F.fifo_data;
		DEBUGMSG(1, (TEXT("%2x "), bufP[i]));
	}

	DEBUGMSG(1, (TEXT("]\r\n")));	
	
	return 0;
}

/*************************************************************************
	Command response with data packets
*************************************************************************/
static
void sendData(PSER_INFO pHWHead, void *argP, int length, int totalLen)
{

	unsigned char *bufP = (unsigned char*)argP;
	struct EP0ICSR1Bits EP0ICSR1;
	int i;

	// Endpoint0 mode
	pHWHead->pUSBCtrlAddr->INDEX.index = 0x0;

	DEBUGMSG(1, (TEXT("[UDC WILL SEND] %d bytes\r\n"), length));

	// Send the data back. The FIFO should be turned around for
	// us already. Just blast away ...
 	for (i=0; (i < length) && (i < EP0Len); i++) 
	{
		IOW_REG_FIELD(struct EP0FBits, 
			&pHWHead->pUSBCtrlAddr->EP0F, fifo_data, bufP[i]);
	}

	// Form the register update. Always set IN_PKT_RDY.
	EP0ICSR1 = pHWHead->pUSBCtrlAddr->EP0ICSR1;
	EP0ICSR1.ipr_ = 1;

	// We can complete on a packet which is full. We need to wait till
	// next time and generate a zero length packet, so only complete
	// if we're at the end and it is not the max packet size.
	if ((i == length) && (i != EP0Len)) 
	{
		savSendPacket       = sendPacket;
		savSendPacketLength = sendPacketLength;
		sendPacket          = NULL;
		sendPacketLength    = length - i;
		sendTotalLength     = totalLen - i;
		EP0ICSR1.de_ff = 1;
	}
	// Need to send another packet.
	else {
		sendPacket          = (char*)&bufP[i];
		sendPacketLength    = length - i;
		sendTotalLength     = totalLen - i;
		savSendPacket       = sendPacket;
		savSendPacketLength = sendPacketLength;

		DEBUGMSG(1, (TEXT("UDC WILL SEND %d more bytes later\r\n"), 
							sendPacketLength));
	}

	/* Update the register
	*/
	UDC_REG_WRITEX(*(volatile unsigned *)&pHWHead->pUSBCtrlAddr->EP0ICSR1, 
	 						*(unsigned *)&EP0ICSR1);
}

/*************************************************************************
	Send command done
*************************************************************************/
static
void sendCommandDone(PSER_INFO pHWHead)
{
	BYTE index;

	// Endpoint0 mode
    index = pHWHead->pUSBCtrlAddr->INDEX.index;
    pHWHead->pUSBCtrlAddr->INDEX.index = 0x0;

    // Set SERVICE_OUT_PKY_RDY bit
    pHWHead->pUSBCtrlAddr->EP0ICSR1.sopr_cdt = 0x1;
    
    pHWHead->pUSBCtrlAddr->INDEX.index = index;
}

/*************************************************************************
	Command response with no data packets
*************************************************************************/
static
void sendDone(PSER_INFO pHWHead, int err)
{
    struct EP0ICSR1Bits EP0ICSR1;
    BYTE index;
    
    EP0ICSR1 = pHWHead->pUSBCtrlAddr->EP0ICSR1;
    EP0ICSR1.sopr_cdt   = 1;
    EP0ICSR1.de_ff      = 1;
    EP0ICSR1.sts_ur     = (BYTE)err;

    // Endpoint 0 mode
    index = pHWHead->pUSBCtrlAddr->INDEX.index;
    pHWHead->pUSBCtrlAddr->INDEX.index = 0x0;

    if (err) {
        DEBUGMSG(ZONE_ERROR, (TEXT("SENT_STALL CLEAR\r\n")));
    }

    UDC_REG_WRITEX( *(volatile DWORD *)&pHWHead->pUSBCtrlAddr->EP0ICSR1,
                    *(DWORD *)&EP0ICSR1 );

    pHWHead->pUSBCtrlAddr->INDEX.index = index;
}

/*************************************************************************
	Set the device address
*************************************************************************/
static
void setAddress(PSER_INFO pHWHead, int addr)
{
    DEBUGMSG(ZONE_USB, (TEXT("[SET ADDRESS to %x]\r\n"), addr));

    pHWHead->pUSBCtrlAddr->udcFAR.func_addr = (BYTE)addr;
    pHWHead->pUSBCtrlAddr->udcFAR.addr_up = 1;
}

/*************************************************************************
	SC2440_USB_Init

	Initialize the UDC.
*************************************************************************/
extern
void SC2440_USB_Init(PSER_INFO pHWHead)
{
	BYTE index;
	int i;

    EnterCriticalSection(&pHWHead->HwRegCritSec);

	USBDMSG(1, (TEXT("++SC2440_USB_Init\r\n")));
	//RETAILMSG(1, (TEXT("++SC2440_USB_Init\r\n")));

	//DebugBreak();;;
	pHWHead->dConfIdx    = 0;
	pHWHead->dSetting    = 0;
	pHWHead->dInterface  = 0;
	pHWHead->dAddress    = 0;
	pHWHead->ModemStatus = 0;		// All lines low till we get connected.
	pHWHead->wSOFStableCnt = 0;

_re_enable:
    switch (pHWHead->State) {
        case IDLE:
            //RETAILMSG(1, (TEXT("IDLE\r\n")));
            //
            // Enable the USB Clocks
            //
			HW_USBClocks(pHWHead);
		break;

        case OFF:
            //RETAILMSG(1, (TEXT("OFF\r\n")));
            //
            // Disable the USB Clocks
            //
			HW_USBClocks(pHWHead);

            DEBUGMSG(ZONE_INIT, (TEXT("rUPLLCON: 0x%X\r\n"), pHWHead->pCLKPWR->rUPLLCON));
            goto _done;
            break;

        case SUSPEND:
            // should never get here
            //RETAILMSG(1, (TEXT("SUSPEND\r\n")));
			ASSERT(0);
            goto _done;
            break;

        case RESUME:
            // Suspend disconnects us from the USB.
            // When we Resume we re-enumerate on the USB.
            //RETAILMSG(1, (TEXT("RESUME => ")));

        default:            
            pHWHead->State = IDLE;
            goto _re_enable;
            break;
    }
    
    //
    // Initialize the USBD Controller
    //
    index = pHWHead->pUSBCtrlAddr->INDEX.index;
    
    // suspend mode disable
    pHWHead->pUSBCtrlAddr->PMR.sus_en = 0x0;
    ASSERT(pHWHead->pUSBCtrlAddr->PMR.sus_en == 0x0);

    // setup endpoint 0
    pHWHead->pUSBCtrlAddr->INDEX.index = 0;
    ASSERT(pHWHead->pUSBCtrlAddr->INDEX.index == 0);

    pHWHead->pUSBCtrlAddr->MAXP.maxp = 0x1;         // 8 BYTE
    ASSERT(pHWHead->pUSBCtrlAddr->MAXP.maxp == 0x1);

    pHWHead->pUSBCtrlAddr->EP0ICSR1.sopr_cdt = 1;   // OUT_PKT_RDY
    //ASSERT(pHWHead->pUSBCtrlAddr->EP0ICSR1.sopr_cdt == 1); // W only

    pHWHead->pUSBCtrlAddr->EP0ICSR1.sse_ = 1;       // SETUP_END
    //ASSERT(pHWHead->pUSBCtrlAddr->EP0ICSR1.sse_ == 1); // W only

    // setup endpoint 1
    pHWHead->pUSBCtrlAddr->INDEX.index = 1;
    ASSERT(pHWHead->pUSBCtrlAddr->INDEX.index == 1);
        
    pHWHead->pUSBCtrlAddr->MAXP.maxp = 0x8;     // 64 BYTE
    ASSERT(pHWHead->pUSBCtrlAddr->MAXP.maxp == 0x8);
    
    pHWHead->pUSBCtrlAddr->ICSR2.mode_in = 1;   // IN
    ASSERT(pHWHead->pUSBCtrlAddr->ICSR2.mode_in == 1);
    
    pHWHead->pUSBCtrlAddr->ICSR2.iso = 0;       // BULK
    ASSERT(pHWHead->pUSBCtrlAddr->ICSR2.iso == 0);

    // setup endpoint 4
    pHWHead->pUSBCtrlAddr->INDEX.index = 4;
    ASSERT(pHWHead->pUSBCtrlAddr->INDEX.index == 4);
    
    pHWHead->pUSBCtrlAddr->MAXP.maxp = 0x8;     // 64 BYTE
    ASSERT(pHWHead->pUSBCtrlAddr->MAXP.maxp == 0x8);
    
    pHWHead->pUSBCtrlAddr->ICSR2.mode_in = 0;   // OUT
    ASSERT(pHWHead->pUSBCtrlAddr->ICSR2.mode_in == 0);
    
    pHWHead->pUSBCtrlAddr->OCSR2.iso = 0;       // BULK
    ASSERT(pHWHead->pUSBCtrlAddr->OCSR2.iso == 0);

    // clear all EP interrupts
    pHWHead->pUSBCtrlAddr->EIR.ep0_int = 0x1;
    ASSERT(pHWHead->pUSBCtrlAddr->EIR.ep0_int == 0);
    
    pHWHead->pUSBCtrlAddr->EIR.ep1_int = 0x1;
    ASSERT(pHWHead->pUSBCtrlAddr->EIR.ep1_int == 0);
    
    pHWHead->pUSBCtrlAddr->EIR.ep2_int = 0x1;
    ASSERT(pHWHead->pUSBCtrlAddr->EIR.ep2_int == 0);
    
    pHWHead->pUSBCtrlAddr->EIR.ep3_int = 0x1;
    ASSERT(pHWHead->pUSBCtrlAddr->EIR.ep3_int == 0);
        
    pHWHead->pUSBCtrlAddr->EIR.ep4_int = 0x1;
    ASSERT(pHWHead->pUSBCtrlAddr->EIR.ep4_int == 0);

    // clear reset int
    pHWHead->pUSBCtrlAddr->UIR.reset_int = 0x1;
    ASSERT(pHWHead->pUSBCtrlAddr->UIR.reset_int == 0x0);

    // EP0, 1, & 4 Enabled, EP2, 3 Disabled

    pHWHead->pUSBCtrlAddr->EIER.ep2_int_en = 0x0;
    ASSERT(pHWHead->pUSBCtrlAddr->EIER.ep2_int_en == 0x0);
    
    pHWHead->pUSBCtrlAddr->EIER.ep3_int_en = 0x0;
    ASSERT(pHWHead->pUSBCtrlAddr->EIER.ep3_int_en == 0x0);

    pHWHead->pUSBCtrlAddr->EIER.ep0_int_en = 0x1;
    ASSERT(pHWHead->pUSBCtrlAddr->EIER.ep0_int_en == 0x1);
    
    pHWHead->pUSBCtrlAddr->EIER.ep1_int_en = 0x1;
    ASSERT(pHWHead->pUSBCtrlAddr->EIER.ep1_int_en == 0x1);
    
    pHWHead->pUSBCtrlAddr->EIER.ep4_int_en = 0x1;
    ASSERT(pHWHead->pUSBCtrlAddr->EIER.ep4_int_en == 0x1);

    // enable reset int 
    pHWHead->pUSBCtrlAddr->UIER.reset_int_en = 0x1;
    ASSERT(pHWHead->pUSBCtrlAddr->UIER.reset_int_en == 0x1);


	if(v_pDMAregs==NULL)
	{
		//RETAILMSG(1, (TEXT("ERROR: SC2440_usb_hw: v_pDMAregs=NULL\r\n")));
		while(1);
	}
	
	pHWHead->pUSBCtrlAddr->INDEX.index = 0x4;	

	*(volatile BYTE *)&pHWHead->pUSBCtrlAddr->EP4DC=0x0; 
	    //may not be needed, not to operate DMA during initialization.
	UsbdInitDma3(0,0);

	usbdShMem->usbdRxRdPt=0;
	usbdShMem->usbdRxWrPt=0;
	usbdShMem->usbdRxCnt=0;
	usbdShMem->usbdEir=0;
 	usbdShMem->usbdUir=0;
	usbdShMem->usbdDma3Int=0; 

    //rEP4_DMA_FIFO=0x40; //not needed for OUT operation.	

	*(volatile BYTE *)&pHWHead->pUSBCtrlAddr->EP4DTL=USBD_GLOBALS_BUF_SIZE&0xff;
	*(volatile BYTE *)&pHWHead->pUSBCtrlAddr->EP4DTM=(USBD_GLOBALS_BUF_SIZE>>8)&0xff;
	*(volatile BYTE *)&pHWHead->pUSBCtrlAddr->EP4DTH=(USBD_GLOBALS_BUF_SIZE>>16)&0x0f;

	*(volatile BYTE *)&pHWHead->pUSBCtrlAddr->OCSR2=
	*(BYTE *)&pHWHead->pUSBCtrlAddr->OCSR2 | (BYTE)EPO_AUTO_CLR /*| EPO_OUT_DMA_INT_MASK*/;  
	//AUTO_CLR(OUT_PKT_READY is cleared automatically), interrupt_masking.

	*(volatile BYTE *)&pHWHead->pUSBCtrlAddr->EP4DU=0x01; //DMA transfer unit=1byte
	*(volatile BYTE *)&pHWHead->pUSBCtrlAddr->EP4DC=UDMA_OUT_DMA_RUN|UDMA_DMA_MODE_EN;
		//deamnd disable,out_dma_run=run,in_dma_run=stop,DMA mode enable

	//wait until DMA_CON is effective.
	*(volatile BYTE *)&pHWHead->pUSBCtrlAddr->EP4DC;
	for(i=0;i<10;i++);	  


	v_pINTregs->rINTMSK&=~(BIT_DMA3);  //enable DMA3 interrupt

	pHWHead->pUSBCtrlAddr->INDEX.index = 0x0;	//del me
	
_done:
	LeaveCriticalSection(&pHWHead->HwRegCritSec);
    
	USBDMSG(1, (TEXT("--SC2440_USB_Init\r\n")));
	//RETAILMSG(1, (TEXT("--SC2440_USB_Init\r\n")));
}

/*************************************************************************
Process Endpoint 0 interrupt. We are called here on every EIR interrupt.
We need to check the EP0 status and send the next packet, if any. We need
to handle errors as follows:

SE - This is set if we received a new command before we finished the last
	one. This happens when the host does not wish to read all reply data
	(it may only be interested in the first 8 bytes of a device descriptor
	to get the max packet size, for example). We will simply discard the
	next data to send and continue with the normal command handling. We
 	need to clear SE by asserting SSE before we continue.
 
SST - This is set when the UDC actually issues a stall. We just clear it
	and continue.

 Normal setup is as follows:

 OPR - This indicates a receive command packet is ready to be read. Commands
	are always 8 bytes in length and follow the form defined in chapter 9.
 
 If OPR is not set and IPR is not set and SE is not set we must be here
 because IPR transitioned from 1 -> 0. This means the last packet has been
 sent (ACK'd). Send the next packet, if any.

 We are emulating a serial line here. The state of the modem control lines
 are sent in a command packet. Extract these and set the modem status bits
 as for a normal serial line. This allows the upper levels to treat this
 link just like a serial line with modem control (no flow control is
 explicitly required and hence is not supported.
**************************************************************************/
void SC2440_USB_DoEndpoint0(PSER_INFO pHWHead, PDWORD pModemStatus)
{
	BYTE dbuf[8];
	WORD len, totLen;
	PUCHAR p;
	unsigned int cnt=0;

	struct EP0ICSR1Bits EP0ICSR1=pHWHead->pUSBCtrlAddr->EP0ICSR1;

	// Endpoint 0 mode
	UDC_REG_BITSET(struct INDEXBits, &pHWHead->pUSBCtrlAddr->INDEX, index, 0x0); 
	
	DEBUGMSG(1, (TEXT("++SC2440_USB_DoEndpoint0\r\n")));
	//RETAILMSG(1, (TEXT("++SC2440_USB_DoEndpoint0\r\n")));

	/* If there is data to send and the transmitter is open go send
	* the next packet. But, do not continue to send if the host has
	* aborted the transfer or sent a new one. We'll clean up below.
	*/
	if ((EP0ICSR1.se_sds == 0) && (EP0ICSR1.ipr_ == 0) && 
				(EP0ICSR1.opr_ipr == 0) && (sendPacket))
	{
		DEBUGMSG( 1, (TEXT("Send remaining data !!!\r\n")));
		
		sendData(pHWHead, sendPacket, sendPacketLength, sendTotalLength);
	}
	
	// If setup end, the host has aborted the last transfer. Kill any
	//remaining packets and continue;
	if (EP0ICSR1.se_sds) {
		sendPacketLength = 0;
		sendPacket = NULL;
		UDC_REG_BITSET(struct EP0ICSR1Bits, &pHWHead->pUSBCtrlAddr->EP0ICSR1, sse_, 1); 
	}

	// If stall ack, not sure what needs to be done here.
	if (EP0ICSR1.sts_ur) 
	{
		DEBUGMSG( 1, (TEXT("Stall ACK set\r\n")));
		UDC_REG_BITSET(struct EP0ICSR1Bits, &pHWHead->pUSBCtrlAddr->EP0ICSR1, sts_ur, 0);  // 0 -> clear
	}

	// If new command received ...

	DEBUGMSG(1, (TEXT("OUT_PKT_RDY = 0x%x\r\n"), EP0ICSR1.opr_ipr));
	// if (EP0ICSR1.opr_ipr) 
	if (EP0ICSR1.opr_ipr)
	{

		// New command with existing data. Not sure how this can happen but if
		// it does just clear the old data and continue.
		if (sendPacket)
	 		sendPacket = NULL;

		cnt = pHWHead->pUSBCtrlAddr->OFCR1.out_cnt_low | pHWHead->pUSBCtrlAddr->OFCR2.out_cnt_high << 8; 
		DEBUGMSG(1,(TEXT("UDC received setup, EP0ICSR1 %x, len %d\n"), 
					EP0ICSR1, cnt));
	
		// Read the command from the input FIFO
		if (getCommand(pHWHead, (void*)&pHWHead->dReq) != 0) 
		{
			sendDone(pHWHead,1);
		}
	
		DEBUGMSG(1, 
			(TEXT("bmRequest: %02x bRequest: %02x wValue: %04x wIndex: %04x wLength: %04x\n"), 
			pHWHead->dReq.bmRequest,
			pHWHead->dReq.bRequest,
			pHWHead->dReq.wValue,
			pHWHead->dReq.wIndex,
			pHWHead->dReq.wLength));
				
		// Decode and execute the command. We support two sets of commands, vendor
		// specific (modem control) and chapter 9 standard commands.
		if (pHWHead->dReq.bmRequest & 0x60)	// vendor or class command
		{ 		
			DEBUGMSG(1, (TEXT("Vendor/Class Command !!\n")));			
			
			if (SET_CONTROL_LINE_STATE == pHWHead->dReq.bRequest) 
			{
				// Host is notifying us of control line state.
				// wValue contains bitmask
				// 0 - DTR
				// 1 - RTS
				DEBUGMSG(1, (TEXT("SET_CONTROL_LINE_STATE %4.4X\r\n"),
					pHWHead->dReq.wValue));		
				
				if (pHWHead->dReq.wValue & 0x01)
					*pModemStatus |= (MS_DSR_ON|MS_RLSD_ON);  // DTR active, set DSR/RLSD
				else
					*pModemStatus &= ~(MS_DSR_ON|MS_RLSD_ON); // DTR clear, clr DSR/RLSD
	        
				if (pHWHead->dReq.wValue & 0x02) 
					*pModemStatus |= MS_CTS_ON;		  // RTS active, set CTS
				else
					*pModemStatus &= ~MS_CTS_ON;		  // RTS clear, clear CTS
			}			
			else {
				// Unknown vendor/class request
				DEBUGMSG(1,(TEXT("Unknown vendor/class request %2.2X\r\n"),
					pHWHead->dReq.bRequest));
			}			
			
			// Command is complete
			sendDone(pHWHead,0);
			return;
		}			
				
		// standard chapter 9 commands
		switch (pHWHead->dReq.bRequest) {
		case GET_STATUS:
			RETAILMSG(1, (TEXT("GET_STATUS\r\n")));
	
			dbuf[0] = 0;
			dbuf[1] = 0;
	
			// If this is a request to the endpoint return the stalled status.
			if (pHWHead->dReq.bmRequest == 0x82) {
				dbuf[0] = 0;
				switch (pHWHead->dReq.wIndex & 15) {
	
				// Control endpoint can't stall (we wouldn't get this message).
				case 0:
					break;			
		  
				// OUT endpoint. Return send_stall.
				case 1:
					UDC_REG_BITSET(struct INDEXBits, &pHWHead->pUSBCtrlAddr->INDEX, index, 0x4); 
					if (pHWHead->pUSBCtrlAddr->OCSR1.send_stall)
						dbuf[0] = 1;
					break;
	
				// IN endpoint. Return send_stall.
				case 2:
				//case 3:
				case 4:
					UDC_REG_BITSET(struct INDEXBits, &pHWHead->pUSBCtrlAddr->INDEX, index, 0x1); 
					if (pHWHead->pUSBCtrlAddr->EP0ICSR1.se_sds)
						dbuf[0] = 1;
					break;
				}
				UDC_REG_BITSET(struct INDEXBits, &pHWHead->pUSBCtrlAddr->INDEX, index, 0);
			}			
				
			// Clear the OPR bit and start the data phase
			sendCommandDone(pHWHead);
	
			// Send the reply data.
			sendData(pHWHead, dbuf, 2, 2);
			//RETAILMSG(1, (TEXT("GS[0x%X]\r\n"), pHWHead->dReq.wIndex & 0xFF));
			break;				
	
		case CLEAR_FEATURE:
			DEBUGMSG(1, (TEXT("CLEAR_FEATURE\r\n")));
	
			if (pHWHead->dReq.bmRequest == 0x02) {		// Halt 
				switch (pHWHead->dReq.wIndex & 15) {
				// Control endpoint. Not suggested.
				case 0:
					break;
	
				// OUT endpoint. Set and clear sendstall to reset DATA0/DATA1.
				case 1:
					UDC_REG_BITSET(struct INDEXBits, &pHWHead->pUSBCtrlAddr->INDEX, index, 0x4); 
					UDC_REG_BITSET(struct OCSR1Bits,
						&pHWHead->pUSBCtrlAddr->OCSR1,send_stall,1);	// STALL!!!
					UDC_REG_BITSET(struct OCSR1Bits,
						&pHWHead->pUSBCtrlAddr->OCSR1,send_stall,0);
					UDC_REG_BITSET(struct OCSR1Bits,
						&pHWHead->pUSBCtrlAddr->OCSR1,sent_stall,0);	// CLEAR STALL
					break;
	
				// IN endpoint. Set and clear sendstall to reset DATA0/DATA1.
				case 2:
				//case 3:
				case 4:
					UDC_REG_WRITE(struct INDEXBits, &pHWHead->pUSBCtrlAddr->INDEX, index, 0x1); 
					UDC_REG_BITSET(struct EP0ICSR1Bits,
						&pHWHead->pUSBCtrlAddr->EP0ICSR1,se_sds,1);	// STALL!!!
					UDC_REG_BITSET(struct EP0ICSR1Bits,
						&pHWHead->pUSBCtrlAddr->EP0ICSR1,se_sds,0);
					UDC_REG_BITSET(struct EP0ICSR1Bits,
						&pHWHead->pUSBCtrlAddr->EP0ICSR1,sds_sts,0);	// CLEAR STALL
					break;
				}
				UDC_REG_BITSET(struct INDEXBits, &pHWHead->pUSBCtrlAddr->INDEX, index, 0);
			}
	
			// Command is complete
			sendDone(pHWHead,0);
			//RETAILMSG(1, (TEXT("CF[0x%X]\r\n"), pHWHead->dReq.wIndex & 0xFF));
			break;
	
		case SET_FEATURE:
			DEBUGMSG(1, (TEXT("SET_FEATURE %d\r\n"), pHWHead->dReq.bmRequest));
	
			if (pHWHead->dReq.bmRequest == 0x02) {
				switch (pHWHead->dReq.wIndex & 15) {
				// Control endpoint. Not suggested.
				case 0:
					break;
	
				/* OUT endpoint. Set sendstall to force stall condition
				*/
				case 1:
					UDC_REG_BITSET(struct INDEXBits, &pHWHead->pUSBCtrlAddr->INDEX, index, 0x4); 
					UDC_REG_BITSET(struct OCSR1Bits,
						&pHWHead->pUSBCtrlAddr->OCSR1, send_stall, 1); // STALL
					break;
	
				/* IN endpoint. Set sendstall to force stall condition
				*/
				case 2:
				//case 3:
				case 4:
					UDC_REG_BITSET(struct INDEXBits, &pHWHead->pUSBCtrlAddr->INDEX, index, 0x1); 
					UDC_REG_BITSET(struct EP0ICSR1Bits, 
						&pHWHead->pUSBCtrlAddr->EP0ICSR1, se_sds, 1); // STALL
					break;
				}
				UDC_REG_BITSET(struct INDEXBits, &pHWHead->pUSBCtrlAddr->INDEX, index, 0);
			}
	
			// Command is complete
			sendDone(pHWHead,0);
			//RETAILMSG(1, (TEXT("SF[0x%X]\r\n"), pHWHead->dReq.wIndex & 0xFF));
			break;
	
		case SET_ADDRESS:
			RETAILMSG(1, (TEXT("SET_ADDRESS - 0x%X\r\n"), pHWHead->dReq.wValue));
			
			pHWHead->dAddress = (BYTE)pHWHead->dReq.wValue;
	
			// Note, this write needs to be started here but it will not read back
			// until the command completes. There is no retry mechanism.
			setAddress(pHWHead, pHWHead->dAddress);
	
			// Command is complete
			sendDone(pHWHead,0);
			break;
	
		case GET_DESCRIPTOR:
			switch ((BYTE)(pHWHead->dReq.wValue>>8)) {
			case DEVICE:	// 0x01
				RETAILMSG(1, (TEXT("GET_DESCRIPTOR:DEVICE 0x%X, 0x%X\r\n"), 
						uStd[0], pHWHead->dReq.wLength));
				//sendCommandDone(pHWHead);
				p = (PUCHAR)uStd;
				len = (BYTE)min(uStd[0],pHWHead->dReq.wLength);
				totLen = uStd[0];
				break;
	
			case CONFIGURATION:	// 0x02
				RETAILMSG(1,(TEXT("GET_DESCRIPTOR:CONFIGURATION\r\n")));
				//sendCommandDone(pHWHead);
				p = (PUCHAR)&uStd[iCONF];
				len = (BYTE)min(CFGLEN,pHWHead->dReq.wLength);
				totLen = CFGLEN;
				break;
	
			case STRING:	// 0x03
				DEBUGMSG(1,(TEXT("GET_DESCRIPTOR:STRING\r\n")));
				//sendCommandDone(pHWHead);
				p = NULL;
				len = 0;
				totLen = 0;
				break;
	
			default:
				DEBUGMSG(1,(TEXT("GET_DESCRIPTOR:Unknown %d\r\n"),
					(pHWHead->dReq.wValue>>8) ));
				sendCommandDone(pHWHead);
				p = NULL;
				len = 0;
				totLen = 0;
				break;		
	
			}
	
			// Clear the OPR bit and start the data phase
			sendCommandDone(pHWHead);
	
			// Send the reply data.
			sendData(pHWHead, p, len, totLen);
			DEBUGMSG(1,(TEXT("GET_DESCRIPTOR:EP0ICSR1 status value = 0x%x\r\n"), pHWHead->pUSBCtrlAddr->EP0ICSR1));
			break;
	
		case GET_CONFIG:
			RETAILMSG(1, (TEXT("GET_CONFIG\r\n")));
	
			// Clear the OPR bit and start the data phase
			sendCommandDone(pHWHead);
	
			// Send the reply data.
			sendData(pHWHead, &pHWHead->dConfIdx, 1, 1);
			break;
	
		case GET_INTERFACE:
			DEBUGMSG(1,(TEXT("GET_INTERFACE\r\n")));
	
			// Clear the OPR bit and start the data phase
			sendCommandDone(pHWHead);
	
			// Send the reply data.
			sendData(pHWHead, &pHWHead->dInterface, 1, 1);
			break; 
	
		case SET_CONFIG:
			RETAILMSG(1,(TEXT("SET_CONFIG %d\r\n"),
				pHWHead->dReq.wValue));
			pHWHead->dConfIdx = (BYTE)pHWHead->dReq.wValue;
	
			// Command done
			sendDone(pHWHead,0);
			break;
	
		case SET_DESCRIPTOR:
			RETAILMSG(1, (TEXT("SET_DESCRIPTOR\r\n")));
	
			// Command done
			sendDone(pHWHead,0);
			break;
	
		case SET_INTERFACE:
			DEBUGMSG(1,(TEXT("SET_INTERFACE : %d,%d\r\n"),
				pHWHead->dReq.wIndex,
				pHWHead->dReq.wValue));
			pHWHead->dInterface = (BYTE)pHWHead->dReq.wIndex;
			pHWHead->dSetting = (BYTE)pHWHead->dReq.wValue;
	
			// Command done
			sendDone(pHWHead,0);
			break;
	
		default:
			DEBUGMSG(1,(TEXT("Unhandled Command : %d\r\n"),
				pHWHead->dReq.bRequest));
	
			// Command done
			sendDone(pHWHead,0);
			break;
		}
	}
	
	DEBUGMSG(1, (TEXT("--SC2440_USB_DoEndpoint0\r\n")));
}

/*************************************************************************
	SC2440_USB_GetInterruptType

	Determine the source of an interrupt and return flags to control 
	which function gets called to handle it. EP0 is handled as modem 
	interrupt, EP1 as TX, EP3 as RX and reset is handle as line 
	interrupt.
*************************************************************************/

INTERRUPT_TYPE SC2440_USB_GetInterruptType( PSER_INFO pHWHead) //:-)
{
	INTERRUPT_TYPE interruptFlags = 0;
	//BYTE saveIndex;

    pHWHead->wPrevSOF = pHWHead->wSOF;
    pHWHead->wSOF = (pHWHead->pUSBCtrlAddr->FNR2.fr_n2 << 8) | pHWHead->pUSBCtrlAddr->FNR1.fr_n1; 
	DEBUGMSG(1, (TEXT("SOF: %u, wPrevSOF: %u\r\n"), pHWHead->wSOF, pHWHead->wPrevSOF));
	//RETAILMSG(1, (TEXT("SOF: %u, wPrevSOF: %u\r\n"), pHWHead->wSOF, pHWHead->wPrevSOF));

	//important notes
	// 1. INDEX register should be preserved here. //:-)
	// 2. 'else if' should not be used here. //:-) 

	if (usbdShMem->usbdEir& USBDEIR_EP1)	// EP1(IN), ready to TX more data.
	{
		interruptFlags |= INTR_TX;
	}
 	if (usbdShMem->usbdEir& USBDEIR_EP4) // EP4(OUT), RX data ready
	{
		interruptFlags |= INTR_RX;
	}

	if (usbdShMem->usbdDma3Int)	// DMA3 done interrupt
	{
		interruptFlags |= INTR_RX;
		USBDMSG(1, (TEXT("<D3Int>\r\n")));
	}

	if (usbdShMem->usbdUir& USBDUIR_RESET)// USB reset, so re-initialize our driver.
	{
		USBDMSG(1, (TEXT("RESET INT\r\n")));
		interruptFlags |= INTR_LINE;	// We use INTR_LINE to handle USB Reset 
		usbdShMem->usbdUir&=~USBDUIR_RESET;
	}	
	
	if (usbdShMem->usbdEir&USBDEIR_EP0) // EP0, Control endpoint
	{
		interruptFlags |= INTR_MODEM;	// Use INTR_MODEM for EP0 interrupts
										// call SerModemIntr function	 by shim 020503
		usbdShMem->usbdEir&=~USBDEIR_EP0;
		*(volatile BYTE *)&pHWHead->pUSBCtrlAddr->INDEX=0x0;  
			//temp_kiw, should be repaired officially.
		USBDMSG(1, (TEXT("<EP0 Int>\r\n")));	
	}

	return (interruptFlags);
}

/*************************************************************************
	SC2440_USB_LineIntHandler

	This handler is called for reset interrupts
*************************************************************************/
void SC2440_USB_LineIntHandler(PSER_INFO pHWHead)
{
	DEBUGMSG(1, (TEXT("++SC2440_USB_LineIntHandler\r\n")));

	SC2440_USB_Init(pHWHead);
	UDC_REG_BITSET(struct INDEXBits, &pHWHead->pUSBCtrlAddr->INDEX, index, 0);

	DEBUGMSG(1, (TEXT("--SC2440_USB_LineIntHandler\r\n")));
}

/*************************************************************************
  SC2440_USB_RxIntHandler
 
  Read characters up to the max packet length from the IN FIFO. Return the
  number of characters read in the supplied argument. The function returns
  a boolean indicating whether event characters are present.
*************************************************************************/


BOOL SC2440_USB_RxIntHandler(PSER_INFO pHWHead,
			     PUCHAR pRxBuffer,
			     ULONG *pBuffLen)
{
	BOOL fRXFlag	   = FALSE;
	UCHAR cEvtChar	   = pHWHead->dcb.EvtChar;
	int buflen		   = *pBuffLen;
	UCHAR cRxChar;
	ULONG usbdRxRdPt=0;
	ULONG usbdRxWrPt=0;	
	ULONG usbdRxCnt;
	BYTE* usbdRxBuf;
	ULONG cnt,saveCnt;
	int   swRdCnt=0;
	BYTE saveIndex;
	DEBUGMSG(1, (TEXT("++SC2440_USB_RxIntHandler\r\n")));
    
	usbdRxRdPt=usbdShMem->usbdRxRdPt;
	usbdRxBuf=(BYTE*)usbdShMem->usbdRxBuf;
	*pBuffLen = 0;
    
	// Endpoint 4 mode
	saveIndex=*(volatile BYTE *)&pHWHead->pUSBCtrlAddr->INDEX;
	*(volatile BYTE *)&pHWHead->pUSBCtrlAddr->INDEX=0x4;

	if (usbdShMem->usbdDma3Int)
	{

		//usbdRxWrPt=v_pDMAregs->rDCDST3 - realPhysicalAddr_UsbdRxBuf;
		USBDMSG(1,(TEXT("[RxPt:%d]"),usbdRxRdPt));	
		if(usbdRxRdPt==0)
		{
			usbdShMem->usbdDma3Int=0;	
			
			USBDMSG(1,(TEXT("[D:%d"),pHWHead->pUSBCtrlAddr->OFCR1.out_cnt_low));	
			// cnt=swRdCnt=pHWHead->pUSBCtrlAddr->OFCR1.out_cnt_low | pHWHead->pUSBCtrlAddr->OFCR1.out_cnt_high;
			swRdCnt=pHWHead->pUSBCtrlAddr->OFCR1.out_cnt_low | pHWHead->pUSBCtrlAddr->OFCR2.out_cnt_high;
			for(cnt=0; cnt < swRdCnt; cnt++)
			{
				usbdRxBuf[cnt]=(UCHAR)pHWHead->pUSBCtrlAddr->EP4F.fifo_data;
			}
			if((pHWHead->pUSBCtrlAddr->OFCR1.out_cnt_low | pHWHead->pUSBCtrlAddr->OFCR2.out_cnt_high)==0 && 
			   pHWHead->pUSBCtrlAddr->OCSR1.out_pkt_rdy==1)
			{
				USBDMSG(1, (TEXT(",OPR CLR,")));
				UDC_REG_BITSET(struct OCSR1Bits,&pHWHead->pUSBCtrlAddr->OCSR1, out_pkt_rdy, 0);
			}

			UsbdInitDma3(0,swRdCnt); 
			*(volatile BYTE *)&pHWHead->pUSBCtrlAddr->EP4DTL=(USBD_GLOBALS_BUF_SIZE-swRdCnt)&0xff;
			*(volatile BYTE *)&pHWHead->pUSBCtrlAddr->EP4DTM=((USBD_GLOBALS_BUF_SIZE-swRdCnt)>>8)&0xff;
			*(volatile BYTE *)&pHWHead->pUSBCtrlAddr->EP4DTH=((USBD_GLOBALS_BUF_SIZE-swRdCnt)>>16)&0x0f;
			*(volatile BYTE *)&pHWHead->pUSBCtrlAddr->EP4DC=UDMA_OUT_DMA_RUN|UDMA_DMA_MODE_EN;
		}

	}
	
	usbdRxWrPt=v_pDMAregs->rDCDST3 - realPhysicalAddr_UsbdRxBuf;
	if(usbdRxWrPt==USBD_GLOBALS_BUF_SIZE)
	{
		usbdRxWrPt=swRdCnt;
		//usbdRxWrPt=0;
	}
	
	if(usbdRxRdPt<=usbdRxWrPt)
		usbdRxCnt=usbdRxWrPt-usbdRxRdPt;
	else
		usbdRxCnt=USBD_GLOBALS_BUF_SIZE-usbdRxRdPt+usbdRxWrPt;	

	if( buflen > usbdRxCnt)
		saveCnt=cnt=usbdRxCnt;
	else
		saveCnt=cnt=buflen;

	if (usbdRxCnt == 0) // No data to read
	{ 
		USBDMSG(1, (TEXT("[R0]")));
	}	 
	else
	{

		USBDMSG(1, (TEXT("<%d>"),cnt));

		while(cnt--) 
		{
			*pRxBuffer++ = cRxChar = (UCHAR)usbdRxBuf[usbdRxRdPt++];
			if(usbdRxRdPt==USBD_GLOBALS_BUF_SIZE)
				usbdRxRdPt=0;
			if( cRxChar == cEvtChar )
				fRXFlag = TRUE;
		}

		(*pBuffLen)=saveCnt;
		usbdRxCnt-=saveCnt;

		usbdShMem->usbdRxRdPt=usbdRxRdPt;
		usbdShMem->usbdEir&=~USBDEIR_EP4;
	}

    //not be needed. Only for test
	if(pHWHead->pUSBCtrlAddr->OFCR1.out_cnt_low==0 ) 
		if(pHWHead->pUSBCtrlAddr->OCSR1.out_pkt_rdy==1)
		{
		    USBDMSG(1, (TEXT("__OPR__(NEVER EXECUTED)")));
			UDC_REG_BITSET(struct OCSR1Bits,&pHWHead->pUSBCtrlAddr->OCSR1, out_pkt_rdy, 0);
		}

	// If we didn't read the whole buffer we need to come back. Leave with
	// the interrupt still enabled so the MDD will recall.
	if (usbdRxCnt>0)
	{
		USBDMSG(1, (TEXT("[%d->MDD]"),usbdRxCnt));
		usbdShMem->usbdEir|=USBDEIR_EP4; 
			// MDD will recall RxIntHandler by chekcing SC2440_USB_GetInterruptType().
			// So, usbdShMem->usbdEir:USBDEIR_EP3 must not be cleared	//:-) 
		//Sleep(100);
		
		*(volatile BYTE *)&pHWHead->pUSBCtrlAddr->INDEX=saveIndex;
		return fRXFlag;
	}
	usbdShMem->usbdEir&=~USBDEIR_EP4;
	DEBUGMSG(1, (TEXT("--SC2440_USB_RxIntHandler\r\n")));
	
	*(volatile BYTE *)&pHWHead->pUSBCtrlAddr->INDEX=saveIndex;
	return fRXFlag;
}




/*************************************************************************
  SC2440_USB_TxIntHandler
 
  Send the next packet to the USB. The first packet gets sent by directly
  calling this function (via dispatch table) from the MDD. After the first
  call the interrupt mechanism will keep things going.
 
  We return the actual number of bytes we've queued to the UDC. Note that
  until the next interrupt we wont know if it has been transmitted. We'll
  deal with the failure by saving the original packet and retransmitting if
  we get an error. In that case we'll return 0 as the number of bytes sent
  since no new bytes have been consumed from the caller's buffer.
 
  When the packet is accepted we return the number of bytes and grab the 
  next packet (or portion thereof).
 
*************************************************************************/
//:-)
void SC2440_USB_TxIntHandler( PSER_INFO pHWHead,
			      PUCHAR pTxBuffer,
			      ULONG *pBuffLen ) 
{
	unsigned int i;
	UCHAR ucLen;
	BYTE saveIndex;
	
	//DEBUGMSG(1, (TEXT("++SC2440_USB_TxIntHandler\r\n")));

	//USBDMSG(1, (TEXT("<T:%d>"),*pBuffLen));
	
	usbdShMem->usbdEir&=~USBDEIR_EP1;

	// Endpoint 1 mode
	saveIndex=*(volatile BYTE *)&pHWHead->pUSBCtrlAddr->INDEX;
	UDC_REG_BITSET(struct INDEXBits, &pHWHead->pUSBCtrlAddr->INDEX, index, 0x1);
    //pHWHead->pUSBCtrlAddr->INDEX.index=0x1; //:-)
	
	pHWHead->CommErrors &= ~CE_TXFULL;

	// If nothing to send, just return after clearing interrupt.
	if (! *pBuffLen ) 
	{
		//DEBUGMSG (1, (TEXT("[TX:nothing to send]")));    
		*(volatile BYTE *)&pHWHead->pUSBCtrlAddr->INDEX=saveIndex;
		return;
	}
    		      	
	// Don't try to send more than EP1 can handle.
	if( *pBuffLen > EP1Len )
    		ucLen = EP1Len;
	else {

		// If we end exactly on a packet boundary, the host doesn't
		// realize there is no more data.  So if we exactly fill the final 
		// packet, truncate it so we can send a short packet next frame
		// indicating end of the transmission
		if( *pBuffLen == EP1Len ) 
		{
			//USBDMSG (1, (TEXT("Tx breaking packet\r\n")));            
			ucLen = EP1Len - 2;
		} else {
			ucLen = (UCHAR)*pBuffLen;
		}

	}

	if (!pHWHead->pUSBCtrlAddr->EP0ICSR1.opr_ipr)
	{
		// Write to the FIFO directly to send the bytes.
		for (i=0; i < ucLen; i++) 
		{
			IOW_REG_FIELD(struct EP1FBits, 
				&pHWHead->pUSBCtrlAddr->EP1F, fifo_data, *pTxBuffer++);
		}

		// Return number of bytes transmitted via pBuffLen.
		*pBuffLen = ucLen;
		UDC_REG_WRITE(struct EP0ICSR1Bits, &pHWHead->pUSBCtrlAddr->EP0ICSR1, opr_ipr, 1);
	} else {
		// Transmit already in progress.  Just return and wait for current
		// transmission to complete before sending more.
		//USBDMSG(0, (TEXT("Write Pend !!!\r\n")));
		*pBuffLen = 0; 
	}

	//DEBUGMSG(0, (TEXT("--SC2440_USB_TxIntHandler\r\n")));
	*(volatile BYTE *)&pHWHead->pUSBCtrlAddr->INDEX=saveIndex;
}



BOOL
HW_PowerOff(
    PSER_INFO pHWHead 
    )
{
	pHWHead->State = OFF;
	// cache the interrupt regs
	pHWHead->cIntStat_uir = *(BYTE *)&pHWHead->pUSBCtrlAddr->UIR;
	pHWHead->cIntStat_eir = *(BYTE *)&pHWHead->pUSBCtrlAddr->EIR;
    
	// disable the USB Clocks
	EnterCriticalSection(&pHWHead->HwRegCritSec);
	//DEBUGMSG(1, (TEXT("USB:HW_PowerOff()\r\n")));
	//RETAILMSG(1, (TEXT("USB:HW_PowerOff()\r\n")));
	HW_USBClocks(pHWHead);
	LeaveCriticalSection(&pHWHead->HwRegCritSec);

    return TRUE;
}


BOOL 
HW_PowerOn(
    PSER_INFO pHWHead
    )
{
		
	pHWHead->State = RESUME;

	// enable the USB Clocks
	EnterCriticalSection(&pHWHead->HwRegCritSec);
	//DEBUGMSG(1, (TEXT("USB:HW_PowerOn()\r\n")));
	//RETAILMSG(1, (TEXT("USB:HW_PowerOn()\r\n")));
	HW_USBClocks(pHWHead);
	LeaveCriticalSection(&pHWHead->HwRegCritSec);

	SetInterruptEvent(pHWHead->pHWObj->dwIntID); 		
    
    return TRUE;
}



VOID
HW_USBClocks(PSER_INFO pHWHead)
{
	if ((pHWHead->State == IDLE) || (pHWHead->State == RESUME))
	{
		DEBUGMSG(1, (TEXT("HW_USBClocks::IDLE\r\n")));
        //
        // Enable the USB Clocks
        //
        pHWHead->pCLKPWR->rCLKCON |= (1<<7);
        DEBUGMSG(ZONE_INIT, (TEXT("rCLKCON: 0x%X\r\n"), pHWHead->pCLKPWR->rCLKCON));
        
        // Fin=12MHz, Fout=48MHz
        //pHWHead->pCLKPWR->rUPLLCON = 0x48032;   // ((0x48 << 12) + (0x3 << 4) + 0x2)
        //DEBUGMSG(ZONE_INIT, (TEXT("rUPLLCON: 0x%X\r\n"), pHWHead->pCLKPWR->rUPLLCON));

        //
        // MISCCR: USBD Pads, Normal mode
        //
        pHWHead->pIrqCtrlAddr->rMISCCR &= ~((3 << 12) | (1 << 3));

        // TO DO :
        // Enable USB_PULLUP on GPIO PIN (tied to USB D+) & set high
        //

        //pHWHead->pIrqCtrlAddr->rGPBCON &= ~(3 << 18);    // clear GPE15
        //pHWHead->pIrqCtrlAddr->rGPBCON |=  (1 << 18);    // config as output
        //pHWHead->pIrqCtrlAddr->rGPBUP  &= ~(1 << 9);    // pullup disabled
        //pHWHead->pIrqCtrlAddr->rGPBDAT |=  (1 << 9);    // set high

        pHWHead->pIrqCtrlAddr->rGPGCON &= ~(3 << 24);    // clear GPE15
        pHWHead->pIrqCtrlAddr->rGPGCON |=  (1 << 24);    // config as output
        pHWHead->pIrqCtrlAddr->rGPGUP  &= ~(1 << 12);    // pullup disabled
        pHWHead->pIrqCtrlAddr->rGPGDAT |=  (1 << 12);    // set high

        DEBUGMSG(ZONE_INIT, (TEXT("rGPDCON: 0x%X\r\n"), pHWHead->pIrqCtrlAddr->rGPDCON));
        DEBUGMSG(ZONE_INIT, (TEXT("rGPDUP: 0x%X\r\n"),  pHWHead->pIrqCtrlAddr->rGPDUP));
        DEBUGMSG(ZONE_INIT, (TEXT("rGPDDAT: 0x%X\r\n"), pHWHead->pIrqCtrlAddr->rGPDDAT));
	}
	else if (pHWHead->State == OFF) 
	{
		DEBUGMSG(1, (TEXT("HW_USBClocks::OFF\r\n")));
        //
        // Disable the USB Clocks
        //
        pHWHead->pCLKPWR->rCLKCON &= ~(1<<7);

        DEBUGMSG(ZONE_INIT, (TEXT("rCLKCON: 0x%X\r\n"), pHWHead->pCLKPWR->rCLKCON));
        
        // Fin=12MHz, Fout=48MHz
        //pHWHead->pCLKPWR->rUPLLCON = 0;
        // MISCCR: USBD Pads, Suspend mode
        pHWHead->pIrqCtrlAddr->rMISCCR |= (3 << 12);

        // TO DO :
        // Disable USB_PULLUP to remove us from the bus
        

        //pHWHead->pIrqCtrlAddr->rGPBCON &= ~(3 << 18);    // clear GPE15
        //pHWHead->pIrqCtrlAddr->rGPBCON |=  (1 << 18);    // config as output
        //pHWHead->pIrqCtrlAddr->rGPBUP  |=  (1 << 9);    // pullup disabled
        //pHWHead->pIrqCtrlAddr->rGPBDAT &= ~(1 << 9);    // set low
        
		pHWHead->pIrqCtrlAddr->rGPGCON &= ~(3 << 24);    // clear GPE15
        pHWHead->pIrqCtrlAddr->rGPGCON |=  (1 << 24);    // config as output
        pHWHead->pIrqCtrlAddr->rGPGUP  |=  (1 << 12);    // pullup disabled
        pHWHead->pIrqCtrlAddr->rGPGDAT &= ~(1 << 12);    // set low
		DEBUGMSG(ZONE_INIT, (TEXT("rUPLLCON: 0x%X\r\n"), pHWHead->pCLKPWR->rUPLLCON));
	}
}


// :-)
/***************************************
  Added routines for USBD Rx:DMA Tx:ISR
 ***************************************/

BOOL InitUsbdDriverGlobals(void)
{
	BOOL	Ret;
	int i;

	if ( v_pDriverGlobals == NULL )
	{
		v_pDriverGlobals =(PDRIVER_GLOBALS)VirtualAlloc( 0,
						DRIVER_GLOBALS_PHYSICAL_MEMORY_SIZE,
						MEM_RESERVE,
						PAGE_NOACCESS);
		if ( v_pDriverGlobals == NULL )
		{
		    USBDMSG( 1, (TEXT( "InitUsbdDriverGlobals : VirtualAlloc failed!\r\n")) );
			return ( FALSE );
		}

		Ret = VirtualCopy( (LPVOID)v_pDriverGlobals,
						   (LPVOID)DRIVER_GLOBALS_PHYSICAL_MEMORY_START,
						   DRIVER_GLOBALS_PHYSICAL_MEMORY_SIZE,
						   PAGE_READWRITE | PAGE_NOCACHE
						   );

		if ( Ret == FALSE )
		{
			DEBUGMSG( ZONE_ERROR, (TEXT( "InitUsbdDriverGlobals: VirtualCopy failed!\r\n")) );
			if ( v_pDriverGlobals )
			{
				VirtualFree( v_pDriverGlobals,
							 DRIVER_GLOBALS_PHYSICAL_MEMORY_SIZE ,
							 MEM_RELEASE
							);
				v_pDriverGlobals = NULL;
			} 
			return ( FALSE );
		}
	}
	
	usbdShMem=&(v_pDriverGlobals->usbd);	

    for(i=0;i<USBD_GLOBALS_BUF_SIZE;i++)
    {
        usbdShMem->usbdRxBuf[i]=0x0;
		//usbdShMem->usbdTxBuf[i]=0x0;
    }
	usbdShMem->usbdRxRdPt=0;
	usbdShMem->usbdRxWrPt=0;
	usbdShMem->usbdRxCnt=0;
	//usbdShMem->usbdTxRdPt=0;
	//usbdShMem->usbdTxWrPt=0;
	//usbdShMem->usbdTxCnt=0;
	usbdShMem->usbdEir=0;
 	usbdShMem->usbdUir=0;
	usbdShMem->usbdDma3Int=0; 
	//usbdShMem->usbdMddRxCnt=0;
	USBDMSG( 1, (TEXT( "USBD:DRIVER_GLOBALS is initialized\r\n")) );
	return( TRUE ); 
}



BOOL UsbdAllocateVm(void)
{
	if(v_pDMAregs == NULL) 
	{
		v_pDMAregs = (volatile DMAreg *) 
			VirtualAlloc(0,sizeof(DMAreg),MEM_RESERVE, PAGE_NOACCESS);
		if(v_pDMAregs == NULL)
		{
			ERRORMSG(1,(TEXT("For DMAreg: VirtualAlloc failed!\r\n")));
			return (FALSE);
		}
		else 
		{
			if(!VirtualCopy((PVOID)v_pDMAregs,(PVOID)(DMA_BASE),sizeof(DMAreg),
				PAGE_READWRITE | PAGE_NOCACHE )) 
			{
				ERRORMSG(1,(TEXT("For pIOPregs: VirtualCopy failed!\r\n")));
				UsbdDeallocateVm();
				return (FALSE);
			}
		}
	}

	if(v_pINTregs == NULL)
	{
    	v_pINTregs = (volatile INTreg *) 
	    		VirtualAlloc(0,sizeof(INTreg),MEM_RESERVE, PAGE_NOACCESS);
   		if(v_pINTregs == NULL)
		{
	   		ERRORMSG(1,(TEXT("For INTreg: VirtualAlloc failed!\r\n")));
			return (FALSE);
	   	}
    	else 
		{
	    	if(!VirtualCopy((PVOID)v_pINTregs,(PVOID)(INT_BASE),sizeof(INTreg),
		    	   PAGE_READWRITE | PAGE_NOCACHE ))
		   	{
				ERRORMSG(1,(TEXT("For INTreg: VirtualCopy failed!\r\n")));
    				return (FALSE);
	    	}
	   	}
	}
	
	return TRUE;
}



void UsbdDeallocateVm(void)
{
	if(v_pDMAregs)
	{
		VirtualFree((void*)v_pDMAregs, sizeof(DMAreg), MEM_RELEASE);
		v_pDMAregs=NULL;
	}

	if(v_pINTregs)
	{
		VirtualFree((void*)v_pINTregs, sizeof(INTreg), MEM_RELEASE);
		v_pINTregs=NULL;
	}
}



void UsbdInitDma3(int bufIndex,int bufOffset)
{
	//ULONG usbdRxRdPt,usbdRxWrPt;

  	//If DMA3 is turned on, DMA3 should be turned off.
	if((v_pDMAregs->rDMASKTRIG3&(1<<1))!=0)
	{
		v_pDMAregs->rDMASKTRIG3=(1<<2);	//DMA3 stop
		while(v_pDMAregs->rDMASKTRIG3&(1<<1)); //wait until DMA3 stop
		usbdShMem->usbdRxRdPt=0;
		USBDMSG( 1, (TEXT("[DMA3 is stopped]\r\n")));
	}

	v_pDMAregs->rDISRCC3 = (1<<1)|(1<<0);		   //src=APB, srcAddr=fixed
	v_pDMAregs->rDISRC3 = REAL_PHYSICAL_ADDR_EP4_FIFO; //srcAddr=EP4_FIFO 
	v_pDMAregs->rDIDSTC3 = (0<<1)|(0<<0);		   //dst=AHB(memory),increase,

#if (DRIVER_GLOBALS_PHYSICAL_MEMORY_START<0xA0000000 || DMA_BUFFER_BASE<0xA0000000) 
	GENERATE_ERROR
		   	//The above two address should be non-cacheable area.
#endif  	

	realPhysicalAddr_UsbdRxBuf = \
		(ULONG)&((DRIVER_GLOBALS *)DRIVER_GLOBALS_PHYSICAL_MEMORY_START)->usbd.usbdRxBuf \
		- DMA_BUFFER_BASE + DMA_PHYSICAL_BASE;	
	//USBDMSG( 1, (TEXT( "[USBD:RPA_URxBuf=%x]"),realPhysicalAddr_UsbdRxBuf) );
	//Real physical address of usbdShMem->usbdRxBuf

	if(bufIndex==0)
	{
	   	v_pDMAregs->rDIDST3=realPhysicalAddr_UsbdRxBuf+0+bufOffset;
	}
	else
	{
		v_pDMAregs->rDIDST3=realPhysicalAddr_UsbdRxBuf+(USBD_GLOBALS_BUF_SIZE/2)+bufOffset;
	}

   	v_pDMAregs->rDCON3=(USBD_GLOBALS_BUF_SIZE-bufOffset)|(1<<31)|(0<<30)|(1<<29)|(0<<28)|(0<<27)|(4<<24)|(1<<23)|(1<<22)|(0<<20); 
	//handshake,requestor=APB,CURR_TC int enable,unit transfer,
	//single service,src=USBD,H/W request,no_autoreload,byte,CURR_TC

	v_pDMAregs->rDMASKTRIG3=(1<<1);	//DMA 3 on
	   

	USBDMSG( 1, (TEXT( "[USBD:rDIDST3=%x,rDCDST3=%x]\r\n"),v_pDMAregs->rDIDST3,v_pDMAregs->rDCDST3) );

}


