/** @file

Copyright (c) 1999 - 2014, Intel Corporation. All rights reserved.<BR>

This program and the accompanying materials
are licensed and made available under the terms and conditions
of the BSD License which accompanies this distribution.  The
full text of the license may be found at
http://opensource.org/licenses/bsd-license.php

THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

**/

#include "BiosSnp16.h"


///
/// EFI Driver Binding Protocol Instance
///
EFI_DRIVER_BINDING_PROTOCOL gBiosSnp16DriverBinding = {
  BiosSnp16DriverBindingSupported,
  BiosSnp16DriverBindingStart,
  BiosSnp16DriverBindingStop,
  0x3,
  NULL,
  NULL
};

///
///  This boolean is used to determine if we should release the cached vector during an error condition.
///
BOOLEAN     mCachedInt1A = FALSE;

//
// Private worker functions;
//

/**
  Start the UNDI interface.

  @param SimpleNetworkDevice A pointer to EFI_SIMPLE_NETWORK_DEV data structure.
  @param Ax                  PCI address of Undi device.
  
  @retval EFI_DEVICE_ERROR Fail to start 16 bit UNDI ROM. 
  @retval Others           Status of start 16 bit UNDI ROM. 
**/
EFI_STATUS
Undi16SimpleNetworkStartUndi (
  EFI_SIMPLE_NETWORK_DEV  *SimpleNetworkDevice,
  UINT16                  Ax
  );

/**
  Start the UNDI interface

  @param SimpleNetworkDevice A pointer to EFI_SIMPLE_NETWORK_DEV data structure.
  
  @retval EFI_DEVICE_ERROR Fail to start 16 bit UNDI ROM. 
  @retval Others           Status of start 16 bit UNDI ROM. 
**/
EFI_STATUS
Undi16SimpleNetworkStopUndi (
  EFI_SIMPLE_NETWORK_DEV  *SimpleNetworkDevice
  );

/**
  Stop the UNDI interface

  @param SimpleNetworkDevice A pointer to EFI_SIMPLE_NETWORK_DEV data structure.
  
  @retval EFI_DEVICE_ERROR Fail to stop 16 bit UNDI ROM. 
  @retval Others           Status of stop 16 bit UNDI ROM. 
**/
EFI_STATUS
Undi16SimpleNetworkCleanupUndi (
  EFI_SIMPLE_NETWORK_DEV  *SimpleNetworkDevice
  );

/**
  Get runtime information for Undi network interface

  @param This A pointer to EFI_SIMPLE_NETWORK_PROTOCOL structure.
  
  @retval EFI_SUCCESS Sucess operation. 
  @retval Others      Fail to get runtime information for Undi network interface. 
**/
EFI_STATUS
Undi16SimpleNetworkGetInformation (
  IN EFI_SIMPLE_NETWORK_PROTOCOL  *This
  );

/**
  Get NIC type

  @param This A pointer to EFI_SIMPLE_NETWORK_PROTOCOL structure.
  
  @retval EFI_SUCCESS Sucess operation. 
  @retval Others      Fail to get NIC type.
**/
EFI_STATUS
Undi16SimpleNetworkGetNicType (
  IN EFI_SIMPLE_NETWORK_PROTOCOL  *This
  );

/**
  Get NDIS information

  @param This A pointer to EFI_SIMPLE_NETWORK_PROTOCOL structure.
  
  @retval EFI_SUCCESS Sucess operation. 
  @retval Others      Fail to get NDIS information.
**/
EFI_STATUS
Undi16SimpleNetworkGetNdisInfo (
  IN EFI_SIMPLE_NETWORK_PROTOCOL  *This
  );

/**
  Signal handlers for ExitBootServices event.
  
  Clean up any Real-mode UNDI residue from the system 
   
  @param Event      ExitBootServices event
  @param Context 
**/
VOID
EFIAPI
Undi16SimpleNetworkEvent (
  IN EFI_EVENT  Event,
  IN VOID       *Context
  );

/**
  Loads the undi driver.

  @param SimpleNetworkDevice A pointer to EFI_SIMPLE_NETWORK_DEV data structure.
  
  @retval   EFI_SUCCESS   - Successfully loads undi driver.
  @retval   EFI_NOT_FOUND - Doesn't find undi driver or undi driver load failure.
**/
EFI_STATUS
Undi16SimpleNetworkLoadUndi (
  EFI_SIMPLE_NETWORK_DEV  *SimpleNetworkDevice
  );

/**
  Unload 16 bit UNDI Option ROM from memory

  @param SimpleNetworkDevice A pointer to EFI_SIMPLE_NETWORK_DEV data structure.
  
  @return EFI_STATUS 
**/
EFI_STATUS
Undi16SimpleNetworkUnloadUndi (
  EFI_SIMPLE_NETWORK_DEV  *SimpleNetworkDevice
  );

/**
  Entry point for EFI drivers.

  @param ImageHandle Handle that identifies the loaded image.
  @param SystemTable System Table for this image.
  
  @return EFI_STATUS Return status from EfiLibInstallAllDriverProtocols. 
**/
EFI_STATUS
EFIAPI
BiosSnp16DriverEntryPoint (
  IN EFI_HANDLE         ImageHandle,
  IN EFI_SYSTEM_TABLE   *SystemTable
  )
{
  return EfiLibInstallDriverBindingComponentName2 (
           ImageHandle,
           SystemTable,
           &gBiosSnp16DriverBinding,
           ImageHandle,
           &gBiosSnp16ComponentName,
           &gBiosSnp16ComponentName2
           );
}

//
// EFI Driver Binding Protocol Functions
//
/**
  Tests to see if this driver supports a given controller.

  @param This                 A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
  @param Controller           The handle of the controller to test.
  @param RemainingDevicePath  A pointer to the remaining portion of a device path.
  
  @retval EFI_SUCCESS    The driver supports given controller.
  @retval EFI_UNSUPPORT  The driver doesn't support given controller.
  @retval Other          Other errors prevent driver finishing to test
                         if the driver supports given controller.
**/
EFI_STATUS
EFIAPI
BiosSnp16DriverBindingSupported (
  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
  IN EFI_HANDLE                   Controller,
  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath
  )
{
  EFI_STATUS                Status;
  EFI_LEGACY_BIOS_PROTOCOL  *LegacyBios;
  EFI_DEVICE_PATH_PROTOCOL  *DevicePath;
  EFI_PCI_IO_PROTOCOL       *PciIo;
  PCI_TYPE00                Pci;

  //
  // See if the Legacy BIOS Protocol is available
  //
  Status = gBS->LocateProtocol (&gEfiLegacyBiosProtocolGuid, NULL, (VOID **) &LegacyBios);
  if (EFI_ERROR (Status)) {
    return Status;
  }
  //
  // Open the IO Abstraction(s) needed to perform the supported test
  //
  Status = gBS->OpenProtocol (
                  Controller,
                  &gEfiDevicePathProtocolGuid,
                  (VOID **) &DevicePath,
                  This->DriverBindingHandle,
                  Controller,
                  EFI_OPEN_PROTOCOL_BY_DRIVER
                  );
  if (EFI_ERROR (Status)) {
    return Status;
  }

  gBS->CloseProtocol (
        Controller,
        &gEfiDevicePathProtocolGuid,
        This->DriverBindingHandle,
        Controller
        );

  //
  // Open the IO Abstraction(s) needed to perform the supported test
  //
  Status = gBS->OpenProtocol (
                  Controller,
                  &gEfiPciIoProtocolGuid,
                  (VOID **) &PciIo,
                  This->DriverBindingHandle,
                  Controller,
                  EFI_OPEN_PROTOCOL_BY_DRIVER
                  );
  if (EFI_ERROR (Status)) {
    return Status;
  }
  //
  // See if this is a PCI Network Controller by looking at the Command register and
  // Class Code Register
  //
  Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint32, 0, sizeof (Pci) / sizeof (UINT32), &Pci);
  if (EFI_ERROR (Status)) {
    Status = EFI_UNSUPPORTED;
    goto Done;
  }

  Status = EFI_UNSUPPORTED;
  if (Pci.Hdr.ClassCode[2] == PCI_CLASS_NETWORK) {
    Status = EFI_SUCCESS;
  }

Done:
  gBS->CloseProtocol (
        Controller,
        &gEfiPciIoProtocolGuid,
        This->DriverBindingHandle,
        Controller
        );

  return Status;
}

/**
  Starts the Snp device controller

  @param This                 A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
  @param Controller           The handle of the controller to test.
  @param RemainingDevicePath  A pointer to the remaining portion of a device path.
  
  @retval  EFI_SUCCESS          - The device was started.   
  @retval  EFI_DEVICE_ERROR     - The device could not be started due to a device error.
  @retval  EFI_OUT_OF_RESOURCES - The request could not be completed due to a lack of resources.
**/
EFI_STATUS
EFIAPI
BiosSnp16DriverBindingStart (
  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
  IN EFI_HANDLE                   Controller,
  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath
  )
{
  EFI_STATUS                Status;
  EFI_LEGACY_BIOS_PROTOCOL  *LegacyBios;
  EFI_DEVICE_PATH_PROTOCOL  *DevicePath;
  EFI_PCI_IO_PROTOCOL       *PciIo;
  EFI_SIMPLE_NETWORK_DEV    *SimpleNetworkDevice;
  EFI_DEV_PATH              Node;
  UINTN                     Index;
  UINTN                     Index2;
  UINTN                     Segment;
  UINTN                     Bus;
  UINTN                     Device;
  UINTN                     Function;
  UINTN                     Flags;
  UINT64                    Supports;

  SimpleNetworkDevice = NULL;
  PciIo               = NULL;

  //
  // See if the Legacy BIOS Protocol is available
  //
  Status = gBS->LocateProtocol (&gEfiLegacyBiosProtocolGuid, NULL, (VOID **) &LegacyBios);
  if (EFI_ERROR (Status)) {
    return Status;
  }
  //
  // Open the IO Abstraction(s) needed
  //
  Status = gBS->OpenProtocol (
                  Controller,
                  &gEfiDevicePathProtocolGuid,
                  (VOID **) &DevicePath,
                  This->DriverBindingHandle,
                  Controller,
                  EFI_OPEN_PROTOCOL_BY_DRIVER
                  );
  if (EFI_ERROR (Status)) {
    goto Done;
  }

  Status = gBS->OpenProtocol (
                  Controller,
                  &gEfiPciIoProtocolGuid,
                  (VOID **) &PciIo,
                  This->DriverBindingHandle,
                  Controller,
                  EFI_OPEN_PROTOCOL_BY_DRIVER
                  );
  if (EFI_ERROR (Status)) {
    goto Done;
  }

  Status = PciIo->Attributes (
                    PciIo,
                    EfiPciIoAttributeOperationSupported,
                    0,
                    &Supports
                    );
  if (!EFI_ERROR (Status)) {
    Supports &= (UINT64)EFI_PCI_DEVICE_ENABLE;
    Status = PciIo->Attributes (
                      PciIo,
                      EfiPciIoAttributeOperationEnable,
                      Supports,
                      NULL
                      );
  }

  if (EFI_ERROR (Status)) {
    goto Done;
  }
  //
  // Check to see if there is a legacy option ROM image associated with this PCI device
  //
  Status = LegacyBios->CheckPciRom (
                         LegacyBios,
                         Controller,
                         NULL,
                         NULL,
                         &Flags
                         );
  if (EFI_ERROR (Status)) {
    goto Done;
  }
  //
  // Post the legacy option ROM if it is available.
  //
  Status = LegacyBios->InstallPciRom (
                         LegacyBios,
                         Controller,
                         NULL,
                         &Flags,
                         NULL,
                         NULL,
                         NULL,
                         NULL
                         );
  if (EFI_ERROR (Status)) {
    goto Done;
  }
  //
  // Allocate memory for this SimpleNetwork device instance
  //
  Status = gBS->AllocatePool (
                  EfiBootServicesData,
                  sizeof (EFI_SIMPLE_NETWORK_DEV),
                  (VOID **) &SimpleNetworkDevice
                  );
  if (EFI_ERROR (Status)) {
    Status = EFI_OUT_OF_RESOURCES;
    goto Done;
  }

  ZeroMem (SimpleNetworkDevice, sizeof (EFI_SIMPLE_NETWORK_DEV));

  //
  // Initialize the SimpleNetwork device instance
  //
  SimpleNetworkDevice->Signature      = EFI_SIMPLE_NETWORK_DEV_SIGNATURE;
  SimpleNetworkDevice->LegacyBios     = LegacyBios;
  SimpleNetworkDevice->BaseDevicePath = DevicePath;
  SimpleNetworkDevice->PciIo          = PciIo;

  //
  // Initialize the Nii Protocol
  //
  SimpleNetworkDevice->Nii.Revision = EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL_REVISION;
  SimpleNetworkDevice->Nii.Type     = EfiNetworkInterfaceUndi;

  CopyMem (&SimpleNetworkDevice->Nii.StringId, "UNDI", 4);

  //
  // Load 16 bit UNDI Option ROM into Memory
  //
  Status = Undi16SimpleNetworkLoadUndi (SimpleNetworkDevice);
  if (EFI_ERROR (Status)) {
    DEBUG ((DEBUG_NET, "ERROR : Could not load UNDI.  Status = %r\n", Status));
    goto Done;
  }

  SimpleNetworkDevice->UndiLoaded = TRUE;

  //
  // Call PXENV_START_UNDI - Initilizes the UNID interface for use.
  //
  PciIo->GetLocation (PciIo, &Segment, &Bus, &Device, &Function);
  Status = Undi16SimpleNetworkStartUndi (
             SimpleNetworkDevice,
             (UINT16) ((Bus << 0x8) | (Device << 0x3) | (Function))
             );
  if (EFI_ERROR (Status)) {
    DEBUG ((DEBUG_NET, "ERROR : Could not StartUndi.  Status = %r\n", Status));
    goto Done;
  }
  //
  // Initialize the Simple Network Protocol
  //
  DEBUG ((DEBUG_NET, "Initialize SimpleNetworkDevice instance\n"));

  SimpleNetworkDevice->SimpleNetwork.Revision       = EFI_SIMPLE_NETWORK_PROTOCOL_REVISION;
  SimpleNetworkDevice->SimpleNetwork.Start          = Undi16SimpleNetworkStart;
  SimpleNetworkDevice->SimpleNetwork.Stop           = Undi16SimpleNetworkStop;
  SimpleNetworkDevice->SimpleNetwork.Initialize     = Undi16SimpleNetworkInitialize;
  SimpleNetworkDevice->SimpleNetwork.Reset          = Undi16SimpleNetworkReset;
  SimpleNetworkDevice->SimpleNetwork.Shutdown       = Undi16SimpleNetworkShutdown;
  SimpleNetworkDevice->SimpleNetwork.ReceiveFilters = Undi16SimpleNetworkReceiveFilters;
  SimpleNetworkDevice->SimpleNetwork.StationAddress = Undi16SimpleNetworkStationAddress;
  SimpleNetworkDevice->SimpleNetwork.Statistics     = Undi16SimpleNetworkStatistics;
  SimpleNetworkDevice->SimpleNetwork.MCastIpToMac   = Undi16SimpleNetworkMCastIpToMac;
  SimpleNetworkDevice->SimpleNetwork.NvData         = Undi16SimpleNetworkNvData;
  SimpleNetworkDevice->SimpleNetwork.GetStatus      = Undi16SimpleNetworkGetStatus;
  SimpleNetworkDevice->SimpleNetwork.Transmit       = Undi16SimpleNetworkTransmit;
  SimpleNetworkDevice->SimpleNetwork.Receive        = Undi16SimpleNetworkReceive;
  SimpleNetworkDevice->SimpleNetwork.Mode           = &(SimpleNetworkDevice->SimpleNetworkMode);

  Status = gBS->CreateEvent (
                  EVT_NOTIFY_WAIT,
                  TPL_NOTIFY,
                  Undi16SimpleNetworkWaitForPacket,
                  &SimpleNetworkDevice->SimpleNetwork,
                  &SimpleNetworkDevice->SimpleNetwork.WaitForPacket
                  );
  if (EFI_ERROR (Status)) {
    DEBUG ((DEBUG_ERROR, "ERROR : Could not create event.  Status = %r\n", Status));
    goto Done;
  }
  //
  // Create an event to be signalled when ExitBootServices occurs in order
  // to clean up nicely
  //
  Status = gBS->CreateEventEx (
                  EVT_NOTIFY_SIGNAL,
                  TPL_NOTIFY,
                  Undi16SimpleNetworkEvent,
                  NULL,
                  &gEfiEventExitBootServicesGuid,
                  &SimpleNetworkDevice->EfiBootEvent
                  );
  if (EFI_ERROR (Status)) {
    DEBUG ((DEBUG_ERROR, "ERROR : Could not create event.  Status = %r\n", Status));
    goto Done;
  }

  //
  // Create an event to be signalled when Legacy Boot occurs to clean up the IVT
  //
  Status = EfiCreateEventLegacyBootEx(
             TPL_NOTIFY, 
             Undi16SimpleNetworkEvent, 
             NULL, 
             &SimpleNetworkDevice->LegacyBootEvent
             );
  
  if (EFI_ERROR(Status)) {
    DEBUG ((DEBUG_ERROR,"ERROR : Could not create event.  Status = %r\n",Status));
    goto Done;
  }

  //
  // Initialize the SimpleNetwork Mode Information
  //
  DEBUG ((DEBUG_NET, "Initialize Mode Information\n"));

  SimpleNetworkDevice->SimpleNetworkMode.State                = EfiSimpleNetworkStopped;
  SimpleNetworkDevice->SimpleNetworkMode.MediaHeaderSize      = 14;
  SimpleNetworkDevice->SimpleNetworkMode.MacAddressChangeable = TRUE;
  SimpleNetworkDevice->SimpleNetworkMode.MultipleTxSupported  = TRUE;
  SimpleNetworkDevice->SimpleNetworkMode.ReceiveFilterMask = EFI_SIMPLE_NETWORK_RECEIVE_UNICAST |
    EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST |
    EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST |
    EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS |
    EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST;
  SimpleNetworkDevice->SimpleNetworkMode.MaxMCastFilterCount = MAXNUM_MCADDR;

  //
  // Initialize the SimpleNetwork Private Information
  //
  DEBUG ((DEBUG_NET, "Initialize Private Information\n"));

  Status = BiosSnp16AllocatePagesBelowOneMb (
             sizeof (PXENV_UNDI_TBD_T) / EFI_PAGE_SIZE + 1,
             (VOID **) &SimpleNetworkDevice->Xmit
             );
  if (EFI_ERROR (Status)) {
    goto Done;
  }

  Status = BiosSnp16AllocatePagesBelowOneMb (
             1,
             &SimpleNetworkDevice->TxRealModeMediaHeader
             );
  if (EFI_ERROR (Status)) {
    goto Done;
  }

  Status = BiosSnp16AllocatePagesBelowOneMb (
             1,
             &SimpleNetworkDevice->TxRealModeDataBuffer
             );
  if (EFI_ERROR (Status)) {
    goto Done;
  }

  Status = BiosSnp16AllocatePagesBelowOneMb (
             1,
             &SimpleNetworkDevice->TxDestAddr
             );
  if (EFI_ERROR (Status)) {
    goto Done;
  }

  SimpleNetworkDevice->Xmit->XmitOffset               = (UINT16) (((UINT32)(UINTN) SimpleNetworkDevice->TxRealModeMediaHeader) & 0x000f);

  SimpleNetworkDevice->Xmit->XmitSegment              = (UINT16) (((UINT32)(UINTN) SimpleNetworkDevice->TxRealModeMediaHeader) >> 4);

  SimpleNetworkDevice->Xmit->DataBlkCount             = 1;

  SimpleNetworkDevice->Xmit->DataBlock[0].TDPtrType   = 1;
  SimpleNetworkDevice->Xmit->DataBlock[0].TDRsvdByte  = 0;

  SimpleNetworkDevice->Xmit->DataBlock[0].TDDataPtrOffset = (UINT16) (((UINT32)(UINTN) SimpleNetworkDevice->TxRealModeDataBuffer) & 0x000f);

  SimpleNetworkDevice->Xmit->DataBlock[0].TDDataPtrSegment = (UINT16) (((UINT32)(UINTN) SimpleNetworkDevice->TxRealModeDataBuffer) >> 4);

  SimpleNetworkDevice->TxBufferFifo.First = 0;
  SimpleNetworkDevice->TxBufferFifo.Last  = 0;

  //
  // Start() the SimpleNetwork device
  //
  DEBUG ((DEBUG_NET, "Start()\n"));

  Status = Undi16SimpleNetworkStart (&SimpleNetworkDevice->SimpleNetwork);
  if (EFI_ERROR (Status)) {
    goto Done;
  }
  //
  // GetInformation() the SimpleNetwork device
  //
  DEBUG ((DEBUG_NET, "GetInformation()\n"));

  Status = Undi16SimpleNetworkGetInformation (&SimpleNetworkDevice->SimpleNetwork);
  if (EFI_ERROR (Status)) {
    goto Done;
  }
  //
  // Build the device path for the child device
  //
  ZeroMem (&Node, sizeof (Node));
  Node.DevPath.Type     = MESSAGING_DEVICE_PATH;
  Node.DevPath.SubType  = MSG_MAC_ADDR_DP;
  SetDevicePathNodeLength (&Node.DevPath, sizeof (MAC_ADDR_DEVICE_PATH));
  CopyMem (
    &Node.MacAddr.MacAddress,
    &SimpleNetworkDevice->SimpleNetworkMode.CurrentAddress,
    sizeof (EFI_MAC_ADDRESS)
    );
  SimpleNetworkDevice->DevicePath = AppendDevicePathNode (
                                      SimpleNetworkDevice->BaseDevicePath,
                                      &Node.DevPath
                                      );

  //
  // GetNicType()  the SimpleNetwork device
  //
  DEBUG ((DEBUG_NET, "GetNicType()\n"));

  Status = Undi16SimpleNetworkGetNicType (&SimpleNetworkDevice->SimpleNetwork);
  if (EFI_ERROR (Status)) {
    goto Done;
  }
  //
  // GetNdisInfo() the SimpleNetwork device
  //
  DEBUG ((DEBUG_NET, "GetNdisInfo()\n"));

  Status = Undi16SimpleNetworkGetNdisInfo (&SimpleNetworkDevice->SimpleNetwork);
  if (EFI_ERROR (Status)) {
    goto Done;
  }
  //
  // Stop() the SimpleNetwork device
  //
  DEBUG ((DEBUG_NET, "Stop()\n"));

  Status = SimpleNetworkDevice->SimpleNetwork.Stop (&SimpleNetworkDevice->SimpleNetwork);
  if (EFI_ERROR (Status)) {
    goto Done;
  }
  //
  // Print Mode information
  //
  DEBUG ((DEBUG_NET, "Mode->State                = %d\n", SimpleNetworkDevice->SimpleNetworkMode.State));
  DEBUG ((DEBUG_NET, "Mode->HwAddressSize        = %d\n", SimpleNetworkDevice->SimpleNetworkMode.HwAddressSize));
  DEBUG ((DEBUG_NET, "Mode->MacAddressChangeable = %d\n", SimpleNetworkDevice->SimpleNetworkMode.MacAddressChangeable));
  DEBUG ((DEBUG_NET, "Mode->MultiplTxSupported   = %d\n", SimpleNetworkDevice->SimpleNetworkMode.MultipleTxSupported));
  DEBUG ((DEBUG_NET, "Mode->NvRamSize            = %d\n", SimpleNetworkDevice->SimpleNetworkMode.NvRamSize));
  DEBUG ((DEBUG_NET, "Mode->NvRamAccessSize      = %d\n", SimpleNetworkDevice->SimpleNetworkMode.NvRamAccessSize));
  DEBUG ((DEBUG_NET, "Mode->ReceiveFilterSetting = %d\n", SimpleNetworkDevice->SimpleNetworkMode.ReceiveFilterSetting));
  DEBUG ((DEBUG_NET, "Mode->IfType               = %d\n", SimpleNetworkDevice->SimpleNetworkMode.IfType));
  DEBUG ((DEBUG_NET, "Mode->MCastFilterCount     = %d\n", SimpleNetworkDevice->SimpleNetworkMode.MCastFilterCount));
  for (Index = 0; Index < SimpleNetworkDevice->SimpleNetworkMode.MCastFilterCount; Index++) {
    DEBUG ((DEBUG_NET, "  Filter[%02d] = ", Index));
    for (Index2 = 0; Index2 < 16; Index2++) {
      DEBUG ((DEBUG_NET, "%02x ", SimpleNetworkDevice->SimpleNetworkMode.MCastFilter[Index].Addr[Index2]));
    }

    DEBUG ((DEBUG_NET, "\n"));
  }

  DEBUG ((DEBUG_NET, "CurrentAddress = "));
  for (Index2 = 0; Index2 < 16; Index2++) {
    DEBUG ((DEBUG_NET, "%02x ", SimpleNetworkDevice->SimpleNetworkMode.CurrentAddress.Addr[Index2]));
  }

  DEBUG ((DEBUG_NET, "\n"));

  DEBUG ((DEBUG_NET, "BroadcastAddress = "));
  for (Index2 = 0; Index2 < 16; Index2++) {
    DEBUG ((DEBUG_NET, "%02x ", SimpleNetworkDevice->SimpleNetworkMode.BroadcastAddress.Addr[Index2]));
  }

  DEBUG ((DEBUG_NET, "\n"));

  DEBUG ((DEBUG_NET, "PermanentAddress = "));
  for (Index2 = 0; Index2 < 16; Index2++) {
    DEBUG ((DEBUG_NET, "%02x ", SimpleNetworkDevice->SimpleNetworkMode.PermanentAddress.Addr[Index2]));
  }

  DEBUG ((DEBUG_NET, "\n"));

  //
  // The network device was started, information collected, and stopped.
  // Install protocol interfaces for the SimpleNetwork device.
  //
  DEBUG ((DEBUG_NET, "Install Protocol Interfaces on network interface\n"));

  Status = gBS->InstallMultipleProtocolInterfaces (
                  &SimpleNetworkDevice->Handle,
                  &gEfiSimpleNetworkProtocolGuid,
                  &SimpleNetworkDevice->SimpleNetwork,
                  &gEfiNetworkInterfaceIdentifierProtocolGuid,
                  &SimpleNetworkDevice->Nii,
                  &gEfiDevicePathProtocolGuid,
                  SimpleNetworkDevice->DevicePath,
                  NULL
                  );
  if (EFI_ERROR (Status)) {
    goto Done;
  }
  //
  // Open PCI I/O from the newly created child handle
  //
  Status = gBS->OpenProtocol (
                  Controller,
                  &gEfiPciIoProtocolGuid,
                  (VOID **) &PciIo,
                  This->DriverBindingHandle,
                  SimpleNetworkDevice->Handle,
                  EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
                  );

  DEBUG ((DEBUG_INIT, "UNDI16 Driver : EFI_SUCCESS\n"));

Done:
  if (EFI_ERROR (Status)) {
    if (SimpleNetworkDevice != NULL) {

      Undi16SimpleNetworkShutdown (&SimpleNetworkDevice->SimpleNetwork);
      //
      // CLOSE + SHUTDOWN
      //
      Undi16SimpleNetworkCleanupUndi (SimpleNetworkDevice);
      //
      // CLEANUP
      //
      Undi16SimpleNetworkStopUndi (SimpleNetworkDevice);
      //
      // STOP
      //
      if (SimpleNetworkDevice->UndiLoaded) {
        Undi16SimpleNetworkUnloadUndi (SimpleNetworkDevice);
      }

      if (SimpleNetworkDevice->SimpleNetwork.WaitForPacket != NULL) {
        gBS->CloseEvent (SimpleNetworkDevice->SimpleNetwork.WaitForPacket);
      }

      if (SimpleNetworkDevice->LegacyBootEvent != NULL) {
        gBS->CloseEvent (SimpleNetworkDevice->LegacyBootEvent);
      }
      
      if (SimpleNetworkDevice->EfiBootEvent != NULL) {
        gBS->CloseEvent (SimpleNetworkDevice->EfiBootEvent);
      }

      if (SimpleNetworkDevice->Xmit != NULL) {
        gBS->FreePages (
               (EFI_PHYSICAL_ADDRESS) (UINTN) SimpleNetworkDevice->Xmit,
               sizeof (PXENV_UNDI_TBD_T) / EFI_PAGE_SIZE + 1
               );
      }

      if (SimpleNetworkDevice->TxRealModeMediaHeader != NULL) {
        gBS->FreePages ((EFI_PHYSICAL_ADDRESS) (UINTN) SimpleNetworkDevice->TxRealModeMediaHeader, 1);
      }

      if (SimpleNetworkDevice->TxRealModeDataBuffer != NULL) {
        gBS->FreePages ((EFI_PHYSICAL_ADDRESS) (UINTN) SimpleNetworkDevice->TxRealModeDataBuffer, 1);
      }

      if (SimpleNetworkDevice->TxDestAddr != NULL) {
        gBS->FreePages ((EFI_PHYSICAL_ADDRESS) (UINTN) SimpleNetworkDevice->TxDestAddr, 1);
      }

      gBS->FreePool (SimpleNetworkDevice);
      
      //
      //  Only restore the vector if it was cached.
      //
      if (mCachedInt1A) {
        RestoreCachedVectorAddress (0x1A);
        mCachedInt1A = FALSE;
      }
    }

    if (PciIo != NULL) {
      Status = PciIo->Attributes (
                        PciIo,
                        EfiPciIoAttributeOperationSupported,
                        0,
                        &Supports
                        );
      if (!EFI_ERROR (Status)) {
        Supports &= (UINT64)EFI_PCI_DEVICE_ENABLE;
        Status = PciIo->Attributes (
                          PciIo,
                          EfiPciIoAttributeOperationDisable,
                          Supports,
                          NULL
                          );
      }
    }

    gBS->CloseProtocol (
           Controller,
           &gEfiPciIoProtocolGuid,
           This->DriverBindingHandle,
           Controller
           );

    gBS->CloseProtocol (
           Controller,
           &gEfiDevicePathProtocolGuid,
           This->DriverBindingHandle,
           Controller
           );
    if (Status != EFI_OUT_OF_RESOURCES) {
      Status = EFI_DEVICE_ERROR;
    }
  }  
  return Status;
}

/**
  Stops the device by given device controller.

  @param This               A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
  @param Controller         The handle of the controller to test.
  @param NumberOfChildren   The number of child device handles in ChildHandleBuffer.
  @param ChildHandleBuffer  An array of child handles to be freed. May be NULL if
                            NumberOfChildren is 0.
  
  @retval  EFI_SUCCESS      - The device was stopped.
  @retval  EFI_DEVICE_ERROR - The device could not be stopped due to a device error.
**/
EFI_STATUS
EFIAPI
BiosSnp16DriverBindingStop (
  IN  EFI_DRIVER_BINDING_PROTOCOL     *This,
  IN  EFI_HANDLE                      Controller,
  IN  UINTN                           NumberOfChildren,
  IN  EFI_HANDLE                      *ChildHandleBuffer
  )
{
  EFI_STATUS                  Status;
  UINTN                       Index;
  BOOLEAN                     AllChildrenStopped;
  EFI_SIMPLE_NETWORK_PROTOCOL *SimpleNetwork;
  EFI_SIMPLE_NETWORK_DEV      *SimpleNetworkDevice;
  EFI_PCI_IO_PROTOCOL         *PciIo;
  UINT64                      Supports;

  //
  // Complete all outstanding transactions to Controller.
  // Don't allow any new transaction to Controller to be started.
  //
  if (NumberOfChildren == 0) {
    //
    // Close the bus driver
    //
    Status = gBS->OpenProtocol (
                    Controller,
                    &gEfiPciIoProtocolGuid,
                    (VOID **) &PciIo,
                    This->DriverBindingHandle,
                    Controller,
                    EFI_OPEN_PROTOCOL_GET_PROTOCOL
                    );
    if (!EFI_ERROR (Status)) {
      Status = PciIo->Attributes (
                        PciIo,
                        EfiPciIoAttributeOperationSupported,
                        0,
                        &Supports
                        );
      if (!EFI_ERROR (Status)) {
        Supports &= (UINT64)EFI_PCI_DEVICE_ENABLE;
        Status = PciIo->Attributes (
                          PciIo,
                          EfiPciIoAttributeOperationDisable,
                          Supports,
                          NULL
                          );
      }
    }

    Status = gBS->CloseProtocol (
                    Controller,
                    &gEfiPciIoProtocolGuid,
                    This->DriverBindingHandle,
                    Controller
                    );

    Status = gBS->CloseProtocol (
                    Controller,
                    &gEfiDevicePathProtocolGuid,
                    This->DriverBindingHandle,
                    Controller
                    );
                    
    if (EFI_ERROR (Status)) {
      Status = EFI_DEVICE_ERROR;
    }
    return Status;
  }

  AllChildrenStopped = TRUE;

  for (Index = 0; Index < NumberOfChildren; Index++) {

    Status = gBS->OpenProtocol (
                    ChildHandleBuffer[Index],
                    &gEfiSimpleNetworkProtocolGuid,
                    (VOID **) &SimpleNetwork,
                    This->DriverBindingHandle,
                    Controller,
                    EFI_OPEN_PROTOCOL_GET_PROTOCOL
                    );
    if (!EFI_ERROR (Status)) {

      SimpleNetworkDevice = EFI_SIMPLE_NETWORK_DEV_FROM_THIS (SimpleNetwork);

      Status = gBS->CloseProtocol (
                      Controller,
                      &gEfiPciIoProtocolGuid,
                      This->DriverBindingHandle,
                      ChildHandleBuffer[Index]
                      );

      Status = gBS->UninstallMultipleProtocolInterfaces (
                      SimpleNetworkDevice->Handle,
                      &gEfiSimpleNetworkProtocolGuid,
                      &SimpleNetworkDevice->SimpleNetwork,
                      &gEfiNetworkInterfaceIdentifierProtocolGuid,
                      &SimpleNetworkDevice->Nii,
                      &gEfiDevicePathProtocolGuid,
                      SimpleNetworkDevice->DevicePath,
                      NULL
                      );
      if (EFI_ERROR (Status)) {
        gBS->OpenProtocol (
               Controller,
               &gEfiPciIoProtocolGuid,
               (VOID **) &PciIo,
               This->DriverBindingHandle,
               ChildHandleBuffer[Index],
               EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
               );
      } else {

        Undi16SimpleNetworkShutdown (&SimpleNetworkDevice->SimpleNetwork);
        //
        // CLOSE + SHUTDOWN
        //
        Undi16SimpleNetworkCleanupUndi (SimpleNetworkDevice);
        //
        // CLEANUP
        //
        Undi16SimpleNetworkStopUndi (SimpleNetworkDevice);
        //
        // STOP
        //
        if (SimpleNetworkDevice->UndiLoaded) {
          Undi16SimpleNetworkUnloadUndi (SimpleNetworkDevice);
        }

        if (SimpleNetworkDevice->SimpleNetwork.WaitForPacket != NULL) {
          gBS->CloseEvent (SimpleNetworkDevice->SimpleNetwork.WaitForPacket);
        }

        if (SimpleNetworkDevice->LegacyBootEvent != NULL) {
          gBS->CloseEvent (SimpleNetworkDevice->LegacyBootEvent);
        }
      
        if (SimpleNetworkDevice->EfiBootEvent != NULL) {
          gBS->CloseEvent (SimpleNetworkDevice->EfiBootEvent);
        }

        if (SimpleNetworkDevice->Xmit != NULL) {
          gBS->FreePages (
                 (EFI_PHYSICAL_ADDRESS) (UINTN) SimpleNetworkDevice->Xmit,
                 sizeof (PXENV_UNDI_TBD_T) / EFI_PAGE_SIZE + 1
                 );
        }

        if (SimpleNetworkDevice->TxRealModeMediaHeader != NULL) {
          gBS->FreePages ((EFI_PHYSICAL_ADDRESS) (UINTN) SimpleNetworkDevice->TxRealModeMediaHeader, 1);
        }

        if (SimpleNetworkDevice->TxRealModeDataBuffer != NULL) {
          gBS->FreePages ((EFI_PHYSICAL_ADDRESS) (UINTN) SimpleNetworkDevice->TxRealModeDataBuffer, 1);
        }

        if (SimpleNetworkDevice->TxDestAddr != NULL) {
          gBS->FreePages ((EFI_PHYSICAL_ADDRESS) (UINTN) SimpleNetworkDevice->TxDestAddr, 1);
        }

        gBS->FreePool (SimpleNetworkDevice);
      }

    }

    if (EFI_ERROR (Status)) {
      AllChildrenStopped = FALSE;
    }
  }

  if (!AllChildrenStopped) {
    return EFI_DEVICE_ERROR;
  }

  return EFI_SUCCESS;
}

//
// FIFO Support Functions
//
/**
  Judge whether transmit FIFO is full.

  @param Fifo Point to trasmit FIFO structure.
  
  @return BOOLEAN whether transmit FIFO is full.
**/
BOOLEAN
SimpleNetworkTransmitFifoFull (
  EFI_SIMPLE_NETWORK_DEV_FIFO *Fifo
  )
{
  if (((Fifo->Last + 1) % EFI_SIMPLE_NETWORK_MAX_TX_FIFO_SIZE) == Fifo->First) {
    return TRUE;
  }

  return FALSE;
}

/**
  Judge whether transmit FIFO is empty.

  @param Fifo Point to trasmit FIFO structure.
  
  @return BOOLEAN whether transmit FIFO is empty.
**/
BOOLEAN
SimpleNetworkTransmitFifoEmpty (
  EFI_SIMPLE_NETWORK_DEV_FIFO *Fifo
  )
{
  if (Fifo->Last == Fifo->First) {
    return TRUE;
  }

  return FALSE;
}


/**
  Add data into transmit buffer.

  @param Fifo Point to trasmit FIFO structure.
  @param Data The data point want to be added.
  
  @retval EFI_OUT_OF_RESOURCES  FIFO is full 
  @retval EFI_SUCCESS           Success operation. 
**/
EFI_STATUS
SimpleNetworkTransmitFifoAdd (
  EFI_SIMPLE_NETWORK_DEV_FIFO *Fifo,
  VOID                        *Data
  )
{
  if (SimpleNetworkTransmitFifoFull (Fifo)) {
    return EFI_OUT_OF_RESOURCES;
  }

  Fifo->Data[Fifo->Last]  = Data;
  Fifo->Last              = (Fifo->Last + 1) % EFI_SIMPLE_NETWORK_MAX_TX_FIFO_SIZE;
  return EFI_SUCCESS;
}

/**
  Get a data and remove it from network transmit FIFO.

  @param Fifo Point to trasmit FIFO structure.
  @param Data On return, point to the data point want to be got and removed.
  
  @retval EFI_OUT_OF_RESOURCES network transmit buffer is empty. 
  @retval EFI_SUCCESS           Success operation. 
**/
EFI_STATUS
SimpleNetworkTransmitFifoRemove (
  EFI_SIMPLE_NETWORK_DEV_FIFO *Fifo,
  VOID                        **Data
  )
{
  if (SimpleNetworkTransmitFifoEmpty (Fifo)) {
    return EFI_OUT_OF_RESOURCES;
  }

  *Data       = Fifo->Data[Fifo->First];
  Fifo->First = (Fifo->First + 1) % EFI_SIMPLE_NETWORK_MAX_TX_FIFO_SIZE;
  return EFI_SUCCESS;
}

/**
  Get recive filter setting according to EFI mask value.

  @param ReceiveFilterSetting  filter setting EFI mask value.
  
  @return UINT16 Undi filter setting value.
**/
UINT16
Undi16GetPacketFilterSetting (
  UINTN ReceiveFilterSetting
  )
{
  UINT16  PktFilter;

  PktFilter = 0;
  if ((ReceiveFilterSetting & EFI_SIMPLE_NETWORK_RECEIVE_UNICAST) != 0) {
    PktFilter |= FLTR_DIRECTED;
  }

  if ((ReceiveFilterSetting & EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST) != 0) {
    PktFilter |= FLTR_DIRECTED;
  }

  if ((ReceiveFilterSetting & EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST) != 0) {
    PktFilter |= FLTR_BRDCST;
  }

  if ((ReceiveFilterSetting & EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS) != 0) {
    PktFilter |= FLTR_PRMSCS;
  }

  if ((ReceiveFilterSetting & EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST) != 0) {
    PktFilter |= FLTR_PRMSCS;
    //
    // @bug : Do not know if this is right????
    //
  }
  //
  // @bug : What is FLTR_SRC_RTG?
  //
  return PktFilter;
}

/**
  Get filter setting from multi cast buffer .
   
  @param Mode           Point to mode structure.
  @param McastBuffer    The multi cast buffer 
  @param HwAddressSize  Size of filter value.
  
**/
VOID
Undi16GetMCastFilters (
  IN EFI_SIMPLE_NETWORK_MODE      *Mode,
  IN OUT PXENV_UNDI_MCAST_ADDR_T  *McastBuffer,
  IN UINTN                        HwAddressSize
  )
{
  UINTN Index;

  //
  // @bug : What if Mode->MCastFilterCount > MAXNUM_MCADDR?
  //
  McastBuffer->MCastAddrCount = (UINT16) Mode->MCastFilterCount;
  for (Index = 0; Index < MAXNUM_MCADDR; Index++) {
    if (Index < McastBuffer->MCastAddrCount) {
      CopyMem (&McastBuffer->MCastAddr[Index], &Mode->MCastFilter[Index], HwAddressSize);
    } else {
      ZeroMem (&McastBuffer->MCastAddr[Index], HwAddressSize);
    }
  }
}
//
// Load 16 bit UNDI Option ROM into memory
//
/**
  Loads the undi driver.

  @param SimpleNetworkDevice A pointer to EFI_SIMPLE_NETWORK_DEV data structure.
  
  @retval   EFI_SUCCESS   - Successfully loads undi driver.
  @retval   EFI_NOT_FOUND - Doesn't find undi driver or undi driver load failure.
**/
EFI_STATUS
Undi16SimpleNetworkLoadUndi (
  EFI_SIMPLE_NETWORK_DEV  *SimpleNetworkDevice
  )
{
  EFI_STATUS                Status;
  EFI_PCI_IO_PROTOCOL       *PciIo;
  UINTN                     RomAddress;
  PCI_EXPANSION_ROM_HEADER  *PciExpansionRomHeader;
  PCI_DATA_STRUCTURE        *PciDataStructure;
  PCI_TYPE00                Pci;
  
  if (!mCachedInt1A) {
    Status = CacheVectorAddress (0x1A);
    if (!EFI_ERROR (Status)) {
      mCachedInt1A = TRUE;    
    }
  }

  PciIo = SimpleNetworkDevice->PciIo;

  PciIo->Pci.Read (
               PciIo,
               EfiPciIoWidthUint32,
               0,
               sizeof (Pci) / sizeof (UINT32),
               &Pci
               );

  for (RomAddress = 0xc0000; RomAddress < 0xfffff; RomAddress += 0x800) {

    PciExpansionRomHeader = (PCI_EXPANSION_ROM_HEADER *) RomAddress;

    if (PciExpansionRomHeader->Signature != PCI_EXPANSION_ROM_HEADER_SIGNATURE) {
      continue;
    }

    DEBUG ((DEBUG_INIT, "Option ROM found at %X\n", RomAddress));

    //
    // If the pointer to the PCI Data Structure is invalid, no further images can be located. 
    // The PCI Data Structure must be DWORD aligned. 
    //
    if (PciExpansionRomHeader->PcirOffset == 0 ||
        (PciExpansionRomHeader->PcirOffset & 3) != 0 ||
        RomAddress + PciExpansionRomHeader->PcirOffset + sizeof (PCI_DATA_STRUCTURE) > 0x100000) {
      break;
    }

    PciDataStructure = (PCI_DATA_STRUCTURE *) (RomAddress + PciExpansionRomHeader->PcirOffset);

    if (PciDataStructure->Signature != PCI_DATA_STRUCTURE_SIGNATURE) {
      continue;
    }

    DEBUG ((DEBUG_INIT, "PCI Data Structure found at %X\n", PciDataStructure));

    if (PciDataStructure->VendorId != Pci.Hdr.VendorId || PciDataStructure->DeviceId != Pci.Hdr.DeviceId) {
      continue;
    }

    DEBUG (
        (DEBUG_INIT, 
         "PCI device with matchinng VendorId and DeviceId (%d,%d)\n",
         (UINTN) PciDataStructure->VendorId,
         (UINTN) PciDataStructure->DeviceId)
        );

    Status = LaunchBaseCode (SimpleNetworkDevice, RomAddress);

    if (!EFI_ERROR (Status)) {
      return EFI_SUCCESS;
    }
    
    //
    // Free resources allocated in LaunchBaseCode
    //
    Undi16SimpleNetworkUnloadUndi (SimpleNetworkDevice);
  }

  return EFI_NOT_FOUND;
}

/**
  Unload 16 bit UNDI Option ROM from memory

  @param SimpleNetworkDevice A pointer to EFI_SIMPLE_NETWORK_DEV data structure.
  
  @return EFI_STATUS 
**/
EFI_STATUS
Undi16SimpleNetworkUnloadUndi (
  EFI_SIMPLE_NETWORK_DEV  *SimpleNetworkDevice
  )
{
  if (SimpleNetworkDevice->UndiLoaderTable != NULL) {
    ZeroMem (SimpleNetworkDevice->UndiLoaderTable, SimpleNetworkDevice->UndiLoaderTablePages << EFI_PAGE_SHIFT);
    gBS->FreePages (
          (EFI_PHYSICAL_ADDRESS) (UINTN) SimpleNetworkDevice->UndiLoaderTable,
          SimpleNetworkDevice->UndiLoaderTablePages
          );
  }

  if (SimpleNetworkDevice->DestinationDataSegment != NULL) {
    ZeroMem (
      SimpleNetworkDevice->DestinationDataSegment,
      SimpleNetworkDevice->DestinationDataSegmentPages << EFI_PAGE_SHIFT
      );
    gBS->FreePages (
          (EFI_PHYSICAL_ADDRESS) (UINTN) SimpleNetworkDevice->DestinationDataSegment,
          SimpleNetworkDevice->DestinationDataSegmentPages
          );
  }

  if (SimpleNetworkDevice->DestinationStackSegment != NULL) {
    ZeroMem (
      SimpleNetworkDevice->DestinationStackSegment,
      SimpleNetworkDevice->DestinationStackSegmentPages << EFI_PAGE_SHIFT
      );
    gBS->FreePages (
          (EFI_PHYSICAL_ADDRESS) (UINTN) SimpleNetworkDevice->DestinationStackSegment,
          SimpleNetworkDevice->DestinationStackSegmentPages
          );
  }

  if (SimpleNetworkDevice->DestinationCodeSegment != NULL) {
    ZeroMem (
      SimpleNetworkDevice->DestinationCodeSegment,
      SimpleNetworkDevice->DestinationCodeSegmentPages << EFI_PAGE_SHIFT
      );
    gBS->FreePages (
          (EFI_PHYSICAL_ADDRESS) (UINTN) SimpleNetworkDevice->DestinationCodeSegment,
          SimpleNetworkDevice->DestinationCodeSegmentPages
          );
  }

  return EFI_SUCCESS;
}

/**
  Start the UNDI interface.

  @param SimpleNetworkDevice A pointer to EFI_SIMPLE_NETWORK_DEV data structure.
  @param Ax                  PCI address of Undi device.
  
  @retval EFI_DEVICE_ERROR Fail to start 16 bit UNDI ROM. 
  @retval Others           Status of start 16 bit UNDI ROM. 
**/
EFI_STATUS
Undi16SimpleNetworkStartUndi (
  EFI_SIMPLE_NETWORK_DEV  *SimpleNetworkDevice,
  UINT16                  Ax
  )
{
  EFI_STATUS          Status;
  PXENV_START_UNDI_T  Start;

  //
  // Call 16 bit UNDI ROM to start the network interface
  //
  //
  // @bug : What is this state supposed to be???
  //
  Start.Status  = INIT_PXE_STATUS;
  Start.Ax      = Ax;
  Start.Bx      = 0x0000;
  Start.Dx      = 0x0000;
  Start.Di      = 0x0000;
  Start.Es      = 0x0000;

  Status        = PxeStartUndi (SimpleNetworkDevice, &Start);
  if (EFI_ERROR (Status)) {
    return Status;
  }
  //
  // Check the status code from the 16 bit UNDI ROM
  //
  if (Start.Status != PXENV_STATUS_SUCCESS) {
    return EFI_DEVICE_ERROR;
  }

  return Status;
}


/**
  Stop the UNDI interface

  @param SimpleNetworkDevice A pointer to EFI_SIMPLE_NETWORK_DEV data structure.
  
  @retval EFI_DEVICE_ERROR Fail to stop 16 bit UNDI ROM. 
  @retval Others           Status of stop 16 bit UNDI ROM. 
**/
EFI_STATUS
Undi16SimpleNetworkStopUndi (
  EFI_SIMPLE_NETWORK_DEV  *SimpleNetworkDevice
  )
{
  EFI_STATUS        Status;
  PXENV_STOP_UNDI_T Stop;

  //
  // Call 16 bit UNDI ROM to start the network interface
  //
  Stop.Status = INIT_PXE_STATUS;

  Status      = PxeUndiStop (SimpleNetworkDevice, &Stop);
  if (EFI_ERROR (Status)) {
    return Status;
  }
  //
  // Check the status code from the 16 bit UNDI ROM
  //
  if (Stop.Status != PXENV_STATUS_SUCCESS) {
    return EFI_DEVICE_ERROR;
  }

  return Status;
}

/**
  Cleanup Unid network interface

  @param SimpleNetworkDevice A pointer to EFI_SIMPLE_NETWORK_DEV data structure.
  
  @retval EFI_DEVICE_ERROR Fail to cleanup 16 bit UNDI ROM. 
  @retval Others           Status of cleanup 16 bit UNDI ROM. 
**/
EFI_STATUS
Undi16SimpleNetworkCleanupUndi (
  EFI_SIMPLE_NETWORK_DEV  *SimpleNetworkDevice
  )
{
  EFI_STATUS            Status;
  PXENV_UNDI_CLEANUP_T  Cleanup;

  //
  // Call 16 bit UNDI ROM to cleanup the network interface
  //
  Cleanup.Status  = INIT_PXE_STATUS;

  Status          = PxeUndiCleanup (SimpleNetworkDevice, &Cleanup);
  if (EFI_ERROR (Status)) {
    return Status;
  }
  //
  // Check the status code from the 16 bit UNDI ROM
  //
  if (Cleanup.Status != PXENV_STATUS_SUCCESS) {
    return EFI_DEVICE_ERROR;
  }

  return Status;
}

/**
  Get runtime information for Undi network interface

  @param This A pointer to EFI_SIMPLE_NETWORK_PROTOCOL structure.
  
  @retval EFI_SUCCESS Sucess operation. 
  @retval Others      Fail to get runtime information for Undi network interface. 
**/
EFI_STATUS
Undi16SimpleNetworkGetInformation (
  IN EFI_SIMPLE_NETWORK_PROTOCOL  *This
  )
{
  EFI_STATUS              Status;
  EFI_SIMPLE_NETWORK_DEV  *SimpleNetworkDevice;
  UINTN                   Index;

  if (This == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  Status              = EFI_SUCCESS;
  SimpleNetworkDevice = EFI_SIMPLE_NETWORK_DEV_FROM_THIS (This);

  if (SimpleNetworkDevice == NULL) {
    return EFI_DEVICE_ERROR;
  }
  //
  // Verify that the current state of the adapter is valid for this call.
  //
  switch (SimpleNetworkDevice->SimpleNetworkMode.State) {
  case EfiSimpleNetworkStarted:
  case EfiSimpleNetworkInitialized:
    break;

  case EfiSimpleNetworkStopped:
    return EFI_NOT_STARTED;

  default:
    return EFI_DEVICE_ERROR;
  }
  //
  // Call 16 bit UNDI ROM to start the network interface
  //
  ZeroMem (&SimpleNetworkDevice->GetInformation, sizeof (PXENV_UNDI_GET_INFORMATION_T));

  SimpleNetworkDevice->GetInformation.Status  = INIT_PXE_STATUS;

  Status = PxeUndiGetInformation (SimpleNetworkDevice, &SimpleNetworkDevice->GetInformation);
  if (EFI_ERROR (Status)) {
    return Status;
  }

  DEBUG ((DEBUG_NET, "  GetInformation.Status      = %d\n", SimpleNetworkDevice->GetInformation.Status));
  DEBUG ((DEBUG_NET, "  GetInformation.BaseIo      = %d\n", SimpleNetworkDevice->GetInformation.BaseIo));
  DEBUG ((DEBUG_NET, "  GetInformation.IntNumber   = %d\n", SimpleNetworkDevice->GetInformation.IntNumber));
  DEBUG ((DEBUG_NET, "  GetInformation.MaxTranUnit = %d\n", SimpleNetworkDevice->GetInformation.MaxTranUnit));
  DEBUG ((DEBUG_NET, "  GetInformation.HwType      = %d\n", SimpleNetworkDevice->GetInformation.HwType));
  DEBUG ((DEBUG_NET, "  GetInformation.HwAddrLen   = %d\n", SimpleNetworkDevice->GetInformation.HwAddrLen));
  DEBUG ((DEBUG_NET, "  GetInformation.ROMAddress  = %d\n", SimpleNetworkDevice->GetInformation.ROMAddress));
  DEBUG ((DEBUG_NET, "  GetInformation.RxBufCt     = %d\n", SimpleNetworkDevice->GetInformation.RxBufCt));
  DEBUG ((DEBUG_NET, "  GetInformation.TxBufCt     = %d\n", SimpleNetworkDevice->GetInformation.TxBufCt));

  DEBUG ((DEBUG_NET, "  GetInformation.CurNodeAddr ="));
  for (Index = 0; Index < 16; Index++) {
    DEBUG ((DEBUG_NET, "%02x ", SimpleNetworkDevice->GetInformation.CurrentNodeAddress[Index]));
  }

  DEBUG ((DEBUG_NET, "\n"));

  DEBUG ((DEBUG_NET, "  GetInformation.PermNodeAddr ="));
  for (Index = 0; Index < 16; Index++) {
    DEBUG ((DEBUG_NET, "%02x ", SimpleNetworkDevice->GetInformation.PermNodeAddress[Index]));
  }

  DEBUG ((DEBUG_NET, "\n"));

  //
  // Check the status code from the 16 bit UNDI ROM
  //
  if (SimpleNetworkDevice->GetInformation.Status != PXENV_STATUS_SUCCESS) {
    return EFI_DEVICE_ERROR;
  }
  //
  // The information has been retrieved.  Fill in Mode data.
  //
  SimpleNetworkDevice->SimpleNetworkMode.HwAddressSize  = SimpleNetworkDevice->GetInformation.HwAddrLen;

  SimpleNetworkDevice->SimpleNetworkMode.MaxPacketSize  = SimpleNetworkDevice->GetInformation.MaxTranUnit;

  SimpleNetworkDevice->SimpleNetworkMode.IfType         = (UINT8) SimpleNetworkDevice->GetInformation.HwType;

  ZeroMem (
    &SimpleNetworkDevice->SimpleNetworkMode.CurrentAddress,
    sizeof SimpleNetworkDevice->SimpleNetworkMode.CurrentAddress
    );

  CopyMem (
    &SimpleNetworkDevice->SimpleNetworkMode.CurrentAddress,
    &SimpleNetworkDevice->GetInformation.CurrentNodeAddress,
    SimpleNetworkDevice->SimpleNetworkMode.HwAddressSize
    );

  ZeroMem (
    &SimpleNetworkDevice->SimpleNetworkMode.PermanentAddress,
    sizeof SimpleNetworkDevice->SimpleNetworkMode.PermanentAddress
    );

  CopyMem (
    &SimpleNetworkDevice->SimpleNetworkMode.PermanentAddress,
    &SimpleNetworkDevice->GetInformation.PermNodeAddress,
    SimpleNetworkDevice->SimpleNetworkMode.HwAddressSize
    );

  //
  // hard code broadcast address - not avail in PXE2.1
  //
  ZeroMem (
    &SimpleNetworkDevice->SimpleNetworkMode.BroadcastAddress,
    sizeof SimpleNetworkDevice->SimpleNetworkMode.BroadcastAddress
    );

  SetMem (
    &SimpleNetworkDevice->SimpleNetworkMode.BroadcastAddress,
    SimpleNetworkDevice->SimpleNetworkMode.HwAddressSize,
    0xff
    );

  return Status;
}

/**
  Get NIC type

  @param This A pointer to EFI_SIMPLE_NETWORK_PROTOCOL structure.
  
  @retval EFI_SUCCESS Sucess operation. 
  @retval Others      Fail to get NIC type.
**/
EFI_STATUS
Undi16SimpleNetworkGetNicType (
  IN EFI_SIMPLE_NETWORK_PROTOCOL  *This
  )
{
  EFI_STATUS              Status;
  EFI_SIMPLE_NETWORK_DEV  *SimpleNetworkDevice;

  if (This == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  Status              = EFI_SUCCESS;
  SimpleNetworkDevice = EFI_SIMPLE_NETWORK_DEV_FROM_THIS (This);

  if (SimpleNetworkDevice == NULL) {
    return EFI_DEVICE_ERROR;
  }

  ZeroMem (&SimpleNetworkDevice->GetNicType, sizeof (PXENV_UNDI_GET_NIC_TYPE_T));

  SimpleNetworkDevice->GetNicType.Status  = INIT_PXE_STATUS;

  Status = PxeUndiGetNicType (SimpleNetworkDevice, &SimpleNetworkDevice->GetNicType);

  if (EFI_ERROR (Status)) {
    return Status;
  }

  DEBUG ((DEBUG_NET, "  GetNicType.Status      = %d\n", SimpleNetworkDevice->GetNicType.Status));
  DEBUG ((DEBUG_NET, "  GetNicType.NicType     = %d\n", SimpleNetworkDevice->GetNicType.NicType));
  //
  // Check the status code from the 16 bit UNDI ROM
  //
  if (SimpleNetworkDevice->GetNicType.Status != PXENV_STATUS_SUCCESS) {
    return EFI_DEVICE_ERROR;
  }
  //
  // The information has been retrieved.  Fill in Mode data.
  //
  return Status;
}

/**
  Get NDIS information

  @param This A pointer to EFI_SIMPLE_NETWORK_PROTOCOL structure.
  
  @retval EFI_SUCCESS Sucess operation. 
  @retval Others      Fail to get NDIS information.
**/
EFI_STATUS
Undi16SimpleNetworkGetNdisInfo (
  IN EFI_SIMPLE_NETWORK_PROTOCOL  *This
  )
{
  EFI_STATUS              Status;
  EFI_SIMPLE_NETWORK_DEV  *SimpleNetworkDevice;

  if (This == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  Status              = EFI_SUCCESS;
  SimpleNetworkDevice = EFI_SIMPLE_NETWORK_DEV_FROM_THIS (This);

  if (SimpleNetworkDevice == NULL) {
    return EFI_DEVICE_ERROR;
  }

  ZeroMem (&SimpleNetworkDevice->GetNdisInfo, sizeof (PXENV_UNDI_GET_NDIS_INFO_T));

  SimpleNetworkDevice->GetNdisInfo.Status = INIT_PXE_STATUS;

  Status = PxeUndiGetNdisInfo (SimpleNetworkDevice, &SimpleNetworkDevice->GetNdisInfo);

  if (EFI_ERROR (Status)) {
    return Status;
  }

  DEBUG ((DEBUG_NET, "  GetNdisInfo.Status       = %d\n", SimpleNetworkDevice->GetNdisInfo.Status));
  DEBUG ((DEBUG_NET, "  GetNdisInfo.IfaceType    = %a\n", SimpleNetworkDevice->GetNdisInfo.IfaceType));
  DEBUG ((DEBUG_NET, "  GetNdisInfo.LinkSpeed    = %d\n", SimpleNetworkDevice->GetNdisInfo.LinkSpeed));
  DEBUG ((DEBUG_NET, "  GetNdisInfo.ServiceFlags = %08x\n", SimpleNetworkDevice->GetNdisInfo.ServiceFlags));

  //
  // Check the status code from the 16 bit UNDI ROM
  //
  if (SimpleNetworkDevice->GetNdisInfo.Status != PXENV_STATUS_SUCCESS) {
    return EFI_DEVICE_ERROR;
  }
  //
  // The information has been retrieved.  Fill in Mode data.
  //
  return Status;
}

/**
  Call Undi ROM 16bit ISR() to check interrupt cause.

  @param This               A pointer to EFI_SIMPLE_NETWORK_PROTOCOL structure.
  @param FrameLength        The length of frame buffer.
  @param FrameHeaderLength  The length of frame buffer's header if has.
  @param Frame              The frame buffer to process network interrupt.
  @param ProtType           The type network transmit protocol
  @param PktType            The type of package.
  
  @retval EFI_DEVICE_ERROR  Fail to execute 16 bit ROM's ISR, or status is invalid. 
  @retval EFI_SUCCESS       Success operation. 
**/
EFI_STATUS
Undi16SimpleNetworkIsr (
  IN EFI_SIMPLE_NETWORK_PROTOCOL * This,
  IN UINTN                       *FrameLength,
  IN UINTN                       *FrameHeaderLength, OPTIONAL
  IN UINT8                       *Frame, OPTIONAL
  IN UINT8                       *ProtType, OPTIONAL
  IN UINT8                       *PktType OPTIONAL
  )
{
  EFI_STATUS              Status;
  EFI_SIMPLE_NETWORK_DEV  *SimpleNetworkDevice;
  BOOLEAN                 FrameReceived;

  if (This == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  Status              = EFI_SUCCESS;
  SimpleNetworkDevice = EFI_SIMPLE_NETWORK_DEV_FROM_THIS (This);

  if (SimpleNetworkDevice == NULL) {
    return EFI_DEVICE_ERROR;
  }

  FrameReceived = FALSE;

  //
  // Verify that the current state of the adapter is valid for this call.
  //
  switch (SimpleNetworkDevice->SimpleNetworkMode.State) {
  case EfiSimpleNetworkInitialized:
    break;

  case EfiSimpleNetworkStopped:
    return EFI_NOT_STARTED;

  case EfiSimpleNetworkStarted:
  default:
    return EFI_DEVICE_ERROR;
  }

  DEBUG ((DEBUG_NET, "Isr() IsrValid = %d\n", SimpleNetworkDevice->IsrValid));

  if (!SimpleNetworkDevice->IsrValid) {
    //
    // Call 16 bit UNDI ROM to open the network interface
    //
    ZeroMem (&SimpleNetworkDevice->Isr, sizeof (PXENV_UNDI_ISR_T));
    SimpleNetworkDevice->Isr.Status   = INIT_PXE_STATUS;
    SimpleNetworkDevice->Isr.FuncFlag = PXENV_UNDI_ISR_IN_START;

    DEBUG ((DEBUG_NET, "Isr() START\n"));

    Status = PxeUndiIsr (SimpleNetworkDevice, &SimpleNetworkDevice->Isr);
    if (EFI_ERROR (Status)) {
      return Status;
    }
    //
    // Check the status code from the 16 bit UNDI ROM
    //
    if (SimpleNetworkDevice->Isr.Status != PXENV_STATUS_SUCCESS) {
      return EFI_DEVICE_ERROR;
    }
    //
    // There have been no events on this UNDI interface, so return EFI_NOT_READY
    //
    if (SimpleNetworkDevice->Isr.FuncFlag == PXENV_UNDI_ISR_OUT_NOT_OURS) {
      return EFI_SUCCESS;
    }
    //
    // There is data to process, so call until all events processed.
    //
    ZeroMem (&SimpleNetworkDevice->Isr, sizeof (PXENV_UNDI_ISR_T));
    SimpleNetworkDevice->Isr.Status   = INIT_PXE_STATUS;
    SimpleNetworkDevice->Isr.FuncFlag = PXENV_UNDI_ISR_IN_PROCESS;

    DEBUG ((DEBUG_NET, "Isr() PROCESS\n"));

    Status = PxeUndiIsr (SimpleNetworkDevice, &SimpleNetworkDevice->Isr);
    if (EFI_ERROR (Status)) {
      return Status;
    }

    SimpleNetworkDevice->IsrValid = TRUE;
  }
  //
  // Call UNDI GET_NEXT until DONE
  //
  while (SimpleNetworkDevice->Isr.FuncFlag != PXENV_UNDI_ISR_OUT_DONE) {
    //
    // Check the status code from the 16 bit UNDI ROM
    //
    if (SimpleNetworkDevice->Isr.Status != PXENV_STATUS_SUCCESS) {
      return EFI_DEVICE_ERROR;
    }
    //
    // UNDI is busy.  Caller will have to call again.
    // This should never happen with a polled mode driver.
    //
    if (SimpleNetworkDevice->Isr.FuncFlag == PXENV_UNDI_ISR_OUT_BUSY) {
      DEBUG ((DEBUG_NET, "  BUSY\n"));
      return EFI_SUCCESS;
    }
    //
    // Check for invalud UNDI FuncFlag
    //
    if (SimpleNetworkDevice->Isr.FuncFlag != PXENV_UNDI_ISR_OUT_RECEIVE &&
        SimpleNetworkDevice->Isr.FuncFlag != PXENV_UNDI_ISR_OUT_TRANSMIT
        ) {
      DEBUG ((DEBUG_NET, "  Invalid SimpleNetworkDevice->Isr.FuncFlag value %d\n", SimpleNetworkDevice->Isr.FuncFlag));
      return EFI_DEVICE_ERROR;
    }
    //
    // Check for Transmit Event
    //
    if (SimpleNetworkDevice->Isr.FuncFlag == PXENV_UNDI_ISR_OUT_TRANSMIT) {
      DEBUG ((DEBUG_NET, "  TRANSMIT\n"));
      SimpleNetworkDevice->InterruptStatus |= EFI_SIMPLE_NETWORK_TRANSMIT_INTERRUPT;
    }
    //
    // Check for Receive Event
    //
    else if (SimpleNetworkDevice->Isr.FuncFlag == PXENV_UNDI_ISR_OUT_RECEIVE) {
      //
      // note - this code will hang on a receive interrupt in a GetStatus loop
      //
      DEBUG ((DEBUG_NET, "  RECEIVE\n"));
      SimpleNetworkDevice->InterruptStatus |= EFI_SIMPLE_NETWORK_RECEIVE_INTERRUPT;

      DEBUG ((DEBUG_NET, "SimpleNetworkDevice->Isr.BufferLength      = %d\n", SimpleNetworkDevice->Isr.BufferLength));
      DEBUG ((DEBUG_NET, "SimpleNetworkDevice->Isr.FrameLength       = %d\n", SimpleNetworkDevice->Isr.FrameLength));
      DEBUG ((DEBUG_NET, "SimpleNetworkDevice->Isr.FrameHeaderLength = %d\n", SimpleNetworkDevice->Isr.FrameHeaderLength));
      DEBUG (
        (
        DEBUG_NET, "SimpleNetworkDevice->Isr.Frame             = %04x:%04x\n", SimpleNetworkDevice->Isr.FrameSegSel,
        SimpleNetworkDevice->Isr.FrameOffset
        )
        );
      DEBUG ((DEBUG_NET, "SimpleNetworkDevice->Isr.ProtType          = 0x%02x\n", SimpleNetworkDevice->Isr.BufferLength));
      DEBUG ((DEBUG_NET, "SimpleNetworkDevice->Isr.PktType           = 0x%02x\n", SimpleNetworkDevice->Isr.BufferLength));

      if (FrameReceived) {
        return EFI_SUCCESS;
      }

      if ((Frame == NULL) || (SimpleNetworkDevice->Isr.FrameLength > *FrameLength)) {
        DEBUG ((DEBUG_NET, "return EFI_BUFFER_TOO_SMALL   *FrameLength = %08x\n", *FrameLength));
        *FrameLength = SimpleNetworkDevice->Isr.FrameLength;
        return EFI_BUFFER_TOO_SMALL;
      }

      *FrameLength = SimpleNetworkDevice->Isr.FrameLength;
      if (FrameHeaderLength != NULL) {
        *FrameHeaderLength = SimpleNetworkDevice->Isr.FrameHeaderLength;
      }

      if (ProtType != NULL) {
        *ProtType = SimpleNetworkDevice->Isr.ProtType;
      }

      if (PktType != NULL) {
        *PktType = SimpleNetworkDevice->Isr.PktType;
      }

      CopyMem (
        Frame,
        (VOID *)(UINTN) ((SimpleNetworkDevice->Isr.FrameSegSel << 4) + SimpleNetworkDevice->Isr.FrameOffset),
        SimpleNetworkDevice->Isr.BufferLength
        );
      Frame = Frame + SimpleNetworkDevice->Isr.BufferLength;
      if (SimpleNetworkDevice->Isr.BufferLength == SimpleNetworkDevice->Isr.FrameLength) {
        FrameReceived = TRUE;
      }
    }
    //
    // There is data to process, so call until all events processed.
    //
    ZeroMem (&SimpleNetworkDevice->Isr, sizeof (PXENV_UNDI_ISR_T));
    SimpleNetworkDevice->Isr.Status   = INIT_PXE_STATUS;
    SimpleNetworkDevice->Isr.FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT;

    DEBUG ((DEBUG_NET, "Isr() GET NEXT\n"));

    Status = PxeUndiIsr (SimpleNetworkDevice, &SimpleNetworkDevice->Isr);
    if (EFI_ERROR (Status)) {
      return Status;
    }
    //
    // Check the status code from the 16 bit UNDI ROM
    //
    //        if (SimpleNetworkDevice->Isr.Status != PXENV_STATUS_SUCCESS) {
    //            return EFI_DEVICE_ERROR;
    //        }
    //
  }

  SimpleNetworkDevice->IsrValid = FALSE;
  return EFI_SUCCESS;
}
//
// ///////////////////////////////////////////////////////////////////////////////////////
// Simple Network Protocol Interface Functions using 16 bit UNDI Option ROMs
/////////////////////////////////////////////////////////////////////////////////////////
//
// Start()
//
/**
  Call 16 bit UNDI ROM to start the network interface

  @param This       A pointer to EFI_SIMPLE_NETWORK_PROTOCOL structure.
  
  @retval EFI_DEVICE_ERROR Network interface has not be initialized.
  @retval EFI_DEVICE_ERROR Fail to execute 16 bit ROM call.
  @retval EFI_SUCESS       Success operation.
**/
EFI_STATUS
EFIAPI
Undi16SimpleNetworkStart (
  IN EFI_SIMPLE_NETWORK_PROTOCOL  *This
  )
{
  EFI_STATUS              Status;
  EFI_SIMPLE_NETWORK_DEV  *SimpleNetworkDevice;
  PXENV_UNDI_STARTUP_T    Startup;

  if (This == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  Status              = EFI_SUCCESS;
  SimpleNetworkDevice = EFI_SIMPLE_NETWORK_DEV_FROM_THIS (This);

  if (SimpleNetworkDevice == NULL) {
    return EFI_DEVICE_ERROR;
  }
  //
  // Verify that the current state of the adapter is valid for this call.
  //
  switch (SimpleNetworkDevice->SimpleNetworkMode.State) {
  case EfiSimpleNetworkStopped:
    break;

  case EfiSimpleNetworkStarted:
  case EfiSimpleNetworkInitialized:
    return EFI_ALREADY_STARTED;

  default:
    return EFI_DEVICE_ERROR;
  }
  //
  // Call 16 bit UNDI ROM to start the network interface
  //
  Startup.Status  = INIT_PXE_STATUS;

  Status          = PxeUndiStartup (SimpleNetworkDevice, &Startup);
  if (EFI_ERROR (Status)) {
    return Status;
  }
  //
  // Check the status code from the 16 bit UNDI ROM
  //
  if (Startup.Status != PXENV_STATUS_SUCCESS) {
    return EFI_DEVICE_ERROR;
  }
  //
  // The UNDI interface has been started, so update the State.
  //
  SimpleNetworkDevice->SimpleNetworkMode.State = EfiSimpleNetworkStarted;

  //
  //
  //
  SimpleNetworkDevice->SimpleNetworkMode.ReceiveFilterSetting = 0;
  SimpleNetworkDevice->SimpleNetworkMode.MCastFilterCount     = 0;

  return Status;
}
//
// Stop()
//
/**
  Call 16 bit UNDI ROM to stop the network interface

  @param This       A pointer to EFI_SIMPLE_NETWORK_PROTOCOL structure.
  
  @retval EFI_DEVICE_ERROR Network interface has not be initialized.
  @retval EFI_DEVICE_ERROR Fail to execute 16 bit ROM call.
  @retval EFI_SUCESS       Success operation.
**/
EFI_STATUS
EFIAPI
Undi16SimpleNetworkStop (
  IN EFI_SIMPLE_NETWORK_PROTOCOL  *This
  )
{
  EFI_STATUS              Status;
  EFI_SIMPLE_NETWORK_DEV  *SimpleNetworkDevice;

  if (This == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  Status              = EFI_SUCCESS;
  SimpleNetworkDevice = EFI_SIMPLE_NETWORK_DEV_FROM_THIS (This);

  if (SimpleNetworkDevice == NULL) {
    return EFI_DEVICE_ERROR;
  }
  //
  // Verify that the current state of the adapter is valid for this call.
  //
  switch (SimpleNetworkDevice->SimpleNetworkMode.State) {
  case EfiSimpleNetworkStarted:
    break;

  case EfiSimpleNetworkStopped:
    return EFI_NOT_STARTED;

  case EfiSimpleNetworkInitialized:
  default:
    return EFI_DEVICE_ERROR;
  }

  SimpleNetworkDevice->SimpleNetworkMode.State = EfiSimpleNetworkStopped;

  return Status;
}

//
// Initialize()
//
/**
  Initialize network interface 

  @param This                 A pointer to EFI_SIMPLE_NETWORK_PROTOCOL structure.
  @param ExtraRxBufferSize    The size of extra request receive buffer.
  @param ExtraTxBufferSize    The size of extra request transmit buffer.
 
  @retval EFI_DEVICE_ERROR Fail to execute 16 bit ROM call.
  @retval EFI_SUCESS       Success operation.
**/
EFI_STATUS
EFIAPI
Undi16SimpleNetworkInitialize (
  IN EFI_SIMPLE_NETWORK_PROTOCOL            *This,
  IN UINTN                                  ExtraRxBufferSize  OPTIONAL,
  IN UINTN                                  ExtraTxBufferSize  OPTIONAL
  )
{
  EFI_STATUS              Status;
  EFI_SIMPLE_NETWORK_DEV  *SimpleNetworkDevice;
  PXENV_UNDI_INITIALIZE_T Initialize;
  PXENV_UNDI_OPEN_T       Open;

  if (This == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  Status              = EFI_SUCCESS;
  SimpleNetworkDevice = EFI_SIMPLE_NETWORK_DEV_FROM_THIS (This);

  if (SimpleNetworkDevice == NULL) {
    return EFI_DEVICE_ERROR;
  }
  //
  // Verify that the current state of the adapter is valid for this call.
  //
  switch (SimpleNetworkDevice->SimpleNetworkMode.State) {
  case EfiSimpleNetworkStopped:
    return EFI_NOT_STARTED;

  case EfiSimpleNetworkStarted:
    break;

  case EfiSimpleNetworkInitialized:
  default:
    return EFI_DEVICE_ERROR;
  }
  //
  // Call 16 bit UNDI ROM to start the network interface
  //
  Initialize.Status       = INIT_PXE_STATUS;
  Initialize.ProtocolIni  = 0;

  Status                  = PxeUndiInitialize (SimpleNetworkDevice, &Initialize);

  if (EFI_ERROR (Status)) {
    DEBUG ((DEBUG_ERROR, "ERROR : PxeUndiInitialize() - Status = %r\n", Status));
    DEBUG ((DEBUG_ERROR, "Initialize.Status == %xh\n", Initialize.Status));

    if (Initialize.Status == PXENV_STATUS_UNDI_MEDIATEST_FAILED) {
      Status = EFI_NO_MEDIA;
    }

    return Status;
  }
  //
  // Check the status code from the 16 bit UNDI ROM
  //
  if (Initialize.Status != PXENV_STATUS_SUCCESS) {
    DEBUG ((DEBUG_ERROR, "ERROR : PxeUndiInitialize() - Initialize.Status = %04x\n", Initialize.Status));
    return EFI_DEVICE_ERROR;
  }
  //
  // Call 16 bit UNDI ROM to open the network interface
  //
  Open.Status     = INIT_PXE_STATUS;
  Open.OpenFlag   = 0;
  Open.PktFilter  = Undi16GetPacketFilterSetting (SimpleNetworkDevice->SimpleNetworkMode.ReceiveFilterSetting);
  Undi16GetMCastFilters (
    &SimpleNetworkDevice->SimpleNetworkMode,
    &Open.McastBuffer,
    SimpleNetworkDevice->SimpleNetworkMode.HwAddressSize
    );

  Status = PxeUndiOpen (SimpleNetworkDevice, &Open);

  if (EFI_ERROR (Status)) {
    DEBUG ((DEBUG_ERROR, "ERROR : PxeUndiOpen() - Status = %r\n", Status));
    return Status;
  }
  //
  // Check the status code from the 16 bit UNDI ROM
  //
  if (Open.Status != PXENV_STATUS_SUCCESS) {
    DEBUG ((DEBUG_ERROR, "ERROR : PxeUndiOpen() - Open.Status = %04x\n", Open.Status));
    return EFI_DEVICE_ERROR;
  }
  //
  // The UNDI interface has been initialized, so update the State.
  //
  SimpleNetworkDevice->SimpleNetworkMode.State = EfiSimpleNetworkInitialized;

  //
  // If initialize succeeds, then assume that media is present.
  //
  SimpleNetworkDevice->SimpleNetworkMode.MediaPresent = TRUE;

  //
  // Reset the recycled transmit buffer FIFO
  //
  SimpleNetworkDevice->TxBufferFifo.First = 0;
  SimpleNetworkDevice->TxBufferFifo.Last  = 0;
  SimpleNetworkDevice->IsrValid           = FALSE;

  return Status;
}
//
// Reset()
//
/**
  Reset network interface.

  @param This                 A pointer to EFI_SIMPLE_NETWORK_PROTOCOL structure.
  @param ExtendedVerification Need extended verfication.
  
  @retval EFI_INVALID_PARAMETER Invalid This paramter.
  @retval EFI_DEVICE_ERROR      Network device has not been initialized.
  @retval EFI_NOT_STARTED       Network device has been stopped.
  @retval EFI_DEVICE_ERROR      Invalid status for network device
  @retval EFI_SUCCESS           Success operation.
**/
EFI_STATUS
EFIAPI
Undi16SimpleNetworkReset (
  IN EFI_SIMPLE_NETWORK_PROTOCOL   *This,
  IN BOOLEAN                       ExtendedVerification
  )
{
  EFI_STATUS              Status;
  EFI_SIMPLE_NETWORK_DEV  *SimpleNetworkDevice;
  PXENV_UNDI_RESET_T      Reset;
  UINT16                  Rx_filter;

  if (This == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  Status              = EFI_SUCCESS;
  SimpleNetworkDevice = EFI_SIMPLE_NETWORK_DEV_FROM_THIS (This);

  if (SimpleNetworkDevice == NULL) {
    return EFI_DEVICE_ERROR;
  }
  //
  // Verify that the current state of the adapter is valid for this call.
  //
  switch (SimpleNetworkDevice->SimpleNetworkMode.State) {
  case EfiSimpleNetworkStopped:
    return EFI_NOT_STARTED;

  case EfiSimpleNetworkInitialized:
    break;

  case EfiSimpleNetworkStarted:
  default:
    return EFI_DEVICE_ERROR;
  }

  Reset.Status  = INIT_PXE_STATUS;

  Rx_filter     = Undi16GetPacketFilterSetting (SimpleNetworkDevice->SimpleNetworkMode.ReceiveFilterSetting);

  Undi16GetMCastFilters (
    &SimpleNetworkDevice->SimpleNetworkMode,
    &Reset.R_Mcast_Buf,
    SimpleNetworkDevice->SimpleNetworkMode.HwAddressSize
    );

  Status = PxeUndiResetNic (SimpleNetworkDevice, &Reset, Rx_filter);

  if (EFI_ERROR (Status)) {
    return Status;
  }
  //
  // Check the status code from the 16 bit UNDI ROM
  //
  if (Reset.Status != PXENV_STATUS_SUCCESS) {
    return EFI_DEVICE_ERROR;
  }
  //
  // Reset the recycled transmit buffer FIFO
  //
  SimpleNetworkDevice->TxBufferFifo.First = 0;
  SimpleNetworkDevice->TxBufferFifo.Last  = 0;
  SimpleNetworkDevice->IsrValid           = FALSE;

  return Status;
}
//
// Shutdown()
//
/**
  Shutdown network interface.

  @param This                 A pointer to EFI_SIMPLE_NETWORK_PROTOCOL structure.
  
  @retval EFI_INVALID_PARAMETER Invalid This paramter.
  @retval EFI_DEVICE_ERROR      Network device has not been initialized.
  @retval EFI_NOT_STARTED       Network device has been stopped.
  @retval EFI_DEVICE_ERROR      Invalid status for network device
  @retval EFI_SUCCESS           Success operation.
**/
EFI_STATUS
EFIAPI
Undi16SimpleNetworkShutdown (
  IN EFI_SIMPLE_NETWORK_PROTOCOL  *This
  )
{
  EFI_STATUS              Status;
  EFI_SIMPLE_NETWORK_DEV  *SimpleNetworkDevice;
  PXENV_UNDI_CLOSE_T      Close;
  PXENV_UNDI_SHUTDOWN_T   Shutdown;

  if (This == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  Status              = EFI_SUCCESS;
  SimpleNetworkDevice = EFI_SIMPLE_NETWORK_DEV_FROM_THIS (This);

  if (SimpleNetworkDevice == NULL) {
    return EFI_DEVICE_ERROR;
  }
  //
  // Verify that the current state of the adapter is valid for this call.
  //
  switch (SimpleNetworkDevice->SimpleNetworkMode.State) {
  case EfiSimpleNetworkStopped:
    return EFI_NOT_STARTED;

  case EfiSimpleNetworkInitialized:
    break;

  case EfiSimpleNetworkStarted:
  default:
    return EFI_DEVICE_ERROR;
  }

  SimpleNetworkDevice->IsrValid = FALSE;

  //
  // Call 16 bit UNDI ROM to start the network interface
  //
  Close.Status  = INIT_PXE_STATUS;

  Status        = PxeUndiClose (SimpleNetworkDevice, &Close);

  if (EFI_ERROR (Status)) {
    return Status;
  }
  //
  // Check the status code from the 16 bit UNDI ROM
  //
  if (Close.Status != PXENV_STATUS_SUCCESS) {
    return EFI_DEVICE_ERROR;
  }
  //
  // Call 16 bit UNDI ROM to open the network interface
  //
  Shutdown.Status = INIT_PXE_STATUS;

  Status          = PxeUndiShutdown (SimpleNetworkDevice, &Shutdown);

  if (EFI_ERROR (Status)) {
    return Status;
  }
  //
  // Check the status code from the 16 bit UNDI ROM
  //
  if (Shutdown.Status != PXENV_STATUS_SUCCESS) {
    return EFI_DEVICE_ERROR;
  }
  //
  // The UNDI interface has been initialized, so update the State.
  //
  SimpleNetworkDevice->SimpleNetworkMode.State = EfiSimpleNetworkStarted;

  //
  // If shutdown succeeds, then assume that media is not present.
  //
  SimpleNetworkDevice->SimpleNetworkMode.MediaPresent = FALSE;

  //
  // Reset the recycled transmit buffer FIFO
  //
  SimpleNetworkDevice->TxBufferFifo.First = 0;
  SimpleNetworkDevice->TxBufferFifo.Last  = 0;

  //
  // A short delay.  Without this an initialize immediately following
  // a shutdown will cause some versions of UNDI-16 to stop operating.
  //
  gBS->Stall (250000);

  return Status;
}
//
// ReceiveFilters()
//
/**
  Reset network interface.

  @param This                 A pointer to EFI_SIMPLE_NETWORK_PROTOCOL structure.
  @param Enable               Enable mask value
  @param Disable              Disable mask value
  @param ResetMCastFilter     Whether reset multi cast filter or not
  @param MCastFilterCnt       Count of mutli cast filter for different MAC address
  @param MCastFilter          Buffer for mustli cast filter for different MAC address.
  
  @retval EFI_INVALID_PARAMETER Invalid This paramter.
  @retval EFI_DEVICE_ERROR      Network device has not been initialized.
  @retval EFI_NOT_STARTED       Network device has been stopped.
  @retval EFI_DEVICE_ERROR      Invalid status for network device
  @retval EFI_SUCCESS           Success operation.
**/
EFI_STATUS
EFIAPI
Undi16SimpleNetworkReceiveFilters (
  IN EFI_SIMPLE_NETWORK_PROTOCOL                     * This,
  IN UINT32                                          Enable,
  IN UINT32                                          Disable,
  IN BOOLEAN                                         ResetMCastFilter,
  IN UINTN                                           MCastFilterCnt     OPTIONAL,
  IN EFI_MAC_ADDRESS                                 * MCastFilter OPTIONAL
  )
{
  EFI_STATUS              Status;
  UINTN                   Index;
  UINT32                  NewFilter;
  EFI_SIMPLE_NETWORK_DEV  *SimpleNetworkDevice;
  PXENV_UNDI_CLOSE_T      Close;
  PXENV_UNDI_OPEN_T       Open;

  if (This == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  Status              = EFI_SUCCESS;
  SimpleNetworkDevice = EFI_SIMPLE_NETWORK_DEV_FROM_THIS (This);

  if (SimpleNetworkDevice == NULL) {
    return EFI_DEVICE_ERROR;
  }
  //
  // Verify that the current state of the adapter is valid for this call.
  //
  switch (SimpleNetworkDevice->SimpleNetworkMode.State) {
  case EfiSimpleNetworkStopped:
    return EFI_NOT_STARTED;

  case EfiSimpleNetworkInitialized:
    break;

  case EfiSimpleNetworkStarted:
  default:
    return EFI_DEVICE_ERROR;
  }
  //
  // First deal with possible filter setting changes
  //
  if ((Enable == 0) && (Disable == 0) && !ResetMCastFilter) {
    return EFI_SUCCESS;
  }

  NewFilter = (SimpleNetworkDevice->SimpleNetworkMode.ReceiveFilterSetting | Enable) &~Disable;

  if ((NewFilter & EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST) != 0) {
    if ((MCastFilterCnt == 0) || (MCastFilter == 0) || MCastFilterCnt > SimpleNetworkDevice->SimpleNetworkMode.MaxMCastFilterCount) {
      return EFI_INVALID_PARAMETER;
    }
  }
  //
  // Call 16 bit UNDI ROM to close the network interface
  //
  Close.Status  = INIT_PXE_STATUS;

  Status        = PxeUndiClose (SimpleNetworkDevice, &Close);

  if (EFI_ERROR (Status)) {
    return Status;
  }
  //
  // Check the status code from the 16 bit UNDI ROM
  //
  if (Close.Status != PXENV_STATUS_SUCCESS) {
    return EFI_DEVICE_ERROR;
  }
  //
  // Call 16 bit UNDI ROM to open the network interface
  //
  //
  // Reset the recycled transmit buffer FIFO
  //
  SimpleNetworkDevice->TxBufferFifo.First = 0;
  SimpleNetworkDevice->TxBufferFifo.Last  = 0;

  //
  // Call 16 bit UNDI ROM to open the network interface
  //
  ZeroMem (&Open, sizeof Open);

  Open.Status     = INIT_PXE_STATUS;
  Open.PktFilter  = Undi16GetPacketFilterSetting (NewFilter);

  if ((NewFilter & EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST) != 0) {
    //
    // Copy the MAC addresses into the UNDI open parameter structure
    //
    Open.McastBuffer.MCastAddrCount = (UINT16) MCastFilterCnt;
    for (Index = 0; Index < MCastFilterCnt; ++Index) {
      CopyMem (
        Open.McastBuffer.MCastAddr[Index],
        &MCastFilter[Index],
        sizeof Open.McastBuffer.MCastAddr[Index]
        );
    }
  } else if (!ResetMCastFilter) {
    for (Index = 0; Index < SimpleNetworkDevice->SimpleNetworkMode.MCastFilterCount; ++Index) {
      CopyMem (
        Open.McastBuffer.MCastAddr[Index],
        &SimpleNetworkDevice->SimpleNetworkMode.MCastFilter[Index],
        sizeof Open.McastBuffer.MCastAddr[Index]
        );
    }
  }

  Status = PxeUndiOpen (SimpleNetworkDevice, &Open);

  if (EFI_ERROR (Status)) {
    return Status;
  }
  //
  // Check the status code from the 16 bit UNDI ROM
  //
  if (Open.Status != PXENV_STATUS_SUCCESS) {
    return EFI_DEVICE_ERROR;
  }

  SimpleNetworkDevice->IsrValid = FALSE;
  SimpleNetworkDevice->SimpleNetworkMode.ReceiveFilterSetting = NewFilter;

  if ((NewFilter & EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST) != 0) {
    SimpleNetworkDevice->SimpleNetworkMode.MCastFilterCount = (UINT32) MCastFilterCnt;
    for (Index = 0; Index < MCastFilterCnt; ++Index) {
      CopyMem (
        &SimpleNetworkDevice->SimpleNetworkMode.MCastFilter[Index],
        &MCastFilter[Index],
        sizeof (EFI_MAC_ADDRESS)
        );      
    }
  }
  //
  // Read back multicast addresses.
  //
  return EFI_SUCCESS;
}
//
// StationAddress()
//
/**
  Set new MAC address.

  @param This                 A pointer to EFI_SIMPLE_NETWORK_PROTOCOL structure.
  @param Reset                Whether reset station MAC address to permenent address
  @param New                  A pointer to New address
  
  @retval EFI_INVALID_PARAMETER Invalid This paramter.
  @retval EFI_DEVICE_ERROR      Network device has not been initialized.
  @retval EFI_NOT_STARTED       Network device has been stopped.
  @retval EFI_DEVICE_ERROR      Invalid status for network device
  @retval EFI_SUCCESS           Success operation.
**/
EFI_STATUS
EFIAPI
Undi16SimpleNetworkStationAddress (
  IN EFI_SIMPLE_NETWORK_PROTOCOL           * This,
  IN BOOLEAN                               Reset,
  IN EFI_MAC_ADDRESS                       * New OPTIONAL
  )
{
  EFI_STATUS                    Status;
  EFI_SIMPLE_NETWORK_DEV        *SimpleNetworkDevice;
  PXENV_UNDI_SET_STATION_ADDR_T SetStationAddr;
  //
  // EFI_DEVICE_PATH_PROTOCOL     *OldDevicePath;
  //
  PXENV_UNDI_CLOSE_T            Close;
  PXENV_UNDI_OPEN_T             Open;

  if (This == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  Status              = EFI_SUCCESS;

  SimpleNetworkDevice = EFI_SIMPLE_NETWORK_DEV_FROM_THIS (This);

  if (SimpleNetworkDevice == NULL) {
    return EFI_DEVICE_ERROR;
  }
  //
  // Verify that the current state of the adapter is valid for this call.
  //
  switch (SimpleNetworkDevice->SimpleNetworkMode.State) {
  case EfiSimpleNetworkInitialized:
    break;

  case EfiSimpleNetworkStopped:
    return EFI_NOT_STARTED;

  case EfiSimpleNetworkStarted:
  default:
    return EFI_DEVICE_ERROR;
  }
  //
  // Call 16 bit UNDI ROM to open the network interface
  //
  SetStationAddr.Status = INIT_PXE_STATUS;

  if (Reset) {
    //
    // If we are reseting the Station Address to the permanent address, and the
    // Station Address is not programmable, then just return EFI_SUCCESS.
    //
    if (!SimpleNetworkDevice->SimpleNetworkMode.MacAddressChangeable) {
      return EFI_SUCCESS;
    }
    //
    // If the address is already the permanent address, then just return success.
    //
    if (CompareMem (
          &SimpleNetworkDevice->SimpleNetworkMode.CurrentAddress,
          &SimpleNetworkDevice->SimpleNetworkMode.PermanentAddress,
          SimpleNetworkDevice->SimpleNetworkMode.HwAddressSize
          ) == 0) {
      return EFI_SUCCESS;
    }
    //
    // Copy the adapters permanent address to the new station address
    //
    CopyMem (
      &SetStationAddr.StationAddress,
      &SimpleNetworkDevice->SimpleNetworkMode.PermanentAddress,
      SimpleNetworkDevice->SimpleNetworkMode.HwAddressSize
      );
  } else {
    //
    // If we are setting the Station Address, and the
    // Station Address is not programmable, return invalid parameter.
    //
    if (!SimpleNetworkDevice->SimpleNetworkMode.MacAddressChangeable) {
      return EFI_INVALID_PARAMETER;
    }
    //
    // If the address is already the new address, then just return success.
    //
    if (CompareMem (
          &SimpleNetworkDevice->SimpleNetworkMode.CurrentAddress,
          New,
          SimpleNetworkDevice->SimpleNetworkMode.HwAddressSize
          ) == 0) {
      return EFI_SUCCESS;
    }
    //
    // Copy New to the new station address
    //
    CopyMem (
      &SetStationAddr.StationAddress,
      New,
      SimpleNetworkDevice->SimpleNetworkMode.HwAddressSize
      );

  }
  //
  // Call 16 bit UNDI ROM to stop the network interface
  //
  Close.Status = INIT_PXE_STATUS;

  PxeUndiClose (SimpleNetworkDevice, &Close);

  //
  // Call 16-bit UNDI ROM to set the station address
  //
  SetStationAddr.Status = PXENV_STATUS_SUCCESS;

  Status                = PxeUndiSetStationAddr (SimpleNetworkDevice, &SetStationAddr);

  //
  // Call 16-bit UNDI ROM to start the network interface
  //
  Open.Status     = PXENV_STATUS_SUCCESS;
  Open.OpenFlag   = 0;
  Open.PktFilter  = Undi16GetPacketFilterSetting (SimpleNetworkDevice->SimpleNetworkMode.ReceiveFilterSetting);
  Undi16GetMCastFilters (
    &SimpleNetworkDevice->SimpleNetworkMode,
    &Open.McastBuffer,
    SimpleNetworkDevice->SimpleNetworkMode.HwAddressSize
    );

  PxeUndiOpen (SimpleNetworkDevice, &Open);

  //
  // Check status from station address change
  //
  if (EFI_ERROR (Status)) {
    return Status;
  }
  //
  // Check the status code from the 16 bit UNDI ROM
  //
  if (SetStationAddr.Status != PXENV_STATUS_SUCCESS) {
    return EFI_DEVICE_ERROR;
  }

  CopyMem (
    &SimpleNetworkDevice->SimpleNetworkMode.CurrentAddress,
    &SetStationAddr.StationAddress,
    SimpleNetworkDevice->SimpleNetworkMode.HwAddressSize
    );

#if 0 /* The device path is based on the permanent address not the current address. */
  //
  // The station address was changed, so update the device path with the new MAC address.
  //
  OldDevicePath                   = SimpleNetworkDevice->DevicePath;
  SimpleNetworkDevice->DevicePath = DuplicateDevicePath (SimpleNetworkDevice->BaseDevicePath);
  SimpleNetworkAppendMacAddressDevicePath (
    &SimpleNetworkDevice->DevicePath,
    &SimpleNetworkDevice->SimpleNetworkMode.CurrentAddress
    );

  Status = LibReinstallProtocolInterfaces (
            SimpleNetworkDevice->Handle,
            &DevicePathProtocol,
            OldDevicePath,
            SimpleNetworkDevice->DevicePath,
            NULL
            );

  if (EFI_ERROR (Status)) {
    DEBUG ((DEBUG_ERROR, "Failed to reinstall the DevicePath protocol for the Simple Network Device\n"));
    DEBUG ((DEBUG_ERROR, "  Status = %r\n", Status));
  }

  FreePool (OldDevicePath);
#endif /* 0 */

  return Status;
}
//
// Statistics()
//
/**
  Resets or collects the statistics on a network interface.

  @param  This            Protocol instance pointer.
  @param  Reset           Set to TRUE to reset the statistics for the network interface.
  @param  StatisticsSize  On input the size, in bytes, of StatisticsTable. On
                          output the size, in bytes, of the resulting table of
                          statistics.
  @param  StatisticsTable A pointer to the EFI_NETWORK_STATISTICS structure that
                          contains the statistics.

  @retval EFI_SUCCESS           The statistics were collected from the network interface.
  @retval EFI_NOT_STARTED       The network interface has not been started.
  @retval EFI_BUFFER_TOO_SMALL  The Statistics buffer was too small. The current buffer
                                size needed to hold the statistics is returned in
                                StatisticsSize.
  @retval EFI_INVALID_PARAMETER One or more of the parameters has an unsupported value.
  @retval EFI_DEVICE_ERROR      The command could not be sent to the network interface.
  @retval EFI_UNSUPPORTED       This function is not supported by the network interface.

**/
EFI_STATUS
EFIAPI
Undi16SimpleNetworkStatistics (
  IN EFI_SIMPLE_NETWORK_PROTOCOL       * This,
  IN BOOLEAN                           Reset,
  IN OUT UINTN                         *StatisticsSize OPTIONAL,
  OUT EFI_NETWORK_STATISTICS           * StatisticsTable OPTIONAL
  )
{
  EFI_STATUS                    Status;
  EFI_SIMPLE_NETWORK_DEV        *SimpleNetworkDevice;
  PXENV_UNDI_CLEAR_STATISTICS_T ClearStatistics;
  PXENV_UNDI_GET_STATISTICS_T   GetStatistics;

  if (This == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  Status              = EFI_SUCCESS;
  SimpleNetworkDevice = EFI_SIMPLE_NETWORK_DEV_FROM_THIS (This);

  if (SimpleNetworkDevice == NULL) {
    return EFI_DEVICE_ERROR;
  }
  //
  // Verify that the current state of the adapter is valid for this call.
  //
  switch (SimpleNetworkDevice->SimpleNetworkMode.State) {
  case EfiSimpleNetworkInitialized:
    break;

  case EfiSimpleNetworkStopped:
    return EFI_NOT_STARTED;

  case EfiSimpleNetworkStarted:
  default:
    return EFI_DEVICE_ERROR;
  }

  if ((StatisticsSize != NULL) && (*StatisticsSize != 0) && (StatisticsTable == NULL)) {
    return EFI_INVALID_PARAMETER;
  }

  //
  // If Reset is TRUE, then clear all the statistics.
  //
  if (Reset) {

    DEBUG ((DEBUG_NET, "  RESET Statistics\n"));

    //
    // Call 16 bit UNDI ROM to open the network interface
    //
    ClearStatistics.Status  = INIT_PXE_STATUS;

    Status                  = PxeUndiClearStatistics (SimpleNetworkDevice, &ClearStatistics);

    if (EFI_ERROR (Status)) {
      return Status;
    }
    //
    // Check the status code from the 16 bit UNDI ROM
    //
    if (ClearStatistics.Status != PXENV_STATUS_SUCCESS) {
      return EFI_DEVICE_ERROR;
    }

    DEBUG ((DEBUG_NET, "  RESET Statistics Complete"));
  }

  if (StatisticsSize != NULL) {
    EFI_NETWORK_STATISTICS  LocalStatisticsTable;

    DEBUG ((DEBUG_NET, "  GET Statistics\n"));

    //
    // If the size if valid, then see if the table is valid
    //
    if (StatisticsTable == NULL) {
      DEBUG ((DEBUG_NET, "  StatisticsTable is NULL\n"));
      return EFI_INVALID_PARAMETER;
    }
    //
    // Call 16 bit UNDI ROM to open the network interface
    //
    GetStatistics.Status            = INIT_PXE_STATUS;
    GetStatistics.XmtGoodFrames     = 0;
    GetStatistics.RcvGoodFrames     = 0;
    GetStatistics.RcvCRCErrors      = 0;
    GetStatistics.RcvResourceErrors = 0;

    Status                          = PxeUndiGetStatistics (SimpleNetworkDevice, &GetStatistics);

    if (EFI_ERROR (Status)) {
      return Status;
    }
    //
    // Check the status code from the 16 bit UNDI ROM
    //
    if (GetStatistics.Status != PXENV_STATUS_SUCCESS) {
      return EFI_DEVICE_ERROR;
    }
    //
    // Fill in the Statistics Table with the collected values.
    //
    SetMem (&LocalStatisticsTable, sizeof LocalStatisticsTable, 0xff);

    LocalStatisticsTable.TxGoodFrames     = GetStatistics.XmtGoodFrames;
    LocalStatisticsTable.RxGoodFrames     = GetStatistics.RcvGoodFrames;
    LocalStatisticsTable.RxCrcErrorFrames = GetStatistics.RcvCRCErrors;
    LocalStatisticsTable.RxDroppedFrames  = GetStatistics.RcvResourceErrors;

    CopyMem (StatisticsTable, &LocalStatisticsTable, *StatisticsSize);

    DEBUG (
      (DEBUG_NET,
      "  Statistics Collected : Size=%d  Buf=%08x\n",
      *StatisticsSize,
      StatisticsTable)
      );

    DEBUG ((DEBUG_NET, "  GET Statistics Complete"));

    if (*StatisticsSize < sizeof LocalStatisticsTable) {
      DEBUG ((DEBUG_NET, "  BUFFER TOO SMALL\n"));
      Status = EFI_BUFFER_TOO_SMALL;
    }

    *StatisticsSize = sizeof LocalStatisticsTable;

    return Status;

  }

  return EFI_SUCCESS;
}
//
// MCastIpToMac()
//
/**
  Translate IP address to MAC address.

  @param This                 A pointer to EFI_SIMPLE_NETWORK_PROTOCOL structure.
  @param IPv6                 IPv6 or IPv4
  @param IP                   A pointer to given Ip address.
  @param MAC                  On return, translated MAC address.
  
  @retval EFI_INVALID_PARAMETER Invalid This paramter.
  @retval EFI_INVALID_PARAMETER Invalid IP address.
  @retval EFI_INVALID_PARAMETER Invalid return buffer for holding MAC address.
  @retval EFI_UNSUPPORTED       Do not support IPv6 
  @retval EFI_DEVICE_ERROR      Network device has not been initialized.
  @retval EFI_NOT_STARTED       Network device has been stopped.
  @retval EFI_DEVICE_ERROR      Invalid status for network device
  @retval EFI_SUCCESS           Success operation.
**/
EFI_STATUS
EFIAPI
Undi16SimpleNetworkMCastIpToMac (
  IN EFI_SIMPLE_NETWORK_PROTOCOL            *This,
  IN BOOLEAN                                IPv6,
  IN EFI_IP_ADDRESS                         *IP,
  OUT EFI_MAC_ADDRESS                       *MAC
  )
{
  EFI_STATUS                  Status;
  EFI_SIMPLE_NETWORK_DEV      *SimpleNetworkDevice;
  PXENV_UNDI_GET_MCAST_ADDR_T GetMcastAddr;

  if (This == NULL || IP == NULL || MAC == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  Status              = EFI_SUCCESS;
  SimpleNetworkDevice = EFI_SIMPLE_NETWORK_DEV_FROM_THIS (This);

  if (SimpleNetworkDevice == NULL) {
    return EFI_DEVICE_ERROR;
  }
  //
  // Verify that the current state of the adapter is valid for this call.
  //
  switch (SimpleNetworkDevice->SimpleNetworkMode.State) {
  case EfiSimpleNetworkStopped:
    return EFI_NOT_STARTED;

  case EfiSimpleNetworkInitialized:
    break;

  case EfiSimpleNetworkStarted:
  default:
    return EFI_DEVICE_ERROR;
  }
  //
  // 16 bit UNDI Option ROMS do not support IPv6.  Check for IPv6 usage.
  //
  if (IPv6) {
    return EFI_UNSUPPORTED;
  }
  //
  // Call 16 bit UNDI ROM to open the network interface
  //
  GetMcastAddr.Status = INIT_PXE_STATUS;
  CopyMem (&GetMcastAddr.InetAddr, IP, 4);

  Status = PxeUndiGetMcastAddr (SimpleNetworkDevice, &GetMcastAddr);

  if (EFI_ERROR (Status)) {
    return Status;
  }
  //
  // Check the status code from the 16 bit UNDI ROM
  //
  if (GetMcastAddr.Status != PXENV_STATUS_SUCCESS) {
    return EFI_DEVICE_ERROR;
  }
  //
  // Copy the MAC address from the returned data structure.
  //
  CopyMem (
    MAC,
    &GetMcastAddr.MediaAddr,
    SimpleNetworkDevice->SimpleNetworkMode.HwAddressSize
    );

  return Status;
}
//
// NvData()
//
/**
  Performs read and write operations on the NVRAM device attached to a 
  network interface.

  @param  This       The protocol instance pointer.
  @param  ReadWrite  TRUE for read operations, FALSE for write operations.
  @param  Offset     Byte offset in the NVRAM device at which to start the read or
                     write operation. This must be a multiple of NvRamAccessSize and
                     less than NvRamSize.
  @param  BufferSize The number of bytes to read or write from the NVRAM device.
                     This must also be a multiple of NvramAccessSize.
  @param  Buffer     A pointer to the data buffer.

  @retval EFI_SUCCESS           The NVRAM access was performed.
  @retval EFI_NOT_STARTED       The network interface has not been started.
  @retval EFI_INVALID_PARAMETER One or more of the parameters has an unsupported value.
  @retval EFI_DEVICE_ERROR      The command could not be sent to the network interface.
  @retval EFI_UNSUPPORTED       This function is not supported by the network interface.

**/
EFI_STATUS
EFIAPI
Undi16SimpleNetworkNvData (
  IN EFI_SIMPLE_NETWORK_PROTOCOL  *This,
  IN BOOLEAN                      ReadWrite,
  IN UINTN                        Offset,
  IN UINTN                        BufferSize,
  IN OUT VOID                     *Buffer
  )
{
  return EFI_UNSUPPORTED;
}
//
// GetStatus()
//
/**
  Reads the current interrupt status and recycled transmit buffer status from 
  a network interface.

  @param  This            The protocol instance pointer.
  @param  InterruptStatus A pointer to the bit mask of the currently active interrupts
                          If this is NULL, the interrupt status will not be read from
                          the device. If this is not NULL, the interrupt status will
                          be read from the device. When the  interrupt status is read,
                          it will also be cleared. Clearing the transmit  interrupt
                          does not empty the recycled transmit buffer array.
  @param  TxBuf           Recycled transmit buffer address. The network interface will
                          not transmit if its internal recycled transmit buffer array
                          is full. Reading the transmit buffer does not clear the
                          transmit interrupt. If this is NULL, then the transmit buffer
                          status will not be read. If there are no transmit buffers to
                          recycle and TxBuf is not NULL, * TxBuf will be set to NULL.

  @retval EFI_SUCCESS           The status of the network interface was retrieved.
  @retval EFI_NOT_STARTED       The network interface has not been started.
  @retval EFI_INVALID_PARAMETER One or more of the parameters has an unsupported value.
  @retval EFI_DEVICE_ERROR      The command could not be sent to the network interface.
  @retval EFI_UNSUPPORTED       This function is not supported by the network interface.

**/
EFI_STATUS
EFIAPI
Undi16SimpleNetworkGetStatus (
  IN EFI_SIMPLE_NETWORK_PROTOCOL  * This,
  OUT UINT32                      *InterruptStatus OPTIONAL,
  OUT VOID                        **TxBuf OPTIONAL
  )
{
  EFI_STATUS              Status;
  EFI_SIMPLE_NETWORK_DEV  *SimpleNetworkDevice;
  UINTN                   FrameLength;

  if (This == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  Status              = EFI_SUCCESS;
  SimpleNetworkDevice = EFI_SIMPLE_NETWORK_DEV_FROM_THIS (This);

  if (SimpleNetworkDevice == NULL) {
    return EFI_DEVICE_ERROR;
  }
  //
  // Verify that the current state of the adapter is valid for this call.
  //
  switch (SimpleNetworkDevice->SimpleNetworkMode.State) {
  case EfiSimpleNetworkInitialized:
    break;

  case EfiSimpleNetworkStopped:
    return EFI_NOT_STARTED;

  case EfiSimpleNetworkStarted:
  default:
    return EFI_DEVICE_ERROR;
  }

  if (InterruptStatus == NULL && TxBuf == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  FrameLength = 0;
  Status      = Undi16SimpleNetworkIsr (This, &FrameLength, NULL, NULL, NULL, NULL);

  if (Status != EFI_BUFFER_TOO_SMALL) {
    if (EFI_ERROR (Status)) {
      return Status;
    }
  }
  //
  // See if the caller wants interrupt info.
  //
  if (InterruptStatus != NULL) {
    *InterruptStatus                      = SimpleNetworkDevice->InterruptStatus;
    SimpleNetworkDevice->InterruptStatus  = 0;
  }
  //
  // See if the caller wants transmit buffer status info.
  //
  if (TxBuf != NULL) {
    *TxBuf = 0;
    SimpleNetworkTransmitFifoRemove (&(SimpleNetworkDevice->TxBufferFifo), TxBuf);
  }

  return EFI_SUCCESS;
}

/**
  Places a packet in the transmit queue of a network interface.

  @param  This       The protocol instance pointer.
  @param  HeaderSize The size, in bytes, of the media header to be filled in by
                     the Transmit() function. If HeaderSize is non-zero, then it
                     must be equal to This->Mode->MediaHeaderSize and the DestAddr
                     and Protocol parameters must not be NULL.
  @param  BufferSize The size, in bytes, of the entire packet (media header and
                     data) to be transmitted through the network interface.
  @param  Buffer     A pointer to the packet (media header followed by data) to be
                     transmitted. This parameter cannot be NULL. If HeaderSize is zero,
                     then the media header in Buffer must already be filled in by the
                     caller. If HeaderSize is non-zero, then the media header will be
                     filled in by the Transmit() function.
  @param  SrcAddr    The source HW MAC address. If HeaderSize is zero, then this parameter
                     is ignored. If HeaderSize is non-zero and SrcAddr is NULL, then
                     This->Mode->CurrentAddress is used for the source HW MAC address.
  @param  DestAddr   The destination HW MAC address. If HeaderSize is zero, then this
                     parameter is ignored.
  @param  Protocol   The type of header to build. If HeaderSize is zero, then this
                     parameter is ignored. See RFC 1700, section "Ether Types", for
                     examples.

  @retval EFI_SUCCESS           The packet was placed on the transmit queue.
  @retval EFI_NOT_STARTED       The network interface has not been started.
  @retval EFI_NOT_READY         The network interface is too busy to accept this transmit request.                      
  @retval EFI_BUFFER_TOO_SMALL  The BufferSize parameter is too small.
  @retval EFI_INVALID_PARAMETER One or more of the parameters has an unsupported value.
  @retval EFI_DEVICE_ERROR      The command could not be sent to the network interface.
  @retval EFI_UNSUPPORTED       This function is not supported by the network interface.

**/
EFI_STATUS
EFIAPI
Undi16SimpleNetworkTransmit (
  IN EFI_SIMPLE_NETWORK_PROTOCOL           *This,
  IN UINTN                                 HeaderSize,
  IN UINTN                                 BufferSize,
  IN VOID                                  *Buffer,
  IN EFI_MAC_ADDRESS                       *SrcAddr OPTIONAL,
  IN EFI_MAC_ADDRESS                       *DestAddr OPTIONAL,
  IN UINT16                                *Protocol OPTIONAL
  )
{
  EFI_STATUS              Status;
  EFI_SIMPLE_NETWORK_DEV  *SimpleNetworkDevice;
  PXENV_UNDI_TRANSMIT_T   XmitInfo;

  if (This == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  Status              = EFI_SUCCESS;
  SimpleNetworkDevice = EFI_SIMPLE_NETWORK_DEV_FROM_THIS (This);

  if (SimpleNetworkDevice == NULL) {
    return EFI_DEVICE_ERROR;
  }
  //
  // Verify that the current state of the adapter is valid for this call.
  //
  switch (SimpleNetworkDevice->SimpleNetworkMode.State) {
  case EfiSimpleNetworkInitialized:
    break;

  case EfiSimpleNetworkStopped:
    return EFI_NOT_STARTED;

  case EfiSimpleNetworkStarted:
  default:
    return EFI_DEVICE_ERROR;
  }

  if (Buffer == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  if (BufferSize < SimpleNetworkDevice->SimpleNetworkMode.MediaHeaderSize) {
    return EFI_BUFFER_TOO_SMALL;
  }

  if (HeaderSize != 0) {
    if (HeaderSize != SimpleNetworkDevice->SimpleNetworkMode.MediaHeaderSize) {
      return EFI_INVALID_PARAMETER;
    }

    if (DestAddr == NULL || Protocol == NULL) {
      return EFI_INVALID_PARAMETER;
    }

    if (DestAddr != NULL) {
      CopyMem (
        Buffer,
        DestAddr,
        SimpleNetworkDevice->SimpleNetworkMode.HwAddressSize
        );
    }

    if (SrcAddr == NULL) {
      SrcAddr = &SimpleNetworkDevice->SimpleNetworkMode.CurrentAddress;
    }

    CopyMem (
      (UINT8 *) Buffer + SimpleNetworkDevice->SimpleNetworkMode.HwAddressSize,
      SrcAddr,
      SimpleNetworkDevice->SimpleNetworkMode.HwAddressSize
      );

    if (Protocol != NULL) {
      *(UINT16 *) ((UINT8 *) Buffer + 2 * SimpleNetworkDevice->SimpleNetworkMode.HwAddressSize) = (UINT16) (((*Protocol & 0xFF) << 8) | ((*Protocol >> 8) & 0xFF));
    }
  }
  //
  // See if the recycled transmit buffer FIFO is full.
  // If it is full, then we can not transmit until the caller calls GetStatus() to pull
  // off recycled transmit buffers.
  //
  if (SimpleNetworkTransmitFifoFull (&(SimpleNetworkDevice->TxBufferFifo))) {
    return EFI_NOT_READY;
  }
  //
  //  Output debug trace message.
  //
  DEBUG ((DEBUG_NET, "Undi16SimpleNetworkTransmit\n\r "));

  //
  // Initialize UNDI WRITE parameter structure.
  //
  XmitInfo.Status           = INIT_PXE_STATUS;
  XmitInfo.Protocol         = P_UNKNOWN;
  XmitInfo.XmitFlag         = XMT_DESTADDR;
  XmitInfo.DestAddrOffset   = (UINT16) ((UINT32)(UINTN) SimpleNetworkDevice->TxDestAddr & 0x000f);
  XmitInfo.DestAddrSegment  = (UINT16) ((UINT32)(UINTN) SimpleNetworkDevice->TxDestAddr >> 4);
  XmitInfo.TBDOffset        = (UINT16) ((UINT32)(UINTN) SimpleNetworkDevice->Xmit & 0x000f);
  XmitInfo.TBDSegment       = (UINT16) ((UINT32)(UINTN) SimpleNetworkDevice->Xmit >> 4);
  XmitInfo.Reserved[0]      = 0;
  XmitInfo.Reserved[1]      = 0;

  CopyMem (
    SimpleNetworkDevice->TxDestAddr,
    Buffer,
    SimpleNetworkDevice->SimpleNetworkMode.HwAddressSize
    );

  CopyMem (
    SimpleNetworkDevice->TxRealModeMediaHeader,
    Buffer,
    SimpleNetworkDevice->SimpleNetworkMode.MediaHeaderSize
    );

  SimpleNetworkDevice->Xmit->ImmedLength            = (UINT16) SimpleNetworkDevice->SimpleNetworkMode.MediaHeaderSize;

  SimpleNetworkDevice->Xmit->DataBlock[0].TDDataLen = (UINT16) (BufferSize - SimpleNetworkDevice->Xmit->ImmedLength);

  CopyMem (
    SimpleNetworkDevice->TxRealModeDataBuffer,
    (UINT8 *) Buffer + SimpleNetworkDevice->SimpleNetworkMode.MediaHeaderSize,
    SimpleNetworkDevice->Xmit->DataBlock[0].TDDataLen
    );

  //
  // Make API call to UNDI TRANSMIT
  //
  XmitInfo.Status = 0;

  Status          = PxeUndiTransmit (SimpleNetworkDevice, &XmitInfo);

  if (EFI_ERROR (Status)) {
    return Status;
  }
  //
  // Check the status code from the 16 bit UNDI ROM
  //
  switch (XmitInfo.Status) {
  case PXENV_STATUS_OUT_OF_RESOURCES:
    return EFI_NOT_READY;

  case PXENV_STATUS_SUCCESS:
    break;

  default:
    return EFI_DEVICE_ERROR;
  }
  //
  // Add address of Buffer to the recycled transmit buffer FIFO
  //
  SimpleNetworkTransmitFifoAdd (&(SimpleNetworkDevice->TxBufferFifo), Buffer);

  return EFI_SUCCESS;
}

/**
  Receives a packet from a network interface.
  
  @param  This       The protocol instance pointer.
  @param  HeaderSize The size, in bytes, of the media header received on the network
                     interface. If this parameter is NULL, then the media header size
                     will not be returned.
  @param  BufferSize On entry, the size, in bytes, of Buffer. On exit, the size, in
                     bytes, of the packet that was received on the network interface.
  @param  Buffer     A pointer to the data buffer to receive both the media header and
                     the data.
  @param  SrcAddr    The source HW MAC address. If this parameter is NULL, the
                     HW MAC source address will not be extracted from the media
                     header.
  @param  DestAddr   The destination HW MAC address. If this parameter is NULL,
                     the HW MAC destination address will not be extracted from the
                     media header.
  @param  Protocol   The media header type. If this parameter is NULL, then the
                     protocol will not be extracted from the media header. See
                     RFC 1700 section "Ether Types" for examples.

  @retval  EFI_SUCCESS           The received data was stored in Buffer, and BufferSize has
                                 been updated to the number of bytes received.
  @retval  EFI_NOT_STARTED       The network interface has not been started.
  @retval  EFI_NOT_READY         The network interface is too busy to accept this transmit
                                 request.
  @retval  EFI_BUFFER_TOO_SMALL  The BufferSize parameter is too small.
  @retval  EFI_INVALID_PARAMETER One or more of the parameters has an unsupported value.
  @retval  EFI_DEVICE_ERROR      The command could not be sent to the network interface.
  @retval  EFI_UNSUPPORTED       This function is not supported by the network interface.

**/
EFI_STATUS
EFIAPI
Undi16SimpleNetworkReceive (
  IN EFI_SIMPLE_NETWORK_PROTOCOL            *This,
  OUT UINTN                                 *HeaderSize OPTIONAL,
  IN OUT UINTN                              *BufferSize,
  OUT VOID                                  *Buffer,
  OUT EFI_MAC_ADDRESS                       *SrcAddr OPTIONAL,
  OUT EFI_MAC_ADDRESS                       *DestAddr OPTIONAL,
  OUT UINT16                                *Protocol OPTIONAL
  )
{
  EFI_STATUS              Status;
  EFI_SIMPLE_NETWORK_DEV  *SimpleNetworkDevice;
  UINTN                   MediaAddrSize;
  UINT8                   ProtType;

  if (This == NULL || BufferSize == NULL || Buffer == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  Status              = EFI_SUCCESS;
  SimpleNetworkDevice = EFI_SIMPLE_NETWORK_DEV_FROM_THIS (This);

  if (SimpleNetworkDevice == NULL) {
    return EFI_DEVICE_ERROR;
  }
  //
  // Verify that the current state of the adapter is valid for this call.
  //
  switch (SimpleNetworkDevice->SimpleNetworkMode.State) {
  case EfiSimpleNetworkInitialized:
    break;

  case EfiSimpleNetworkStopped:
    return EFI_NOT_STARTED;

  case EfiSimpleNetworkStarted:
  default:
    return EFI_DEVICE_ERROR;
  }

  Status = Undi16SimpleNetworkIsr (
            This,
            BufferSize,
            HeaderSize,
            Buffer,
            &ProtType,
            NULL
            );

  if (EFI_ERROR (Status)) {
    return Status;
  }

  if ((SimpleNetworkDevice->InterruptStatus & EFI_SIMPLE_NETWORK_RECEIVE_INTERRUPT) == 0) {
    return EFI_NOT_READY;

  }

  SimpleNetworkDevice->InterruptStatus &= ~EFI_SIMPLE_NETWORK_RECEIVE_INTERRUPT;

  MediaAddrSize = This->Mode->HwAddressSize;

  if (SrcAddr != NULL) {
    CopyMem (SrcAddr, (UINT8 *) Buffer + MediaAddrSize, MediaAddrSize);
  }

  if (DestAddr != NULL) {
    CopyMem (DestAddr, Buffer, MediaAddrSize);
  }

  if (Protocol != NULL) {
    *((UINT8 *) Protocol)     = *((UINT8 *) Buffer + (2 * MediaAddrSize) + 1);
    *((UINT8 *) Protocol + 1) = *((UINT8 *) Buffer + (2 * MediaAddrSize));
  }

  DEBUG ((DEBUG_NET, "Packet Received: BufferSize=%d  HeaderSize = %d\n", *BufferSize, *HeaderSize));

  return Status;

}
//
// WaitForPacket()
//
/**
  wait for a packet to be received.

  @param Event      Event used with WaitForEvent() to wait for a packet to be received.
  @param Context    Event Context
  
**/
VOID
EFIAPI
Undi16SimpleNetworkWaitForPacket (
  IN EFI_EVENT               Event,
  IN VOID                    *Context
  )
{
  //
  // Someone is waiting on the receive packet event, if there's
  // a packet pending, signal the event
  //
  if (!EFI_ERROR (Undi16SimpleNetworkCheckForPacket (Context))) {
    gBS->SignalEvent (Event);
  }
}
//
// CheckForPacket()
//
/**
  Check whether packet is ready for receive.

  @param This The protocol instance pointer.
  
  @retval  EFI_SUCCESS           Receive data is ready.
  @retval  EFI_NOT_STARTED       The network interface has not been started.
  @retval  EFI_NOT_READY         The network interface is too busy to accept this transmit
                                 request.
  @retval  EFI_BUFFER_TOO_SMALL  The BufferSize parameter is too small.
  @retval  EFI_INVALID_PARAMETER One or more of the parameters has an unsupported value.
  @retval  EFI_DEVICE_ERROR      The command could not be sent to the network interface.
  @retval  EFI_UNSUPPORTED       This function is not supported by the network interface.
**/
EFI_STATUS
Undi16SimpleNetworkCheckForPacket (
  IN EFI_SIMPLE_NETWORK_PROTOCOL *This
  )
{
  EFI_STATUS              Status;
  EFI_SIMPLE_NETWORK_DEV  *SimpleNetworkDevice;
  UINTN                   FrameLength;

  if (This == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  Status              = EFI_SUCCESS;
  SimpleNetworkDevice = EFI_SIMPLE_NETWORK_DEV_FROM_THIS (This);

  if (SimpleNetworkDevice == NULL) {
    return EFI_DEVICE_ERROR;
  }
  //
  // Verify that the current state of the adapter is valid for this call.
  //
  switch (SimpleNetworkDevice->SimpleNetworkMode.State) {
  case EfiSimpleNetworkInitialized:
    break;

  case EfiSimpleNetworkStopped:
    return EFI_NOT_STARTED;

  case EfiSimpleNetworkStarted:
  default:
    return EFI_DEVICE_ERROR;
  }

  FrameLength = 0;
  Status = Undi16SimpleNetworkIsr (
            This,
            &FrameLength,
            NULL,
            NULL,
            NULL,
            NULL
            );

  if (Status != EFI_BUFFER_TOO_SMALL) {
    if (EFI_ERROR (Status)) {
      return Status;
    }
  }

  return ((SimpleNetworkDevice->InterruptStatus & EFI_SIMPLE_NETWORK_RECEIVE_INTERRUPT) != 0) ? EFI_SUCCESS : EFI_NOT_READY;
}

/**
  Signal handlers for ExitBootServices event.
  
  Clean up any Real-mode UNDI residue from the system 
   
  @param Event      ExitBootServices event
  @param Context 
**/
VOID
EFIAPI
Undi16SimpleNetworkEvent (
  IN EFI_EVENT        Event,
  IN VOID             *Context
  )
{
  //
  // NOTE:  This is not the only way to effect this cleanup.  The prescribed mechanism
  //        would be to perform an UNDI STOP command.  This strategam has been attempted
  //        but results in problems making some of the EFI core services from TPL_CALLBACK.
  //        This issue needs to be resolved, but the other alternative has been to perform
  //        the unchain logic explicitly, as done below.
  //
  RestoreCachedVectorAddress (0x1A);
}

/**
  Allocate buffer below 1M for real mode.

  @param NumPages     The number pages want to be allocated.
  @param Buffer       On return, allocated buffer.
  
  @return Status of allocating pages.
**/
EFI_STATUS
BiosSnp16AllocatePagesBelowOneMb (
  UINTN  NumPages,
  VOID   **Buffer
  )
{
  EFI_STATUS            Status;
  EFI_PHYSICAL_ADDRESS  PhysicalAddress;

  PhysicalAddress = 0x000fffff;
  Status = gBS->AllocatePages (
                  AllocateMaxAddress,
                  EfiRuntimeServicesData,
                  NumPages,
                  &PhysicalAddress
                  );
  if (EFI_ERROR (Status)) {
    return Status;
  }

  *Buffer = (VOID *) (UINTN) PhysicalAddress;
  return EFI_SUCCESS;
}