/**
* @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;
}