text: /** @file
    Implement the interface to the AX88772 Ethernet controller.
  
    This module implements the interface to the ASIX AX88772
    USB to Ethernet MAC with integrated 10/100 PHY.  Note that this implementation
    only supports the integrated PHY since no other test cases were available.
  
    Copyright (c) 2011, Intel Corporation. All rights reserved.<BR>
    SPDX-License-Identifier: BSD-2-Clause-Patent
  
  **/
  
  #include "Ax88772.h"
  
  
  /**
    Compute the CRC
  
    @param [in] pMacAddress      Address of a six byte buffer to containing the MAC address.
  
    @returns The CRC-32 value associated with this MAC address
  
  **/
  UINT32
  Ax88772Crc (
    IN UINT8 * pMacAddress
    )
  {
    UINT32 BitNumber;
    INT32 Carry;
    INT32 Crc;
    UINT32 Data;
    UINT8 * pEnd;
  
    DBG_ENTER ( );
  
    //
    //  Walk the MAC address
    //
    Crc = -1;
    pEnd = &pMacAddress[ PXE_HWADDR_LEN_ETHER ];
    while ( pEnd > pMacAddress ) {
      Data = *pMacAddress++;
      
      
      //
      //  CRC32: x32 + x26 + x23 + x22 + x16 + x12 + x11 + x10 + x8 + x7 + x5 + x4 + x2 + x + 1
      //
      //          1 0000 0100 1100 0001 0001 1101 1011 0111
      //
      for ( BitNumber = 0; 8 > BitNumber; BitNumber++ ) {
        Carry = (( Crc >> 31 ) & 1 ) ^ ( Data & 1 );
        Crc <<= 1;
        if ( 0 != Carry ) {
          Crc ^= 0x04c11db7;
        }
        Data >>= 1;
      }
    }
  
    //
    //  Return the CRC value
    //
    DBG_EXIT_HEX ( Crc );
    return (UINT32) Crc;
  }
  
  
  /**
    Get the MAC address
  
    This routine calls ::Ax88772UsbCommand to request the MAC
    address from the network adapter.
  
    @param [in] pNicDevice       Pointer to the NIC_DEVICE structure
    @param [out] pMacAddress      Address of a six byte buffer to receive the MAC address.
  
    @retval EFI_SUCCESS          The MAC address is available.
    @retval other                The MAC address is not valid.
  
  **/
  EFI_STATUS
  Ax88772MacAddressGet (
    IN NIC_DEVICE * pNicDevice,
    OUT UINT8 * pMacAddress
    )
  {
    USB_DEVICE_REQUEST SetupMsg;
    EFI_STATUS Status;
    
    DBG_ENTER ( );
    
    //
    //  Set the register address.
    //
    SetupMsg.RequestType = USB_ENDPOINT_DIR_IN
                         | USB_REQ_TYPE_VENDOR
                         | USB_TARGET_DEVICE;
    SetupMsg.Request = CMD_MAC_ADDRESS_READ;
    SetupMsg.Value = 0;
    SetupMsg.Index = 0;
    SetupMsg.Length = PXE_HWADDR_LEN_ETHER;
  
    //
    //  Read the PHY register
    //
    Status = Ax88772UsbCommand ( pNicDevice,
                                 &SetupMsg,
                                 pMacAddress );
  
    //
    // Return the operation status
    //
    DBG_EXIT_STATUS ( Status );
    return Status;
  }
  
  
  /**
    Set the MAC address
  
    This routine calls ::Ax88772UsbCommand to set the MAC address
    in the network adapter.
  
    @param [in] pNicDevice       Pointer to the NIC_DEVICE structure
    @param [in] pMacAddress      Address of a six byte buffer to containing the new MAC address.
  
    @retval EFI_SUCCESS          The MAC address was set.
    @retval other                The MAC address was not set.
  
  **/
  EFI_STATUS
  Ax88772MacAddressSet (
    IN NIC_DEVICE * pNicDevice,
    IN UINT8 * pMacAddress
    )
  {
    USB_DEVICE_REQUEST SetupMsg;
    EFI_STATUS Status;
    
    DBG_ENTER ( );
    
    //
    //  Set the register address.
    //
    SetupMsg.RequestType = USB_REQ_TYPE_VENDOR
                         | USB_TARGET_DEVICE;
    SetupMsg.Request = CMD_MAC_ADDRESS_WRITE;
    SetupMsg.Value = 0;
    SetupMsg.Index = 0;
    SetupMsg.Length = PXE_HWADDR_LEN_ETHER;
    
    //
    //  Read the PHY register
    //
    Status = Ax88772UsbCommand ( pNicDevice,
                                 &SetupMsg,
                                 pMacAddress );
    
    //
    // Return the operation status
    //
    DBG_EXIT_STATUS ( Status );
    return Status;
  }
  
  
  /**
    Clear the multicast hash table
  
    @param [in] pNicDevice       Pointer to the NIC_DEVICE structure
  
  **/
  VOID
  Ax88772MulticastClear (
    IN NIC_DEVICE * pNicDevice
    )
  {
    DBG_ENTER ( );
  
    //
    // Clear the multicast hash table
    //
    pNicDevice->MulticastHash[0] = 0;
    pNicDevice->MulticastHash[1] = 0;
  
    DBG_EXIT ( );
  }
  
  
  /**
    Enable a multicast address in the multicast hash table
  
    This routine calls ::Ax88772Crc to compute the hash bit for
    this MAC address.
  
    @param [in] pNicDevice       Pointer to the NIC_DEVICE structure
    @param [in] pMacAddress      Address of a six byte buffer to containing the MAC address.
  
  **/
  VOID
  Ax88772MulticastSet (
    IN NIC_DEVICE * pNicDevice,
    IN UINT8 * pMacAddress
    )
  {
    UINT32 BitNumber;
    UINT32 Crc;
    UINT32 Mask;
  
    DBG_ENTER ( );
  
    //
    //  Compute the CRC on the destination address
    //
    Crc = Ax88772Crc ( pMacAddress );
  
    //
    //  Set the bit corresponding to the destination address
    //
    BitNumber = Crc >> 26;
    if ( 32 > BitNumber ) {
      Mask = 1 << BitNumber;
      pNicDevice->MulticastHash[0] |= Mask;
    }
    else {
      Mask = 1 << ( BitNumber - 32 );
      pNicDevice->MulticastHash[1] |= Mask;
    }
  
    //
    //  Display the multicast address
    //
    DEBUG (( DEBUG_RX_MULTICAST | DEBUG_INFO,
              "Enable multicast: 0x%02x-%02x-%02x-%02x-%02x-%02x, CRC: 0x%08x, Bit number: 0x%02x\r\n",
              pMacAddress[0],
              pMacAddress[1],
              pMacAddress[2],
              pMacAddress[3],
              pMacAddress[4],
              pMacAddress[5],
              Crc,
              BitNumber ));
  
    DBG_EXIT ( );
  }
  
  
  /**
    Start the link negotiation
  
    This routine calls ::Ax88772PhyWrite to start the PHY's link
    negotiation.
  
    @param [in] pNicDevice       Pointer to the NIC_DEVICE structure
  
    @retval EFI_SUCCESS          The link negotiation was started.
    @retval other                Failed to start the link negotiation.
  
  **/
  EFI_STATUS
  Ax88772NegotiateLinkStart (
    IN NIC_DEVICE * pNicDevice
    )
  {
    UINT16 Control;
    EFI_STATUS Status;
  
    DBG_ENTER ( );
  
    //
    // Set the supported capabilities.
    //
    Status = Ax88772PhyWrite ( pNicDevice,
                               PHY_ANAR,
                               AN_CSMA_CD
                               | AN_TX_FDX | AN_TX_HDX
                               | AN_10_FDX | AN_10_HDX );
    if ( !EFI_ERROR ( Status )) {
      //
      // Set the link speed and duplex
      //
      Control = BMCR_AUTONEGOTIATION_ENABLE
              | BMCR_RESTART_AUTONEGOTIATION;
      if ( pNicDevice->b100Mbps ) {
        Control |= BMCR_100MBPS;
      }
      if ( pNicDevice->bFullDuplex ) {
        Control |= BMCR_FULL_DUPLEX;
      }
      Status = Ax88772PhyWrite ( pNicDevice, PHY_BMCR, Control );
    }
  
    //
    // Return the operation status
    //
    DBG_EXIT_STATUS ( Status );
    return Status;
  }
  
  
  /**
    Complete the negotiation of the PHY link
  
    This routine calls ::Ax88772PhyRead to determine if the
    link negotiation is complete.
  
    @param [in] pNicDevice       Pointer to the NIC_DEVICE structure
    @param [in, out] pPollCount  Address of number of times this routine was polled
    @param [out] pbComplete      Address of boolean to receive complate status.
    @param [out] pbLinkUp        Address of boolean to receive link status, TRUE=up.
    @param [out] pbHiSpeed       Address of boolean to receive link speed, TRUE=100Mbps.
    @param [out] pbFullDuplex    Address of boolean to receive link duplex, TRUE=full.
  
    @retval EFI_SUCCESS          The MAC address is available.
    @retval other                The MAC address is not valid.
  
  **/
  EFI_STATUS
  Ax88772NegotiateLinkComplete (
    IN NIC_DEVICE * pNicDevice,
    IN OUT UINTN * pPollCount,
    OUT BOOLEAN * pbComplete,
    OUT BOOLEAN * pbLinkUp,
    OUT BOOLEAN * pbHiSpeed,
    OUT BOOLEAN * pbFullDuplex
    )
  {
    UINT16 Mask;
    UINT16 PhyData;
    EFI_STATUS  Status;
  
    DBG_ENTER ( );
    
    //
    //  Determine if the link is up.
    //
    *pbComplete = FALSE;
  
    //
    //  Get the link status
    //
    Status = Ax88772PhyRead ( pNicDevice,
                              PHY_BMSR,
                              &PhyData );
    if ( !EFI_ERROR ( Status )) {
      //
      //  Determine if the autonegotiation is complete.
      //
      *pbLinkUp = (BOOLEAN)( 0 != ( PhyData & BMSR_LINKST ));
      *pbComplete = *pbLinkUp;
      if ( 0 != *pbComplete ) {
        //
        //  Get the partners capabilities.
        //
        Status = Ax88772PhyRead ( pNicDevice,
                                  PHY_ANLPAR,
                                  &PhyData );
        if ( !EFI_ERROR ( Status )) {
          //
          //  Autonegotiation is complete
          //  Determine the link speed.
          //
          *pbHiSpeed = (BOOLEAN)( 0 != ( PhyData & ( AN_TX_FDX | AN_TX_HDX )));
  
          //
          //  Determine the link duplex.
          //
          Mask = ( *pbHiSpeed ) ? AN_TX_FDX : AN_10_FDX;
          *pbFullDuplex = (BOOLEAN)( 0 != ( PhyData & Mask ));
        }
      }
    }
  
    //
    // Return the operation status
    //
    DBG_EXIT_STATUS ( Status );
    return Status;
  }
  
  
  /**
    Read a register from the PHY
  
    This routine calls ::Ax88772UsbCommand to read a PHY register.
  
    @param [in] pNicDevice       Pointer to the NIC_DEVICE structure
    @param [in] RegisterAddress  Number of the register to read.
    @param [in, out] pPhyData    Address of a buffer to receive the PHY register value
  
    @retval EFI_SUCCESS          The PHY data is available.
    @retval other                The PHY data is not valid.
  
  **/
  EFI_STATUS
  Ax88772PhyRead (
    IN NIC_DEVICE * pNicDevice,
    IN UINT8 RegisterAddress,
    IN OUT UINT16 * pPhyData
    )
  {
    USB_DEVICE_REQUEST SetupMsg;
    EFI_STATUS Status;
  
    DBG_ENTER ( );
  
    //
    //  Request access to the PHY
    //
    SetupMsg.RequestType = USB_REQ_TYPE_VENDOR
                         | USB_TARGET_DEVICE;
    SetupMsg.Request = CMD_PHY_ACCESS_SOFTWARE;
    SetupMsg.Value = 0;
    SetupMsg.Index = 0;
    SetupMsg.Length = 0;
    Status = Ax88772UsbCommand ( pNicDevice,
                                 &SetupMsg,
                                 NULL );
    if ( !EFI_ERROR ( Status )) {
      //
      //  Read the PHY register address.
      //
      SetupMsg.RequestType = USB_ENDPOINT_DIR_IN
                           | USB_REQ_TYPE_VENDOR
                           | USB_TARGET_DEVICE;
      SetupMsg.Request = CMD_PHY_REG_READ;
      SetupMsg.Value = pNicDevice->PhyId;
      SetupMsg.Index = RegisterAddress;
      SetupMsg.Length = sizeof ( *pPhyData );
      Status = Ax88772UsbCommand ( pNicDevice,
                                   &SetupMsg,
                                   pPhyData );
      if ( !EFI_ERROR ( Status )) {
        DEBUG (( DEBUG_PHY | DEBUG_INFO,
                  "PHY %d: 0x%02x --> 0x%04x\r\n",
                  pNicDevice->PhyId,
                  RegisterAddress,
                  *pPhyData ));
  
        //
        //  Release the PHY to the hardware
        //
        SetupMsg.RequestType = USB_REQ_TYPE_VENDOR
                             | USB_TARGET_DEVICE;
        SetupMsg.Request = CMD_PHY_ACCESS_HARDWARE;
        SetupMsg.Value = 0;
        SetupMsg.Index = 0;
        SetupMsg.Length = 0;
        Status = Ax88772UsbCommand ( pNicDevice,
                                     &SetupMsg,
                                     NULL );
      }
    }
  
    //
    //  Return the operation status.
    //
    DBG_EXIT_STATUS ( Status );
    return Status;
  }
  
  
  /**
    Write to a PHY register
  
    This routine calls ::Ax88772UsbCommand to write a PHY register.
  
    @param [in] pNicDevice       Pointer to the NIC_DEVICE structure
    @param [in] RegisterAddress  Number of the register to read.
    @param [in] PhyData          Address of a buffer to receive the PHY register value
  
    @retval EFI_SUCCESS          The PHY data was written.
    @retval other                Failed to wwrite the PHY register.
  
  **/
  EFI_STATUS
  Ax88772PhyWrite (
    IN NIC_DEVICE * pNicDevice,
    IN UINT8 RegisterAddress,
    IN UINT16 PhyData
    )
  {
    USB_DEVICE_REQUEST SetupMsg;
    EFI_STATUS Status;
    
    DBG_ENTER ( );
    
    //
    //  Request access to the PHY
    //
    SetupMsg.RequestType = USB_REQ_TYPE_VENDOR
                         | USB_TARGET_DEVICE;
    SetupMsg.Request = CMD_PHY_ACCESS_SOFTWARE;
    SetupMsg.Value = 0;
    SetupMsg.Index = 0;
    SetupMsg.Length = 0;
    Status = Ax88772UsbCommand ( pNicDevice,
                                 &SetupMsg,
                                 NULL );
    if ( !EFI_ERROR ( Status )) {
      //
      //  Write the PHY register
      //
      SetupMsg.RequestType = USB_REQ_TYPE_VENDOR
                           | USB_TARGET_DEVICE;
      SetupMsg.Request = CMD_PHY_REG_WRITE;
      SetupMsg.Value = pNicDevice->PhyId;
      SetupMsg.Index = RegisterAddress;
      SetupMsg.Length = sizeof ( PhyData );
      Status = Ax88772UsbCommand ( pNicDevice,
                                   &SetupMsg,
                                   &PhyData );
      if ( !EFI_ERROR ( Status )) {
        DEBUG (( DEBUG_PHY | DEBUG_INFO,
                  "PHY %d: 0x%02x <-- 0x%04x\r\n",
                  pNicDevice->PhyId,
                  RegisterAddress,
                  PhyData ));
  
        //
        //  Release the PHY to the hardware
        //
        SetupMsg.RequestType = USB_REQ_TYPE_VENDOR
                             | USB_TARGET_DEVICE;
        SetupMsg.Request = CMD_PHY_ACCESS_HARDWARE;
        SetupMsg.Value = 0;
        SetupMsg.Index = 0;
        SetupMsg.Length = 0;
        Status = Ax88772UsbCommand ( pNicDevice,
                                     &SetupMsg,
                                     NULL );
      }
    }
  
    //
    //  Return the operation status.
    //
    DBG_EXIT_STATUS ( Status );
    return Status;
  }
  
  
  /**
    Reset the AX88772
  
    This routine uses ::Ax88772UsbCommand to reset the network
    adapter.  This routine also uses ::Ax88772PhyWrite to reset
    the PHY.
  
    @param [in] pNicDevice       Pointer to the NIC_DEVICE structure
  
    @retval EFI_SUCCESS          The MAC address is available.
    @retval other                The MAC address is not valid.
  
  **/
  EFI_STATUS
  Ax88772Reset (
    IN NIC_DEVICE * pNicDevice
    )
  {
    USB_DEVICE_REQUEST SetupMsg;
    EFI_STATUS Status;
    
    DBG_ENTER ( );
  
    //
    //  Turn off the MAC
    //
    SetupMsg.RequestType = USB_REQ_TYPE_VENDOR
                         | USB_TARGET_DEVICE;
    SetupMsg.Request = CMD_RX_CONTROL_WRITE;
    SetupMsg.Value = 0;
    SetupMsg.Index = 0;
    SetupMsg.Length = 0;
    Status = Ax88772UsbCommand ( pNicDevice,
                                 &SetupMsg,
                                 NULL );
    if ( !EFI_ERROR ( Status )) {
      DEBUG (( DEBUG_PHY | DEBUG_RX_BROADCAST | DEBUG_RX_MULTICAST
                | DEBUG_RX_UNICAST | DEBUG_TX | DEBUG_INFO,
                "MAC reset\r\n" ));
  
      //
      //  The link is now idle
      //
      pNicDevice->bLinkIdle = TRUE;
  
      //
      //  Delay for a bit
      //
      gBS->Stall ( RESET_MSEC );
  
      //
      //  Select the internal PHY
      //
      SetupMsg.RequestType = USB_REQ_TYPE_VENDOR
                           | USB_TARGET_DEVICE;
      SetupMsg.Request = CMD_PHY_SELECT;
      SetupMsg.Value = SPHY_PSEL;
      SetupMsg.Index = 0;
      SetupMsg.Length = 0;
      Status = Ax88772UsbCommand ( pNicDevice,
                                   &SetupMsg,
                                   NULL );
      if ( !EFI_ERROR ( Status )) {
        //
        //  Delay for a bit
        //
        gBS->Stall ( PHY_RESET_MSEC );
  
        //
        //  Clear the internal PHY reset
        //
        SetupMsg.Request = CMD_RESET;
        SetupMsg.Value = SRR_IPRL | SRR_PRL;
        Status = Ax88772UsbCommand ( pNicDevice,
                                     &SetupMsg,
                                     NULL );
        if ( !EFI_ERROR ( Status )) {
          //
          //  Reset the PHY
          //
          Status = Ax88772PhyWrite ( pNicDevice,
                                     PHY_BMCR,
                                     BMCR_RESET );
          if ( !EFI_ERROR ( Status )) {
            //
            //  Set the gaps
            //
            SetupMsg.RequestType = USB_REQ_TYPE_VENDOR
                                 | USB_TARGET_DEVICE;
            SetupMsg.Request = CMD_GAPS_WRITE;
            SetupMsg.Value = 0x0c15;
            SetupMsg.Index = 0x0e;
            SetupMsg.Length = 0;
            Status = Ax88772UsbCommand ( pNicDevice,
                                         &SetupMsg,
                                         NULL );
          }
        }
      }
    }
  
    //
    //  Return the operation status.
    //
    DBG_EXIT_STATUS ( Status );
    return Status;
  }
  
  
  VOID 
  FillPkt2Queue (
    IN NIC_DEVICE * pNicDevice,
    IN UINTN BufLength)
  {
  
    UINT16 * pLength;
    UINT16 * pLengthBar;
    UINT8* pData;
    UINT32 offset;
    RX_TX_PACKET * pRxPacket;
    EFI_STATUS Status;
    
    for ( offset = 0; offset < BufLength; ){
      pLength = (UINT16*) (pNicDevice->pBulkInBuff + offset);
      pLengthBar = (UINT16*) (pNicDevice->pBulkInBuff + offset +2);
      
      *pLength &= 0x7ff;
      *pLengthBar &= 0x7ff;
      *pLengthBar |= 0xf800;
        
      if ((*pLength ^ *pLengthBar ) != 0xFFFF) {
        DEBUG (( EFI_D_ERROR , "Pkt length error. BufLength = %d\n", BufLength));
        return;
      }
        
      pRxPacket = pNicDevice->pRxFree;
      if ( NULL == pRxPacket ) {
        Status = gBS->AllocatePool ( EfiRuntimeServicesData,
                                     sizeof( RX_TX_PACKET ),
                                     (VOID **) &pRxPacket );
        if ( !EFI_ERROR ( Status )) {
          //
          //  Add this packet to the free packet list
          //
          pNicDevice->pRxFree = pRxPacket;
          pRxPacket->pNext = NULL;
        }
        else {
          //
          //  Use the discard packet buffer
          //
          //pRxPacket = &Packet;
        }
      }
        
  
      pData = pNicDevice->pBulkInBuff + offset + 4;
      pRxPacket->Length = *pLength;
      pRxPacket->LengthBar = *(UINT16*) (pNicDevice->pBulkInBuff + offset +2);
      CopyMem (&pRxPacket->Data[0], pData, *pLength);
      //DEBUG((DEBUG_INFO, "Packet [%d]\n", *pLength));
      
      pNicDevice->pRxFree = pRxPacket->pNext;
      pRxPacket->pNext = NULL;
      
      if ( NULL == pNicDevice->pRxTail ) {
        pNicDevice->pRxHead = pRxPacket;
      }
      else {
        pNicDevice->pRxTail->pNext = pRxPacket;
      }
      pNicDevice->pRxTail = pRxPacket;
      offset += (*pLength + 4);
                
    }
  }
  
  
  
  /**
    Receive a frame from the network.
  
    This routine polls the USB receive interface for a packet.  If a packet
    is available, this routine adds the receive packet to the list of
    pending receive packets.
  
    This routine calls ::Ax88772NegotiateLinkComplete to verify
    that the link is up.  This routine also calls ::SN_Reset to
    reset the network adapter when necessary.  Finally this
    routine attempts to receive one or more packets from the
    network adapter.
  
    @param [in] pNicDevice  Pointer to the NIC_DEVICE structure
    @param [in] bUpdateLink TRUE = Update link status
  
  **/
  VOID
  Ax88772Rx (
    IN NIC_DEVICE * pNicDevice,
    IN BOOLEAN bUpdateLink
    )
  {
    BOOLEAN bFullDuplex;
    BOOLEAN bLinkUp;
    BOOLEAN bRxPacket;
    BOOLEAN bSpeed100;
    UINTN LengthInBytes;
    RX_TX_PACKET Packet;
    RX_TX_PACKET * pRxPacket;
    EFI_USB_IO_PROTOCOL *pUsbIo;
    EFI_STATUS Status;
    EFI_TPL TplPrevious;
    UINT32 TransferStatus;
  
    //
    //  Synchronize with Ax88772Timer
    //
    VERIFY_TPL ( TPL_AX88772 );
    TplPrevious = gBS->RaiseTPL ( TPL_AX88772 );
    DEBUG (( DEBUG_TPL | DEBUG_INFO,
              "%d: TPL\r\n",
              TPL_AX88772 ));
  
    //
    //  Get the link status
    //
    if ( bUpdateLink ) {
      bLinkUp = pNicDevice->bLinkUp;
      bSpeed100 = pNicDevice->b100Mbps;
      bFullDuplex = pNicDevice->bFullDuplex;
      Status = Ax88772NegotiateLinkComplete ( pNicDevice,
                                              &pNicDevice->PollCount,
                                              &pNicDevice->bComplete,
                                              &pNicDevice->bLinkUp,
                                              &pNicDevice->b100Mbps,
                                              &pNicDevice->bFullDuplex );
  
      //
      // Determine if the autonegotiation is complete
      //
      if ( pNicDevice->bComplete ) {
        if ( pNicDevice->bLinkUp ) {
          if (( bSpeed100 && ( !pNicDevice->b100Mbps ))
            || (( !bSpeed100 ) && pNicDevice->b100Mbps )
            || ( bFullDuplex && ( !pNicDevice->bFullDuplex ))
            || (( !bFullDuplex ) && pNicDevice->bFullDuplex )) {
            pNicDevice->PollCount = 0;
            DEBUG (( DEBUG_LINK | DEBUG_INFO,
                      "Reset to establish proper link setup: %d Mbps, %s duplex\r\n",
                      pNicDevice->b100Mbps ? 100 : 10,
                      pNicDevice->bFullDuplex ? L"Full" : L"Half" ));
            Status = SN_Reset ( &pNicDevice->SimpleNetwork, FALSE );
          }
          if (( !bLinkUp ) && pNicDevice->bLinkUp ) {
            //
            // Display the autonegotiation status
            //
            DEBUG (( DEBUG_LINK | DEBUG_INFO,
                      "Link: Up, %d Mbps, %s duplex\r\n",
                      pNicDevice->b100Mbps ? 100 : 10,
                      pNicDevice->bFullDuplex ? L"Full" : L"Half" ));
          }
        }
      }
  
      //
      //  Update the link status
      //
      if ( bLinkUp && ( !pNicDevice->bLinkUp )) {
        DEBUG (( DEBUG_LINK | DEBUG_INFO, "Link: Down\r\n" ));
      }
    }
  
    //
    //  Loop until all the packets are emptied from the receiver
    //
    do {
      bRxPacket = FALSE;
  
      //
      //  Locate a packet for use
      //
      pRxPacket = pNicDevice->pRxFree;
      LengthInBytes = MAX_BULKIN_SIZE;
      if ( NULL == pRxPacket ) {
        Status = gBS->AllocatePool ( EfiRuntimeServicesData,
                                     sizeof ( *pRxPacket ),
                                     (VOID **) &pRxPacket );
        if ( !EFI_ERROR ( Status )) {
          //
          //  Add this packet to the free packet list
          //
          pNicDevice->pRxFree = pRxPacket;
          pRxPacket->pNext = NULL;
        }
        else {
          //
          //  Use the discard packet buffer
          //
          pRxPacket = &Packet;
        }
      }
  
      //
      //  Attempt to receive a packet
      //
      SetMem (&pNicDevice->pBulkInBuff[0], MAX_BULKIN_SIZE, 0);
      pUsbIo = pNicDevice->pUsbIo;
      Status = pUsbIo->UsbBulkTransfer ( pUsbIo,
                                         USB_ENDPOINT_DIR_IN | BULK_IN_ENDPOINT,
                                         &pNicDevice->pBulkInBuff[0],
                                         &LengthInBytes,
                                         2,
                                         &TransferStatus );
      if ( LengthInBytes > 0 ) {
        FillPkt2Queue(pNicDevice, LengthInBytes);
      }
      pRxPacket = pNicDevice->pRxHead;
      if (( !EFI_ERROR ( Status ))
        && ( 0 < pRxPacket->Length )
        && ( pRxPacket->Length <= sizeof ( pRxPacket->Data ))
        && ( LengthInBytes > 0)) {
  
        //
        //  Determine if the packet should be received
        //
        bRxPacket = TRUE;
        LengthInBytes = pRxPacket->Length;
        pNicDevice->bLinkIdle = FALSE;
        if ( pNicDevice->pRxFree == pRxPacket ) {
          //
          //  Display the received packet
          //
          if ( 0 != ( pRxPacket->Data[0] & 1 )) {
            if (( 0xff == pRxPacket->Data[0])
              && ( 0xff == pRxPacket->Data[1])
              && ( 0xff == pRxPacket->Data[2])
              && ( 0xff == pRxPacket->Data[3])
              && ( 0xff == pRxPacket->Data[4])
              && ( 0xff == pRxPacket->Data[5])) {
              DEBUG (( DEBUG_RX_BROADCAST | DEBUG_INFO,
                        "RX: %02x-%02x-%02x-%02x-%02x-%02x  %02x-%02x-%02x-%02x-%02x-%02x  %02x-%02x  %d bytes\r\n",
                        pRxPacket->Data[0],
                        pRxPacket->Data[1],
                        pRxPacket->Data[2],
                        pRxPacket->Data[3],
                        pRxPacket->Data[4],
                        pRxPacket->Data[5],
                        pRxPacket->Data[6],
                        pRxPacket->Data[7],
                        pRxPacket->Data[8],
                        pRxPacket->Data[9],
                        pRxPacket->Data[10],
                        pRxPacket->Data[11],
                        pRxPacket->Data[12],
                        pRxPacket->Data[13],
                        LengthInBytes ));
            }
            else {
              DEBUG (( DEBUG_RX_MULTICAST | DEBUG_INFO,
                        "RX: %02x-%02x-%02x-%02x-%02x-%02x  %02x-%02x-%02x-%02x-%02x-%02x  %02x-%02x  %d bytes\r\n",
                        pRxPacket->Data[0],
                        pRxPacket->Data[1],
                        pRxPacket->Data[2],
                        pRxPacket->Data[3],
                        pRxPacket->Data[4],
                        pRxPacket->Data[5],
                        pRxPacket->Data[6],
                        pRxPacket->Data[7],
                        pRxPacket->Data[8],
                        pRxPacket->Data[9],
                        pRxPacket->Data[10],
                        pRxPacket->Data[11],
                        pRxPacket->Data[12],
                        pRxPacket->Data[13],
                        LengthInBytes ));
            }
          }
          else {
            DEBUG (( DEBUG_RX_UNICAST | DEBUG_INFO,
                      "RX: %02x-%02x-%02x-%02x-%02x-%02x  %02x-%02x-%02x-%02x-%02x-%02x  %02x-%02x  %d bytes\r\n",
                      pRxPacket->Data[0],
                      pRxPacket->Data[1],
                      pRxPacket->Data[2],
                      pRxPacket->Data[3],
                      pRxPacket->Data[4],
                      pRxPacket->Data[5],
                      pRxPacket->Data[6],
                      pRxPacket->Data[7],
                      pRxPacket->Data[8],
                      pRxPacket->Data[9],
                      pRxPacket->Data[10],
                      pRxPacket->Data[11],
                      pRxPacket->Data[12],
                      pRxPacket->Data[13],
                      LengthInBytes ));
          }
          
        }
        else {
          //
          //  Error, not enough buffers for this packet, discard packet
          //
          DEBUG (( DEBUG_WARN | DEBUG_INFO,
                    "WARNING - No buffer, discarding RX packet: %02x-%02x-%02x-%02x-%02x-%02x  %02x-%02x-%02x-%02x-%02x-%02x  %02x-%02x  %d bytes\r\n",
                    pRxPacket->Data[0],
                    pRxPacket->Data[1],
                    pRxPacket->Data[2],
                    pRxPacket->Data[3],
                    pRxPacket->Data[4],
                    pRxPacket->Data[5],
                    pRxPacket->Data[6],
                    pRxPacket->Data[7],
                    pRxPacket->Data[8],
                    pRxPacket->Data[9],
                    pRxPacket->Data[10],
                    pRxPacket->Data[11],
                    pRxPacket->Data[12],
                    pRxPacket->Data[13],
                    LengthInBytes ));
        }
      }
    }while ( bRxPacket );
  
    //
    //  Release the synchronization withhe Ax88772Timer
    //
    gBS->RestoreTPL ( TplPrevious );
    DEBUG (( DEBUG_TPL | DEBUG_INFO,
              "%d: TPL\r\n",
              TplPrevious ));
  }
  
  
  /**
    Enable or disable the receiver
  
    This routine calls ::Ax88772UsbCommand to update the
    receiver state.  This routine also calls ::Ax88772MacAddressSet
    to establish the MAC address for the network adapter.
  
    @param [in] pNicDevice       Pointer to the NIC_DEVICE structure
    @param [in] RxFilter         Simple network RX filter mask value
  
    @retval EFI_SUCCESS          The MAC address was set.
    @retval other                The MAC address was not set.
  
  **/
  EFI_STATUS
  Ax88772RxControl (
    IN NIC_DEVICE * pNicDevice,
    IN UINT32 RxFilter
    )
  {
    UINT16 MediumStatus;
    INT32 MulticastHash[2];
    UINT16 RxControl;
    USB_DEVICE_REQUEST SetupMsg;
    EFI_STATUS Status;
  
    DBG_ENTER ( );
  
    //
    //  Disable all multicast
    //
    MulticastHash[0] = 0;
    MulticastHash[1] = 0;
  
    //
    // Enable the receiver if something is to be received
    //
    Status = EFI_SUCCESS;
    RxControl = RXC_SO | RXC_MFB_16384;
    if ( 0 != RxFilter ) {
      //
      //  Enable the receiver
      //
      SetupMsg.RequestType = USB_ENDPOINT_DIR_IN
                           | USB_REQ_TYPE_VENDOR
                           | USB_TARGET_DEVICE;
      SetupMsg.Request = CMD_MEDIUM_STATUS_READ;
      SetupMsg.Value = 0;
      SetupMsg.Index = 0;
      SetupMsg.Length = sizeof ( MediumStatus );
      Status = Ax88772UsbCommand ( pNicDevice,
                                   &SetupMsg,
                                   &MediumStatus );
      if ( !EFI_ERROR ( Status )) {
        if ( 0 == ( MediumStatus & MS_RE )) {
          MediumStatus |= MS_RE | MS_ONE;
          if ( pNicDevice->bFullDuplex ) {
            MediumStatus |= MS_TFC | MS_RFC;
          }
          SetupMsg.RequestType = USB_REQ_TYPE_VENDOR
                               | USB_TARGET_DEVICE;
          SetupMsg.Request = CMD_MEDIUM_STATUS_WRITE;
          SetupMsg.Value = MediumStatus;
          SetupMsg.Index = 0;
          SetupMsg.Length = 0;
          Status = Ax88772UsbCommand ( pNicDevice,
                                       &SetupMsg,
                                       NULL );
          if ( EFI_ERROR ( Status )) {
            DEBUG (( DEBUG_ERROR | DEBUG_INFO,
                      "ERROR - Failed to enable receiver, Status: %r\r\n",
                      Status ));
          }
        }
      }
      else {
        DEBUG (( DEBUG_ERROR | DEBUG_INFO,
                  "ERROR - Failed to read receiver status, Status: %r\r\n",
                  Status ));
      }
  
      //
      //  Enable multicast if requested
      //
      if ( 0 != ( RxFilter & EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST )) {
        RxControl |= RXC_AM;
        MulticastHash[0] = pNicDevice->MulticastHash[0];
        MulticastHash[1] = pNicDevice->MulticastHash[1];
      }
  
      //
      //  Enable all multicast if requested
      //
      if ( 0 != ( RxFilter & EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST )) {
        RxControl |= RXC_AMALL;
        MulticastHash[0] = -1;
        MulticastHash[1] = -1;
      }
  
      //
      //  Enable broadcast if requested
      //
      if ( 0 != ( RxFilter & EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST )) {
        RxControl |= RXC_AB;
      }
  
      //
      //  Enable promiscuous mode if requested
      //
      if ( 0 != ( RxFilter & EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS )) {
        RxControl |= RXC_PRO;
        MulticastHash[0] = -1;
        MulticastHash[1] = -1;
      }
    }
  
    //
    //  Update the MAC address
    //
    if ( !EFI_ERROR ( Status )) {
      Status = Ax88772MacAddressSet ( pNicDevice, &pNicDevice->SimpleNetworkData.CurrentAddress.Addr[0]);
    }
  
    //
    //  Update the receiver control
    //
    if ( !EFI_ERROR ( Status )) {
      SetupMsg.RequestType = USB_REQ_TYPE_VENDOR
                           | USB_TARGET_DEVICE;
      SetupMsg.Request = CMD_RX_CONTROL_WRITE;
      SetupMsg.Value = RxControl;
      SetupMsg.Index = 0;
      SetupMsg.Length = 0;
      Status = Ax88772UsbCommand ( pNicDevice,
                                   &SetupMsg,
                                   NULL );
      if ( !EFI_ERROR ( Status )) {
        DEBUG (( DEBUG_RX_BROADCAST | DEBUG_RX_MULTICAST | DEBUG_RX_UNICAST | DEBUG_INFO,
                  "RxControl: 0x%04x\r\n",
                  RxControl ));
  
        //
        //  Update the multicast hash table
        //
        SetupMsg.RequestType = USB_REQ_TYPE_VENDOR
                             | USB_TARGET_DEVICE;
        SetupMsg.Request = CMD_MULTICAST_HASH_WRITE;
        SetupMsg.Value = 0;
        SetupMsg.Index = 0;
        SetupMsg.Length = sizeof ( pNicDevice ->MulticastHash );
        Status = Ax88772UsbCommand ( pNicDevice,
                                     &SetupMsg,
                                     &pNicDevice->MulticastHash );
        if ( !EFI_ERROR ( Status )) {
          DEBUG (( DEBUG_RX_MULTICAST | DEBUG_INFO,
                    "Multicast Hash: 0x%02x %02x %02x %02x %02x %02x %02x %02x\r\n",
                    (UINT8) MulticastHash[0],
                    (UINT8)( MulticastHash[0] >> 8 ),
                    (UINT8)( MulticastHash[0] >> 16 ),
                    (UINT8)( MulticastHash[0] >> 24 ),
                    (UINT8) MulticastHash[1],
                    (UINT8)( MulticastHash[1] >> 8 ),
                    (UINT8)( MulticastHash[1] >> 16 ),
                    (UINT8)( MulticastHash[1] >> 24 )));
        }
        else {
          DEBUG (( DEBUG_ERROR | DEBUG_INFO,
                    "ERROR - Failed to update multicast hash table, Status: %r\r\n",
                    Status ));
        }
      }
      else {
        DEBUG (( DEBUG_ERROR | DEBUG_INFO,
                  "ERROR - Failed to set receiver control, Status: %r\r\n",
                  Status ));
      }
    }
  
    //
    // Return the operation status
    //
    DBG_EXIT_STATUS ( Status );
    return Status;
  }
  
  
  /**
    Read an SROM location
  
    This routine calls ::Ax88772UsbCommand to read data from the
    SROM.
  
    @param [in] pNicDevice       Pointer to the NIC_DEVICE structure
    @param [in] Address          SROM address
    @param [out] pData           Buffer to receive the data
  
    @retval EFI_SUCCESS          The read was successful
    @retval other                The read failed
  
  **/
  EFI_STATUS
  Ax88772SromRead (
    IN NIC_DEVICE * pNicDevice,
    IN UINT32 Address,
    OUT UINT16 * pData
    )
  {
    USB_DEVICE_REQUEST SetupMsg;
    EFI_STATUS Status;
  
    DBG_ENTER ( );
  
    //
    //  Read a value from the SROM
    //
    SetupMsg.RequestType = USB_ENDPOINT_DIR_IN
                         | USB_REQ_TYPE_VENDOR
                         | USB_TARGET_DEVICE;
    SetupMsg.Request = CMD_SROM_READ;
    SetupMsg.Value = (UINT16) Address;
    SetupMsg.Index = 0;
    SetupMsg.Length = sizeof ( *pData );
    Status = Ax88772UsbCommand ( pNicDevice,
                                 &SetupMsg,
                                 pData );
  
    //
    // Return the operation status
    //
    DBG_EXIT_STATUS ( Status );
    return Status;
  }
  
  
  /**
    This routine is called at a regular interval to poll for
    receive packets.
  
    This routine polls the link state and gets any receive packets
    by calling ::Ax88772Rx.
  
    @param [in] Event            Timer event
    @param [in] pNicDevice       Pointer to the NIC_DEVICE structure
  
  **/
  VOID
  Ax88772Timer (
    IN EFI_EVENT Event,
    IN NIC_DEVICE * pNicDevice
    )
  {
    //
    //  Use explicit DEBUG messages since the output frequency is too
    //  high for DEBUG_INFO to keep up and have spare cycles for the
    //  shell
    //
    DEBUG (( DEBUG_TIMER, "Entering Ax88772Timer\r\n" ));
  
    //
    //  Poll the link state and get any receive packets
    //
    Ax88772Rx ( pNicDevice, FALSE );
  
    DEBUG (( DEBUG_TIMER, "Exiting Ax88772Timer\r\n" ));
  }
  
  
  /**
    Send a command to the USB device.
  
    @param [in] pNicDevice       Pointer to the NIC_DEVICE structure
    @param [in] pRequest         Pointer to the request structure
    @param [in, out] pBuffer     Data buffer address
  
    @retval EFI_SUCCESS          The USB transfer was successful
    @retval other                The USB transfer failed
  
  **/
  EFI_STATUS
  Ax88772UsbCommand (
    IN NIC_DEVICE * pNicDevice,
    IN USB_DEVICE_REQUEST * pRequest,
    IN OUT VOID * pBuffer
    )
  {
    UINT32 CmdStatus;
    EFI_USB_DATA_DIRECTION Direction;
    EFI_USB_IO_PROTOCOL * pUsbIo;
    EFI_STATUS Status;
  
    DBG_ENTER ( );
  
    //
    // Determine the transfer direction
    //
    Direction = EfiUsbNoData;
    if ( 0 != pRequest->Length ) {
      Direction = ( 0 != ( pRequest->RequestType & USB_ENDPOINT_DIR_IN ))
                ? EfiUsbDataIn : EfiUsbDataOut;
    }
  
    //
    // Issue the command
    //
    pUsbIo = pNicDevice->pUsbIo;
    Status = pUsbIo->UsbControlTransfer ( pUsbIo,
                                          pRequest,
                                          Direction,
                                          USB_BUS_TIMEOUT,
                                          pBuffer,
                                          pRequest->Length,
                                          &CmdStatus );
  
    //
    // Determine the operation status
    //
    if ( !EFI_ERROR ( Status )) {
      Status = CmdStatus;
    }
    else {
      //
      // Display any errors
      //
      DEBUG (( DEBUG_INFO,
                "Ax88772UsbCommand - Status: %r\n",
                Status ));
  
      //
      // Only use status values associated with the Simple Network protocol
      //
      if ( EFI_TIMEOUT == Status ) {
        Status = EFI_DEVICE_ERROR;
      }
    }
  
    //
    // Return the operation status
    //
    DBG_EXIT_STATUS ( Status );
    return Status;
  }
  