//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft end-user
// license agreement (EULA) under which you licensed this SOFTWARE PRODUCT.
// If you did not accept the terms of the EULA, you are not authorized to use
// this source code. For a copy of the EULA, please see the LICENSE.RTF on your
// install media.
//
/*++
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.

Module Name:  
    halether.c
    
Abstract:

    Platform specific code for debug ethernet services (debug messages,
    kernel debugger, text shell (PPSH)).  These functions are all called
    from ethdbg.lib.  They are non-preemptible, and cannot make any system calls.

Functions:


Notes: 

--*/
#include <windows.h>
#include <nkintr.h>
#include <ethdbg.h>
#include <halether.h>
#include <hal.h>
#include <PCIreg.h>
#include "pc.h"
#include "bootarg.h"
#include "oalintr.h"


int __cdecl _inp  (unsigned short);
int __cdecl _outp (unsigned short, int);

//
// Function pointers to the support library functions of the currently installed debug ethernet controller.
//
PFN_EDBG_INIT                   pfnEDbgInit;
PFN_EDBG_INIT_DMABUFFER         pfnEDbgInitDMA;
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;
PFN_EDBG_CURRENT_PACKET_FILTER  pfnCurrentPacketFilter;
PFN_EDBG_MULTICAST_LIST         pfnMulticastList;

#define PLATFORM_STRING            "CEPC"
#define ETHDMA_BUFFER_BASE         0x80200000
#define ETHDMA_BUFFER_SIZE         0x00020000

static BOOT_ARGS *pBootArgs;

// Buffer for receiving frames into.  
UCHAR RcvBuf[1500];
extern DWORD EdbgDebugZone;

USHORT wLocalMAC[3];    // Saved copy of the mac address

// Well known DHCP ports for use with UDP
#define DHCP_SERVER_PORT 0x4300
#define DHCP_CLIENT_PORT 0x4400

// We retry more than Odo since CEPC BIOS's take longer to boot.
#define MAX_BOOTME_CNT   20
#define BOOTME_INTERVAL  3

#ifdef IMGSHAREETH
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;
BOOL    ucMultiAddr[8][6];      //  The new list..  
                                //  VMINI assumes 8 multicast list entry..
                                //                          
#endif

static void
itoa10(
    int n,
    char s[]
    )
{
    int i = 0; 

    // Get absolute value of number
    unsigned int val = (unsigned int)((n < 0) ? -n : n);

    // Extract digits in reverse order
    do {
        s[i++] = (val % 10) + '0';
    } while (val /= 10);

    // Add sign if number negative
    if (n < 0) s[i++] = '-';

    s[i--] = '\0';

    // Reverse string
    for (n = 0; n < i; n++, i--) {
        char swap = s[n];
        s[n] = s[i];
        s[i] = swap;
    }
}

static DWORD
UpperDWFromMAC(EDBG_ADDR * pAddr)
{
    DWORD ret;

    //
    // The WORDs in wMAC field are in net order, so we need to do some
    // serious shifting around.
    // A hex ethernet address of 12 34 56 78 9a bc is stored in wMAC array as
    // wMAC[0] = 3412, wMAC[1] = 7856, wMAC[2] = bc9a.
    // The 4 byte return value should look like 0x00123456
    //
    ret = (pAddr->wMAC[0] & 0x00ff) << 16;
    ret |= pAddr->wMAC[0] & 0xff00;
    ret |= pAddr->wMAC[1] & 0x00ff;
    return ret;
}

void
CreateDeviceName(EDBG_ADDR *pMyAddr, char *szBuf)
{
    int i;
    DWORD dwUpperMAC = UpperDWFromMAC(pMyAddr);

    // If a device name was passed up by the bootloader, use it.
    //
    if ((pBootArgs->dwVersionSig == BOOT_ARG_VERSION_SIG) &&
        strlen(pBootArgs->szDeviceNameRoot))
        strncpy(szBuf, pBootArgs->szDeviceNameRoot, EDBG_MAX_DEV_NAMELEN);
    else
        strcpy(szBuf, PLATFORM_STRING);
    szBuf += strlen(szBuf);

    for (i=0;i<NUM_EDBG_VENDORIDS;i++) {
        if (dwUpperMAC == EdbgVendorIds[i].dwUpperMAC) {
            strcat(szBuf,EdbgVendorIds[i].szAbbrev);
            szBuf += strlen(szBuf);
            break;
        }
    }
    itoa10(((pMyAddr->wMAC[2]>>8) | ((pMyAddr->wMAC[2] & 0x00ff) << 8)), szBuf);
}


#define IPSTATE_NONE    0
#define IPSTATE_GOTIP   1
#define IPSTATE_ARP     2
#define IPSTATE_ARPED   3
#define IPSTATE_RETRY   4

#define MAX_DHCP_RETRY  3


/* OEMEthInit
 *
 *  Initialization routine - called from EdbgInit() to perform platform specific
 *  HW init.  The CEPC platform uses either an SMC 91C94 Ethernet controller or
 *  an NE2000 compatible adapter (NSC 8390 ethernet controller).
 *
 *  Return Value:
 *    Return TRUE if init is successful, FALSE if error.
 */
BOOL
OEMEthInit(EDBG_ADAPTER *pAdapter)
{
    pBootArgs = (BOOT_ARGS *) ((ULONG)(*(PBYTE *)BOOT_ARG_PTR_LOCATION) | 0x80000000);

    // Some parameters included in boot args for future use, make sure these aren't set
    switch (pBootArgs->ucEdbgAdapterType) {
    case EDBG_ADAPTER_SMC9000:
        pfnEDbgInit           = SMCInit;
        pfnEDbgInitDMA        = NULL;
        pfnEDbgEnableInts     = SMCEnableInts;     
        pfnEDbgDisableInts    = SMCDisableInts;    
        pfnEDbgGetPendingInts = SMCGetPendingInterrupts; 
        pfnEDbgGetFrame       = SMCGetFrame;       
        pfnEDbgSendFrame      = SMCSendFrame;      
        pfnEDbgReadEEPROM     = SMCReadEEPROM;     
        pfnEDbgWriteEEPROM    = SMCWriteEEPROM;
        pfnEDbgSetOptions     = SMCSetOptions;
        pfnCurrentPacketFilter= NULL;
        pfnMulticastList      = NULL;
        break;
    case EDBG_ADAPTER_DEFAULT:
    case EDBG_ADAPTER_NE2000:
        pfnEDbgInit           = NE2000Init;
        pfnEDbgInitDMA        = NULL;
        pfnEDbgEnableInts     = NE2000EnableInts;     
        pfnEDbgDisableInts    = NE2000DisableInts;    
        pfnEDbgGetPendingInts = NE2000GetPendingInts; 
        pfnEDbgGetFrame       = NE2000GetFrame;       
        pfnEDbgSendFrame      = NE2000SendFrame;      
        pfnEDbgReadEEPROM     = NE2000ReadEEPROM;     
        pfnEDbgWriteEEPROM    = NE2000WriteEEPROM;    
        pfnEDbgSetOptions     = NE2000SetOptions;
        pfnCurrentPacketFilter= Ne2000CurrentPacketFilter;
        pfnMulticastList      = NE2000MulticastList;
        break;
    case EDBG_USB_RNDIS:
        EdbgOutputDebugString(" RNDIS Edbg!\r\n");
        pfnEDbgInit           = HostMiniInit;
        pfnEDbgEnableInts     = RndisEnableInts;     
        pfnEDbgDisableInts    = RndisDisableInts;    
        pfnEDbgGetPendingInts = RndisGetPendingInts; 
        pfnEDbgGetFrame       = RndisEDbgGetFrame;       
        pfnEDbgSendFrame      = RndisEDbgSendFrame;      
        pfnEDbgReadEEPROM     = NULL;
        pfnEDbgWriteEEPROM    = NULL;
        pfnEDbgSetOptions     = RndisSetOptions;
        pfnCurrentPacketFilter= RndisCurrentPacketFilter;
        pfnMulticastList      = RndisMulticastList;
        break ;
    case EDBG_ADAPTER_DP83815:
        pfnEDbgInit           = DP83815Init;
        pfnEDbgInitDMA        = DP83815InitDMABuffer;
        pfnEDbgEnableInts     = DP83815EnableInts;
        pfnEDbgDisableInts    = DP83815DisableInts;
        pfnEDbgGetPendingInts = DP83815GetPendingInts;
        pfnEDbgGetFrame       = DP83815GetFrame;
        pfnEDbgSendFrame      = DP83815SendFrame;
        pfnEDbgReadEEPROM     = DP83815ReadEEPROM;
        pfnEDbgWriteEEPROM    = DP83815WriteEEPROM;
        pfnEDbgSetOptions     = DP83815SetOptions;
        pfnCurrentPacketFilter= NULL;
        pfnMulticastList      = NULL;
        break;
    case EDBG_ADAPTER_RTL8139:
        pfnEDbgInit           = RTL8139Init;
        pfnEDbgInitDMA        = RTL8139InitDMABuffer;
        pfnEDbgEnableInts     = RTL8139EnableInts;     
        pfnEDbgDisableInts    = RTL8139DisableInts;    
        pfnEDbgGetPendingInts = RTL8139GetPendingInts; 
        pfnEDbgGetFrame       = RTL8139GetFrame;       
        pfnEDbgSendFrame      = RTL8139SendFrame;      
        pfnEDbgReadEEPROM     = RTL8139ReadEEPROM;     
        pfnEDbgWriteEEPROM    = RTL8139WriteEEPROM;    
        pfnEDbgSetOptions     = RTL8139SetOptions;
        pfnCurrentPacketFilter= RTL8139CurrentPacketFilter;
        pfnMulticastList      = RTL8139MulticastList;
        break;
    case EDBG_ADAPTER_FET:
        pfnEDbgInit           = FETInit;
        pfnEDbgInitDMA        = FETInitDMABuffer;
        pfnEDbgEnableInts     = FETEnableInts;
        pfnEDbgDisableInts    = FETDisableInts;
        pfnEDbgGetPendingInts = FETGetPendingInts;
        pfnEDbgGetFrame       = FETGetFrame;
        pfnEDbgSendFrame      = FETSendFrame;
        pfnEDbgReadEEPROM     = FETReadEEPROM;
        pfnEDbgWriteEEPROM    = FETWriteEEPROM;
        pfnEDbgSetOptions     = FETSetOptions;
        pfnCurrentPacketFilter= FETCurrentPacketFilter;
        pfnMulticastList      = FETMulticastList;
        break;
    case EDBG_ADAPTER_3C90X:
        pfnEDbgInit           = D3C90XInit;
        pfnEDbgInitDMA        = D3C90XInitDMABuffer;
        pfnEDbgEnableInts     = D3C90XEnableInts;     
        pfnEDbgDisableInts    = D3C90XDisableInts;    
        pfnEDbgGetPendingInts = NULL;
        pfnEDbgGetFrame       = D3C90XGetFrame;       
        pfnEDbgSendFrame      = D3C90XSendFrame;      
        pfnEDbgReadEEPROM     = NULL;     
        pfnEDbgWriteEEPROM    = NULL;
        pfnEDbgSetOptions     = D3C90XSetOptions;
        pfnCurrentPacketFilter= D3C90XCurrentPacketFilter;
        pfnMulticastList      = D3C90XMulticastList;
//	pBootArgs->ucEdbgIRQ  = 0;
        break;
    default:
        EdbgOutputDebugString("Unsupported debug Ethernet parameters - adapter: %u, IRQ:%u\n",
                              pBootArgs->ucEdbgAdapterType, pBootArgs->ucEdbgIRQ);
        return (FALSE);
    }

    pAdapter->Addr=pBootArgs->EdbgAddr;// Inherit Eth address from Bootloader first.
    // Initialize DMA buffers, if needed...
    //
    if (pfnEDbgInitDMA && !pfnEDbgInitDMA(ETHDMA_BUFFER_BASE, ETHDMA_BUFFER_SIZE))               
    {
        EdbgOutputDebugString("ERROR: Failed to initialize DMA buffers..\r\n");
        return FALSE;
    }

    if (!pfnEDbgInit((BYTE *) pBootArgs->dwEdbgBaseAddr,1,pAdapter->Addr.wMAC) )
        return (FALSE);

    // Check the MAC address - this can be invalid if the switches are set wrong.
    if ( !pAdapter->Addr.wMAC[0] && !pAdapter->Addr.wMAC[1] && !pAdapter->Addr.wMAC[2] ) {
        EdbgOutputDebugString("Invalid Ethernet address read from the debug ethernet controller, check switch settings\n");
        return (FALSE);
    }

    // Save out local mac address.
    memcpy ((char *)wLocalMAC, pAdapter->Addr.wMAC, sizeof(wLocalMAC));
    
    EdbgOutputDebugString("Debug Ethernet card initialized, MAC Address:%B:%B:%B:%B:%B:%B\r\n",
                          pAdapter->Addr.wMAC[0] & 0x00FF, pAdapter->Addr.wMAC[0] >> 8,
                          pAdapter->Addr.wMAC[1] & 0x00FF, pAdapter->Addr.wMAC[1] >> 8,
                          pAdapter->Addr.wMAC[2] & 0x00FF, pAdapter->Addr.wMAC[2] >> 8  );

#if IMGSHAREETH 
    VBridgeInit();
    VBridgeKSetLocalMacAddress((char *)wLocalMAC);
#endif

    // Fill in our SYSINTR value for the EDBG subsystem. Since we have limited IRQ
    // resources, just run in polled mode unless otherwise configured.
    if ( pBootArgs->ucEdbgIRQ )
        pAdapter->SysIntrVal = OEMTranslateIrq(pBootArgs->ucEdbgIRQ);
    else
        pAdapter->SysIntrVal = EDBG_SYSINTR_NOINTR;

    pAdapter->Addr.dwIP = pBootArgs->EdbgAddr.dwIP;
    pAdapter->DHCPLeaseTime = pBootArgs->DHCPLeaseTime;
    pAdapter->EdbgFlags = pBootArgs->EdbgFlags;

    // Enable filtering of broadcast packets (all except ARP packets) to reduce overhead.  We still have
    // to take an interrupt, but the driver can quickly determine whether a packet may be discarded without
    // having to copy all the data from the chip.   Note that we cannot turn this option on until DHCP is
    // done processing, since some DHCP servers return responses in broadcast frames.

#ifndef IMGSHAREETH
    if (pAdapter->Addr.dwIP)
        pfnEDbgSetOptions(OPT_BROADCAST_FILTERING);
#endif

    return (TRUE);
}

/* OEMEthEnableInts
 *
 *  Turn on HW interrupts.  Return TRUE if interrupts supported, or FALSE
 *  to run in polling mode for all clients.
 */
void
OEMEthEnableInts()
{
    pfnEDbgEnableInts();
}

/* OEMEthDisableInts
 *
 *  Disable HW interrupts.
 */
void
OEMEthDisableInts()
{
    pfnEDbgDisableInts();
}

#if IMGSHAREETH

/* 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

/* 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()
{
#if IMGSHAREETH
    ProcessVMiniSend();
#endif

    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 both the SMC9000 and NE2000 drivers.
 *
 * 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
{
#if IMGSHAREETH
    BOOL    bStatus;
    BOOL    bTaken;
    UINT16  wOriginalLength;

    ProcessVMiniSend();

    //
    //  ne2000 ether library has a minor problem of returning incorrect
    //  Ethernet packet size if we give it buffer > max Ether MTU.
    //  So, pass in only ether MTU if the incoming buffer is greater
    //  than ether MTU.
    //

    if (*pwLength >= 1514)
        wOriginalLength = 1514;
    else
        wOriginalLength = *pwLength;
    
    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
    
}

/* 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) )
        {

#if IMGSHAREETH
            ProcessVMiniSend();
#endif
            return (TRUE);
        }
        else
            EdbgOutputDebugString("!OEMEthSendFrame failure, retry %u\n",retries);
    }
    return (FALSE);
}


/* OEMEthGetSecs
 *
 *  Return a count of seconds from some arbitrary time (the absolute value is not important,
 *  so long as it increments appropriately). Read this value from the RTC values in CMOS. Note
 *  that reading the CMOS requires multiple register operations, so this function must be called
 *  only when scheduling is disabled.
 */
static DWORD dwLastTime;  // For making sure we aren't running backward
extern BOOL fRTCInit;
extern BOOL Bare_GetRealTime(LPSYSTEMTIME lpst);
extern volatile DWORD CurMSec;

DWORD
OEMEthGetSecs()
{
    SYSTEMTIME st;
    DWORD dwRet;
    static DWORD dwBias;

    if (!fRTCInit) {
        OEMGetRealTime( &st );
        dwRet = ((60UL * (60UL * (24UL * (31UL * st.wMonth + st.wDay) + st.wHour) + st.wMinute)) + st.wSecond);
        dwBias = dwRet;
    } else {
        dwRet = (CurMSec/1000) + dwBias;
    }

    if (dwRet < dwLastTime) {
        EdbgOutputDebugString("! Time went backwards (or wrapped): cur: %u, last %u\n",
                              dwRet,dwLastTime);
    }
    dwLastTime = dwRet;
    return (dwRet);
}

#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)
{

    if (pfnCurrentPacketFilter)
    {
        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;
    }
    else
    {
        //
        //  Eth driver does not yet support filter setting
        //  So if it is PACKET_TYPE_DIRECTED, PACKET_TYPE_MULTICAST, 
        //  PACKET_TYPE_BROADCAST, we just indicate that we are ok with it.
        //  Since EDBG will run in this mode anyway..
        //

        DWORD   dwInherentlySupported = 
                    PACKET_TYPE_MULTICAST   | 
                    PACKET_TYPE_DIRECTED    | 
                    PACKET_TYPE_BROADCAST;

        if (*pdwRequestedFilter == dwInherentlySupported)
            return TRUE;
        else
            return FALSE;
    }

}   //  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]);
    }

    if (pfnMulticastList)
    {
        //
        //  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;
    }
    else
        bReturnValue = FALSE;

    
    EdbgOutputDebugString(
        "OEMEthMulticastList returning [%d]\r\n",
        bReturnValue);


    return bReturnValue;
    

}   //  OEMEthMulticastList()

#endif
