/**
* @file MnpVlan.c
*
* @brief VLAN Configuration Protocol Implementation
*
* This file implements the VLAN_CONFIG_PROTOCOL interfaces:
* - MnpVlanSet: Set/Add a VLAN ID and priority pair
* - MnpVlanFind: Find VLAN entries (specific or all)
* - MnpVlanRemove: Remove a VLAN ID and priority pair
*
* It also provides internal helpers:
* - MnpCreateVlanChildHandle: Create a child handle with VLAN config protocol
* - MnpParseVlanTag: Parse a VLAN tag from a received packet
* - MnpInsertVlanTag: Insert/reorder VLAN tag in a packet header
* - MnpReadVlanConfiguration: Read the "NetworkStackVar" variable
* - MnpFindServiceByVlan: Walk the service list to find matching VLAN ID
*
* Source: AmiNetworkPkg/UefiNetworkStack/Common/MnpDxe/MnpVlan.c
*/
#include "MnpDxe.h"
//
// External globals
//
extern EFI_BOOT_SERVICES *gBS;
extern EFI_RUNTIME_SERVICES *gRT;
extern VOID *gDpcProtocol;
extern UINT64 gImageHandle;
/* ====================================================================== */
/* MnpParseVlanTag */
/* ====================================================================== */
/**
* Parse a VLAN tag from a received packet.
*
* Scans the packet data at the expected VLAN tag position (based on the
* underlying SNP's MAC header size) and checks for VLAN_TPID (0x8100).
* If found, the VLAN ID is extracted, the tag is removed from the data
* (shrinking the packet by 4 bytes), and the function returns TRUE.
*
* @param[in] MnpDeviceData Pointer to MNP_DEVICE_DATA.
* @param[in] Nbuf Packet buffer to parse (NET_BUF, at offset +0x38
* for block op protocol pointer).
* @param[out] VlanId On success, the 12-bit VLAN ID from the tag.
*
* @retval 1 VLAN tag found and parsed.
* @retval 0 No VLAN tag present (TPID did not match 0x8100).
*/
UINT8
MnpParseVlanTag (
IN MNP_DEVICE_DATA *MnpDeviceData,
IN NET_BUF *Nbuf,
OUT UINT16 *VlanId
)
{
UINT16 Count;
UINT8 *PacketData;
//
// Calculate the byte offset where the VLAN tag would be in the MAC header.
// Snp->Mode->MediaHeaderSize * 2 gives the position (offset into the frame).
//
Count = (UINT16)(2 * MnpDeviceData->SnpModeData->MediaHeaderSize);
//
// Get a pointer to the packet data at offset 0 (the raw frame buffer).
// The block-op protocol inside Nbuf at offset +0x38 can access the data.
//
PacketData = (UINT8 *)NetbufGetRawData (Nbuf);
if (PacketData == NULL) {
DEBUG ((DEBUG_ERROR, "MnpParseVlanTag: Packet data is NULL\n"));
return 0;
}
//
// Read the TPID at the Ethernet VLAN tag position (big-endian).
//
if (SwapBytes16 (*(UINT16 *)&PacketData[Count]) != VLAN_TPID) {
*VlanId = 0;
return 0;
}
//
// Extract the 12-bit VLAN ID from the TCI field (at Count+2, big-endian).
//
*VlanId = SwapBytes16 (*(UINT16 *)&PacketData[Count + 2]) & 0x0FFF;
//
// Remove the 4-byte VLAN tag from the packet: shift the data after
// the tag (the actual L3 header) backward by 4 bytes.
//
CopyMem (PacketData + 4, PacketData, Count);
//
// Trim 4 bytes from the front of the buffer.
//
NetbufTrim (Nbuf, 4, NET_BUF_TRIM_HEAD);
return 1;
}
/* ====================================================================== */
/* MnpInsertVlanTag */
/* ====================================================================== */
/**
* Insert or update a VLAN tag in the MAC header of a packet.
*
* For untagged packets (no existing VLAN), the source MAC address is
* shifted to make room for the 4-byte TPID/TCI tag. For already-tagged
* packets, only the tag is refreshed. The VLAN ID is taken from the
* MnpServiceData and the VLAN_TPID (0x8100) is written in big-endian.
*
* @param[in] MnpServiceData Pointer to MNP_SERVICE_DATA (VlanId and
* Priority fields used).
* @param[in] Packet Opaque packet context (contains MAC header
* buffer).
* @param[in,out] MacHeader Pointer to current MAC header start.
* @param[in,out] HeaderLen Total header length (in/out).
*/
VOID
MnpInsertVlanTag (
IN MNP_SERVICE_DATA *MnpServiceData,
IN VOID *Packet,
IN OUT UINT8 **MacHeader,
IN OUT UINT32 *HeaderLen
)
{
MNP_DEVICE_DATA *MnpDeviceData;
UINT8 *NewMacHeader;
UINT32 MediaHeaderLen;
MnpDeviceData = MnpServiceData->MnpDeviceData;
MediaHeaderLen = MnpDeviceData->SnpModeData->MediaHeaderSize;
*MacHeader -= 4;
*HeaderLen += 4;
NewMacHeader = *MacHeader;
//
// Check if the packet already has a VLAN tag by inspecting the
// 2-byte field at the expected position (EtherType/LEN offset).
//
if (*(UINT16 *)(*MacHeader + MediaHeaderLen) != 0) {
//
// Already tagged: shift the source MAC + EtherType right by 2 bytes
// to accommodate a new TPID, then restore the source MAC.
//
CopyMem (NewMacHeader, NewMacHeader + 4, MediaHeaderLen - 2);
*(UINT16 *)&NewMacHeader[MediaHeaderLen - 2] = 0x0081; // VLAN_TPID (little-endian: 0x8100)
} else {
//
// Untagged: shift the source MAC area right by 4 bytes and write
// the VLAN TPID in big-endian.
//
*(UINT16 *)&NewMacHeader[MediaHeaderLen + 2] = SwapBytes16 (
*(UINT16 *)(MnpServiceData->MnpDeviceData->SnpModeData->CurrentAddress.Addr)
);
}
//
// Write TCI: combine priority (3 bits) + VLAN ID (12 bits).
//
*(UINT16 *)&NewMacHeader[MediaHeaderLen] =
(*(UINT16 *)&NewMacHeader[MediaHeaderLen] & 0xE000) |
(MnpServiceData->VlanId & 0x0FFF);
//
// Store the final TCI value in big-endian.
//
*(UINT16 *)&NewMacHeader[MediaHeaderLen] = SwapBytes16 (
(*(UINT16 *)&NewMacHeader[MediaHeaderLen] & 0x1FFF) |
((UINT16)MnpServiceData->Priority << 13)
);
}
/* ====================================================================== */
/* MnpReadVlanConfiguration */
/* ====================================================================== */
/**
* Read the "NetworkStackVar" UEFI variable into an allocated buffer.
*
* This variable stores the list of configured VLAN ID / Priority pairs.
* Each entry is a 16-bit value: priority in the upper 3 bits, VLAN ID in
* the lower 12 bits (bit 15 is a flag, bit 12-14 = priority).
*
* @param[in] MnpDeviceData Pointer to MNP_DEVICE_DATA (ControllerHandle
* is used for the variable access).
* @param[out] NumberOfVlan Number of VLAN entries in the returned buffer.
* @param[out] VlanBuffer Allocated buffer containing the VLAN entries,
* or NULL on failure. Caller must free.
*
* @retval EFI_SUCCESS Variable read successfully.
* @retval EFI_OUT_OF_RESOURCES Memory allocation failed.
* @retval EFI_NOT_FOUND Variable does not exist.
*/
EFI_STATUS
MnpReadVlanConfiguration (
IN MNP_DEVICE_DATA *MnpDeviceData,
OUT UINT16 *NumberOfVlan,
OUT UINT8 **VlanBuffer
)
{
UINT64 VariableSize;
UINT8 *Buffer;
EFI_STATUS Status;
//
// 1. Query the required buffer size (expected to return EFI_BUFFER_TOO_SMALL).
//
VariableSize = 0;
*NumberOfVlan = 0;
*VlanBuffer = NULL;
Status = gRT->GetVariable (
L"NetworkStackVar",
&gNetworkStackVarGuid,
NULL,
&VariableSize,
NULL
);
if (Status != EFI_BUFFER_TOO_SMALL) {
return EFI_NOT_FOUND;
}
//
// 2. Allocate buffer of the required size.
//
Buffer = (UINT8 *)AllocatePool ((UINTN)VariableSize);
if (Buffer == NULL) {
return EFI_OUT_OF_RESOURCES;
}
//
// 3. Read the variable contents.
//
Status = gRT->GetVariable (
L"NetworkStackVar",
&gNetworkStackVarGuid,
NULL,
&VariableSize,
Buffer
);
if (EFI_ERROR (Status)) {
FreePool (Buffer);
return Status;
}
//
// 4. Return the count (each entry is 2 bytes, so count = size / 2).
//
*NumberOfVlan = (UINT16)(VariableSize >> 1);
*VlanBuffer = Buffer;
return EFI_SUCCESS;
}
/* ====================================================================== */
/* MnpCreateVlanChildHandle */
/* ====================================================================== */
/**
* Create a child handle for a VLAN-tagged MNP service and install the
* VLAN_CONFIG_PROTOCOL on it.
*
* 1. Opens the VLAN_CONFIG_PROTOCOL on the controller handle to find
* existing VLAN children.
* 2. Builds a MAC address with the VLAN TCI bytes (VLAN ID in Addr[0..1]).
* 3. Calls MnpCreateChildWithMac to create a new child device path with the
* MAC address appended.
* 4. Installs the VLAN_CONFIG_PROTOCOL (as a protocol interface) on the new
* child handle.
*
* @param[in] ImageHandle The driver's image handle.
* @param[in] ControllerHandle The physical NIC controller handle.
* @param[in] VlanId The VLAN ID to associate.
* @param[out] ChildHandle On success, the new child handle.
*
* @retval Handle of the new child, or 0 on failure.
*/
EFI_HANDLE
MnpCreateVlanChildHandle (
IN EFI_HANDLE ImageHandle,
IN EFI_HANDLE ControllerHandle,
IN UINT16 VlanId,
OUT EFI_HANDLE *ChildHandle OPTIONAL
)
{
EFI_STATUS Status;
VLAN_CONFIG_PROTOCOL *VlanConfig;
UINT8 DstBuf[6];
UINT8 VlanTci[2];
EFI_HANDLE NewHandle;
EFI_DEVICE_PATH_PROTOCOL *VlanDevicePath;
//
// 1. Open the VLAN_CONFIG_PROTOCOL on the ControllerHandle (by agent).
//
Status = gBS->OpenProtocol (
ControllerHandle,
&gVlanConfigProtocolGuid,
(VOID **)&VlanConfig,
ImageHandle,
ControllerHandle,
EFI_OPEN_PROTOCOL_BY_AGENT
);
if (EFI_ERROR (Status)) {
return 0;
}
//
// 2. Build a MAC address with the VLAN TCI embedded.
// Copy from the hardcoded template (src_3 at 0xA9B0 in binary).
//
CopyMem (DstBuf, &gMnpVlanTemplateMac, sizeof (DstBuf));
//
// 3. Build a device path containing the MAC address + VLAN TCI.
//
VlanTci[0] = (UINT8)(VlanId & 0xFF);
VlanTci[1] = (UINT8)((VlanId >> 8) & 0xFF);
VlanDevicePath = MnpCreateDevicePathWithMac (
VlanConfig,
DstBuf
);
if (VlanDevicePath == NULL) {
return 0;
}
//
// 4. Install the VLAN_CONFIG_PROTOCOL on a new handle with the device path.
//
NewHandle = 0;
Status = gBS->InstallProtocolInterface (
&NewHandle,
&gVlanConfigProtocolGuid,
EFI_NATIVE_INTERFACE,
VlanDevicePath
);
if (EFI_ERROR (Status)) {
FreePool (VlanDevicePath);
return 0;
}
if (ChildHandle != NULL) {
*ChildHandle = NewHandle;
}
return NewHandle;
}
/* ====================================================================== */
/* MnpVlanSet */
/* ====================================================================== */
/**
* Set/add a VLAN configuration entry.
*
* This is the VLAN_CONFIG_PROTOCOL.Set() implementation.
* It validates inputs, finds or creates an MNP_SERVICE_DATA for the given
* VLAN ID, stores the priority, updates the VLAN configuration variable,
* and optionally starts the service.
*
* @param[in] This VLAN_CONFIG_PROTOCOL instance pointer.
* @param[in] VlanId VLAN identifier (0-4094, 0xFFF is reserved).
* @param[in] Priority VLAN priority (0-7).
*
* @retval EFI_SUCCESS VLAN entry added/updated.
* @retval EFI_INVALID_PARAMETER This is NULL, VlanId > 0xFFE, or Priority > 7.
* @retval EFI_OUT_OF_RESOURCES Memory allocation failed.
*/
EFI_STATUS
EFIAPI
MnpVlanSet (
IN VLAN_CONFIG_PROTOCOL *This,
IN UINT16 VlanId,
IN UINT8 Priority
)
{
MNP_DEVICE_DATA *MnpDeviceData;
MNP_SERVICE_DATA *MnpServiceData;
UINTN Index;
UINT8 IsNewService;
UINT16 NumberOfVlan;
UINT8 *VlanBuffer;
UINT8 *NewBuffer;
EFI_STATUS Status;
if (This == NULL || VlanId > 0xFFE || Priority > 7) {
return EFI_INVALID_PARAMETER;
}
//
// CR: recover MNP_DEVICE_DATA from the VLAN_CONFIG_PROTOCOL pointer.
// The offset from the protocol interface to the MNP_DEVICE_DATA structure
// is -24 bytes (VlanConfigProtocol is at +0x18 from MnpDeviceData base).
//
MnpDeviceData = CR (This, MNP_DEVICE_DATA, VlanConfigProtocol, MNP_DEVICE_DATA_SIGNATURE);
IsNewService = 0;
//
// Check if we already have a service data entry for this VLAN ID.
//
MnpServiceData = MnpFindServiceByVlanId (MnpDeviceData, VlanId);
if (MnpServiceData != NULL) {
//
// Service already exists; just update priority.
//
MnpServiceData->Priority = Priority;
MnpServiceData->VlanId = VlanId;
goto UpdateVariable;
}
//
// New VLAN: need to create a new MNP_SERVICE_DATA.
// First, if this is the first VLAN (no default untagged service), ensure
// the untagged (VlanId=0) service is created and started.
//
IsNewService = 1;
if (MnpDeviceData->MnpServiceSnp == NULL) {
//
// No services exist yet. Create the default (VlanId=0) service first
// if we are adding a non-zero VLAN.
//
if (VlanId != 0) {
MnpServiceData = MnpFindServiceByVlanId (MnpDeviceData, 0);
if (MnpServiceData == NULL) {
MnpServiceData = MnpCreateServiceData (MnpDeviceData, 0, 0);
if (MnpServiceData == NULL) {
return EFI_OUT_OF_RESOURCES;
}
}
//
// Start the default service.
//
Status = MnpStartService (MnpServiceData);
if (EFI_ERROR (Status)) {
return Status;
}
}
} else {
//
// Default service exists; start it if not already running.
//
Status = MnpStartService ((MNP_SERVICE_DATA *)MnpDeviceData->MnpServiceSnp);
if (EFI_ERROR (Status)) {
return Status;
}
}
//
// Now create the new service data for this VLAN.
//
MnpServiceData = MnpCreateServiceData (MnpDeviceData, VlanId, Priority);
if (MnpServiceData == NULL) {
return EFI_OUT_OF_RESOURCES;
}
UpdateVariable:
//
// Rebuild the "NetworkStackVar" variable with all current VLAN entries.
// Walk the service list and serialize VlanId + Priority for each entry.
//
NumberOfVlan = (UINT16)MnpDeviceData->NumChildren;
NewBuffer = NULL;
Status = MnpReadVlanConfiguration (MnpDeviceData, &NumberOfVlan, &VlanBuffer);
if (EFI_ERROR (Status)) {
VlanBuffer = NULL;
NumberOfVlan = 0;
}
//
// Allocate buffer for the updated list (old count + 1, or just old count
// if this service already existed).
//
NewBuffer = (UINT8 *)AllocatePool ((UINTN)((NumberOfVlan + 1) * 2));
if (NewBuffer == NULL) {
if (VlanBuffer != NULL) {
FreePool (VlanBuffer);
}
return EFI_OUT_OF_RESOURCES;
}
if (VlanBuffer != NULL) {
CopyMem (NewBuffer, VlanBuffer, (UINTN)(NumberOfVlan * 2));
//
// Update the existing entry or append at the right position.
//
for (Index = 0; Index < NumberOfVlan; Index++) {
if (((UINT16 *)VlanBuffer)[Index] == VlanId) {
break;
}
}
} else {
Index = NumberOfVlan;
}
if (!IsNewService && Index < NumberOfVlan) {
//
// Existing entry: update in-place.
//
((UINT16 *)NewBuffer)[Index] &= 0x1000;
((UINT16 *)NewBuffer)[Index] |= (VlanId & 0x0FFF) | ((UINT16)Priority << 13);
} else {
//
// New entry: append.
//
((UINT16 *)NewBuffer)[NumberOfVlan] = (VlanId & 0x0FFF) | ((UINT16)Priority << 13);
NumberOfVlan++;
}
Status = gRT->SetVariable (
L"NetworkStackVar",
&gNetworkStackVarGuid,
EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS,
2 * NumberOfVlan,
NewBuffer
);
FreePool (NewBuffer);
if (VlanBuffer != NULL) {
FreePool (VlanBuffer);
}
return Status;
}
/* ====================================================================== */
/* MnpVlanFind */
/* ====================================================================== */
/**
* Find VLAN configuration entries.
*
* Implementation of VLAN_CONFIG_PROTOCOL.Find().
* If VlanId is non-NULL, find that specific VLAN and return its priority.
* If VlanId is NULL, return the count and a buffer of all VLAN entries.
*
* @param[in] This VLAN_CONFIG_PROTOCOL instance pointer.
* @param[in] VlanId Optional VLAN ID to search for.
* @param[out] Number Number of found entries.
* @param[out] Buffer Allocated buffer of VLAN entries (each 4 bytes:
* VlanId + Priority).
*
* @retval EFI_SUCCESS Query completed.
* @retval EFI_INVALID_PARAMETER This is NULL, or Number/Buffer is NULL.
* @retval EFI_NOT_FOUND Specified VlanId not found, or no VLANs.
*/
EFI_STATUS
EFIAPI
MnpVlanFind (
IN VLAN_CONFIG_PROTOCOL *This,
IN UINT16 VlanId OPTIONAL,
OUT UINT16 *Number,
OUT VOID **Buffer
)
{
MNP_DEVICE_DATA *MnpDeviceData;
MNP_SERVICE_DATA *MnpServiceData;
MNP_SERVICE_DATA *Entry;
LIST_ENTRY *ListHead;
LIST_ENTRY *ListEntry;
UINT8 *Buf;
UINTN Count;
if (This == NULL || Number == NULL || Buffer == NULL) {
return EFI_INVALID_PARAMETER;
}
*Number = 0;
*Buffer = NULL;
MnpDeviceData = CR (This, MNP_DEVICE_DATA, VlanConfigProtocol, MNP_DEVICE_DATA_SIGNATURE);
if (MnpDeviceData->NumChildren == 0) {
return EFI_NOT_FOUND;
}
if (VlanId > 0) {
//
// Specific VLAN lookup.
//
MnpServiceData = MnpFindServiceByVlanId (MnpDeviceData, VlanId);
if (MnpServiceData == NULL) {
return EFI_NOT_FOUND;
}
Buf = (UINT8 *)AllocatePool (4);
if (Buf == NULL) {
return EFI_OUT_OF_RESOURCES;
}
*(UINT16 *)Buf = MnpServiceData->VlanId;
*(UINT8 *)(Buf + 2) = MnpServiceData->Priority;
*Number = 1;
*Buffer = Buf;
return EFI_SUCCESS;
}
//
// Enumerate all VLANS.
//
ListHead = &MnpDeviceData->ServiceList;
Count = 0;
//
// Walk the service list once to count.
//
for (ListEntry = ListHead->ForwardLink;
ListEntry != ListHead;
ListEntry = ListEntry->ForwardLink)
{
Count++;
}
if (Count == 0) {
return EFI_NOT_FOUND;
}
Buf = (UINT8 *)AllocatePool ((UINTN)(Count * 4));
if (Buf == NULL) {
return EFI_OUT_OF_RESOURCES;
}
Count = 0;
for (ListEntry = ListHead->ForwardLink;
ListEntry != ListHead;
ListEntry = ListEntry->ForwardLink)
{
Entry = CR (ListEntry, MNP_SERVICE_DATA, Link, MNP_SERVICE_DATA_SIGNATURE);
*(UINT16 *)&Buf[Count * 4] = Entry->VlanId;
*(UINT8 *)&Buf[Count * 4 + 2] = Entry->Priority;
Count++;
}
*Number = (UINT16)Count;
*Buffer = Buf;
return EFI_SUCCESS;
}
/* ====================================================================== */
/* MnpVlanRemove */
/* ====================================================================== */
/**
* Remove a VLAN configuration entry and its associated MNP_SERVICE_DATA.
*
* Implementation of VLAN_CONFIG_PROTOCOL.Remove().
* Finds the service data by VLAN ID, decrements the refcount, destroys
* the service (if last VLAN entry), and updates the NetworkStackVar
* variable.
*
* @param[in] This VLAN_CONFIG_PROTOCOL instance pointer.
* @param[in] VlanId VLAN identifier to remove (0 is not allowed for
* removal; the default untagged service is kept).
*
* @retval EFI_SUCCESS VLAN entry removed.
* @retval EFI_INVALID_PARAMETER This is NULL, or VlanId > 0xFFE.
* @retval EFI_NOT_FOUND VLAN ID not configured.
*/
EFI_STATUS
EFIAPI
MnpVlanRemove (
IN VLAN_CONFIG_PROTOCOL *This,
IN UINT16 VlanId
)
{
MNP_DEVICE_DATA *MnpDeviceData;
MNP_SERVICE_DATA *MnpServiceData;
UINT16 NumberOfVlan;
UINT8 *VlanBuffer;
UINT8 *NewBuffer;
UINTN Index;
EFI_STATUS Status;
MNP_SERVICE_DATA *Entry;
LIST_ENTRY *ListEntry;
if (This == NULL || VlanId > 0xFFE) {
return EFI_INVALID_PARAMETER;
}
MnpDeviceData = CR (This, MNP_DEVICE_DATA, VlanConfigProtocol, MNP_DEVICE_DATA_SIGNATURE);
if (MnpDeviceData->MnpServiceSnp == NULL) {
return EFI_NOT_FOUND;
}
//
// Find the service data for this VLAN.
//
MnpServiceData = MnpFindServiceByVlanId (MnpDeviceData, VlanId);
if (MnpServiceData == NULL) {
return EFI_NOT_FOUND;
}
//
// Decrement the child service count.
//
MnpDeviceData->NumChildren--;
//
// If there are other non-zero VLAN services, or the zero-VLAN default
// still exists, we only need to stop this one VLAN service.
//
if (VlanId != 0 || MnpDeviceData->NumChildren > 0) {
Status = MnpStopService (MnpServiceData);
if (EFI_ERROR (Status)) {
return Status;
}
Status = MnpDestroyServiceData (MnpServiceData);
if (EFI_ERROR (Status)) {
return Status;
}
}
//
// Rebuild the NetworkStackVar variable without this VLAN entry.
//
if (MnpDeviceData->NumChildren > 0) {
NewBuffer = (UINT8 *)AllocatePool ((UINTN)(MnpDeviceData->NumChildren * 2));
if (NewBuffer == NULL) {
return EFI_OUT_OF_RESOURCES;
}
Index = 0;
for (ListEntry = MnpDeviceData->ServiceList.ForwardLink;
ListEntry != &MnpDeviceData->ServiceList;
ListEntry = ListEntry->ForwardLink)
{
Entry = CR (ListEntry, MNP_SERVICE_DATA, Link, MNP_SERVICE_DATA_SIGNATURE);
*(UINT16 *)&NewBuffer[Index * 2] &= 0x1000;
*(UINT16 *)&NewBuffer[Index * 2] |= (Entry->VlanId & 0x0FFF) | ((UINT16)Entry->Priority << 13);
Index++;
}
Status = gRT->SetVariable (
L"NetworkStackVar",
&gNetworkStackVarGuid,
EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS,
(UINTN)(MnpDeviceData->NumChildren * 2),
NewBuffer
);
FreePool (NewBuffer);
} else {
//
// No VLANs left: delete the variable.
//
Status = gRT->SetVariable (
L"NetworkStackVar",
&gNetworkStackVarGuid,
0,
0,
NULL
);
}
return Status;
}
/* ====================================================================== */
/* MnpFindServiceByVlanId (internal helper) */
/* ====================================================================== */
/**
* Iterate the service list and return the MNP_SERVICE_DATA matching
* the given VLAN ID.
*
* @param[in] MnpDeviceData The device data whose ServiceList to walk.
* @param[in] VlanId 12-bit VLAN ID to match.
*
* @return Pointer to the matching MNP_SERVICE_DATA, or NULL.
*/
MNP_SERVICE_DATA *
MnpFindServiceByVlanId (
IN MNP_DEVICE_DATA *MnpDeviceData,
IN UINT16 VlanId
)
{
LIST_ENTRY *ListEntry;
MNP_SERVICE_DATA *Service;
for (ListEntry = MnpDeviceData->ServiceList.ForwardLink;
ListEntry != &MnpDeviceData->ServiceList;
ListEntry = ListEntry->ForwardLink)
{
Service = CR (ListEntry, MNP_SERVICE_DATA, Link, MNP_SERVICE_DATA_SIGNATURE);
if (Service->VlanId == VlanId) {
return Service;
}
}
return NULL;
}