/**
* @file MnpConfig.c
*
* @brief MNP Configuration Helpers
*
* Implements the internal configuration logic:
* - MnpInitializeDeviceData / MnpReleaseDeviceData
* - MnpCreateServiceData / MnpDestroyServiceData
* - MnpInitializeInstanceData / MnpCleanInstance
* - MnpConfigureInstance (per-instance configure/reset)
* - MnpConfigReceiveFilters (SNP receive filters)
* - MnpStart / MnpStop (underlying service-level start/stop)
* - MnpGroupOp (multicast group add/remove)
* - MnpReadVlanConfiguration
*
* Source: AmiNetworkPkg/UefiNetworkStack/Common/MnpDxe/MnpConfig.c
*/
#include "MnpDxe.h"
extern EFI_BOOT_SERVICES *gBS;
extern EFI_HANDLE gImageHandle;
/**
* Free NET_BUF queue signature template.
*/
STATIC CONST NET_BUF_QUEUE gFreeNbufQueueTemplate = {
NET_BUF_QUEUE_SIGNATURE,
0,
{ &gFreeNbufQueueTemplate.Buffers.Flink,
&gFreeNbufQueueTemplate.Buffers.Blink }
};
/**
* Default MNP config data template.
*/
STATIC CONST EFI_MNP_CONFIG_DATA gMnpDefaultConfigData = { 0 };
/**
* MNP instance flags: initial zero state.
*/
STATIC CONST MNP_INSTANCE_FLAGS gMnpInstanceConfigTemplate = { 0 };
/* ====================================================================== */
/* MnpInitializeDeviceData */
/* ====================================================================== */
/**
* Initialize the per-NIC MNP_DEVICE_DATA structure.
*
* Opens the SNP protocol, retrieves the MAC address string, initializes
* the free NET_BUF queue, allocates receive buffers, and creates timer
* events for polling, timeout check, media detection, and TX timeout.
*
* @param[out] MnpDeviceData Device data to initialize.
* @param[in] ImageHandle The driver image handle.
* @param[in] ControllerHandle The NIC controller handle.
*
* @retval EFI_SUCCESS Initialized successfully.
* @retval EFI_UNSUPPORTED SNP protocol not found.
* @retval EFI_OUT_OF_RESOURCES Memory allocation failed.
*/
EFI_STATUS
MnpInitializeDeviceData (
OUT MNP_DEVICE_DATA *MnpDeviceData,
IN EFI_HANDLE ImageHandle,
IN EFI_HANDLE ControllerHandle
)
{
EFI_SIMPLE_NETWORK *Snp;
EFI_SIMPLE_NETWORK_MODE *SnpMode;
EFI_STATUS Status;
ASSERT (MnpDeviceData != NULL);
//
// Initialize header fields.
//
MnpDeviceData->Signature = MNP_DEVICE_DATA_SIGNATURE;
MnpDeviceData->ImageHandle = ImageHandle;
MnpDeviceData->ControllerHandle = ControllerHandle;
//
// Open SNP protocol exclusively.
//
Status = gBS->OpenProtocol (
ControllerHandle,
&gMnpSnpProtocolGuid,
(VOID **)&Snp,
ImageHandle,
ControllerHandle,
EFI_OPEN_PROTOCOL_BY_DRIVER | EFI_OPEN_PROTOCOL_EXCLUSIVE
);
if (EFI_ERROR (Status)) {
return EFI_UNSUPPORTED;
}
MnpDeviceData->Snp = Snp;
SnpMode = Snp->Mode;
//
// Compute buffer and header sizes from SNP mode.
//
MnpDeviceData->BufferLength = SnpMode->MediaHeaderSize +
SnpMode->Snip.HeaderSize +
SnpMode->Snip.DataSize;
MnpDeviceData->HeaderLength = ALIGN_VALUE (SnpMode->MediaHeaderSize, 4) +
sizeof (UINT32);
//
// Get MAC address string.
//
Status = GetMacAddressString (
ControllerHandle,
&MnpDeviceData->MacString
);
if (EFI_ERROR (Status)) {
goto ErrorCloseSnp;
}
//
// Initialize service list and group address list.
//
InitializeListHead (&MnpDeviceData->ServiceList);
InitializeListHead (&MnpDeviceData->GroupAddressList);
//
// Initialize free NET_BUF queue.
//
CopyMem (&MnpDeviceData->FreeNbufQueue,
&gFreeNbufQueueTemplate,
sizeof (NET_BUF_QUEUE));
InitializeListHead (&MnpDeviceData->FreeNbufQueue.Buffers);
MnpDeviceData->FreeNbufCount = 0;
MnpDeviceData->TotalFreeNbufs = 0;
//
// Allocate initial free NET_BUFs.
//
Status = MnpAddFreeNbuf (MnpDeviceData, MNP_DEFAULT_NBUF_COUNT);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR,
"MnpInitializeDeviceData: MnpAddFreeNbuf failed, %r.\n",
Status));
goto ErrorFreeMacString;
}
//
// Allocate the receive packet buffer.
//
MnpDeviceData->RcvPacket = NetbufAlloc (MnpDeviceData->BufferLength);
if (MnpDeviceData->RcvPacket == NULL) {
Status = EFI_OUT_OF_RESOURCES;
goto ErrorFreeMacString;
}
NetbufTrim (MnpDeviceData->RcvPacket, MnpDeviceData->HeaderLength, 0);
//
// Allocate the receive buffer pool.
//
MnpDeviceData->RcvBufPool = AllocatePool (MnpDeviceData->BufferLength);
if (MnpDeviceData->RcvBufPool == NULL) {
DEBUG ((DEBUG_ERROR,
"MnpInitializeDeviceData: AllocatePool failed.\n"));
Status = EFI_OUT_OF_RESOURCES;
goto ErrorFreeRcvPacket;
}
//
// Create timer events.
//
Status = gBS->CreateEvent (
EVT_TIMER | EVT_NOTIFY_SIGNAL,
TPL_CALLBACK,
MnpPollTimer,
MnpDeviceData,
&MnpDeviceData->PollTimerEvent
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR,
"MnpInitializeDeviceData: CreateEvent for poll timer failed.\n"));
goto ErrorFreePool;
}
Status = gBS->CreateEvent (
EVT_TIMER | EVT_NOTIFY_SIGNAL,
TPL_CALLBACK,
MnpCheckPacketTimeout,
MnpDeviceData,
&MnpDeviceData->TimeoutCheckEvent
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR,
"MnpInitializeDeviceData: CreateEvent for packet timeout "
"check failed.\n"));
goto ErrorCloseTimer1;
}
Status = gBS->CreateEvent (
EVT_TIMER | EVT_NOTIFY_SIGNAL,
TPL_CALLBACK,
MnpMediaDetect,
MnpDeviceData,
&MnpDeviceData->MediaDetectEvent
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR,
"MnpInitializeDeviceData: CreateEvent for media detection "
"failed.\n"));
goto ErrorCloseTimer2;
}
Status = gBS->CreateEvent (
EVT_TIMER,
TPL_CALLBACK,
NULL,
NULL,
&MnpDeviceData->TxTimeoutEvent
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR,
"MnpInitializeDeviceData: CreateEvent for tx timeout event "
"failed.\n"));
goto ErrorCloseTimer3;
}
return EFI_SUCCESS;
ErrorCloseTimer3:
gBS->CloseEvent (MnpDeviceData->MediaDetectEvent);
ErrorCloseTimer2:
gBS->CloseEvent (MnpDeviceData->TimeoutCheckEvent);
ErrorCloseTimer1:
gBS->CloseEvent (MnpDeviceData->PollTimerEvent);
ErrorFreePool:
if (MnpDeviceData->RcvBufPool != NULL) {
FreePool (MnpDeviceData->RcvBufPool);
}
ErrorFreeRcvPacket:
if (MnpDeviceData->RcvPacket != NULL) {
NetbufFree (MnpDeviceData->RcvPacket);
}
ErrorFreeMacString:
if (MnpDeviceData->MacString != NULL) {
FreePool (MnpDeviceData->MacString);
}
gBS->CloseProtocol (
ControllerHandle,
&gMnpSnpProtocolGuid,
ImageHandle,
ControllerHandle
);
ErrorCloseSnp:
return Status;
}
/* ====================================================================== */
/* MnpReleaseDeviceData */
/* ====================================================================== */
/**
* Release resources associated with a MNP_DEVICE_DATA.
*
* @param[in] MnpDeviceData Device data to release.
* @param[in] ImageHandle Driver image handle for closing SNP.
*/
VOID
MnpReleaseDeviceData (
IN MNP_DEVICE_DATA *MnpDeviceData,
IN EFI_HANDLE ImageHandle
)
{
ASSERT (MnpDeviceData->Signature == MNP_DEVICE_DATA_SIGNATURE);
ASSERT (IsListEmpty (&MnpDeviceData->GroupAddressList));
//
// Close all timer events.
//
if (MnpDeviceData->TxTimeoutEvent != NULL) {
gBS->CloseEvent (MnpDeviceData->TxTimeoutEvent);
}
if (MnpDeviceData->TimeoutCheckEvent != NULL) {
gBS->CloseEvent (MnpDeviceData->TimeoutCheckEvent);
}
if (MnpDeviceData->MediaDetectEvent != NULL) {
gBS->CloseEvent (MnpDeviceData->MediaDetectEvent);
}
if (MnpDeviceData->PollTimerEvent != NULL) {
gBS->CloseEvent (MnpDeviceData->PollTimerEvent);
}
//
// Free receive buffer pool.
//
if (MnpDeviceData->RcvBufPool != NULL) {
FreePool (MnpDeviceData->RcvBufPool);
}
//
// Return the receive packet.
//
if (MnpDeviceData->RcvPacket != NULL) {
NetbufFree (MnpDeviceData->RcvPacket);
}
//
// Update free queue counter.
//
MnpDeviceData->NbufAllocated -= MnpDeviceData->FreeNbufCount;
//
// Flush the free NET_BUF queue.
//
NetbufQueFlush (&MnpDeviceData->FreeNbufQueue);
//
// Close SNP protocol.
//
gBS->CloseProtocol (
MnpDeviceData->ControllerHandle,
&gMnpSnpProtocolGuid,
ImageHandle,
MnpDeviceData->ControllerHandle
);
//
// Free MAC string.
//
if (MnpDeviceData->MacString != NULL) {
FreePool (MnpDeviceData->MacString);
}
}
/* ====================================================================== */
/* MnpAddFreeNbuf */
/* ====================================================================== */
/**
* Allocate additional free NET_BUFs and add them to the free queue.
*
* @param[in] MnpDeviceData Device data.
* @param[in] Count Number of NET_BUFs to allocate.
*
* @retval EFI_SUCCESS Allocated.
* @retval EFI_OUT_OF_RESOURCES Allocation failed.
*/
EFI_STATUS
MnpAddFreeNbuf (
IN MNP_DEVICE_DATA *MnpDeviceData,
IN UINTN Count
)
{
UINTN Index;
for (Index = 0; Index < Count; Index++) {
NET_BUF *Nbuf;
Nbuf = NetbufAlloc (MnpDeviceData->BufferLength +
MnpDeviceData->HeaderLength);
if (Nbuf == NULL) {
return EFI_OUT_OF_RESOURCES;
}
NetbufTrim (Nbuf, MnpDeviceData->HeaderLength, 0);
NetbufQueAppend (&MnpDeviceData->FreeNbufQueue, Nbuf);
MnpDeviceData->FreeNbufCount++;
MnpDeviceData->NbufAllocated++;
MnpDeviceData->TotalFreeNbufs++;
}
return EFI_SUCCESS;
}
/* ====================================================================== */
/* MnpAllocNbuf (from free queue) */
/* ====================================================================== */
/**
* Allocate a NET_BUF from the free queue.
*
* @param[in] MnpDeviceData Device data.
*
* @return NET_BUF pointer, or NULL if queue is empty.
*/
NET_BUF *
MnpAllocNbuf (
IN MNP_DEVICE_DATA *MnpDeviceData
)
{
NET_BUF *Nbuf;
if (IsListEmpty (&MnpDeviceData->FreeNbufQueue.Buffers)) {
if (MnpAddFreeNbuf (MnpDeviceData, 1) != EFI_SUCCESS) {
return NULL;
}
}
Nbuf = (NET_BUF *)GetFirstNode (&MnpDeviceData->FreeNbufQueue.Buffers);
RemoveEntryList (&Nbuf->Link);
MnpDeviceData->FreeNbufCount--;
return Nbuf;
}
/* ====================================================================== */
/* MnpFreeNbuf (return to free queue) */
/* ====================================================================== */
/**
* Return a NET_BUF to the free queue.
*
* @param[in] MnpDeviceData Device data.
* @param[in] Nbuf Buffer to return.
*/
VOID
MnpFreeNbuf (
IN MNP_DEVICE_DATA *MnpDeviceData,
IN NET_BUF *Nbuf
)
{
if (Nbuf->RefCount > 2) {
//
// Buffer is still referenced; keep it.
//
InsertTailList (&MnpDeviceData->FreeNbufQueue.Buffers, &Nbuf->Link);
MnpDeviceData->FreeNbufCount++;
}
}
/* ====================================================================== */
/* MnpCreateServiceData */
/* ====================================================================== */
/**
* Create a new MNP_SERVICE_DATA for a given VLAN ID.
*
* Allocates the service data, initializes the MNP protocol interface
* (partial copy from template), creates a child handle if VLAN is used,
* and installs the device path protocol on the child.
*
* @param[in] MnpDeviceData Parent device data.
* @param[in] VlanId VLAN ID (0 = default/no VLAN).
* @param[in] Priority VLAN priority (0-7).
*
* @return Pointer to MNP_SERVICE_DATA, or NULL on failure.
*/
MNP_SERVICE_DATA *
MnpCreateServiceData (
IN MNP_DEVICE_DATA *MnpDeviceData,
IN UINT16 VlanId,
IN UINT8 Priority
)
{
MNP_SERVICE_DATA *Service;
EFI_SIMPLE_NETWORK_MODE *SnpMode;
EFI_STATUS Status;
Service = AllocateZeroPool (sizeof (MNP_SERVICE_DATA));
if (Service == NULL) {
DEBUG ((DEBUG_ERROR,
"MnpCreateServiceData: Faild to allocate memory for the new "
"Mnp Service Data.\n"));
return NULL;
}
//
// Insert into the device's service list.
//
InsertTailList (&MnpDeviceData->ServiceList, &Service->Link);
Service->Signature = MNP_SERVICE_DATA_SIGNATURE;
Service->MnpDeviceData = MnpDeviceData;
//
// Copy the MNP protocol template (partial: GetModeData + Configure).
//
CopyMem (&Service->Protocol, &mMnpProtocolTemplate,
sizeof (EFI_MANAGED_NETWORK_PROTOCOL));
InitializeListHead (&Service->InstanceList);
SnpMode = MnpDeviceData->Snp->Mode;
Service->MtuSize = SnpMode->MediaHeaderSize;
if (VlanId != 0) {
//
// Create a child handle for VLAN.
//
Status = MnpCreateVlanChildHandle (
MnpDeviceData->ImageHandle,
MnpDeviceData->ControllerHandle,
VlanId,
&Service->ControllerHandle
);
if (Status != EFI_SUCCESS) {
DEBUG ((DEBUG_ERROR,
"MnpCreateServiceData: Faild to create child handle.\n"));
MnpDestroyServiceData (Service);
return NULL;
}
//
// Open the driver binding protocol on the child.
//
Status = gBS->OpenProtocol (
MnpDeviceData->ControllerHandle,
&gMnpDriverBindingGuid,
(VOID **)&DevicePathInterface,
MnpDeviceData->ImageHandle,
Service->ControllerHandle,
EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
);
if (EFI_ERROR (Status)) {
MnpDestroyServiceData (Service);
return NULL;
}
Service->MtuSize = (UINT32)(SnpMode->MediaHeaderSize - 4);
} else {
Service->ControllerHandle = MnpDeviceData->ControllerHandle;
}
Service->VlanId = VlanId;
Service->Priority = Priority;
Service->Handle = Service->ControllerHandle;
//
// Install device path protocol on the service handle.
//
Status = gBS->InstallMultipleProtocolInterfaces (
&Service->Handle,
&gMnpDevicePathGuid,
Service + 1, /* Device path after service struct */
NULL
);
if (EFI_ERROR (Status)) {
MnpDestroyServiceData (Service);
return NULL;
}
return Service;
}
/* ====================================================================== */
/* MnpDestroyServiceData */
/* ====================================================================== */
/**
* Destroy a MNP_SERVICE_DATA and all its child instances.
*
* @param[in] Service Service data to destroy.
*
* @retval EFI_SUCCESS Destroyed successfully.
*/
EFI_STATUS
MnpDestroyServiceData (
IN MNP_SERVICE_DATA *Service
)
{
MNP_DEVICE_DATA *MnpDeviceData;
LIST_ENTRY *Entry;
LIST_ENTRY *NextEntry;
ASSERT (Service->Signature == MNP_SERVICE_DATA_SIGNATURE);
MnpDeviceData = Service->MnpDeviceData;
//
// Destroy all instances.
//
for (Entry = Service->InstanceList.Flink;
Entry != &Service->InstanceList;
Entry = NextEntry) {
MNP_INSTANCE_DATA *Instance;
NextEntry = Entry->Flink;
Instance = MNP_INSTANCE_DATA_FROM_LINK (Entry);
MnpCleanInstance (Instance);
RemoveEntryList (Entry);
Service->InstanceCount--;
FreePool (Instance);
}
//
// Remove from service list.
//
RemoveEntryList (&Service->Link);
//
// Uninstall device path protocol.
//
gBS->UninstallMultipleProtocolInterfaces (
Service->Handle,
&gMnpDevicePathGuid,
Service + 1,
NULL
);
//
// Close the child controller protocol if this was a VLAN service.
//
if (Service->VlanId != 0) {
gBS->CloseProtocol (
MnpDeviceData->ControllerHandle,
&gMnpDriverBindingGuid,
MnpDeviceData->ImageHandle,
Service->ControllerHandle
);
}
FreePool (Service);
return EFI_SUCCESS;
}
/* ====================================================================== */
/* MnpInitializeInstanceData */
/* ====================================================================== */
/**
* Initialize an MNP_INSTANCE_DATA.
*
* @param[in] Service Parent service data.
* @param[in] Instance Instance data to initialize.
*/
VOID
MnpInitializeInstanceData (
IN MNP_SERVICE_DATA *Service,
IN MNP_INSTANCE_DATA *Instance
)
{
ASSERT (Service->Signature == MNP_SERVICE_DATA_SIGNATURE);
ASSERT (Instance != NULL);
Instance->Signature = MNP_INSTANCE_DATA_SIGNATURE;
Instance->MnpServiceData = Service;
//
// Copy the full MNP protocol interface.
//
CopyMem (&Instance->Protocol, &mMnpProtocolTemplate,
sizeof (EFI_MANAGED_NETWORK_PROTOCOL));
//
// Copy the default config data.
//
CopyMem (&Instance->ConfigData, &gMnpDefaultConfigData,
sizeof (EFI_MNP_CONFIG_DATA));
//
// Initialize lists.
//
InitializeListHead (&Instance->GroupCtrlBlkList);
InitializeListHead (&Instance->TxDataList);
InitializeListHead (&Instance->RcvdPacketQueue);
//
// Initialize NET_MAP for receive tokens.
//
InitializeListHead (&Instance->RcvMap.Free);
InitializeListHead (&Instance->RcvMap.Used);
Instance->RcvMap.ItemCount = 0;
}
/* ====================================================================== */
/* MnpCleanInstance */
/* ====================================================================== */
/**
* Clean instance state: flush config, reset filters, flush queues.
*
* @param[in] Instance Instance to clean.
*/
VOID
MnpCleanInstance (
IN MNP_INSTANCE_DATA *Instance
)
{
MNP_DEVICE_DATA *Device;
MNP_CONFIG_DATA *Config;
ASSERT (Instance->Signature == MNP_INSTANCE_DATA_SIGNATURE);
Device = Instance->MnpServiceData->MnpDeviceData;
//
// Decrement filter counts on the device.
//
if (Instance->EnableUnicastReceive) Device->UnicastFilterCnt--;
if (Instance->EnableMulticastReceive) Device->MulticastFilterCnt--;
if (Instance->EnablePromiscuousReceive) Device->PromiscuousFilterCnt--;
if (Instance->FlushQueue) Device->FlushQueueCnt--;
//
// Apply config from template.
//
Config = &Instance->ConfigData;
if (Config->EnableUnicastReceive) {
Device->UnicastFilterCnt++;
}
if (Config->EnableMulticastReceive) {
Device->MulticastFilterCnt++;
}
if (Config->EnablePromiscuousReceive) {
Device->PromiscuousFilterCnt++;
}
if (Config->FlushQueue) {
Device->FlushQueueCnt++;
}
Instance->Flags = 0;
}
/* ====================================================================== */
/* MnpConfigureInstance */
/* ====================================================================== */
/**
* Configure or reset an MNP instance.
*
* Updates the device's filter counters, flushes group addresses on reset,
* copies the configuration data, and calls MnpStart or MnpStop as
* appropriate.
*
* @param[in] Instance Instance to configure.
* @param[in] MnpConfigData Configuration data, or NULL to reset.
*
* @retval EFI_SUCCESS Configured/reset.
* @retval EFI_ALREADY_STARTED Already configured with non-NULL data.
*/
EFI_STATUS
MnpConfigureInstance (
IN MNP_INSTANCE_DATA *Instance,
IN EFI_MNP_CONFIG_DATA *MnpConfigData OPTIONAL
)
{
MNP_DEVICE_DATA *Device;
BOOLEAN IsFirstStart;
MNP_CONFIG_DATA *ApplyConfig;
ASSERT (Instance->Signature == MNP_INSTANCE_DATA_SIGNATURE);
ASSERT (Instance->MnpServiceData != NULL);
ASSERT (Instance->MnpServiceData->Signature == MNP_SERVICE_DATA_SIGNATURE);
ASSERT (MnpConfigData == NULL || !MnpConfigData->Reserved);
if (MnpConfigData != NULL && MnpConfigData->Reserved != 0) {
return EFI_ALREADY_STARTED;
}
Device = Instance->MnpServiceData->MnpDeviceData;
ASSERT (Device->Signature == MNP_DEVICE_DATA_SIGNATURE);
IsFirstStart = !Instance->IsConfigured && (MnpConfigData != NULL);
//
// Decrement old filter counters.
//
if (Instance->EnableUnicastReceive) Device->UnicastFilterCnt--;
if (Instance->EnableMulticastReceive) Device->MulticastFilterCnt--;
if (Instance->EnablePromiscuousReceive) Device->PromiscuousFilterCnt--;
if (Instance->FlushQueue) Device->FlushQueueCnt--;
//
// Apply new configuration.
//
if (Instance->EnableRxReclaim) {
MnpCleanInstance (Instance);
}
if (MnpConfigData == NULL) {
//
// Full reset: call stop on the service.
//
goto DoneApply;
}
ApplyConfig = MnpConfigData;
//
// Increment new filter counters.
//
if (ApplyConfig->EnableUnicastReceive) {
Device->UnicastFilterCnt++;
Instance->Flags |= MNP_INSTANCE_FLAG_UNICAST;
}
if (ApplyConfig->EnableMulticastReceive) {
Device->MulticastFilterCnt++;
}
if (ApplyConfig->EnablePromiscuousReceive) {
Device->PromiscuousFilterCnt++;
Instance->Flags |= MNP_INSTANCE_FLAG_PROMISCUOUS;
}
if (ApplyConfig->FlushQueue) {
Device->FlushQueueCnt++;
}
//
// Flush all group addresses if MC receive is now disabled.
//
if (!ApplyConfig->EnableMulticastReceive) {
MnpGroupOp (Instance, FALSE, NULL);
}
DoneApply:
//
// Copy the config data to the instance.
//
CopyMem (&Instance->ConfigData, ApplyConfig, sizeof (EFI_MNP_CONFIG_DATA));
Instance->IsConfigured = (MnpConfigData != NULL) ? TRUE : FALSE;
if (MnpConfigData != NULL) {
return MnpStart (Instance->MnpServiceData,
IsFirstStart,
MnpConfigData->EnablePolling);
} else {
return MnpStop (Instance->MnpServiceData);
}
}
/* ====================================================================== */
/* MnpStart */
/* ====================================================================== */
/**
* Start the underlying SNP adapter. Only the first config call actually
* starts the hardware; subsequent calls just update polling timers.
*
* @param[in] Service Service data.
* @param[in] IsFirstStart TRUE if this is the first instance starting.
* @param[in] EnablePolling TRUE to enable polling timer.
*
* @retval EFI_SUCCESS Started successfully.
*/
EFI_STATUS
MnpStart (
IN MNP_SERVICE_DATA *Service,
IN BOOLEAN IsFirstStart,
IN BOOLEAN EnablePolling
)
{
MNP_DEVICE_DATA *Device;
EFI_SIMPLE_NETWORK *Snp;
EFI_STATUS Status;
ASSERT (Service->Signature == MNP_SERVICE_DATA_SIGNATURE);
Device = Service->MnpDeviceData;
Snp = Device->Snp;
if (!IsFirstStart) {
//
// Not the first child; skip hardware initialization.
//
goto SetPolling;
}
if (Snp == NULL) {
ASSERT (Snp != NULL);
return EFI_DEVICE_ERROR;
}
//
// Start and initialize the SNP adapter.
//
Status = Snp->Start (Snp);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "MnpStart: MnpStartSnp failed, %r.\n", Status));
return Status;
}
Status = Snp->Initialize (Snp, 0, 0);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "MnpStart: MnpStartSnp failed, %r.\n", Status));
return Status;
}
//
// Set timer for timeout check (500ms).
//
Status = gBS->SetTimer (
Device->TimeoutCheckEvent,
TimerPeriodic,
500 * 10000 /* 500ms in 100ns units */
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR,
"MnpStart, gBS->SetTimer for TimeoutCheckTimer %r.\n",
Status));
return Status;
}
//
// Set timer for media detection (5s).
//
Status = gBS->SetTimer (
Device->MediaDetectEvent,
TimerPeriodic,
5000 * 10000 /* 5s in 100ns units */
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR,
"MnpStart, gBS->SetTimer for MediaDetectTimer %r.\n",
Status));
return Status;
}
SetPolling:
//
// Update polling timer if changed.
//
if (Device->PollingEnabled != EnablePolling) {
Status = gBS->SetTimer (
Device->PollTimerEvent,
EnablePolling ? TimerPeriodic : TimerCancel,
100000 /* 10ms in 100ns units */
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR,
"MnpStart: gBS->SetTimer for PollTimer failed, %r.\n",
Status));
return Status;
}
Device->PollingEnabled = EnablePolling;
}
//
// Reconfigure SNP receive filters to match current config.
//
return MnpConfigReceiveFilters (Device);
}
/* ====================================================================== */
/* MnpStop */
/* ====================================================================== */
/**
* Stop the underlying SNP adapter. Only the last instance actually stops
* the hardware.
*
* @param[in] Service Service data.
*
* @retval EFI_SUCCESS Stopped successfully.
*/
EFI_STATUS
MnpStop (
IN MNP_SERVICE_DATA *Service
)
{
MNP_DEVICE_DATA *Device;
EFI_SIMPLE_NETWORK *Snp;
EFI_STATUS Status;
ASSERT (Service->Signature == MNP_SERVICE_DATA_SIGNATURE);
Device = Service->MnpDeviceData;
ASSERT (Device->ConfiguredChildrenNumber > 0);
//
// Reconfigure filters.
//
MnpConfigReceiveFilters (Device);
if (--Device->ConfiguredChildrenNumber != 0) {
//
// Still other children active.
//
return EFI_SUCCESS;
}
//
// Stop polling timer.
//
if (Device->PollingEnabled) {
gBS->SetTimer (Device->PollTimerEvent, TimerCancel, 0);
Device->PollingEnabled = FALSE;
}
//
// Cancel timeout and media detect timers.
//
gBS->SetTimer (Device->TimeoutCheckEvent, TimerCancel, 0);
gBS->SetTimer (Device->MediaDetectEvent, TimerCancel, 0);
//
// Reset and stop SNP.
//
Snp = Device->Snp;
ASSERT (Snp != NULL);
Status = Snp->Reset (Snp);
if (!EFI_ERROR (Status)) {
Status = Snp->Shutdown (Snp);
}
return Status;
}
/* ====================================================================== */
/* MnpConfigReceiveFilters */
/* ====================================================================== */
/**
* Configure the SNP receive filters based on the current device state.
*
* @param[in] MnpDeviceData Device data.
*
* @retval EFI_SUCCESS Filters configured.
* @retval EFI_OUT_OF_RESOURCES Memory allocation for MC filter list failed.
*/
EFI_STATUS
MnpConfigReceiveFilters (
IN MNP_DEVICE_DATA *MnpDeviceData
)
{
EFI_SIMPLE_NETWORK *Snp;
EFI_SIMPLE_NETWORK_MODE *SnpMode;
UINT32 FilterSetting;
UINT32 OldFilterSetting;
UINT32 McastFilterCnt;
EFI_MAC_ADDRESS *McastFilter;
BOOLEAN ResetMCastFilters;
LIST_ENTRY *Entry;
UINTN Index;
EFI_STATUS Status;
ASSERT (MnpDeviceData->Signature == MNP_DEVICE_DATA_SIGNATURE);
Snp = MnpDeviceData->Snp;
SnpMode = Snp->Mode;
ResetMCastFilters = TRUE;
//
// Build the filter setting:
// bit 0 = unicast
// bit 1 = multicast (if MC filter count > hw limit, use promiscuous)
// bit 2 = promiscuous
//
FilterSetting = (MnpDeviceData->UnicastFilterCnt != 0) ? 1u : 0u;
if (MnpDeviceData->PromiscuousFilterCnt != 0) {
FilterSetting |= 4u;
}
if (MnpDeviceData->MulticastFilterCnt != 0) {
UINT32 MaxMcast = SnpMode->MaxMCastFilterCount;
if (MnpDeviceData->GroupMCastFilterCnt != 0) {
//
// Check if we can use perfect filtering.
//
if (MnpDeviceData->MCastFilterCnt <= MaxMcast) {
FilterSetting |= 2u; /* perfect filter */
ResetMCastFilters = FALSE;
McastFilterCnt = MnpDeviceData->MCastFilterCnt;
McastFilter = AllocatePool (sizeof (EFI_MAC_ADDRESS) *
McastFilterCnt);
if (McastFilter == NULL) {
DEBUG ((DEBUG_ERROR,
"MnpConfigReceiveFilters: Failed to allocate memory "
"resource for MCastFilter.\n"));
return EFI_OUT_OF_RESOURCES;
}
//
// Copy multicast addresses from the device list.
//
Index = 0;
for (Entry = MnpDeviceData->GroupAddressList.Flink;
Entry != &MnpDeviceData->GroupAddressList;
Entry = Entry->Flink) {
MNP_GROUP_ADDRESS *Group;
Group = MNP_GROUP_ADDRESS_FROM_LINK (Entry);
CopyMem (&McastFilter[Index], &Group->Address,
sizeof (EFI_MAC_ADDRESS));
Index++;
ASSERT (Index <= McastFilterCnt);
}
} else {
if (SnpMode->MediaType & MEDIA_TYPE_MULTICAST_PROMISCUOUS) {
FilterSetting |= 0x10u;
} else {
FilterSetting |= 8u; /* disable multicast entirely */
}
}
} else {
FilterSetting |= 8u; /* no group addresses to receive */
}
}
if (MnpDeviceData->PromiscuousFilterCnt == 0) {
FilterSetting |= 8u; /* disable promiscuous */
}
//
// Call SNP to set the receive filters.
//
Status = Snp->ReceiveFilters (
Snp,
FilterSetting,
FilterSetting ^ (SnpMode->ReceiveFilterSetting &
~EFI_SIMPLE_NETWORK_RECEIVE_UNICAST &
~EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST &
~EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS &
~EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST),
ResetMCastFilters,
McastFilterCnt,
McastFilter
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR,
"MnpConfigReceiveFilters: Snp->ReceiveFilters failed, %r.\n",
Status));
}
if (McastFilter != NULL) {
FreePool (McastFilter);
}
return Status;
}
/* ====================================================================== */
/* MnpGroupOp */
/* ====================================================================== */
/**
* Add or remove a multicast group address for an instance.
*
* @param[in] Instance The MNP instance.
* @param[in] Flag TRUE = add, FALSE = remove.
* @param[in] MacAddress MAC address to add/remove (NULL = flush all).
*
* @retval EFI_SUCCESS Operation completed.
* @retval EFI_ALREADY_STARTED Group already exists (on add).
* @retval EFI_NOT_FOUND Group not found (on remove).
*/
EFI_STATUS
MnpGroupOp (
IN MNP_INSTANCE_DATA *Instance,
IN BOOLEAN Flag,
IN EFI_MAC_ADDRESS *MacAddress OPTIONAL
)
{
MNP_DEVICE_DATA *Device;
MNP_GROUP_ADDRESS *Group;
LIST_ENTRY *Entry;
EFI_STATUS Status;
Device = Instance->MnpServiceData->MnpDeviceData;
if (MacAddress != NULL) {
//
// Search the device's group list.
//
for (Entry = Device->GroupAddressList.Flink;
Entry != &Device->GroupAddressList;
Entry = Entry->Flink) {
Group = MNP_GROUP_ADDRESS_FROM_LINK (Entry);
if (!CompareMem (MacAddress, &Group->Address,
Device->Snp->Mode->HwAddressSize)) {
//
// Found matching address.
//
if (Flag) {
// Already present.
return EFI_ALREADY_STARTED;
}
//
// Remove group.
//
RemoveEntryList (&Group->InstanceLink);
if (IsListEmpty (&Group->InstanceLinkList)) {
//
// No more instances reference this group; remove from device.
//
RemoveEntryList (&Group->Link);
Device->GroupMCastFilterCnt--;
}
FreePool (Group);
Status = MnpConfigReceiveFilters (Device);
if (EFI_ERROR (Status)) {
return Status;
}
return EFI_SUCCESS;
}
}
if (!Flag) {
return EFI_NOT_FOUND;
}
//
// Add new group.
//
Group = AllocateZeroPool (sizeof (MNP_GROUP_ADDRESS));
if (Group == NULL) {
return EFI_OUT_OF_RESOURCES;
}
CopyMem (&Group->Address, MacAddress, Device->Snp->Mode->HwAddressSize);
InitializeListHead (&Group->InstanceLinkList);
InsertTailList (&Device->GroupAddressList, &Group->Link);
InsertTailList (&Group->InstanceLinkList, &Group->InstanceLink);
Device->GroupMCastFilterCnt++;
Status = MnpConfigReceiveFilters (Device);
if (EFI_ERROR (Status)) {
//
// Rollback.
//
RemoveEntryList (&Group->Link);
Device->GroupMCastFilterCnt--;
FreePool (Group);
return Status;
}
return EFI_SUCCESS;
}
//
// MacAddress == NULL: flush all groups for this instance.
//
return MnpGroupOpFlush (Instance);
}
/* ====================================================================== */
/* MnpGroupOpFlush */
/* ====================================================================== */
/**
* Flush all group addresses for the given instance.
*
* @param[in] Instance The MNP instance.
*
* @retval EFI_SUCCESS All groups flushed.
*/
EFI_STATUS
MnpGroupOpFlush (
IN MNP_INSTANCE_DATA *Instance
)
{
MNP_DEVICE_DATA *Device;
Device = Instance->MnpServiceData->MnpDeviceData;
//
// Iterate all groups and remove those belonging to this instance.
//
LIST_ENTRY *Entry;
LIST_ENTRY *NextEntry;
for (Entry = Device->GroupAddressList.Flink;
Entry != &Device->GroupAddressList;
Entry = NextEntry) {
MNP_GROUP_ADDRESS *Group;
NextEntry = Entry->Flink;
Group = MNP_GROUP_ADDRESS_FROM_LINK (Entry);
RemoveEntryList (&Group->InstanceLink);
if (IsListEmpty (&Group->InstanceLinkList)) {
RemoveEntryList (&Group->Link);
Device->GroupMCastFilterCnt--;
FreePool (Group);
}
}
return MnpConfigReceiveFilters (Device);
}
/* ====================================================================== */
/* MnpReadVlanConfiguration */
/* ====================================================================== */
/**
* Read VLAN configuration from the UEFI variable "NetworkStackVar".
*
* @param[in] MnpDeviceData Device data.
* @param[out] NumberOfVlan Number of VLAN entries.
* @param[out] VlanVariable Buffer of VLAN entries (UINT16 per entry).
*
* @retval EFI_SUCCESS Read successfully.
* @retval EFI_NOT_FOUND Variable does not exist.
*/
EFI_STATUS
MnpReadVlanConfiguration (
IN MNP_DEVICE_DATA *MnpDeviceData,
OUT UINTN *NumberOfVlan,
OUT UINT16 **VlanVariable
)
{
CHAR16 VariableName[] = L"NetworkStackVar";
UINTN BufferSize;
UINT16 *Buffer;
EFI_STATUS Status;
BufferSize = 0;
Buffer = NULL;
Status = gRT->GetVariable (
VariableName,
&gMnpVlanConfigGuid,
NULL,
&BufferSize,
NULL
);
if (Status != EFI_BUFFER_TOO_SMALL) {
*NumberOfVlan = 0;
*VlanVariable = NULL;
return EFI_NOT_FOUND;
}
Buffer = AllocatePool (BufferSize);
if (Buffer == NULL) {
return EFI_OUT_OF_RESOURCES;
}
Status = gRT->GetVariable (
VariableName,
&gMnpVlanConfigGuid,
NULL,
&BufferSize,
Buffer
);
if (EFI_ERROR (Status)) {
FreePool (Buffer);
*NumberOfVlan = 0;
*VlanVariable = NULL;
return Status;
}
*NumberOfVlan = BufferSize / sizeof (UINT16);
*VlanVariable = Buffer;
return EFI_SUCCESS;
}