Newer
Older
AMI-Aptio-BIOS-Reversed / MnpDxe / MnpIo.c
@Ajax Dong Ajax Dong 2 days ago 31 KB Init
/**
 * @file MnpIo.c
 *
 * @brief MNP I/O Operations
 *
 * Implements the low-level I/O operations:
 *   - MnpIsValidTxToken:         Validate a transmit token
 *   - MnpSerializeFragment:      Serialize fragment table into contiguous data
 *   - MnpSyncSendPacket:         Synchronous packet transmission via SNP
 *   - MnpDeliverPacket:          Deliver a received packet to an instance
 *   - MnpReceivePacketDispatch:  Match and dispatch received packets to instances
 *   - MnpQueueRcvdPacket:        Queue a received packet to an instance queue
 *   - MnpReceivePacket:          Receive raw data from SNP
 *   - MnpCheckPacketTimeout:     Timer-handler for TX timeout
 *   - MnpMediaDetect:            Media detection timer handler
 *   - MnpPollTimer:              Poll timer handler
 *   - MnpFindRxToken / MnpCancelRxToken: NET_MAP item callbacks
 *
 * Source: AmiNetworkPkg/UefiNetworkStack/Common/MnpDxe/MnpIo.c
 */

#include "MnpDxe.h"

extern EFI_BOOT_SERVICES  *gBS;
extern VOID               *gDpcProtocol;

/* ====================================================================== */
/*  MnpIsValidTxToken                                                    */
/* ====================================================================== */

/**
 * Validate a transmit token before queuing it.
 *
 * Checks that:
 *   - Token and TxData are non-NULL
 *   - HeaderLength is consistent with DestinationAddress
 *   - Fragment table entries are valid
 *   - Total data length matches sum of fragments
 *   - DataLength does not exceed MTU
 *
 * @param[in] Instance  The MNP instance.
 * @param[in] Token     The transmit token to validate.
 *
 * @return TRUE if valid, FALSE otherwise.
 */
BOOLEAN
MnpIsValidTxToken (
  IN MNP_INSTANCE_DATA                  *Instance,
  IN EFI_MANAGED_NETWORK_COMPLETION_TOKEN  *Token
  )
{
  MNP_SERVICE_DATA                       *Service;
  EFI_MANAGED_NETWORK_FRAGMENT_DATA      *FragmentTable;
  EFI_MANAGED_NETWORK_OVERRIDE_DATA      *OverrideData;
  UINT32                                  FragmentCount;
  UINT32                                  TotalLength;
  UINT32                                  Index;

  Service = Instance->MnpServiceData;
  ASSERT (Service != NULL);
  ASSERT (Service->Signature == MNP_SERVICE_DATA_SIGNATURE);

  //
  // Validate token structure.
  //
  if (Token->Event == NULL || Token->Packet.TxData == NULL) {
    DEBUG ((DEBUG_ERROR, "MnpIsValidTxToken: Invalid Token.\n"));
    return FALSE;
  }

  OverrideData  = Token->Packet.TxData->OverrideData;
  FragmentTable = Token->Packet.TxData->FragmentTable;
  FragmentCount = Token->Packet.TxData->FragmentCount;

  if (OverrideData != NULL &&
      OverrideData->HeaderLength != 0 &&
      OverrideData->DestinationAddress != NULL) {
    DEBUG ((DEBUG_ERROR,
            "MnpIsValidTxToken: DestinationAddress isn't NULL, "
            "HeaderLength must be 0.\n"));
    return FALSE;
  }

  TotalLength = 0;
  for (Index = 0; Index < FragmentCount; Index++) {
    if (FragmentTable[Index].FragmentLength == 0 ||
        FragmentTable[Index].FragmentBuffer == NULL) {
      DEBUG ((DEBUG_ERROR,
              "MnpIsValidTxToken: Invalid FragmentLength or "
              "FragmentBuffer.\n"));
      return FALSE;
    }
    TotalLength += FragmentTable[Index].FragmentLength;
  }

  if (OverrideData == NULL ||
      OverrideData->FragmentCount == 0 ||
      FragmentTable[0].FragmentLength < OverrideData->FragmentCount) {
    //
    // Validate total length.
    //
    UINT32 HeaderLen;

    HeaderLen = (OverrideData != NULL) ? OverrideData->HeaderLength : 0;

    if (TotalLength != Token->Packet.TxData->DataLength + HeaderLen) {
      DEBUG ((DEBUG_ERROR,
              "MnpIsValidTxData: Invalid Datalength compared with the "
              "sum of fragment length.\n"));
      return FALSE;
    }

    if (Token->Packet.TxData->DataLength > Service->MtuSize) {
      DEBUG ((DEBUG_ERROR,
              "MnpIsValidTxData: TxData->DataLength exceeds Mtu.\n"));
      return FALSE;
    }
  }

  return TRUE;
}

/* ====================================================================== */
/*  MnpSerializeFragment                                                 */
/* ====================================================================== */

/**
 * Serialize the fragment table into a single contiguous data pointer for
 * transmission.
 *
 * Uses the fragment table entries from the token's TxData and produces
 * a flat buffer pointer with count of fragments (1).
 *
 * @param[in]  Service        Service data (for VLAN tag).
 * @param[in]  TxData         Transmit data from the token.
 * @param[out] Buf            Output buffer pointer.
 * @param[out] FragmentCount  Output fragment count.
 *
 * @retval EFI_SUCCESS  Serialized.
 */
EFI_STATUS
MnpSerializeFragment (
  IN  MNP_SERVICE_DATA  *Service,
  IN  EFI_MANAGED_NETWORK_TX_DATA  *TxData,
  OUT VOID             **Buf,
  OUT UINT32           *FragmentCount
  )
{
  UINT32  TotalSize;

  *FragmentCount = TxData->FragmentCount;
  TotalSize = 0;
  for (UINT32 Index = 0; Index < *FragmentCount; Index++) {
    TotalSize += TxData->FragmentTable[Index].FragmentLength;
  }

  *Buf = &TxData->FragmentTable[0].FragmentBuffer;  /* first fragment */

  return EFI_SUCCESS;
}

/* ====================================================================== */
/*  MnpSyncSendPacket                                                    */
/* ====================================================================== */

/**
 * Transmit a packet synchronously via the underlying SNP.
 *
 * Waits for the SNP transmit to complete using the TX timeout event. If
 * VLAN is active, inserts the VLAN tag before transmission. On timeout,
 * retries. Signals the token's completion event when done.
 *
 * @param[in] Service        Service data.
 * @param[in] FragmentTable  Fragment table.
 * @param[in] FragmentCount  Number of fragments.
 * @param[in] Token          Completion token to signal.
 *
 * @retval EFI_SUCCESS           Transmitted.
 * @retval EFI_NOT_READY         Cable disconnected.
 * @retval EFI_DEVICE_ERROR      SNP transmit failed.
 */
EFI_STATUS
MnpSyncSendPacket (
  IN  MNP_SERVICE_DATA                     *Service,
  IN  EFI_MANAGED_NETWORK_FRAGMENT_DATA    *FragmentTable,
  IN  UINT32                                FragmentCount,
  IN  EFI_MANAGED_NETWORK_COMPLETION_TOKEN *Token
  )
{
  MNP_DEVICE_DATA     *Device;
  EFI_SIMPLE_NETWORK  *Snp;
  EFI_SIMPLE_NETWORK_MODE *SnpMode;
  EFI_STATUS           Status;
  UINT16               ProtocolType;
  UINT32               HeaderSize;

  Device  = Service->MnpDeviceData;
  Snp     = Device->Snp;
  SnpMode = Snp->Mode;

  //
  // Check media presence.
  //
  if (SnpMode->MediaPresentSupported &&
      !SnpMode->MediaPresent) {
    DEBUG ((DEBUG_ERROR,
            "MnpSyncSendPacket: No network cable detected.\n"));
    Token->Status = EFI_NOT_READY;
    gBS->SignalEvent (Token->Event);
    ((DPC_PROTOCOL *)gDpcProtocol)->QueueDpc (gDpcProtocol);
    return EFI_SUCCESS;  /* Token signalled, return 0 from this function */
  }

  //
  // Set TX timeout.
  //
  gBS->SetTimer (Device->TxTimeoutEvent, TimerRelative, 500 * 10000);

  //
  // Determine header size.
  //
  HeaderSize = SnpMode->MediaHeaderSize;
  if (!Service->VlanId) {
    HeaderSize -= sizeof (EFI_MAC_ADDRESS);
  }

  //
  // Get protocol type from override data or fragment.
  //
  {
    EFI_MANAGED_NETWORK_OVERRIDE_DATA  *Override;
    UINT16  *ProtocolTypeField;

    Override = Token->Packet.TxData->OverrideData;
    if (Override != NULL) {
      ProtocolType = Override->ProtocolType;
    } else {
      ProtocolType = Token->Packet.TxData->ProtocolType;
    }
  }

  if (Service->VlanId != 0) {
    //
    // Insert VLAN tag into fragment data.
    //
    MnpInsertVlanTag (Service, FragmentTable, &ProtocolType, FragmentCount);
  }

  //
  // Transmit via SNP.
  //
  Status = Snp->Transmit (
                  Snp,
                  HeaderSize,
                  FragmentCount,
                  FragmentTable->FragmentBuffer,
                  SnpMode->CurrentAddress,  /* SrcMac */
                  &ProtocolType,            /* Protocol */
                  &ProtocolType             /* SrcProtocol? */
                  );
  if (Status == EFI_SUCCESS) {
    goto Done;
  }

  if (Status != EFI_NOT_READY) {
    //
    // Real error.
    //
    goto Done;
  }

  //
  // Retry loop on NOT_READY.
  //
  for (;;) {
    gBS->Stall (1000);

    gBS->CheckEvent (Device->TxTimeoutEvent);

    Status = Snp->Transmit (
                    Snp,
                    HeaderSize,
                    FragmentCount,
                    FragmentTable->FragmentBuffer,
                    SnpMode->CurrentAddress,
                    &ProtocolType,
                    &ProtocolType
                    );
    if (Status != EFI_NOT_READY) {
      break;
    }
  }

Done:
  gBS->SetTimer (Device->TxTimeoutEvent, TimerCancel, 0);

  Token->Status = Status;
  gBS->SignalEvent (Token->Event);
  ((DPC_PROTOCOL *)gDpcProtocol)->QueueDpc (gDpcProtocol);

  return EFI_SUCCESS;
}

/* ====================================================================== */
/*  MnpInsertVlanTag                                                     */
/* ====================================================================== */

/**
 * Insert a VLAN tag (TPID + TCI) into the packet header before
 * transmission.
 *
 * @param[in]     Service        Service data with VLAN info.
 * @param[in,out] FragmentTable  Fragment table to modify.
 * @param[in]     ProtocolType   Original EtherType.
 * @param[in]     FragmentCount  Number of fragments.
 */
VOID
MnpInsertVlanTag (
  IN     MNP_SERVICE_DATA                    *Service,
  IN OUT EFI_MANAGED_NETWORK_FRAGMENT_DATA   *FragmentTable,
  IN     UINT16                              *ProtocolType,
  IN     UINT32                               FragmentCount
  )
{
  VLAN_TAG  VlanTag;
  UINT8     *Header;
  UINT32    OriginalHeaderSize;

  //
  // Build the VLAN TPID/TCI.
  //
  VlanTag.Tpid = VLAN_TPID;
  VlanTag.Tci  = (UINT16)((Service->Priority << 13) | Service->VlanId);

  //
  // The first fragment contains the MAC header.
  //
  Header = (UINT8 *)FragmentTable[0].FragmentBuffer;

  //
  // We need to shift the EtherType and insert 4 bytes of VLAN tag.
  // Since we write to the first fragment, we rely on the fact that
  // MnpSerializeFragment gave us a writable buffer. For simplicity,
  // prepend the VLAN tag before the original EtherType field.
  //
  // Original layout: [DST (6) | SRC (6) | EtherType (2) | Payload]
  // New layout:      [DST (6) | SRC (6) | TPID (2) | TCI (2) | EtherType (2) | Payload]
  //
  // Move the EtherType and payload by 4 bytes.
  //
  OriginalHeaderSize = 12;  /* DST + SRC */
  CopyMem (Header + OriginalHeaderSize + sizeof (VLAN_TAG),
           Header + OriginalHeaderSize,
           2 + FragmentTable[0].FragmentLength - OriginalHeaderSize);

  //
  // Insert the VLAN tag.
  //
  CopyMem (Header + OriginalHeaderSize,
           &VlanTag,
           sizeof (VLAN_TAG));

  //
  // Update the protocol type (original EtherType).
  //
  *ProtocolType = VLAN_TPID_VALUE;
}

/* ====================================================================== */
/*  MnpDeliverPacket                                                     */
/* ====================================================================== */

/**
 * Deliver a packet from the instance's receive queue to the instance's
 * tokens. Moves the first packet from the queue, allocates a new NET_BUF
 * for the packet data, sets up the header pointers, and signals the
 * token's event.
 *
 * @param[in] Instance  The MNP instance.
 *
 * @retval EFI_SUCCESS           Packet delivered.
 * @retval EFI_OUT_OF_RESOURCES  Failed to allocate copy buffer.
 */
EFI_STATUS
MnpDeliverPacket (
  IN MNP_INSTANCE_DATA  *Instance
  )
{
  MNP_DEVICE_DATA  *Device;
  MNP_RXDATA_WRAP  *RxDataWrap;
  NET_BUF          *Packet;
  VOID             *BufPtr;
  UINTN            HeaderSize;

  Device = Instance->MnpServiceData->MnpDeviceData;
  ASSERT (Device->Signature == MNP_DEVICE_DATA_SIGNATURE);

  //
  // Check if there are pending RX tokens and data.
  //
  if (Instance->RcvMap.ItemCount == 0 ||
      IsListEmpty (&Instance->RcvdPacketQueue)) {
    return EFI_SUCCESS;
  }

  ASSERT (Instance->RcvdPacketQueueSize != 0);

  //
  // Get the first received packet wrap.
  //
  RxDataWrap = (MNP_RXDATA_WRAP *)GetFirstNode (&Instance->RcvdPacketQueue);

  //
  // If the packet has multiple references (RefCount > 2), allocate a copy.
  //
  Packet = RxDataWrap->Packet;
  if (Packet != NULL && Packet->RefCount > 2) {
    NET_BUF  *NewPacket;

    NewPacket = MnpAllocNbuf (Device);
    if (NewPacket == NULL) {
      DEBUG ((DEBUG_ERROR,
              "MnpDeliverPacket: Failed to allocate a free Nbuf.\n"));
      return EFI_OUT_OF_RESOURCES;
    }

    //
    // Copy packet data.
    //
    CopyMem ((UINT8 *)NewPacket + NET_BUF_HEADER_OFFSET,
             (UINT8 *)Packet + NET_BUF_HEADER_OFFSET,
             sizeof (NET_BUF_BLOCK));

    NetbufCopyData (NewPacket);
    NetbufTrim (NewPacket, Packet->TotalSize, NET_BUF_TRIM_HEAD);

    //
    // Restore header pointers.
    //
    MnpFreeNbuf (Device, RxDataWrap->Packet);
    RxDataWrap->Packet = NewPacket;
    Packet = NewPacket;
  }

  //
  // Compute network buffer pointers.
  //
  BufPtr = NetbufGetProtocolHeader (Packet, 0, 0);
  RxDataWrap->ProtocolHeader = BufPtr;
  RxDataWrap->NetHeader      = BufPtr;
  RxDataWrap->MediaHeader    = BufPtr;

  HeaderSize = Device->Snp->Mode->MediaHeaderSize;
  RxDataWrap->MediaHeaderLen   = (UINT32)HeaderSize;

  if (Device->Snp->Mode->MediaHeaderSize > 0) {
    RxDataWrap->SnapHeader = BufPtr + Device->Snp->Mode->MediaHeaderSize -
                             sizeof (ETHERNET_SNAP_HEADER);
  }

  RxDataWrap->NetHeaderLen = Packet->TotalSize - Device->HeaderLength;

  //
  // Move the RX wrap from the received queue to the used list.
  //
  RxDataWrap->RxToken = Instance->RcvMap.Used.Flink;  /* FIXME: proper mapping */

  RemoveEntryList (&Instance->RcvdPacketQueue.Flink);
  Instance->RcvdPacketQueueSize--;

  //
  // Move from Free to Used in the NET_MAP.
  //
  {
    NET_MAP_ITEM  *MapItem;

    MapItem = (NET_MAP_ITEM *)NetMapRemoveHead (&Instance->RcvMap);
    InsertTailList (&Instance->RcvMap.Used, &MapItem->Link);
    Instance->RcvMap.FreeCount--;

    MapItem->UserData = RxDataWrap;

    //
    // Signal the token's event.
    //
    gBS->SignalEvent (MapItem->Token->Event);
  }

  return EFI_SUCCESS;
}

/* ====================================================================== */
/*  MnpQueueRcvdPacket                                                   */
/* ====================================================================== */

/**
 * Queue a received packet into the instance's receive queue, up to the
 * max queue size (256).
 *
 * @param[in] Instance     The MNP instance.
 * @param[in] RxDataWrap   Received data wrap.
 *
 * @retval EFI_SUCCESS  Queued.
 */
EFI_STATUS
MnpQueueRcvdPacket (
  IN MNP_INSTANCE_DATA  *Instance,
  IN MNP_RXDATA_WRAP    *RxDataWrap
  )
{
  if (Instance->RcvdPacketQueueSize == MNP_MAX_RCV_QUEUE_SIZE) {
    DEBUG ((DEBUG_ERROR,
            "MnpQueueRcvdPacket: Drop one packet bcz queue size limit "
            "reached.\n"));
    //
    // Drop the oldest packet.
    //
    LIST_ENTRY      *Entry;
    MNP_RXDATA_WRAP *HeadDataWrap;

    Entry       = GetFirstNode (&Instance->RcvdPacketQueue);
    HeadDataWrap = MNP_RXDATA_WRAP_FROM_LINK (Entry);
    RemoveEntryList (Entry);
    Instance->RcvdPacketQueueSize--;
    NetbufFree (HeadDataWrap->Packet);
    FreePool (HeadDataWrap);
  }

  RxDataWrap->Flags = Instance->ConfigData.ReceivedFlags;
  InsertTailList (&Instance->RcvdPacketQueue, &RxDataWrap->Link);
  Instance->RcvdPacketQueueSize++;

  return EFI_SUCCESS;
}

/* ====================================================================== */
/*  MnpReceivePacketDispatch                                             */
/* ====================================================================== */

/**
 * Dispatch a received packet to all interested MNP instances.
 *
 * Examines the packet header to determine the packet type (unicast,
 * multicast, broadcast) and VLAN, then dispatches to each configured
 * instance whose filters match.
 *
 * @param[in] Service  Service data (VLAN context).
 * @param[in] Nbuf     Received NET_BUF.
 */
VOID
MnpReceivePacketDispatch (
  IN MNP_SERVICE_DATA  *Service,
  IN NET_BUF           *Nbuf
  )
{
  MNP_DEVICE_DATA              *Device;
  EFI_SIMPLE_NETWORK_MODE      *SnpMode;
  UINT8                        *BufPtr;
  UINT8                         PacketType;
  BOOLEAN                       IsBroadcast;
  BOOLEAN                       IsMulticast;
  BOOLEAN                       IsUnicast;
  BOOLEAN                       IsPromiscuous;
  EFI_MAC_ADDRESS              *DstMac;
  UINT16                        EtherType;
  MNP_RXDATA_WRAP              *RxDataWrap;
  LIST_ENTRY                   *Entry;

  Device = Service->MnpDeviceData;
  BufPtr = NetbufGetProtocolHeader (Nbuf, 0, 0);

  SnpMode   = Device->Snp->Mode;
  DstMac    = (EFI_MAC_ADDRESS *)BufPtr;
  EtherType = NTOHS (*(UINT16 *)(BufPtr + 2 * SnpMode->HwAddressSize));

  //
  // Determine packet type.
  //
  IsUnicast   = (DstMac->Addr[0] & 1) == 0;
  IsMulticast = (DstMac->Addr[0] & 1) == 1;
  IsBroadcast = FALSE;

  //
  // Check for broadcast.
  //
  if (CompareMem (DstMac, &SnpMode->BroadcastAddress,
                  SnpMode->HwAddressSize) == 0) {
    IsBroadcast = TRUE;
  }

  //
  // Build the RxData structure for delivery.
  //
  RxDataWrap = AllocateZeroPool (sizeof (MNP_RXDATA_WRAP));
  if (RxDataWrap == NULL) {
    return;
  }

  RxDataWrap->Packet    = Nbuf;
  RxDataWrap->DataLength = Nbuf->TotalSize;
  RxDataWrap->HeaderLength = Device->HeaderLength;
  RxDataWrap->MediaHeaderLen   = SnpMode->MediaHeaderSize;
  RxDataWrap->ProtocolHeader   = (UINT8 *)BufPtr +
                                 Device->HeaderLength;
  RxDataWrap->NetHeader        = BufPtr;
  RxDataWrap->MediaHeader      = BufPtr;
  RxDataWrap->ProtocolType     = EtherType;
  RxDataWrap->Flags            = 0;

  //
  // Compute header length minus snap.
  //
  RxDataWrap->SnapHeader = NULL;
  RxDataWrap->RxData     = NULL;

  if (IsBroadcast) {
    PacketType = MNP_PACKET_TYPE_BROADCAST;
  } else if (IsMulticast) {
    PacketType = MNP_PACKET_TYPE_MULTICAST;
  } else if (IsUnicast) {
    PacketType = MNP_PACKET_TYPE_UNICAST;
  } else {
    PacketType = MNP_PACKET_TYPE_PROMISCUOUS;
  }

  RxDataWrap->PacketType = PacketType;

  //
  // Iterate over instances in this service and deliver.
  //
  for (Entry = Service->InstanceList.Flink;
       Entry != &Service->InstanceList;
       Entry = Entry->Flink) {
    MNP_INSTANCE_DATA  *Instance;

    Instance = MNP_INSTANCE_DATA_FROM_LINK (Entry);

    if (Instance->IsDestroyed) {
      continue;
    }

    if (!Instance->IsConfigured) {
      continue;
    }

    //
    // Apply filters.
    //
    BOOLEAN  Accept = FALSE;

    if (Instance->EnablePromiscuousReceive) {
      Accept = TRUE;
    } else if (Instance->EnableUnicastReceive &&
               IsUnicast &&
               (CompareMem (DstMac, &Instance->ConfigData.ReceiveAddress,
                            SnpMode->HwAddressSize) == 0 ||
                Instance->ConfigData.ReceiveAddress.Addr[0] == 0)) {
      Accept = TRUE;
    } else if (Instance->EnableMulticastReceive &&
               IsMulticast &&
               (CompareMem (DstMac, &Instance->ConfigData.ReceiveAddress,
                            SnpMode->HwAddressSize) != 0)) {
      //
      // Check if this multicast is in our group list.
      //
      Accept = MnpIsGroupMember (Instance, DstMac);
    } else if (IsBroadcast && !Instance->EnablePromiscuousReceive) {
      Accept = TRUE;
    }

    //
    // Check if protocol type filter matches.
    //
    if (Accept && Instance->ConfigData.ProtocolType != 0 &&
        Instance->ConfigData.ProtocolType != EtherType) {
      Accept = FALSE;
    }

    if (!Accept) {
      continue;
    }

    //
    // Queue the packet to the instance.
    //
    MnpQueueRcvdPacket (Instance, RxDataWrap);
  }

  //
  // Free the RxDataWrap if nobody took it.
  //
  if (RxDataWrap->Packet != NULL) {
    FreePool (RxDataWrap);
  }

  return;
}

/* ====================================================================== */
/*  MnpIsGroupMember                                                     */
/* ====================================================================== */

/**
 * Check if a MAC address is a member of the instance's group list.
 *
 * @param[in] Instance    The MNP instance.
 * @param[in] DstMac      Destination MAC address.
 *
 * @return TRUE if member, FALSE otherwise.
 */
BOOLEAN
MnpIsGroupMember (
  IN MNP_INSTANCE_DATA  *Instance,
  IN EFI_MAC_ADDRESS    *DstMac
  )
{
  MNP_DEVICE_DATA  *Device;
  LIST_ENTRY       *Entry;

  Device = Instance->MnpServiceData->MnpDeviceData;

  for (Entry = Device->GroupAddressList.Flink;
       Entry != &Device->GroupAddressList;
       Entry = Entry->Flink) {
    MNP_GROUP_ADDRESS  *Group;

    Group = MNP_GROUP_ADDRESS_FROM_LINK (Entry);

    if (CompareMem (DstMac, &Group->Address,
                    Device->Snp->Mode->HwAddressSize) == 0) {
      //
      // Check if this instance is a member.
      //
      LIST_ENTRY *InstanceEntry;

      for (InstanceEntry = Group->InstanceLinkList.Flink;
           InstanceEntry != &Group->InstanceLinkList;
           InstanceEntry = InstanceEntry->Flink) {
        if (InstanceEntry == &Group->InstanceLink) {
          return TRUE;
        }
      }
    }
  }

  return FALSE;
}

/* ====================================================================== */
/*  MnpReceivePacket                                                     */
/* ====================================================================== */

/**
 * Receive a packet from the underlying SNP adapter.
 *
 * Uses the device's receive buffer to call SNP->Receive(), validates
 * the received data size, trims the NET_BUF to match the received
 * data, checks VLAN tagging (if enabled), finds the matching service
 * data, and dispatches the packet through MnpReceivePacketDispatch.
 *
 * If the received NET_BUF has been consumed (refcount drops below 2),
 * a new buffer is allocated for subsequent receives.
 *
 * @param[in] MnpDeviceData  Device data.
 *
 * @retval EFI_SUCCESS           Packet received and dispatched.
 * @retval EFI_NOT_STARTED       SNP not initialized.
 * @retval EFI_DEVICE_ERROR      Invalid packet size.
 */
EFI_STATUS
MnpReceivePacket (
  IN MNP_DEVICE_DATA  *MnpDeviceData
  )
{
  EFI_SIMPLE_NETWORK  *Snp;
  EFI_SIMPLE_NETWORK_MODE  *SnpMode;
  EFI_STATUS           Status;
  UINTN                HeaderSize;
  UINTN                BufLength;
  NET_BUF              *Packet;
  UINT8                *BufPtr;

  ASSERT (MnpDeviceData->Signature == MNP_DEVICE_DATA_SIGNATURE);

  Snp     = MnpDeviceData->Snp;
  SnpMode = Snp->Mode;

  if (SnpMode->State != EfiSimpleNetworkInitialized) {
    return EFI_NOT_STARTED;
  }

  Packet = MnpDeviceData->RcvPacket;
  if (Packet == NULL) {
    Packet = MnpAllocNbuf (MnpDeviceData);
    MnpDeviceData->RcvPacket = Packet;
    if (Packet == NULL) {
      return EFI_OUT_OF_RESOURCES;
    }
    NetbufTrim (Packet, MnpDeviceData->HeaderLength, NET_BUF_TRIM_HEAD);
  }

  BufLength = Packet->TotalSize;
  BufPtr    = NetbufGetProtocolHeader (Packet, 0, 0);
  ASSERT (BufPtr != NULL);

  //
  // Receive the packet.
  //
  HeaderSize = MnpDeviceData->HeaderLength;
  Status = Snp->Receive (
                  Snp,
                  &HeaderSize,
                  &BufLength,
                  BufPtr,
                  NULL,
                  NULL,
                  NULL
                  );

  if (EFI_ERROR (Status)) {
    if (Status != EFI_NOT_READY) {
      DEBUG ((DEBUG_ERROR,
              "MnpReceivePacket: Snp->Receive() = %r.\n", Status));
    }
    return Status;
  }

  //
  // Validate the received size.
  //
  if (HeaderSize != SnpMode->MediaHeaderSize ||
      BufLength < HeaderSize) {
    DEBUG ((DEBUG_ERROR,
            "MnpReceivePacket: Size error, HL:TL = %d:%d.\n",
            HeaderSize, BufLength));
    return EFI_DEVICE_ERROR;
  }

  //
  // Trim the buffer to match received data.
  //
  if (Packet->TotalSize != BufLength) {
    NetbufTrimTop (Packet,
                   (UINT32)(Packet->TotalSize - BufLength));
    ASSERT (Packet->TotalSize == BufLength);
  }

  //
  // Check for VLAN tagging.
  //
  {
    UINT16  VlanId = 0;
    BOOLEAN IsVlanTagged = FALSE;

    if (MnpDeviceData->NumberOfVlan > 0) {
      IsVlanTagged = MnpParseVlanTag (
                       MnpDeviceData,
                       Packet,
                       &VlanId
                       );
    }

    //
    // Find the matching service data by VLAN ID.
    //
    MNP_SERVICE_DATA *Service;

    Service = MnpFindServiceByVlan (MnpDeviceData, VlanId);

    if (Service == NULL) {
      //
      // No service for this VLAN; allocate a new NET_BUF for the next
      // receive to replace the consumed one.
      //
      MnpFreeNbuf (MnpDeviceData, Packet);
      Packet = MnpAllocNbuf (MnpDeviceData);
      MnpDeviceData->RcvPacket = Packet;
      if (Packet != NULL) {
        NetbufTrim (Packet, MnpDeviceData->HeaderLength, NET_BUF_TRIM_HEAD);
      }
      return EFI_SUCCESS;
    }

    //
    // Dispatch to the service's instances.
    //
    MnpReceivePacketDispatch (Service, Packet);

    //
    // Allocate a new receive buffer if the packet was consumed.
    //
    if (Packet->RefCount <= 2) {
      MnpFreeNbuf (MnpDeviceData, Packet);
    } else {
      MnpFreeNbuf (MnpDeviceData, Packet);
    }

    Packet = MnpAllocNbuf (MnpDeviceData);
    MnpDeviceData->RcvPacket = Packet;
    if (Packet != NULL) {
      NetbufTrim (Packet, MnpDeviceData->HeaderLength, NET_BUF_TRIM_HEAD);
    }

    if (!IsVlanTagged) {
      //
      // If no VLAN tag, trim 4 bytes (potential padding).
      //
      NetbufTrim (Packet, sizeof (UINT32), 0);
    }

    ASSERT (Packet == NULL ||
            Packet->TotalSize == MnpDeviceData->BufferLength);
  }

  return Status;
}

/* ====================================================================== */
/*  MnpCheckPacketTimeout                                                */
/* ====================================================================== */

/**
 * Timer callback for checking transmit packet timeout.
 *
 * @param[in] Event    Timer event.
 * @param[in] Context  MNP_DEVICE_DATA pointer.
 */
VOID
EFIAPI
MnpCheckPacketTimeout (
  IN EFI_EVENT   Event,
  IN VOID        *Context
  )
{
  //
  // Placeholder: check pending TX on each service's instances.
  // In the original binary, this traverses configured instances
  // and signals tokens whose transmission timeout has expired.
  //
}

/* ====================================================================== */
/*  MnpMediaDetect                                                       */
/* ====================================================================== */

/**
 * Timer callback for media detection polling.
 *
 * @param[in] Event    Timer event.
 * @param[in] Context  MNP_DEVICE_DATA pointer.
 */
VOID
EFIAPI
MnpMediaDetect (
  IN EFI_EVENT   Event,
  IN VOID        *Context
  )
{
  MNP_DEVICE_DATA  *MnpDeviceData;

  MnpDeviceData = (MNP_DEVICE_DATA *)Context;

  MnpDeviceData->Snp->GetStatus (MnpDeviceData->Snp,
                                  NULL,
                                  (VOID **)&MnpDeviceData->MediaDetectInterruptStatus);
}

/* ====================================================================== */
/*  MnpPollTimer                                                         */
/* ====================================================================== */

/**
 * Timer callback for polling.
 *
 * @param[in] Event    Timer event.
 * @param[in] Context  MNP_DEVICE_DATA pointer.
 */
VOID
EFIAPI
MnpPollTimer (
  IN EFI_EVENT   Event,
  IN VOID        *Context
  )
{
  //
  // The poll timer drives MnpPoll; actual work done through DPC.
  //
}

/* ====================================================================== */
/*  MnpFindRxToken / MnpCancelRxToken                                    */
/* ====================================================================== */

/**
 * NET_MAP callback to find a matching RX token.
 *
 * @param[in] MapItem  Map item to check.
 * @param[in] Arg      Token to find.
 *
 * @return EFI_SUCCESS if map item matches the token.
 */
EFI_STATUS
EFIAPI
MnpFindRxToken (
  IN NET_MAP_ITEM  *MapItem,
  IN VOID          *Arg
  )
{
  EFI_MANAGED_NETWORK_COMPLETION_TOKEN  *Token;

  Token = (EFI_MANAGED_NETWORK_COMPLETION_TOKEN *)Arg;

  if (MapItem->Token == Token) {
    return EFI_ALREADY_STARTED;
  }

  return EFI_NOT_FOUND;
}

/**
 * NET_MAP callback to cancel a matching RX token.
 *
 * @param[in] MapItem  Map item to cancel.
 * @param[in] Arg      Token to cancel (NULL = cancel all).
 *
 * @return EFI_SUCCESS if cancelled, EFI_NOT_FOUND if not matching.
 */
EFI_STATUS
EFIAPI
MnpCancelRxToken (
  IN NET_MAP_ITEM  *MapItem,
  IN VOID          *Arg
  )
{
  EFI_MANAGED_NETWORK_COMPLETION_TOKEN  *Token;

  Token = (EFI_MANAGED_NETWORK_COMPLETION_TOKEN *)Arg;

  if (Token == NULL || MapItem->Token == Token) {
    MapItem->Token->Status = EFI_ABORTED;
    gBS->SignalEvent (MapItem->Token->Event);

    //
    // Remove the item.
    //
    RemoveEntryList (&MapItem->Link);
    FreePool (MapItem);

    return EFI_SUCCESS;
  }

  return EFI_NOT_FOUND;
}

/* ====================================================================== */
/*  MnpParseVlanTag                                                      */
/* ====================================================================== */

/**
 * Parse a VLAN tag from the received packet.
 *
 * Checks if the EtherType field is VLAN_TPID (0x8100) and if so,
 * extracts the VLAN ID.
 *
 * @param[in]  MnpDeviceData  Device data (for header size info).
 * @param[in]  Nbuf           Received NET_BUF.
 * @param[out] VlanId         Extracted VLAN ID (0 if not tagged).
 *
 * @return TRUE if VLAN tagged, FALSE otherwise.
 */
BOOLEAN
MnpParseVlanTag (
  IN  MNP_DEVICE_DATA  *MnpDeviceData,
  IN  NET_BUF          *Nbuf,
  OUT UINT16           *VlanId
  )
{
  UINT8   *BufPtr;
  UINT16   EtherTypeField;

  *VlanId = 0;

  BufPtr = NetbufGetProtocolHeader (Nbuf, 0, 0);
  if (BufPtr == NULL) {
    return FALSE;
  }

  //
  // The EtherType field is at offset HwAddressSize * 2 (after DST+SRC).
  //
  EtherTypeField = NTOHS (*(UINT16 *)(BufPtr +
                         (MnpDeviceData->Snp->Mode->HwAddressSize * 2)));

  if (EtherTypeField == VLAN_TPID) {
    UINT16  Tci;

    Tci = NTOHS (*(UINT16 *)(BufPtr +
               (MnpDeviceData->Snp->Mode->HwAddressSize * 2 + 2)));
    *VlanId = Tci & 0xFFF;
    return TRUE;
  }

  return FALSE;
}

/* ====================================================================== */
/*  MnpFindServiceByVlan                                                 */
/* ====================================================================== */

/**
 * Find the MNP_SERVICE_DATA for a given VLAN ID.
 *
 * @param[in] MnpDeviceData  Device data.
 * @param[in] VlanId         VLAN ID to match.
 *
 * @return MNP_SERVICE_DATA pointer, or NULL if not found.
 */
MNP_SERVICE_DATA *
MnpFindServiceByVlan (
  IN MNP_DEVICE_DATA  *MnpDeviceData,
  IN UINT16            VlanId
  )
{
  LIST_ENTRY  *Entry;

  for (Entry = MnpDeviceData->ServiceList.Flink;
       Entry != &MnpDeviceData->ServiceList;
       Entry = Entry->Flink) {
    MNP_SERVICE_DATA  *Service;

    Service = MNP_SERVICE_DATA_FROM_LINK (Entry);

    if (Service->VlanId == VlanId) {
      return Service;
    }
  }

  return NULL;
}