/**
* @file MnpMain.c
*
* @brief MNP Protocol Implementation
*
* This file implements the EFI_MANAGED_NETWORK_PROTOCOL interfaces:
* - GetModeData: Retrieve MNP and SNP mode data
* - Configure: Configure or reset the MNP instance
* - MulticastReceive: Join/leave multicast groups
* - Transmit: Transmit a packet via the MNP service
* - Receive: Receive a packet asynchronously
* - Cancel: Cancel a pending transmit/receive token
* - Poll: Poll for incoming packets
*
* Source: AmiNetworkPkg/UefiNetworkStack/Common/MnpDxe/MnpMain.c
*/
#include "MnpDxe.h"
//
// External globals (defined in ModuleEntry.c or MnpDriver.c)
//
extern EFI_BOOT_SERVICES *gBS;
extern EFI_HANDLE gImageHandle;
extern VOID *gDpcProtocol; /* DPC protocol interface */
/* ====================================================================== */
/* GetModeData */
/* ====================================================================== */
/**
* Retrieve the current MNP configuration data and/or underlying SNP mode
* data for this instance.
*
* @param[in] This Protocol instance pointer.
* @param[out] MnpConfigData Optional buffer to receive MNP config data.
* @param[out] SnpModeData Optional buffer to receive SNP mode data.
*
* @retval EFI_SUCCESS Data retrieved successfully.
* @retval EFI_NOT_STARTED Instance has not been configured.
* @retval EFI_INVALID_PARAMETER This is NULL.
*/
EFI_STATUS
EFIAPI
MnpGetModeData (
IN EFI_MANAGED_NETWORK_PROTOCOL *This,
OUT EFI_MNP_CONFIG_DATA *MnpConfigData OPTIONAL,
OUT EFI_SIMPLE_NETWORK_MODE *SnpModeData OPTIONAL
)
{
MNP_INSTANCE_DATA *Instance;
MNP_SERVICE_DATA *Service;
MNP_DEVICE_DATA *Device;
EFI_TPL OldTpl;
if (This == NULL) {
return EFI_INVALID_PARAMETER;
}
Instance = MNP_INSTANCE_DATA_FROM_THIS (This);
CR_CHECK (Instance, MNP_INSTANCE_DATA_SIGNATURE);
OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
Service = Instance->MnpServiceData;
Device = Service->MnpDeviceData;
if (MnpConfigData != NULL) {
CopyMem (MnpConfigData, &Instance->ConfigData, sizeof (EFI_MNP_CONFIG_DATA));
}
if (SnpModeData != NULL && Device->Snp != NULL) {
CopyMem (SnpModeData, Device->Snp->Mode, sizeof (EFI_SIMPLE_NETWORK_MODE));
}
gBS->RestoreTPL (OldTpl);
return Instance->IsConfigured ? EFI_SUCCESS : EFI_NOT_STARTED;
}
/* ====================================================================== */
/* Configure */
/* ====================================================================== */
/**
* Configure or reset the MNP instance.
*
* When MnpConfigData is NULL, the instance is reset (stopped).
* When MnpConfigData is valid, the instance is configured with the provided
* settings.
*
* @param[in] This Protocol instance pointer.
* @param[in] MnpConfigData Configuration data, or NULL to reset.
*
* @retval EFI_SUCCESS Configured/reset successfully.
* @retval EFI_INVALID_PARAMETER This is NULL, or config data is invalid.
* @retval EFI_ALREADY_STARTED Instance is already configured.
*/
EFI_STATUS
EFIAPI
MnpConfigure (
IN EFI_MANAGED_NETWORK_PROTOCOL *This,
IN EFI_MNP_CONFIG_DATA *MnpConfigData OPTIONAL
)
{
MNP_INSTANCE_DATA *Instance;
EFI_STATUS Status;
EFI_TPL OldTpl;
if (This == NULL) {
return EFI_INVALID_PARAMETER;
}
if (MnpConfigData != NULL &&
MnpConfigData->ProtocolType > 0 &&
MnpConfigData->ProtocolType <= 1500) {
return EFI_INVALID_PARAMETER;
}
Instance = MNP_INSTANCE_DATA_FROM_THIS (This);
OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
if (MnpConfigData != NULL || Instance->IsConfigured) {
Status = MnpConfigureInstance (Instance, MnpConfigData);
} else {
Status = EFI_SUCCESS;
}
gBS->RestoreTPL (OldTpl);
return Status;
}
/* ====================================================================== */
/* MulticastReceive (protocol entry point) */
/* ====================================================================== */
/**
* Build the multicast MAC address from the IP address into the provided
* buffer. This wraps SNP's MCastIPtoMAC or handles it locally.
*
* @param[in] This Protocol instance pointer.
* @param[in] Flag TRUE for join, FALSE for leave.
* @param[in] MacAddress IP address to translate.
* @param[out] buf Buffer for the resulting MAC address.
*
* @retval EFI_SUCCESS MAC address resolved.
* @retval EFI_INVALID_PARAMETER Invalid parameters.
*/
EFI_STATUS
EFIAPI
MnpMulticastReceive (
IN EFI_MANAGED_NETWORK_PROTOCOL *This,
IN BOOLEAN Flag,
IN EFI_MAC_ADDRESS *MacAddress,
OUT EFI_MAC_ADDRESS *buf
)
{
MNP_INSTANCE_DATA *Instance;
MNP_DEVICE_DATA *Device;
EFI_SIMPLE_NETWORK *Snp;
EFI_STATUS Status;
EFI_TPL OldTpl;
UINT8 *SnpModeData;
Status = EFI_SUCCESS;
if (This == NULL || MacAddress == NULL || buf == NULL) {
return EFI_INVALID_PARAMETER;
}
if (!MnpIsValidMulticastAddress (Flag, MacAddress)) {
return EFI_INVALID_PARAMETER;
}
Instance = MNP_INSTANCE_DATA_FROM_THIS (This);
OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
if (!Instance->IsConfigured) {
Status = EFI_NOT_STARTED;
goto Exit;
}
Device = Instance->MnpServiceData->MnpDeviceData;
Snp = Device->Snp;
if (Snp == NULL) {
Status = EFI_NOT_STARTED;
goto Exit;
}
//
// Clear the buffer and build the MAC.
//
ZeroMem (buf, sizeof (EFI_MAC_ADDRESS));
SnpModeData = Snp->Mode;
if (SnpModeData[648] == 1) {
//
// IP4: build ETH multicast MAC.
//
if (Flag) {
*(UINT16 *)buf = 0x3333;
buf->Addr[2] = MacAddress->Addr[12];
buf->Addr[3] = MacAddress->Addr[13];
buf->Addr[4] = MacAddress->Addr[14];
buf->Addr[5] = MacAddress->Addr[15];
} else {
*(UINT16 *)buf = 0x0100;
buf->Addr[2] = 0x5E;
buf->Addr[3] = MacAddress->Addr[1] & 0x7F;
buf->Addr[4] = MacAddress->Addr[2];
buf->Addr[5] = MacAddress->Addr[3];
}
} else {
Status = Snp->MCastIPtoMAC (Snp, Flag, MacAddress, buf);
}
Exit:
gBS->RestoreTPL (OldTpl);
return Status;
}
/* ====================================================================== */
/* Transmit */
/* ====================================================================== */
/**
* Transmit a packet via the MNP service.
*
* @param[in] This Protocol instance pointer.
* @param[in] Token Completion token containing TxData.
*
* @retval EFI_SUCCESS Token queued for transmission.
* @retval EFI_INVALID_PARAMETER This or Token is NULL.
* @retval EFI_NOT_STARTED Instance is not configured.
*/
EFI_STATUS
EFIAPI
MnpTransmit (
IN EFI_MANAGED_NETWORK_PROTOCOL *This,
IN EFI_MANAGED_NETWORK_COMPLETION_TOKEN *Token
)
{
MNP_INSTANCE_DATA *Instance;
MNP_SERVICE_DATA *Service;
EFI_STATUS Status;
EFI_TPL OldTpl;
if (This == NULL || Token == NULL) {
return EFI_INVALID_PARAMETER;
}
Instance = MNP_INSTANCE_DATA_FROM_THIS (This);
OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
if (!Instance->IsConfigured) {
Status = EFI_NOT_STARTED;
goto Exit;
}
if (!MnpIsValidTxToken (Instance, Token)) {
Status = EFI_INVALID_PARAMETER;
goto Exit;
}
Service = Instance->MnpServiceData;
if (Service == NULL || Service->Signature != MNP_SERVICE_DATA_SIGNATURE) {
DEBUG ((DEBUG_ERROR, "Invalid MnpServiceData in MnpTransmit\n"));
Status = EFI_INVALID_PARAMETER;
goto Exit;
}
//
// Serialize fragment table and send synchronously.
//
Status = MnpSerializeFragment (
Service,
Token->Packet.TxData,
&Token->Packet.TxData->FragmentTable[0],
&Token->Packet.TxData->FragmentCount
);
if (Status == EFI_SUCCESS) {
Status = MnpSyncSendPacket (
Service,
Token->Packet.TxData->FragmentTable,
Token->Packet.TxData->FragmentCount,
Token
);
}
Exit:
gBS->RestoreTPL (OldTpl);
return Status;
}
/* ====================================================================== */
/* Receive */
/* ====================================================================== */
/**
* Receive a packet asynchronously. A token is queued and signalled when
* data arrives.
*
* @param[in] This Protocol instance pointer.
* @param[in] Token Completion token for the receive operation.
*
* @retval EFI_SUCCESS Token queued for receive.
* @retval EFI_INVALID_PARAMETER This, Token, or Token->Event is NULL.
* @retval EFI_NOT_STARTED Instance is not configured.
* @retval EFI_OUT_OF_RESOURCES No resources for receive map entry.
*/
EFI_STATUS
EFIAPI
MnpReceive (
IN EFI_MANAGED_NETWORK_PROTOCOL *This,
IN EFI_MANAGED_NETWORK_COMPLETION_TOKEN *Token
)
{
MNP_INSTANCE_DATA *Instance;
EFI_STATUS Status;
EFI_TPL OldTpl;
if (This == NULL || Token == NULL || Token->Event == NULL) {
return EFI_INVALID_PARAMETER;
}
Instance = MNP_INSTANCE_DATA_FROM_THIS (This);
OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
if (!Instance->IsConfigured) {
Status = EFI_NOT_STARTED;
goto Exit;
}
//
// Check if token is already registered.
//
Status = NetMapFindPosition (
&Instance->RcvMap,
(NET_MAP_ITEM_CALLBACK)MnpFindRxToken,
Token
);
if (Status == EFI_SUCCESS) {
//
// Token not found; allocate a map entry and queue it.
//
NET_MAP_ITEM *MapItem;
MapItem = NetMapAlloc (&Instance->RcvMap);
if (MapItem == NULL) {
Status = EFI_OUT_OF_RESOURCES;
goto Exit;
}
MapItem->Token = Token;
MapItem->UserData = NULL;
InsertTailList (&Instance->RcvMap.Free, &MapItem->Link);
Instance->RcvMap.ItemCount++;
//
// Try to deliver any pending packet immediately.
//
Status = MnpDeliverPacket (Instance);
((DPC_PROTOCOL *)gDpcProtocol)->QueueDpc (gDpcProtocol);
}
Exit:
gBS->RestoreTPL (OldTpl);
return Status;
}
/* ====================================================================== */
/* Cancel */
/* ====================================================================== */
/**
* Cancel a pending token.
*
* If Token is NULL, all pending tokens are cancelled.
*
* @param[in] This Protocol instance pointer.
* @param[in] Token Optional token to cancel.
*
* @retval EFI_SUCCESS Token(s) cancelled.
* @retval EFI_INVALID_PARAMETER This is NULL.
* @retval EFI_NOT_STARTED Instance is not configured.
*/
EFI_STATUS
EFIAPI
MnpCancel (
IN EFI_MANAGED_NETWORK_PROTOCOL *This,
IN EFI_MANAGED_NETWORK_COMPLETION_TOKEN *Token OPTIONAL
)
{
MNP_INSTANCE_DATA *Instance;
EFI_STATUS Status;
EFI_TPL OldTpl;
if (This == NULL) {
return EFI_INVALID_PARAMETER;
}
Instance = MNP_INSTANCE_DATA_FROM_THIS (This);
OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
if (!Instance->IsConfigured) {
Status = EFI_NOT_STARTED;
goto Exit;
}
Status = NetMapIterate (
&Instance->RcvMap,
(NET_MAP_ITEM_CALLBACK)MnpCancelRxToken,
Token
);
((DPC_PROTOCOL *)gDpcProtocol)->QueueDpc (gDpcProtocol);
Exit:
gBS->RestoreTPL (OldTpl);
return Status;
}
/* ====================================================================== */
/* Poll */
/* ====================================================================== */
/**
* Poll for incoming packets.
*
* @param[in] This Protocol instance pointer.
*
* @retval EFI_SUCCESS Poll completed.
* @retval EFI_INVALID_PARAMETER This is NULL.
* @retval EFI_NOT_STARTED Instance is not configured.
*/
EFI_STATUS
EFIAPI
MnpPoll (
IN EFI_MANAGED_NETWORK_PROTOCOL *This
)
{
MNP_INSTANCE_DATA *Instance;
MNP_DEVICE_DATA *Device;
EFI_STATUS Status;
EFI_TPL OldTpl;
if (This == NULL) {
return EFI_INVALID_PARAMETER;
}
Instance = MNP_INSTANCE_DATA_FROM_THIS (This);
OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
if (!Instance->IsConfigured) {
Status = EFI_NOT_STARTED;
goto Exit;
}
Device = Instance->MnpServiceData->MnpDeviceData;
Status = MnpReceivePacket (Device);
((DPC_PROTOCOL *)gDpcProtocol)->QueueDpc (gDpcProtocol);
Exit:
gBS->RestoreTPL (OldTpl);
return Status;
}