/******************************************************************************
 *
 * System On Chip(SOC)
 *
 * Copyright (c) 2002 Software Center, Samsung Electronics, Inc.
 * All rights reserved.
 *
 * This software is the confidential and proprietary information of Samsung 
 * Electronics, Inc("Confidential Information"). You Shall not disclose such 
 * Confidential Information and shall use it only in accordance with the terms 
 * of the license agreement you entered into Samsung.
 *
 *-----------------------------------------------------------------------------
 *
 *	S3C2440 BSP
 *
 * halether.c :
 *
 *	Platform specific code for debug ethernet services (debug messages,          
 *	kernel debugger, text shell (CESH)).  These functions are all called         	
 *	from ethdbg.lib.  They are non-preemptible, and cannot make any system calls.
 *
 *
 * @author	zartoven@samsung.com (SOC, SWC, SAMSUNG Electronics)
 *
 * @date	2002/04/04
 * 
 * Log:
 *	2002/04/04  Start(From ODO's BSP)
 *      
 ******************************************************************************
 */


#include <windows.h>
#include <nkintr.h>
#include <ethdbg.h>
#include <halether.h>
#include <kitl.h>
#include "s2440.h"
#include "drv_glob.h"
#include "oalintr.h"

#define pDriverGlobals			((PDRIVER_GLOBALS) DRIVER_GLOBALS_PHYSICAL_MEMORY_START)

BOOL   CS8900DBG_Init(BYTE *iobase, DWORD membase, USHORT MacAddr[3]);
BOOL   CS8900DBG_IsReceivedPacket(void);
UINT16 CS8900DBG_GetFrame(BYTE *pbData, UINT16 *pwLength);
UINT16 CS8900DBG_SendFrame( BYTE *pbData, DWORD dwLength );
BOOL   CS8900DBG_ReInit(DWORD iobase, DWORD membase);
BOOL   CS8900DBG_MulticastList(PUCHAR pucMulticastAddresses, DWORD dwNumAddresses);
void   CS8900DBG_CurrentPacketFilter(DWORD dwFilter);
void   CS8900DBG_EnableInts(void);
void   CS8900DBG_DisableInts(void);
DWORD  CS8900DBG_GetPendingInts(void);

PVOID PCMCIA_Init(void);


//
// Function pointers to the support library functions of the currently installed debug ethernet controller.
//
PFN_EDBG_INIT             pfnEDbgInit;
PFN_EDBG_ENABLE_INTS      pfnEDbgEnableInts;
PFN_EDBG_DISABLE_INTS     pfnEDbgDisableInts;
PFN_EDBG_GET_PENDING_INTS pfnEDbgGetPendingInts;
PFN_EDBG_GET_FRAME        pfnEDbgGetFrame;
PFN_EDBG_SEND_FRAME       pfnEDbgSendFrame;
PFN_EDBG_READ_EEPROM      pfnEDbgReadEEPROM;
PFN_EDBG_WRITE_EEPROM     pfnEDbgWriteEEPROM;
PFN_EDBG_SET_OPTIONS      pfnEDbgSetOptions;

#ifdef IMGSHAREETH
PFN_EDBG_CURRENT_PACKET_FILTER  pfnCurrentPacketFilter;
PFN_EDBG_MULTICAST_LIST			pfnMulticastList;

BOOL OEMEthCurrentPacketFilter(PDWORD pdwRequestedFilter);

// Multicast list - Vmini assumes a maximum of 8 entries.
//
#define MAX_MULTICAST_LIST      8
BOOL	ucMultiAddr[MAX_MULTICAST_LIST][6];

BOOL	bNewFilter = FALSE;		//	User mode --> Kernel mode to set new filter.
DWORD	dwFilter;				//	The filter..
BOOL	bNewMulticast = FALSE;	//	User mode --> Kernel mode for new list
DWORD	dwNoOfEntry;

/* ProcessVMiniSend()
 *
 *		This routine drains the pending VMINI TX.
 * 
 */
void ProcessVMiniSend(void)
{
	PBYTE   pVMiniData;
    DWORD   dwVMiniDataLength;

	////////////////////////////////////////////////////////////////////////////
	//	Handle the filter if we need to..
	//
	if (bNewFilter && pfnCurrentPacketFilter)
	{
		bNewFilter = FALSE;		
		pfnCurrentPacketFilter(dwFilter);
	}		

	//
    //  Handle new multicast list..
    //
    if (bNewMulticast && pfnMulticastList)
    {
        bNewMulticast = FALSE;
        pfnMulticastList((PUCHAR)ucMultiAddr, dwNoOfEntry);
    }

    ////////////////////////////////////////////////////////////////////////////
    //  Consume all the client packets.
    //
    while (VBridgeKGetOneTxBuffer(&pVMiniData, &dwVMiniDataLength) == TRUE)
    {
        pfnEDbgSendFrame (pVMiniData, dwVMiniDataLength);
        VBridgeKGetOneTxBufferComplete(pVMiniData);
    } 

}	//	ProcessVMiniSend()

#endif //IMGSHAREETH



/* OEMEthInit
 *
 *  Initialization routine
 *
 *  Return Value:
 *    Return TRUE if init is successful, FALSE if error.
 */
BOOL
OEMEthInit(EDBG_ADAPTER *pAdapter) 
{
	PBYTE pBaseIOAddress;

	// Driver globals from the bootloader.
	//
	if (pDriverGlobals->eth.EbootMagicNum == EBOOT_MAGIC_NUM)
	{
		memcpy(pAdapter, &pDriverGlobals->eth.TargetAddr, sizeof(EDBG_ADAPTER));

		switch(pDriverGlobals->misc.EbootDevice)
		{
		case(DOWNLOAD_DEVICE_PCMCIA):	// NE2000 CF card.
			pBaseIOAddress  = (PBYTE)PCMCIA_Init();
			if (pBaseIOAddress)
			{
				// Initialize the built-in Ethenet controller.
				//
				if (!NE2000Init((PBYTE)pBaseIOAddress, 1, pAdapter->Addr.wMAC))
				{
					EdbgOutputDebugString("ERROR: OEMEthInit: Failed to initialize Ethernet controller.\r\n");
					return(FALSE);
				}
			}
			pfnEDbgInit           	= NE2000Init;
			pfnEDbgEnableInts     	= NE2000EnableInts;
			pfnEDbgDisableInts    	= NE2000DisableInts;
			pfnEDbgGetPendingInts 	= NE2000GetPendingInts;
			pfnEDbgGetFrame       	= NE2000GetFrame;
			pfnEDbgSendFrame      	= NE2000SendFrame;
			pfnEDbgReadEEPROM     	= NE2000ReadEEPROM;
			pfnEDbgWriteEEPROM    	= NE2000WriteEEPROM;
			pfnEDbgSetOptions     	= NE2000SetOptions;
		#ifdef IMGSHAREETH
			pfnCurrentPacketFilter	= Ne2000CurrentPacketFilter;
			pfnMulticastList		= NE2000MulticastList;
		#endif	// IMGSHAREETH.
			break;
		case(DOWNLOAD_DEVICE_CS8900):	// CS8900A.
			// Initialize the CS8900.
			//
			if (!CS8900DBG_Init((PBYTE)CS8900DBG_IOBASE, CS8900DBG_MEMBASE, pAdapter->Addr.wMAC))
			{
				EdbgOutputDebugString("ERROR: OEMEthInit: CS8900 initialization failed.\r\n");
				return(FALSE);
			}

			pfnEDbgInit				= CS8900DBG_Init;
			pfnEDbgEnableInts     	= CS8900DBG_EnableInts;
			pfnEDbgDisableInts    	= CS8900DBG_DisableInts;
			pfnEDbgGetFrame			= CS8900DBG_GetFrame;
			pfnEDbgSendFrame		= CS8900DBG_SendFrame;
			pfnEDbgGetPendingInts 	= CS8900DBG_GetPendingInts;
		#ifdef IMGSHAREETH
			pfnCurrentPacketFilter	= CS8900DBG_CurrentPacketFilter;
			pfnMulticastList		= CS8900DBG_MulticastList;
		#endif	// IMGSHAREETH.
			break;
		default:
			EdbgOutputDebugString("ERROR: OEMInit: Unknown download NIC (0x%x).\r\n", pDriverGlobals->misc.EbootDevice);
			return(FALSE);
		}
	}
	else
	{
		// TODO - retrieve CS8900 MAC address from flash...
		// TODO - intialize the CS8900 from scratch...
	}

	EdbgOutputDebugString("::: OEMEthInit() IP Address : %s\r\n", inet_ntoa(pAdapter->Addr.dwIP));
	EdbgOutputDebugString("::: OEMEthInit() Netmask    : %s\r\n", inet_ntoa(pDriverGlobals->eth.SubnetMask));

	// Setup SYSINTR used for KITL NIC.
	//
	if (pDriverGlobals->misc.EbootDevice == DOWNLOAD_DEVICE_PCMCIA)
		pAdapter->SysIntrVal    = SYSINTR_PCMCIA_LEVEL;
	else
		pAdapter->SysIntrVal    = SYSINTR_ETHER;

	pAdapter->DHCPLeaseTime = DEFAULT_DHCP_LEASE;
    pAdapter->EdbgFlags     = pDriverGlobals->eth.EdbgFlags;
		
#ifdef IMGSHAREETH
    VBridgeInit();
    VBridgeKSetLocalMacAddress((char *)pAdapter->Addr.wMAC);
#endif	// IMGSHAREETH.

	return(TRUE);
}


/* OEMEthEnableInts
 *
 *  Turn on HW interrupts.  
 */
void
OEMEthEnableInts()
{
	if (pfnEDbgEnableInts)
	{
		pfnEDbgEnableInts();
	}
}


/* OEMEthDisableInts
 *
 *  Disable HW interrupts.
 */
void
OEMEthDisableInts()
{
	if (pfnEDbgDisableInts)
	{
		pfnEDbgDisableInts();
	}
}

/* OEMEthISR
 *
 *    ISR routine, called by EDBG IST when Ethernet controller interrupts. Also
 *    called in polling mode, to check for received data.
 *
 * Return Value:
 *    Return bitmask indicating which interrupts are pending.  Currently, the ones
 *    that EDBG cares about are the following (others should be handled internally):
 *       INTR_TYPE_RX   -- Receive interrupt. IST will call into GetFrame to read data.
 */
DWORD
OEMEthISR()
{
#ifdef IMGSHAREETH
    ProcessVMiniSend();
#endif	// IMGSHAREETH.

    return (pfnEDbgGetPendingInts());
}

/* OEMEthGetFrame
 *
 *   Check to see if a frame has been received, and if so copy to buffer. An optimization
 *   which may be performed in the Ethernet driver is to filter out all received broadcast
 *   packets except for ARPs.  This is done in the SMC9000 driver.
 *
 * Return Value:
 *    Return TRUE if frame has been received, FALSE if not.
 */
BOOL
OEMEthGetFrame(
    BYTE *pData,       // OUT - Receives frame data
    UINT16 *pwLength)  // IN  - Length of Rx buffer
                       // OUT - Number of bytes received
{
#ifdef IMGSHAREETH
	BOOL	bStatus;
	BOOL	bTaken;		
	UINT16	wOriginalLength = *pwLength;
	
	ProcessVMiniSend();

	while (1)
	{		
		*pwLength = wOriginalLength;

		bStatus	 = pfnEDbgGetFrame(pData, pwLength);
			
		if (bStatus)
		{
			////////////////////////////////////////////////////////////////////
			VBridgeKIndicateOneRxBuffer(pData, *pwLength, FALSE, &bTaken);
			if (!bTaken)				
				return bStatus;
		}
		else
			break;
	}

    return (FALSE);
#else
   return pfnEDbgGetFrame(pData, pwLength);
#endif 	// IMGSHAREETH.
   
}


/* OEMEthSendFrame
 *
 *   Send Ethernet frame.  
 *
 *  Return Value:
 *   TRUE if frame successfully sent, FALSE otherwise.
 */
BOOL
OEMEthSendFrame(
    BYTE *pData,     // IN - Data buffer
    DWORD dwLength)  // IN - Length of buffer
{
    int retries = 0;

    while (retries++ < 4) {
        if (!pfnEDbgSendFrame(pData, dwLength))
		{
#ifdef IMGSHAREETH
			ProcessVMiniSend();
#endif //IMGSHAREETH
            return TRUE;
		}
        else
            EdbgOutputDebugString("!OEMEthSendFrame failure, retry %u\n",retries);
    }
    return FALSE;
}


/* OEMEthQueryClientInfo
 *
 *    Return address information for default ethernet services, plus a buffer pool to use
 *    for formatting and receiving EDBG packets (single buffer pool specified, divided in
 *    two for Rx and Tx buffers).  By specifying a smaller window size, less memory can be
 *    used (but the protocol will be less efficient...).  The amount of memory required per
 *    client is (2*WindowSize*1500) bytes.
 *
 *    For Odo, we reserve 3 buffer pools worth of memory in the bib file, based on the IMGEBOOT
 *    flag being set.
 *
 *  Return Value:
 *    If client can be configured, return TRUE and fill in addressing and buffer info. Otherwise
 *    return FALSE.  For Odo, configure clients based on the flags set by Eshell (received in the
 *    JUMPIMG command by eboot, and placed in the uninitalized driver globals section).
 */
/* OEMEthQueryClientInfo
 *
 *    Return address information for default ethernet services, plus a buffer pool to use
 *    for formatting and receiving EDBG packets (single buffer pool specified, divided in
 *    two for Rx and Tx buffers).  By specifying a smaller window size, less memory can be
 *    used (but the protocol will be less efficient...).  The amount of memory required per
 *    client is (2*WindowSize*1500) bytes.
 *
 *    For Odo, we reserve 3 buffer pools worth of memory in the bib file, based on the IMGEBOOT
 *    flag being set.
 *
 *  Return Value:
 *    If client can be configured, return TRUE and fill in addressing and buffer info. Otherwise
 *    return FALSE.  For Odo, configure clients based on the flags set by Eshell (received in the
 *    JUMPIMG command by eboot, and placed in the uninitalized driver globals section).
 */
BOOL
OEMEthQueryClientInfo(
    UCHAR Service,         // IN - Service ID (one of EDBG_SVC defs from ethdbg.h).
    EDBG_ADDR *pPeerAddr,  // OUT -Filled in with the peer Ether/IP address and UDP port number.
    PUCHAR  pWindowSize,   // OUT -Filled in with the client window size.
    PUCHAR *ppBufferPool)  // OUT -Filled in with the packet buffer pool address.
{
    
    // We use the default window size (8) for all services
    *pWindowSize = EDBG_WINDOW_SIZE;
    
    switch (Service)
    {
        // Check the flag in driver globals (set by eboot when it receives the JUMPIMG command)
        case EDBG_SVC_DBGMSG:
            if (! (pDriverGlobals->eth.etherFlags & EDBG_FL_DBGMSG)) {
                EdbgOutputDebugString("FAIL : EDBG_SVC_DBGMSG \n");
                return FALSE;
            }
            memcpy(pPeerAddr, &pDriverGlobals->eth.DbgHostAddr,sizeof(EDBG_ADDR));
            *ppBufferPool = (UCHAR *)EDBG_PHYSICAL_MEMORY_START;
            EdbgOutputDebugString("Querying to ethernet for debug messages, host: %s, port: %d \r\n\n",
                                    inet_ntoa(pDriverGlobals->eth.DbgHostAddr.dwIP),
                                    ntohs(pDriverGlobals->eth.DbgHostAddr.wPort));
            break;
        case EDBG_SVC_PPSH:
            if (! (pDriverGlobals->eth.etherFlags & EDBG_FL_PPSH)) {
                EdbgOutputDebugString("FAIL : EDBG_SVC_PPSH \n");
                return FALSE;
            }
            memcpy(pPeerAddr, &pDriverGlobals->eth.PpshHostAddr,sizeof(EDBG_ADDR));
            *ppBufferPool = (UCHAR *)EDBG_PHYSICAL_MEMORY_START + EDBG_DFLT_BUFFER_POOL_SIZE;
            EdbgOutputDebugString("Querying ethernet for PPSH, host: %s, port: %d \r\n\n",
                                    inet_ntoa(pDriverGlobals->eth.PpshHostAddr.dwIP),
                                    ntohs(pDriverGlobals->eth.PpshHostAddr.wPort));  
            break;
        case EDBG_SVC_KDBG:
            if (! (pDriverGlobals->eth.etherFlags & EDBG_FL_KDBG)) {
                EdbgOutputDebugString("FAIL : EDBG_SVC_KDBG \n");
                return FALSE;
            }
            memcpy(pPeerAddr, &pDriverGlobals->eth.KdbgHostAddr,sizeof(EDBG_ADDR));    
            *ppBufferPool = (UCHAR *)EDBG_PHYSICAL_MEMORY_START + 2*EDBG_DFLT_BUFFER_POOL_SIZE;
			EdbgOutputDebugString("Querying ethernet for KDBG, host: %s, port: %d \r\n\n",
                                    inet_ntoa(pDriverGlobals->eth.KdbgHostAddr.dwIP),
                                    ntohs(pDriverGlobals->eth.KdbgHostAddr.wPort));
            break;            
        default:
            return FALSE;
    }
    return TRUE;
}


/* OEMEthGetSecs
 *
 *  Return a count of seconds from some arbitrary time (the absolute value is not important,
 *  so long as it increments appropriately).
 */

DWORD 
OEMEthGetSecs( void ) 
{
    SYSTEMTIME st;
	DWORD ti;

    OEMGetRealTime( &st );
	ti = ((60UL * (60UL * (24UL * (31UL * st.wMonth + st.wDay) + st.wHour) + st.wMinute)) + st.wSecond);

	return ti;
}


#ifdef IMGSHAREETH
//
//  These functions are only needed if vmini is in.
//  i.e. if IMGSHAREETH is set.
//


////////////////////////////////////////////////////////////////////////////////
//  OEMEthSetFilter()
//
//  Description:
//      
//      This function is used by VMINI to inform the underlying ethernet
//      library on the filtering mode it requires.
//
//  Arguments:
//
//      pdwRequestedFilter  :: The requested filter.
//                             Identical constants have been added in 
//                             that mimics NDIS_PACKET_TYPE_XXX constants.
//
//  Return Value:
//
//      TRUE    :: if we can set the filter.
//      FALSE   :: otherwise..
//
//
//  Note:
//      
//      As a minimum to get vmini to work, we need to support
//      PACKET_TYPE_DIRECTED 
//      PACKET_TYPE_BROADCAST
//
//  
BOOL
OEMEthCurrentPacketFilter(PDWORD pdwRequestedFilter)
{
    
    //EdbgOutputDebugString(
        //"OEMEthCurrentPacketFilter set to [0x%x]\r\n",
        //*pdwRequestedFilter);   
    
    //
    //  Note that we can't do it immediately here, since we are called 
    //  by user mode code.
    //  So what we do is to set the flag here for the kernel mode code
    //  to pick up.
    //
    
    dwFilter   = *pdwRequestedFilter;
    bNewFilter = TRUE;
    return TRUE;

}   //  OEMEthCurrentPacketFilter()



////////////////////////////////////////////////////////////////////////////////
//  OEMEthMulticastList()
//
//  Description:
//
//      This function is used by VMINI to inform the underlying ethernet 
//      library on multicast addresses that winsock app is interested in.
//      
//  Arguments:
//
//      pucMulticastAddressList :: Pointer to an array of multicast addresses.
//      dwNoOfAddresses         :: Number of addresses passed in to us.
//
//  Return Value:
//      
//      TRUE    ::  if we can set the underlying edbg ethernet libary to start
//                      filtering on these multicast addresses.
//
//      FALSE   ::  otherwise.
//
BOOL
OEMEthMulticastList(PUCHAR  pucMulticastAddressList, DWORD  dwNoOfAddresses)
{
    //
    //  This platform does not support multicast yet..
    //

    DWORD   i;
    BOOL    bReturnValue;

    EdbgOutputDebugString(
        "OEMEthMulticastList():: No of Entries [%d]\r\n",
            dwNoOfAddresses);   

    //
    //  We can check how many entries an adapter that are attached to 
    //  can support.
    //  To make things simple, we just assume we can support minimum that
    //  vmini thinks we support (i.e. 8).
    //

    if (dwNoOfAddresses > 8)
    {
        //
        //  This should never happen, since VMINI is known to support
        //  8 entries only... 
        //
        
        EdbgOutputDebugString(
            "Multicast list requeste [%d] > 8 !!!\r\n",
            dwNoOfAddresses);

        return FALSE;
    }


    //
    //   8 entries, 6 bytes each..
    //

    memset(
        ucMultiAddr,
        0x00,
        8 * 6);     

    //
    //  6 bytes per entry..
    //

    memcpy(
        ucMultiAddr,
        pucMulticastAddressList,
        dwNoOfAddresses * 6);


    for (i = 0 ; i < dwNoOfAddresses ; i++)
    {
        EdbgOutputDebugString(
            "[%d] : %x - %x - %x - %x - %x - %x\r\n",
            i,
            pucMulticastAddressList[6*i+0],
            pucMulticastAddressList[6*i+1],
            pucMulticastAddressList[6*i+2],
            pucMulticastAddressList[6*i+3],
            pucMulticastAddressList[6*i+4],
            pucMulticastAddressList[6*i+5]);
    }

    //
    //  We are in KernelIOCTL call, some h/w requires the 
    //  setting of the MULTICAST addresses in interrupt service routine.
    //  So we will do it later, and mark it so the ISR will know about it..
    //  Note:
    //  Order is important here.    Don't set bNewMulticast to true until
    //  all the entries and the dwNoOfEntry are properly set up.
    //  This is because we can be switched out in the middle of 
    /// KernelIOCTL!!
    //
    
    dwNoOfEntry   = dwNoOfAddresses;
    bNewMulticast = TRUE;       
    bReturnValue  = TRUE;

    
    EdbgOutputDebugString(
        "OEMEthMulticastList returning [%d]\r\n",
        bReturnValue);


    return bReturnValue;
    

}   //  OEMEthMulticastList()

#endif
