 /** @file
  Implement the interface to the AX88179 Ethernet controller.

  This module implements the interface to the ASIX AX88179
  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
  Copyright (c) 2020, ARM Limited. All rights reserved.

  SPDX-License-Identifier: BSD-2-Clause-Patent

**/

#include "Ax88179.h"


/**
  Compute the CRC

  @param [in] MacAddress      Address of a six byte buffer to containing the MAC address.

  @returns The CRC-32 value associated with this MAC address

**/
UINT32
Ax88179Crc (
  IN UINT8 *MacAddress
  )
{
  UINT32 BitNumber;
  INT32  Carry;
  INT32  Crc;
  UINT32 Data;
  UINT8  *End;

  //
  //  Walk the MAC address
  //
  Crc = -1;
  End = &MacAddress[PXE_HWADDR_LEN_ETHER];
  while (End > MacAddress) {
    Data = *MacAddress++;


    //
    //  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 (Carry != 0) {
        Crc ^= 0x04c11db7;
      }
      Data >>= 1;
    }
  }

  //
  //  Return the CRC value
  //
  return (UINT32) Crc;
}

/**
  Get the MAC address

  This routine calls ::Ax88179UsbCommand to request the MAC
  address from the network adapter.

  @param [in] NicDevice       Pointer to the NIC_DEVICE structure
  @param [out] MacAddress      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
Ax88179MacAddressGet (
  IN  NIC_DEVICE *NicDevice,
  OUT UINT8      *MacAddress
  )
{
  EFI_STATUS Status;

  Status  = Ax88179MacRead (NODE_ID,
                            PXE_HWADDR_LEN_ETHER,
                            NicDevice,
                            MacAddress);

  return Status;
}

/**
  Set the MAC address

  This routine calls ::Ax88179UsbCommand to set the MAC address
  in the network adapter.

  @param [in] NicDevice       Pointer to the NIC_DEVICE structure
  @param [in] MacAddress      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
Ax88179MacAddressSet (
  IN  NIC_DEVICE *NicDevice,
  OUT UINT8      *MacAddress
  )
{
  EFI_STATUS Status;

  Status  = Ax88179MacWrite (NODE_ID,
                             PXE_HWADDR_LEN_ETHER,
                             NicDevice,
                             MacAddress);

  return Status;
}

/**
  Clear the multicast hash table

  @param [in] NicDevice       Pointer to the NIC_DEVICE structure

**/
VOID
Ax88179MulticastClear (
  IN NIC_DEVICE *NicDevice
  )
{
  int Index = 0;
  //
  // Clear the multicast hash table
  //
  for (Index = 0 ; Index < 8 ; Index ++)
    NicDevice->MulticastHash[Index] = 0;
}

/**
  Enable a multicast address in the multicast hash table

  This routine calls ::Ax88179Crc to compute the hash bit for
  this MAC address.

  @param [in] NicDevice       Pointer to the NIC_DEVICE structure
  @param [in] MacAddress      Address of a six byte buffer to containing the MAC address.

**/
VOID
Ax88179MulticastSet (
  IN NIC_DEVICE *NicDevice,
  IN UINT8      *MacAddress
  )
{
  UINT32 Crc;

  //
  // Compute the CRC on the destination address
  //
  Crc = Ax88179Crc (MacAddress) >> 26;

  //
  //  Set the bit corresponding to the destination address
  //
  NicDevice->MulticastHash [Crc >> 3] |= (1 << (Crc & 7));

}

/**
  Start the link negotiation

  This routine calls ::Ax88179PhyWrite to start the PHY's link
  negotiation.

  @param [in] NicDevice       Pointer to the NIC_DEVICE structure

  @retval EFI_SUCCESS          The link negotiation was started.
  @retval other                Failed to start the link negotiation.

**/
EFI_STATUS
Ax88179NegotiateLinkStart (
  IN NIC_DEVICE *NicDevice
  )
{
  UINT16     Control = 0;
  EFI_STATUS Status;

#if FORCE_100Mbps
  Ax88179PhyRead (NicDevice,
                   0x09,
                   &Control);
  Control &= 0xFCFF;
  Ax88179PhyWrite (NicDevice, 0x09, Control);
#endif

  //
  // Set the link speed and duplex
  //
  Control = BMCR_AUTONEGOTIATION_ENABLE
          | BMCR_RESTART_AUTONEGOTIATION;
  if (NicDevice->LinkSpeed1000Mbps) {
    Control |= BMCR_1000MBPS;
  } else if (NicDevice->LinkSpeed100Mbps) {
    Control |= BMCR_100MBPS;
  }

  if (NicDevice->FullDuplex) {
    Control |= BMCR_FULL_DUPLEX;
  }
  Status = Ax88179PhyWrite (NicDevice, PHY_BMCR, Control);
  if (!EFI_ERROR(Status))
    gBS->Stall(3000000);
  //
  // Return the operation status
  //
  return Status;
}



/**
  Complete the negotiation of the PHY link

  This routine calls ::Ax88179PhyRead to determine if the
  link negotiation is complete.

  @param [in] NicDevice       Pointer to the NIC_DEVICE structure
  @param [in, out] PollCount  Address of number of times this routine was polled
  @param [out] Complete      Address of boolean to receive complate status.
  @param [out] LinkUp        Address of boolean to receive link status, TRUE=up.
  @param [out] HiSpeed       Address of boolean to receive link speed, TRUE=100Mbps.
  @param [out] FullDuplex    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
Ax88179NegotiateLinkComplete (
  IN     NIC_DEVICE *NicDevice,
  IN OUT UINTN      *PollCount,
  OUT    BOOLEAN    *Complete,
  OUT    BOOLEAN    *LinkUp,
  OUT    BOOLEAN    *HiSpeed,
  OUT    BOOLEAN    *GigaSpeed,
  OUT    BOOLEAN    *FullDuplex
  )
{
  UINT16      PhyData;
  EFI_STATUS  Status;

  //
  //  Determine if the link is up.
  //
  *Complete = FALSE;

  //
  //  Get the link status
  //
  Status = Ax88179PhyRead (NicDevice,
                            PHY_PHYSR,
                            &PhyData);
  if (EFI_ERROR (Status)) {
    return Status;
  }

  *LinkUp = ((PhyData & PHYSR_LINK) != 0);
  if (*LinkUp) {
    //
    //  Determine if the autonegotiation is complete.
    //
    Status = Ax88179PhyRead (NicDevice,
                          PHY_BMSR,
                          &PhyData);
    if (EFI_ERROR(Status)) {
      return Status;
    }

    *Complete = ((PhyData & BMSR_AUTONEG_CMPLT) != 0);

    if (*Complete != 0) {
      //
      //  Get the partners capabilities.
      //
      Status = Ax88179PhyRead (NicDevice,
                                PHY_PHYSR,
                                &PhyData);
      if (EFI_ERROR (Status)) {
        return Status;
      }

      //
      //  Autonegotiation is complete
      //  Determine the link speed.
      //
      *GigaSpeed = ((PhyData & PHYSR_SPEED_MASK) == PHYSR_1000);
      *HiSpeed = ((PhyData & PHYSR_SPEED_MASK) == PHYSR_100);

      //
      //  Determine the link duplex.
      //
      *FullDuplex = ((PhyData & PHYSR_FULLDUP) == PHYSR_FULLDUP);
    }
  }


  //
  // Return the operation status
  //
  return Status;
}


/**
  Read a register from the PHY

  This routine calls ::Ax88179UsbCommand to read a PHY register.

  @param [in] NicDevice       Pointer to the NIC_DEVICE structure
  @param [in] RegisterAddress  Number of the register to read.
  @param [in, out] PhyData    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
Ax88179PhyRead (
  IN     NIC_DEVICE *NicDevice,
  IN     UINT8      RegisterAddress,
  IN OUT UINT16     *PhyData
  )
{
  USB_DEVICE_REQUEST SetupMsg;
  EFI_STATUS         Status;

  //
  //  Read the PHY register address.
  //
  SetupMsg.RequestType = USB_ENDPOINT_DIR_IN
                       | USB_REQ_TYPE_VENDOR
                       | USB_TARGET_DEVICE;
  SetupMsg.Request = ACCESS_PHY;
  SetupMsg.Value = NicDevice->PhyId;
  SetupMsg.Index = RegisterAddress;
  SetupMsg.Length = sizeof (*PhyData);
  Status = Ax88179UsbCommand (NicDevice,
                               &SetupMsg,
                               PhyData);

  //
  //  Return the operation status.
  //
  return Status;
}


/**
  Write to a PHY register

  This routine calls ::Ax88179UsbCommand to write a PHY register.

  @param [in] NicDevice       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
Ax88179PhyWrite (
  IN NIC_DEVICE *NicDevice,
  IN UINT8      RegisterAddress,
  IN UINT16     PhyData
  )
{
  USB_DEVICE_REQUEST SetupMsg;
  EFI_STATUS         Status;

  //
  //  Write the PHY register
  //
  SetupMsg.RequestType = USB_REQ_TYPE_VENDOR
                       | USB_TARGET_DEVICE;
  SetupMsg.Request = ACCESS_PHY;
  SetupMsg.Value = NicDevice->PhyId;
  SetupMsg.Index = RegisterAddress;
  SetupMsg.Length = sizeof (PhyData);
  Status = Ax88179UsbCommand (NicDevice,
                               &SetupMsg,
                               &PhyData);
  //
  //  Return the operation status.
  //
  return Status;
}


/**
  Reset the AX88179

  This routine uses ::Ax88179UsbCommand to reset the network
  adapter.  This routine also uses ::Ax88179PhyWrite to reset
  the PHY.

  @param [in] NicDevice       Pointer to the NIC_DEVICE structure

  @retval EFI_SUCCESS          The MAC address is available.
  @retval other                The MAC address is not valid.

**/
EFI_STATUS
Ax88179Reset (
  IN NIC_DEVICE *NicDevice
  )
{
  EFI_STATUS Status;
  UINT16     Val;
  UINT8      Val8;

  Status = Ax88179SetIInInterval(NicDevice, 0xff);

  if (EFI_ERROR(Status)) goto err;

  Val8 = 0;
  Status  = Ax88179MacRead (PLSR,
                            sizeof(Val8),
                            NicDevice,
                            &Val8);

  if (EFI_ERROR(Status)) goto err;

  if (Val8 & PLSR_USB_SS)
    NicDevice->UsbMaxPktSize = 1024;
  else
    NicDevice->UsbMaxPktSize = 512;

  Val = 0;
  Status = Ax88179MacWrite (PHYPWRRSTCTL,
                             sizeof (Val),
                             NicDevice,
                             &Val);

  if (EFI_ERROR(Status)) goto err;

  gBS->Stall (10000);

  Val =  PHYPWRRSTCTL_IPRL;
  Status = Ax88179MacWrite (PHYPWRRSTCTL,
                             sizeof (Val),
                             NicDevice,
                             &Val);

  if (EFI_ERROR(Status)){
    goto err;
  }

  gBS->Stall (200000);

  Val = CLKSELECT_BCS | CLKSELECT_ACS;
  Status = Ax88179MacWrite (CLKSELECT,
                             1,
                             NicDevice,
                             &Val);

  if (EFI_ERROR(Status)) goto err;

  gBS->Stall (100000);

  Val = 0x52;
  Status = Ax88179MacWrite (PAUSE_WATERLVL_HIGH,
                             1,
                             NicDevice,
                             &Val);

  if (EFI_ERROR(Status)) goto err;

  Val = 0x34;
  Status = Ax88179MacWrite (PAUSE_WATERLVL_LOW,
                             1,
                             NicDevice,
                             &Val);

  if (EFI_ERROR(Status)) goto err;

  Val = RXBINQCTRL_TIMEN | RXBINQCTRL_IFGEN | RXBINQCTRL_SIZEN;

  Status =  Ax88179MacWrite (RXBINQCTRL,
                              0x01,
                              NicDevice,
                              &Val);

  if (EFI_ERROR(Status)) goto err;

  Val = 0;
  Status =  Ax88179MacWrite (RXBINQTIMERL,
                              0x01,
                              NicDevice,
                              &Val);

  if (EFI_ERROR(Status)) goto err;

  Val = 0;
  Status =  Ax88179MacWrite (RXBINQTIMERH,
                              0x01,
                              NicDevice,
                              &Val);

  if (EFI_ERROR(Status)) goto err;

  Val = 12; //AX88179_BULKIN_SIZE_INK - 1;
  Status =  Ax88179MacWrite (RXBINQSIZE,
                              0x01,
                              NicDevice,
                              &Val);

  if (EFI_ERROR(Status)) goto err;

  Val = 0x0F;
  Status =  Ax88179MacWrite (RXBINQIFG,
                              0x01,
                              NicDevice,
                              &Val);

err:
  return Status;
}

EFI_STATUS
Ax88179RxControl (
  IN NIC_DEVICE *NicDevice,
  IN UINT32     RxFilter
  )
{
  UINT16     MediumStatus;
  UINT16     RxControl = 0;
  EFI_STATUS Status = EFI_SUCCESS;

  //
  // Enable the receiver if something is to be received
  //
  if (RxFilter != 0) {
    //
    //  Enable the receiver
    //
    Status  = Ax88179MacRead (MEDIUMSTSMOD,
                              sizeof (MediumStatus),
                              NicDevice,
                              &MediumStatus);

    if (!EFI_ERROR (Status) && NicDevice->CurMediumStatus != MediumStatus) {
        MediumStatus = MEDIUMSTSMOD_RE | MEDIUMSTSMOD_ONE;
        if (NicDevice->FullDuplex) {
          MediumStatus |= MEDIUMSTSMOD_TFC | MEDIUMSTSMOD_RFC | MEDIUMSTSMOD_FD;
        } else {
          MediumStatus &= ~(MEDIUMSTSMOD_TFC | MEDIUMSTSMOD_RFC | MEDIUMSTSMOD_FD);
        }
        if (NicDevice->LinkSpeed1000Mbps) {
          MediumStatus |= MEDIUMSTSMOD_GM;
          MediumStatus |= MEDIUMSTSMOD_ENCK;
          MediumStatus &= ~MEDIUMSTSMOD_PS;
        } else {
          MediumStatus &= ~MEDIUMSTSMOD_GM;
          MediumStatus &= ~MEDIUMSTSMOD_ENCK;
          if (NicDevice->LinkSpeed100Mbps) {
            MediumStatus |= MEDIUMSTSMOD_PS;
          }  else {
            MediumStatus &= ~MEDIUMSTSMOD_PS;
          }
        }
        Status  = Ax88179MacWrite (MEDIUMSTSMOD,
                                   sizeof (MediumStatus),
                                   NicDevice,
                                   &MediumStatus);
        if (!EFI_ERROR (Status)) {
          NicDevice->CurMediumStatus = MediumStatus;
        }
    }
  }

  RxControl = RXCTL_SO | RXCTL_IPE;

  //
  //  Enable multicast if requested
  //
  if ((RxFilter & EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST) != 0) {
    RxControl |= RXCTL_AM;
    //
    //  Update the multicast hash table
    //
    Status  = Ax88179MacWrite (MULCATFLTARRY,
                               8,
                               NicDevice,
                               &NicDevice->MulticastHash);
  }

  //
  //  Enable all multicast if requested
  //
  if ((RxFilter & EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST) != 0) {
    RxControl |= RXCTL_AMALL;
  }
  //
  //  Enable broadcast if requested
  //
  if ((RxFilter & EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST) != 0) {
    RxControl |= RXCTL_AB;
  }

  //
  //  Enable promiscuous mode if requested
  //
  if ((RxFilter & EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS) != 0) {
    RxControl |= RXCTL_PRO;
  }

  //
  //  Update the receiver control
  //
  if (NicDevice->CurRxControl != RxControl) {
    Status  = Ax88179MacWrite (RXCTL,
                               0x02,
                               NicDevice,
                               &RxControl);
    if (!EFI_ERROR (Status))
      NicDevice->CurRxControl = RxControl;
  }

  //
  // Return the operation status
  //
  return Status;
}

EFI_STATUS
Ax88179ReloadSrom  (
  IN NIC_DEVICE *NicDevice
 )
{
  EFI_STATUS Status;

  Status = EFI_UNSUPPORTED;
  return Status;

}

/**
  Read an SROM location

  This routine calls ::Ax88179UsbCommand to read data from the
  SROM.

  @param [in] NicDevice       Pointer to the NIC_DEVICE structure
  @param [in] Address          SROM address
  @param [out] Data           Buffer to receive the data

  @retval EFI_SUCCESS          The read was successful
  @retval other                The read failed

**/
EFI_STATUS
Ax88179SromRead (
  IN  NIC_DEVICE *NicDevice,
  IN  UINT32     Address,
  OUT UINT16     *Data
  )
{
  EFI_STATUS Status;

  Status = EFI_UNSUPPORTED;
  return Status;
}

EFI_STATUS
Ax88179EnableSromWrite  (
  IN NIC_DEVICE *NicDevice
  )
{
  EFI_STATUS Status;

  Status = EFI_UNSUPPORTED;
  return Status;
}


EFI_STATUS
Ax88179DisableSromWrite  (
  IN NIC_DEVICE *NicDevice
  )
{
  EFI_STATUS Status;

  Status = EFI_UNSUPPORTED;
  return Status;
}

/**
  Write an SROM location

  This routine calls ::Ax88179UsbCommand to write data from the
  SROM.

  @param [in] NicDevice       Pointer to the NIC_DEVICE structure
  @param [in] Address          SROM address
  @param [out] Data           Buffer of data to write

  @retval EFI_SUCCESS          The write was successful
  @retval other                The write failed

**/
EFI_STATUS
Ax88179SromWrite (
  IN NIC_DEVICE *NicDevice,
  IN UINT32     Address,
  IN UINT16     *Data
  )
{
  EFI_STATUS Status;

  Status = EFI_UNSUPPORTED;
  return Status;
}

/**
  Send a command to the USB device.

  @param [in] NicDevice       Pointer to the NIC_DEVICE structure
  @param [in] Request         Pointer to the request structure
  @param [in, out] Buffer     Data buffer address

  @retval EFI_SUCCESS          The USB transfer was successful
  @retval other                The USB transfer failed

**/
EFI_STATUS
Ax88179UsbCommand (
  IN     NIC_DEVICE         *NicDevice,
  IN     USB_DEVICE_REQUEST *Request,
  IN OUT VOID               *Buffer
  )
{
  EFI_USB_DATA_DIRECTION Direction;
  EFI_USB_IO_PROTOCOL    *UsbIo;
  EFI_STATUS             Status = EFI_TIMEOUT;
  UINT32                 CmdStatus = EFI_USB_NOERROR;
  int                    i;
  //
  // Determine the transfer direction
  //
  Direction = EfiUsbNoData;
  if (Request->Length != 0) {
    Direction = ((Request->RequestType & USB_ENDPOINT_DIR_IN) != 0)
                        ? EfiUsbDataIn : EfiUsbDataOut;
  }

  //
  // Issue the command
  //
  UsbIo = NicDevice->UsbIo;

  for (i = 0 ; i < 3 && EFI_TIMEOUT == Status; i++) {
    Status = UsbIo->UsbControlTransfer (UsbIo,
                                        Request,
                                        Direction,
                                        USB_BUS_TIMEOUT,
                                        Buffer,
                                        Request->Length,
                                        &CmdStatus);
  }
  //
  // Determine the operation status
  //
  if (EFI_ERROR(Status) || EFI_ERROR(CmdStatus))
    Status = EFI_DEVICE_ERROR;
  //
  // Return the operation status
  //
  return Status;
}

EFI_STATUS
Ax88179MacRead (
  IN       UINT8      Offset,
  IN       UINT8      Length,
  IN       NIC_DEVICE *NicDevice,
  IN  OUT  VOID       *Data
  )
{

  EFI_STATUS         Status;
  USB_DEVICE_REQUEST SetupMsg;

  SetupMsg.RequestType = USB_REQ_TYPE_VENDOR
                       | USB_TARGET_DEVICE
                       | USB_ENDPOINT_DIR_IN;

  SetupMsg.Request = ACCESS_MAC;
  SetupMsg.Value  = Offset;
  SetupMsg.Index  = Length;
  SetupMsg.Length = SetupMsg.Index;

  Status = Ax88179UsbCommand (NicDevice,
                               &SetupMsg,
                               Data);

  return Status;

}

EFI_STATUS
Ax88179MacWrite (
  IN       UINT8      Offset,
  IN       UINT8      Length,
  IN       NIC_DEVICE *NicDevice,
  IN  OUT  VOID       *Data
 )
{

  EFI_STATUS         Status;
  USB_DEVICE_REQUEST SetupMsg;

  SetupMsg.RequestType = USB_REQ_TYPE_VENDOR
                       | USB_TARGET_DEVICE;

  SetupMsg.Request = ACCESS_MAC;
  SetupMsg.Value  = Offset;
  SetupMsg.Index  = Length;
  SetupMsg.Length = SetupMsg.Index;

  Status = Ax88179UsbCommand (NicDevice,
                               &SetupMsg,
                               Data);

  return Status;

}

EFI_STATUS
Ax88179SetIInInterval (
  IN  NIC_DEVICE *NicDevice,
  IN  UINT8      Interval
 )
{

  EFI_STATUS         Status;
  USB_DEVICE_REQUEST SetupMsg;

  SetupMsg.RequestType = USB_REQ_TYPE_VENDOR
                       | USB_TARGET_DEVICE;

  SetupMsg.Request = 0x91;
  SetupMsg.Value  = Interval;
  SetupMsg.Index  = 0;
  SetupMsg.Length = 0;

  Status = Ax88179UsbCommand (NicDevice,
                               &SetupMsg,
                               NULL);

  return Status;

}

EFI_STATUS
Ax88179SetMedium (
  IN NIC_DEVICE *NicDevice
 )
{
  UINT16     MediumStatus;
  EFI_STATUS Status;

  MediumStatus = MEDIUMSTSMOD_RE | MEDIUMSTSMOD_ONE;
  if (NicDevice->FullDuplex) {
    MediumStatus |= MEDIUMSTSMOD_TFC | MEDIUMSTSMOD_RFC | MEDIUMSTSMOD_FD;
  } else {
    MediumStatus &= ~(MEDIUMSTSMOD_TFC | MEDIUMSTSMOD_RFC | MEDIUMSTSMOD_FD);
  }
  if (NicDevice->LinkSpeed1000Mbps) {
    MediumStatus |= MEDIUMSTSMOD_GM;
    MediumStatus &= ~MEDIUMSTSMOD_PS;
  } else {
    MediumStatus &= ~MEDIUMSTSMOD_GM;
    if (NicDevice->LinkSpeed100Mbps) {
      MediumStatus |= MEDIUMSTSMOD_PS;
    } else {
      MediumStatus &= ~MEDIUMSTSMOD_PS;
    }
  }
  Status  = Ax88179MacWrite (MEDIUMSTSMOD,
                             sizeof (MediumStatus),
                             NicDevice,
                             &MediumStatus);
  if (!EFI_ERROR (Status)) {
    NicDevice->CurMediumStatus = MediumStatus;
  }

  return Status;
}


BOOLEAN
Ax88179GetLinkStatus (
  IN NIC_DEVICE *NicDevice
)
{
  UINT32              CmdStatus;
  EFI_USB_IO_PROTOCOL *UsbIo;
  UINT64              IntData = 0;
  UINTN               IntDataLeng = 8;
  EFI_STATUS          Status;

  //
  // Issue the command
  //
  UsbIo = NicDevice->UsbIo;
  Status = UsbIo->UsbSyncInterruptTransfer(UsbIo,
                                        USB_ENDPOINT_DIR_IN | INTERRUPT_ENDPOINT,
                                        &IntData,
                                        &IntDataLeng,
                                        USB_BUS_TIMEOUT,
                                        &CmdStatus);

  if (EFI_ERROR(Status) || EFI_ERROR(CmdStatus) || 0 == IntDataLeng)
    return FALSE;

  return (IntData & 0x10000)? TRUE : FALSE;

}

EFI_STATUS
Ax88179BulkIn(
  IN NIC_DEVICE *NicDevice
)
{
  int i;
  UINT16  Val;
  UINTN               LengthInBytes = 0;
  UINTN               TmpLen = AX88179_MAX_BULKIN_SIZE;
  UINTN               CURBufSize = AX88179_MAX_BULKIN_SIZE;
  UINTN               PREBufSize = 0;
  EFI_STATUS          Status = EFI_NOT_READY;
  EFI_USB_IO_PROTOCOL *UsbIo;
  UINT32 TransferStatus;

  NicDevice->SkipRXCnt = 0;

  UsbIo = NicDevice->UsbIo;
  for (i = 0 ; i < (AX88179_MAX_BULKIN_SIZE / 512) && UsbIo != NULL; i++) {
    VOID* TmpAddr = 0;

    if (NicDevice->SetZeroLen) {
      Val =  PHYPWRRSTCTL_IPRL | PHYPWRRSTCTL_BZ;
      Status = Ax88179MacWrite (PHYPWRRSTCTL,
                                 sizeof (Val),
                                 NicDevice,
                                 &Val);
      if (EFI_ERROR(Status)) {
        LengthInBytes = 0;
        Status = EFI_NOT_READY;
        goto no_pkt;
      }
      NicDevice->SetZeroLen = FALSE;
    }
    TmpAddr = (VOID*) &NicDevice->BulkInbuf[LengthInBytes];

    Status =  EFI_NOT_READY;
    Status = UsbIo->UsbBulkTransfer (UsbIo,
                          USB_ENDPOINT_DIR_IN | BULK_IN_ENDPOINT,
                          TmpAddr,
                          &TmpLen,
                          BULKIN_TIMEOUT,
                          &TransferStatus);

    if ((!EFI_ERROR (Status)) && (!EFI_ERROR (TransferStatus)) && TmpLen != 0) {
      LengthInBytes += TmpLen;
      if ((TmpLen % NicDevice->UsbMaxPktSize) != 0) {
        goto done;
      }
      CURBufSize = CURBufSize - TmpLen;
      TmpLen = CURBufSize;
      NicDevice->SetZeroLen = TRUE;
    } else if ((!EFI_ERROR (Status)) &&
               (!EFI_ERROR (TransferStatus)) &&
               (TmpLen == 0) &&
               LengthInBytes) {
      if (PREBufSize == CURBufSize) {
        goto done;
      }
      TmpLen = CURBufSize;
      PREBufSize = CURBufSize;
      NicDevice->SetZeroLen = TRUE;
    } else if ((!EFI_ERROR (Status)) &&
               (!EFI_ERROR (TransferStatus)) &&
               (TmpLen == 0)) {
      NicDevice->SetZeroLen = TRUE;
      LengthInBytes = 0;
      Status = EFI_NOT_READY;
      goto done;
    } else if (EFI_TIMEOUT == Status && EFI_USB_ERR_TIMEOUT == TransferStatus) {
      NicDevice->SetZeroLen = TRUE;
      LengthInBytes = 0;
      Status = EFI_NOT_READY;
      goto done;
    } else {
      NicDevice->SetZeroLen = TRUE;
      LengthInBytes = 0;
      Status = EFI_NOT_READY;
      goto done;
    }
  }

done:
  if (LengthInBytes != 0) {
    UINT16 tmplen = 0;
    UINT16 TmpPktCnt = 0;

    TmpPktCnt = *((UINT16 *) (NicDevice->BulkInbuf + LengthInBytes - 4));
    tmplen =  *((UINT16*) (NicDevice->BulkInbuf + LengthInBytes - 2));

    if (((UINTN)(((TmpPktCnt * 4 + 4 + 7) & 0xfff8) + tmplen)) == LengthInBytes) {
      NicDevice->PktCnt = TmpPktCnt;
      NicDevice->CurPktHdrOff = NicDevice->BulkInbuf + tmplen;
      NicDevice->CurPktOff = NicDevice->BulkInbuf;
      *((UINT16 *) (NicDevice->BulkInbuf + LengthInBytes - 4)) = 0;
      *((UINT16*) (NicDevice->BulkInbuf + LengthInBytes - 2)) = 0;
      Status = EFI_SUCCESS;
    } else {
      Status = EFI_NOT_READY;
    }
  } else {
    Status = EFI_NOT_READY;
  }
no_pkt:
   return Status;
}
