summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--MdeModulePkg/Include/Library/IpIoLib.h2
-rw-r--r--MdeModulePkg/Include/Library/NetLib.h75
-rw-r--r--MdeModulePkg/Library/DxeIpIoLib/DxeIpIoLib.inf19
-rw-r--r--MdeModulePkg/Library/DxeNetLib/DxeNetLib.inf27
-rw-r--r--MdeModulePkg/Library/DxeNetLib/NetDebug.c536
-rw-r--r--MdeModulePkg/Library/DxeUdpIoLib/DxeUdpIoLib.inf21
-rw-r--r--MdeModulePkg/MdeModulePkg.dsc4
-rw-r--r--MdeModulePkg/Universal/Network/MnpDxe/ComponentName.c162
-rw-r--r--MdeModulePkg/Universal/Network/MnpDxe/MnpConfig.c1466
-rw-r--r--MdeModulePkg/Universal/Network/MnpDxe/MnpDebug.h28
-rw-r--r--MdeModulePkg/Universal/Network/MnpDxe/MnpDriver.c560
-rw-r--r--MdeModulePkg/Universal/Network/MnpDxe/MnpDriver.h136
-rw-r--r--MdeModulePkg/Universal/Network/MnpDxe/MnpDxe.inf63
-rw-r--r--MdeModulePkg/Universal/Network/MnpDxe/MnpDxe.msa70
-rw-r--r--MdeModulePkg/Universal/Network/MnpDxe/MnpImpl.h274
-rw-r--r--MdeModulePkg/Universal/Network/MnpDxe/MnpIo.c1087
-rw-r--r--MdeModulePkg/Universal/Network/MnpDxe/MnpMain.c655
-rw-r--r--MdeModulePkg/Universal/Network/SnpDxe/ComponentName.c163
-rw-r--r--MdeModulePkg/Universal/Network/SnpDxe/SnpDxe.inf75
-rw-r--r--MdeModulePkg/Universal/Network/SnpDxe/SnpDxe.msa90
-rw-r--r--MdeModulePkg/Universal/Network/SnpDxe/WaitForPacket.c97
-rw-r--r--MdeModulePkg/Universal/Network/SnpDxe/callback.c611
-rw-r--r--MdeModulePkg/Universal/Network/SnpDxe/get_status.c195
-rw-r--r--MdeModulePkg/Universal/Network/SnpDxe/initialize.c245
-rw-r--r--MdeModulePkg/Universal/Network/SnpDxe/mcast_ip_to_mac.c165
-rw-r--r--MdeModulePkg/Universal/Network/SnpDxe/nvdata.c185
-rw-r--r--MdeModulePkg/Universal/Network/SnpDxe/receive.c255
-rw-r--r--MdeModulePkg/Universal/Network/SnpDxe/receive_filters.c406
-rw-r--r--MdeModulePkg/Universal/Network/SnpDxe/reset.c128
-rw-r--r--MdeModulePkg/Universal/Network/SnpDxe/shutdown.c148
-rw-r--r--MdeModulePkg/Universal/Network/SnpDxe/snp.c1269
-rw-r--r--MdeModulePkg/Universal/Network/SnpDxe/snp.h454
-rw-r--r--MdeModulePkg/Universal/Network/SnpDxe/start.c191
-rw-r--r--MdeModulePkg/Universal/Network/SnpDxe/station_address.c237
-rw-r--r--MdeModulePkg/Universal/Network/SnpDxe/statistics.c201
-rw-r--r--MdeModulePkg/Universal/Network/SnpDxe/stop.c118
-rw-r--r--MdeModulePkg/Universal/Network/SnpDxe/transmit.c399
-rw-r--r--MdeModulePkg/Universal/Network/Tcp4Dxe/ComponentName.c169
-rw-r--r--MdeModulePkg/Universal/Network/Tcp4Dxe/SockImpl.c1269
-rw-r--r--MdeModulePkg/Universal/Network/Tcp4Dxe/SockImpl.h84
-rw-r--r--MdeModulePkg/Universal/Network/Tcp4Dxe/SockInterface.c980
-rw-r--r--MdeModulePkg/Universal/Network/Tcp4Dxe/Socket.h518
-rw-r--r--MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Dispatcher.c680
-rw-r--r--MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Driver.c669
-rw-r--r--MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Driver.h141
-rw-r--r--MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Dxe.inf77
-rw-r--r--MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Dxe.msa90
-rw-r--r--MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Func.h353
-rw-r--r--MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Input.c1486
-rw-r--r--MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Io.c117
-rw-r--r--MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Main.c564
-rw-r--r--MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Main.h176
-rw-r--r--MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Misc.c1091
-rw-r--r--MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Option.c380
-rw-r--r--MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Option.h107
-rw-r--r--MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Output.c1218
-rw-r--r--MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Proto.h355
-rw-r--r--MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Timer.c582
-rw-r--r--MdeModulePkg/Universal/Network/Udp4Dxe/ComponentName.c169
-rw-r--r--MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Driver.c526
-rw-r--r--MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Driver.h70
-rw-r--r--MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Dxe.inf66
-rw-r--r--MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Dxe.msa77
-rw-r--r--MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Impl.c1951
-rw-r--r--MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Impl.h299
-rw-r--r--MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Main.c869
66 files changed, 25898 insertions, 52 deletions
diff --git a/MdeModulePkg/Include/Library/IpIoLib.h b/MdeModulePkg/Include/Library/IpIoLib.h
index 15cfbf8f43..894e07bed4 100644
--- a/MdeModulePkg/Include/Library/IpIoLib.h
+++ b/MdeModulePkg/Include/Library/IpIoLib.h
@@ -171,7 +171,7 @@ typedef struct _IP_IO_SEND_ENTRY {
EFI_IP4_COMPLETION_TOKEN *SndToken;
} IP_IO_SEND_ENTRY;
-typedef struct _EFI_IP4_OVERRIDE_DATA IP_IO_OVERRIDE;
+typedef EFI_IP4_OVERRIDE_DATA IP_IO_OVERRIDE;
typedef struct _IP_IO_IP_INFO {
IP4_ADDR Addr;
diff --git a/MdeModulePkg/Include/Library/NetLib.h b/MdeModulePkg/Include/Library/NetLib.h
index 215a2fb094..6bc3431855 100644
--- a/MdeModulePkg/Include/Library/NetLib.h
+++ b/MdeModulePkg/Include/Library/NetLib.h
@@ -809,4 +809,79 @@ NetPseudoHeadChecksum (
IN UINT8 Proto,
IN UINT16 Len
);
+
+//
+// The debug level definition. This value is also used as the
+// syslog's servity level. Don't change it.
+//
+enum {
+ NETDEBUG_LEVEL_TRACE = 5,
+ NETDEBUG_LEVEL_WARNING = 4,
+ NETDEBUG_LEVEL_ERROR = 3,
+};
+
+#ifdef EFI_NETWORK_STACK_DEBUG
+
+//
+// The debug output expects the ASCII format string, Use %a to print ASCII
+// string, and %s to print UNICODE string. PrintArg must be enclosed in ().
+// For example: NET_DEBUG_TRACE ("Tcp", ("State transit to %a\n", Name));
+//
+#define NET_DEBUG_TRACE(Module, PrintArg) \
+ NetDebugOutput ( \
+ NETDEBUG_LEVEL_TRACE, \
+ Module, \
+ __FILE__, \
+ __LINE__, \
+ NetDebugASPrint PrintArg \
+ )
+
+#define NET_DEBUG_WARNING(Module, PrintArg) \
+ NetDebugOutput ( \
+ NETDEBUG_LEVEL_WARNING, \
+ Module, \
+ __FILE__, \
+ __LINE__, \
+ NetDebugASPrint PrintArg \
+ )
+
+#define NET_DEBUG_ERROR(Module, PrintArg) \
+ NetDebugOutput ( \
+ NETDEBUG_LEVEL_ERROR, \
+ Module, \
+ __FILE__, \
+ __LINE__, \
+ NetDebugASPrint PrintArg \
+ )
+
+#else
+#define NET_DEBUG_TRACE(Module, PrintString)
+#define NET_DEBUG_WARNING(Module, PrintString)
+#define NET_DEBUG_ERROR(Module, PrintString)
+#endif
+
+UINT8 *
+NetDebugASPrint (
+ UINT8 *Format,
+ ...
+ );
+
+EFI_STATUS
+NetDebugOutput (
+ UINT32 Level,
+ UINT8 *Module,
+ UINT8 *File,
+ UINT32 Line,
+ UINT8 *Message
+ );
+
+//
+// Network debug message is sent out as syslog.
+//
+enum {
+ NET_SYSLOG_FACILITY = 16, // Syslog local facility local use
+ NET_SYSLOG_PACKET_LEN = 512,
+ NET_DEBUG_MSG_LEN = 470, // 512 - (ether+ip+udp head length)
+ NET_SYSLOG_TX_TIMEOUT = 500 *1000 *10, // 500ms
+};
#endif
diff --git a/MdeModulePkg/Library/DxeIpIoLib/DxeIpIoLib.inf b/MdeModulePkg/Library/DxeIpIoLib/DxeIpIoLib.inf
index 9430d8abba..f7aa52a0bf 100644
--- a/MdeModulePkg/Library/DxeIpIoLib/DxeIpIoLib.inf
+++ b/MdeModulePkg/Library/DxeIpIoLib/DxeIpIoLib.inf
@@ -41,25 +41,12 @@
[LibraryClasses]
- MemoryAllocationLib
- UefiLib
+ IpIoLib
BaseLib
- UefiBootServicesTableLib
- UefiRuntimeServicesTableLib
- BaseMemoryLib
DebugLib
- PrintLib
-
+ UefiBootServicesTableLib
+ MemoryAllocationLib
[Protocols]
gEfiIp4ProtocolGuid # PROTOCOL ALWAYS_CONSUMED
- gEfiDriverDiagnosticsProtocolGuid # PROTOCOL ALWAYS_CONSUMED
- gEfiUdp4ServiceBindingProtocolGuid # PROTOCOL ALWAYS_CONSUMED
- gEfiLoadedImageProtocolGuid # PROTOCOL ALWAYS_CONSUMED
gEfiIp4ServiceBindingProtocolGuid # PROTOCOL ALWAYS_CONSUMED
- gEfiDriverConfigurationProtocolGuid # PROTOCOL ALWAYS_CONSUMED
- gEfiSimpleNetworkProtocolGuid # PROTOCOL ALWAYS_CONSUMED
- gEfiDriverBindingProtocolGuid # PROTOCOL ALWAYS_CONSUMED
- gEfiUdp4ProtocolGuid # PROTOCOL ALWAYS_CONSUMED
- gEfiComponentNameProtocolGuid # PROTOCOL ALWAYS_CONSUMED
-
diff --git a/MdeModulePkg/Library/DxeNetLib/DxeNetLib.inf b/MdeModulePkg/Library/DxeNetLib/DxeNetLib.inf
index be9ff1b379..3ae0a04ee4 100644
--- a/MdeModulePkg/Library/DxeNetLib/DxeNetLib.inf
+++ b/MdeModulePkg/Library/DxeNetLib/DxeNetLib.inf
@@ -35,6 +35,7 @@
[Sources.common]
DxeNetLib.c
NetBuffer.c
+ NetDebug.c
[Packages]
MdePkg/MdePkg.dec
@@ -42,25 +43,15 @@
[LibraryClasses]
- MemoryAllocationLib
- UefiLib
- BaseLib
- UefiBootServicesTableLib
- UefiRuntimeServicesTableLib
- BaseMemoryLib
- DebugLib
- PrintLib
-
+ NetLib
+ BaseLib
+ DebugLib
+ BaseMemoryLib
+ UefiBootServicesTableLib
+ UefiRuntimeServicesTableLib
+ UefiLib
+ MemoryAllocationLib
[Protocols]
- gEfiIp4ProtocolGuid # PROTOCOL ALWAYS_CONSUMED
- gEfiDriverDiagnosticsProtocolGuid # PROTOCOL ALWAYS_CONSUMED
- gEfiUdp4ServiceBindingProtocolGuid # PROTOCOL ALWAYS_CONSUMED
- gEfiLoadedImageProtocolGuid # PROTOCOL ALWAYS_CONSUMED
- gEfiIp4ServiceBindingProtocolGuid # PROTOCOL ALWAYS_CONSUMED
- gEfiDriverConfigurationProtocolGuid # PROTOCOL ALWAYS_CONSUMED
gEfiSimpleNetworkProtocolGuid # PROTOCOL ALWAYS_CONSUMED
- gEfiDriverBindingProtocolGuid # PROTOCOL ALWAYS_CONSUMED
- gEfiUdp4ProtocolGuid # PROTOCOL ALWAYS_CONSUMED
- gEfiComponentNameProtocolGuid # PROTOCOL ALWAYS_CONSUMED
diff --git a/MdeModulePkg/Library/DxeNetLib/NetDebug.c b/MdeModulePkg/Library/DxeNetLib/NetDebug.c
new file mode 100644
index 0000000000..afcc1b3b42
--- /dev/null
+++ b/MdeModulePkg/Library/DxeNetLib/NetDebug.c
@@ -0,0 +1,536 @@
+/** @file
+
+Copyright (c) 2005 - 2006, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+
+ NetDebug.c
+
+Abstract:
+
+ Network debug facility. The debug information is wrapped in
+ SYSLOG packets, then sent over SNP. This debug facility can't
+ be used by SNP. Apply caution when used in MNP and non-network
+ module because SNP is most likely not "thread safe". We assume
+ that the SNP supports the EHTERNET.
+
+
+**/
+
+
+#include <PiDxe.h>
+
+#include <Protocol/SimpleNetwork.h>
+
+#include <Library/BaseLib.h>
+#include <Library/NetLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiRuntimeServicesTableLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/PrintLib.h>
+
+
+//
+// Any error level digitally larger than mNetDebugLevelMax
+// will be silently discarded.
+//
+UINTN mNetDebugLevelMax = NETDEBUG_LEVEL_ERROR;
+UINT32 mSyslogPacketSeq = 0xDEADBEEF;
+
+//
+// You can change mSyslogDstMac mSyslogDstIp and mSyslogSrcIp
+// here to direct the syslog packets to the syslog deamon. The
+// default is broadcast to both the ethernet and IP.
+//
+UINT8 mSyslogDstMac [NET_ETHER_ADDR_LEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+UINT32 mSyslogDstIp = 0xffffffff;
+UINT32 mSyslogSrcIp = 0;
+
+UINT8 *
+MonthName[] = {
+ "Jan",
+ "Feb",
+ "Mar",
+ "Apr",
+ "May",
+ "Jun",
+ "Jul",
+ "Aug",
+ "Sep",
+ "Oct",
+ "Nov",
+ "Dec"
+};
+
+
+/**
+ Locate the handles that support SNP, then open one of them
+ to send the syslog packets. The caller isn't required to close
+ the SNP after use because the SNP is opened by HandleProtocol.
+
+ None
+
+ @return The point to SNP if one is properly openned. Otherwise NULL
+
+**/
+EFI_SIMPLE_NETWORK_PROTOCOL *
+SyslogLocateSnp (
+ VOID
+ )
+{
+ EFI_SIMPLE_NETWORK_PROTOCOL *Snp;
+ EFI_STATUS Status;
+ EFI_HANDLE *Handles;
+ UINTN HandleCount;
+ UINTN Index;
+
+ //
+ // Locate the handles which has SNP installed.
+ //
+ Handles = NULL;
+ Status = gBS->LocateHandleBuffer (
+ ByProtocol,
+ &gEfiSimpleNetworkProtocolGuid,
+ NULL,
+ &HandleCount,
+ &Handles
+ );
+
+ if (EFI_ERROR (Status) || (HandleCount == 0)) {
+ return NULL;
+ }
+
+ //
+ // Try to open one of the ethernet SNP protocol to send packet
+ //
+ Snp = NULL;
+
+ for (Index = 0; Index < HandleCount; Index++) {
+ Status = gBS->HandleProtocol (
+ Handles[Index],
+ &gEfiSimpleNetworkProtocolGuid,
+ (VOID **) &Snp
+ );
+
+ if ((Status == EFI_SUCCESS) && (Snp != NULL) &&
+ (Snp->Mode->IfType == NET_IFTYPE_ETHERNET) &&
+ (Snp->Mode->MaxPacketSize >= NET_SYSLOG_PACKET_LEN)) {
+
+ break;
+ }
+
+ Snp = NULL;
+ }
+
+ gBS->FreePool (Handles);
+ return Snp;
+}
+
+
+/**
+ Transmit a syslog packet synchronously through SNP. The Packet
+ already has the ethernet header prepended. This function should
+ fill in the source MAC because it will try to locate a SNP each
+ time it is called to avoid the problem if SNP is unloaded.
+ This code snip is copied from MNP.
+
+ @param Packet The Syslog packet
+ @param Length The length of the packet
+
+ @retval EFI_DEVICE_ERROR Failed to locate a usable SNP protocol
+ @retval EFI_TIMEOUT Timeout happened to send the packet.
+ @retval EFI_SUCCESS Packet is sent.
+
+**/
+EFI_STATUS
+SyslogSendPacket (
+ IN UINT8 *Packet,
+ IN UINT32 Length
+ )
+{
+ EFI_SIMPLE_NETWORK_PROTOCOL *Snp;
+ ETHER_HEAD *Ether;
+ EFI_STATUS Status;
+ EFI_EVENT TimeoutEvent;
+ UINT8 *TxBuf;
+
+ Snp = SyslogLocateSnp ();
+
+ if (Snp == NULL) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ Ether = (ETHER_HEAD *) Packet;
+ CopyMem (Ether->SrcMac, Snp->Mode->CurrentAddress.Addr, NET_ETHER_ADDR_LEN);
+
+ //
+ // Start the timeout event.
+ //
+ Status = gBS->CreateEvent (
+ EVT_TIMER,
+ TPL_NOTIFY,
+ NULL,
+ NULL,
+ &TimeoutEvent
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = gBS->SetTimer (TimeoutEvent, TimerRelative, NET_SYSLOG_TX_TIMEOUT);
+
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ for (;;) {
+ //
+ // Transmit the packet through SNP.
+ //
+ Status = Snp->Transmit (Snp, 0, Length, Packet, NULL, NULL, NULL);
+
+ if ((Status != EFI_SUCCESS) && (Status != EFI_NOT_READY)) {
+ Status = EFI_DEVICE_ERROR;
+ break;
+ }
+
+ //
+ // If Status is EFI_SUCCESS, the packet is put in the transmit queue.
+ // if Status is EFI_NOT_READY, the transmit engine of the network
+ // interface is busy. Both need to sync SNP.
+ //
+ TxBuf = NULL;
+
+ do {
+ //
+ // Get the recycled transmit buffer status.
+ //
+ Snp->GetStatus (Snp, NULL, &TxBuf);
+
+ if (!EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) {
+ Status = EFI_TIMEOUT;
+ break;
+ }
+
+ } while (TxBuf == NULL);
+
+ if ((Status == EFI_SUCCESS) || (Status == EFI_TIMEOUT)) {
+ break;
+ }
+
+ //
+ // Status is EFI_NOT_READY. Restart the timer event and
+ // call Snp->Transmit again.
+ //
+ gBS->SetTimer (TimeoutEvent, TimerRelative, NET_SYSLOG_TX_TIMEOUT);
+ }
+
+ gBS->SetTimer (TimeoutEvent, TimerCancel, 0);
+
+ON_EXIT:
+ gBS->CloseEvent (TimeoutEvent);
+ return Status;
+}
+
+
+/**
+ Compute checksum for a bulk of data. This code is copied from the
+ Netbuffer library.
+
+ @param Bulk Pointer to the data.
+ @param Len Length of the data, in bytes.
+
+ @retval UINT16 The computed checksum.
+
+**/
+UINT16
+SyslogChecksum (
+ IN UINT8 *Bulk,
+ IN UINT32 Len
+ )
+{
+ register UINT32 Sum;
+
+ Sum = 0;
+
+ while (Len > 1) {
+ Sum += *(UINT16 *) Bulk;
+ Bulk += 2;
+ Len -= 2;
+ }
+
+ //
+ // Add left-over byte, if any
+ //
+ if (Len > 0) {
+ Sum += *(UINT8 *) Bulk;
+ }
+
+ //
+ // Fold 32-bit sum to 16 bits
+ //
+ while (Sum >> 16) {
+ Sum = (Sum & 0xffff) + (Sum >> 16);
+ }
+
+ return (UINT16) ~Sum;
+}
+
+
+/**
+ Build a syslog packet, including the Ethernet/Ip/Udp headers
+ and user's message.
+
+ @param Buf The buffer to put the packet data
+ @param BufLen The lenght of the Buf
+ @param Level Syslog servity level
+ @param Module The module that generates the log
+ @param File The file that contains the current log
+ @param Line The line of code in the File that contains the
+ current log
+ @param Message The log message
+
+ @return The length of the syslog packet built.
+
+**/
+UINT32
+SyslogBuildPacket (
+ UINT8 *Buf,
+ UINT32 BufLen,
+ UINT32 Level,
+ UINT8 *Module,
+ UINT8 *File,
+ UINT32 Line,
+ UINT8 *Message
+ )
+{
+ ETHER_HEAD *Ether;
+ IP4_HEAD *Ip4;
+ EFI_UDP4_HEADER *Udp4;
+ EFI_TIME Time;
+ UINT32 Pri;
+ UINT32 Len;
+
+ //
+ // Fill in the Ethernet header. Leave alone the source MAC.
+ // SyslogSendPacket will fill in the address for us.
+ //
+ Ether = (ETHER_HEAD *) Buf;
+ CopyMem (Ether->DstMac, mSyslogDstMac, NET_ETHER_ADDR_LEN);
+ ZeroMem (Ether->SrcMac, NET_ETHER_ADDR_LEN);
+
+ Ether->EtherType = HTONS (0x0800); // IP protocol
+
+ Buf += sizeof (ETHER_HEAD);
+ BufLen -= sizeof (ETHER_HEAD);
+
+ //
+ // Fill in the IP header
+ //
+ Ip4 = (IP4_HEAD *) Buf;
+ Ip4->HeadLen = 5;
+ Ip4->Ver = 4;
+ Ip4->Tos = 0;
+ Ip4->TotalLen = 0;
+ Ip4->Id = (UINT16) mSyslogPacketSeq;
+ Ip4->Fragment = 0;
+ Ip4->Ttl = 16;
+ Ip4->Protocol = 0x11;
+ Ip4->Checksum = 0;
+ Ip4->Src = mSyslogSrcIp;
+ Ip4->Dst = mSyslogDstIp;
+
+ Buf += sizeof (IP4_HEAD);
+ BufLen -= sizeof (IP4_HEAD);
+
+ //
+ // Fill in the UDP header, Udp checksum is optional. Leave it zero.
+ //
+ Udp4 = (EFI_UDP4_HEADER*) Buf;
+ Udp4->SrcPort = HTONS (514);
+ Udp4->DstPort = HTONS (514);
+ Udp4->Length = 0;
+ Udp4->Checksum = 0;
+
+ Buf += sizeof (EFI_UDP4_HEADER);
+ BufLen -= sizeof (EFI_UDP4_HEADER);
+
+ //
+ // Build the syslog message body with <PRI> Timestamp machine module Message
+ //
+ Pri = ((NET_SYSLOG_FACILITY & 31) << 3) | (Level & 7);
+ gRT->GetTime (&Time, NULL);
+
+ //
+ // Use %a to format the ASCII strings, %s to format UNICODE strings
+ //
+ Len = 0;
+ Len += (UINT32) AsciiSPrint (
+ Buf,
+ BufLen,
+ "<%d> %a %d %d:%d:%d ",
+ Pri,
+ MonthName [Time.Month-1],
+ Time.Day,
+ Time.Hour,
+ Time.Minute,
+ Time.Second
+ );
+ Len--;
+
+ Len += (UINT32) AsciiSPrint (
+ Buf + Len,
+ BufLen - Len,
+ "Tiano %a: %a (Line: %d File: %a)",
+ Module,
+ Message,
+ Line,
+ File
+ );
+ Len--;
+
+ //
+ // OK, patch the IP length/checksum and UDP length fields.
+ //
+ Len += sizeof (EFI_UDP4_HEADER);
+ Udp4->Length = HTONS ((UINT16) Len);
+
+ Len += sizeof (IP4_HEAD);
+ Ip4->TotalLen = HTONS ((UINT16) Len);
+ Ip4->Checksum = SyslogChecksum ((UINT8 *) Ip4, sizeof (IP4_HEAD));
+
+ return Len + sizeof (ETHER_HEAD);
+}
+
+
+/**
+ Allocate a buffer, then format the message to it. This is a
+ help function for the NET_DEBUG_XXX macros. The PrintArg of
+ these macros treats the variable length print parameters as a
+ single parameter, and pass it to the NetDebugASPrint. For
+ example, NET_DEBUG_TRACE ("Tcp", ("State transit to %a\n", Name))
+ if extracted to:
+ NetDebugOutput (
+ NETDEBUG_LEVEL_TRACE,
+ "Tcp",
+ __FILE__,
+ __LINE__,
+ NetDebugASPrint ("State transit to %a\n", Name)
+ )
+ This is exactly what we want.
+
+ @param Format The ASCII format string.
+ @param ... The variable length parameter whose format is
+ determined by the Format string.
+
+ @return The buffer containing the formatted message, or NULL if failed to
+ @return allocate memory.
+
+**/
+UINT8 *
+NetDebugASPrint (
+ UINT8 *Format,
+ ...
+ )
+{
+ VA_LIST Marker;
+ UINT8 *Buf;
+
+ Buf = AllocatePool (NET_DEBUG_MSG_LEN);
+
+ if (Buf == NULL) {
+ return NULL;
+ }
+
+ VA_START (Marker, Format);
+ AsciiVSPrint (Buf, NET_DEBUG_MSG_LEN, Format, Marker);
+ VA_END (Marker);
+
+ return Buf;
+}
+
+
+/**
+ Output a debug message to syslog. This function will locate a
+ instance of SNP then send the message through it. Because it
+ isn't open the SNP BY_DRIVER, apply caution when using it.
+
+ @param Level The servity level of the message.
+ @param Module The Moudle that generates the log.
+ @param File The file that contains the log.
+ @param Line The exact line that contains the log.
+ @param Message The user message to log.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the packet
+ @retval EFI_SUCCESS The log is discard because that it is more verbose
+ than the mNetDebugLevelMax. Or, it has been sent
+ out.
+
+**/
+EFI_STATUS
+NetDebugOutput (
+ UINT32 Level,
+ UINT8 *Module,
+ UINT8 *File,
+ UINT32 Line,
+ UINT8 *Message
+ )
+{
+ UINT8 *Packet;
+ UINT32 Len;
+ EFI_STATUS Status;
+
+ //
+ // Check whether the message should be sent out
+ //
+ if (Message == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ if (Level > mNetDebugLevelMax) {
+ Status = EFI_SUCCESS;
+ goto ON_EXIT;
+ }
+
+ //
+ // Allocate a maxium of 1024 bytes, the caller should ensure
+ // that the message plus the ethernet/ip/udp header is shorter
+ // than this
+ //
+ Packet = AllocatePool (NET_SYSLOG_PACKET_LEN);
+
+ if (Packet == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+
+ //
+ // Build the message: Ethernet header + IP header + Udp Header + user data
+ //
+ Len = SyslogBuildPacket (
+ Packet,
+ NET_SYSLOG_PACKET_LEN,
+ Level,
+ Module,
+ File,
+ Line,
+ Message
+ );
+
+ mSyslogPacketSeq++;
+ Status = SyslogSendPacket (Packet, Len);
+ gBS->FreePool (Packet);
+
+ON_EXIT:
+ gBS->FreePool (Message);
+ return Status;
+}
diff --git a/MdeModulePkg/Library/DxeUdpIoLib/DxeUdpIoLib.inf b/MdeModulePkg/Library/DxeUdpIoLib/DxeUdpIoLib.inf
index 421eaa011a..3b286980f6 100644
--- a/MdeModulePkg/Library/DxeUdpIoLib/DxeUdpIoLib.inf
+++ b/MdeModulePkg/Library/DxeUdpIoLib/DxeUdpIoLib.inf
@@ -41,25 +41,12 @@
[LibraryClasses]
- MemoryAllocationLib
- UefiLib
+ UdpIoLib
BaseLib
- UefiBootServicesTableLib
- UefiRuntimeServicesTableLib
- BaseMemoryLib
DebugLib
- PrintLib
-
+ UefiBootServicesTableLib
+ MemoryAllocationLib
[Protocols]
- gEfiIp4ProtocolGuid # PROTOCOL ALWAYS_CONSUMED
- gEfiDriverDiagnosticsProtocolGuid # PROTOCOL ALWAYS_CONSUMED
gEfiUdp4ServiceBindingProtocolGuid # PROTOCOL ALWAYS_CONSUMED
- gEfiLoadedImageProtocolGuid # PROTOCOL ALWAYS_CONSUMED
- gEfiIp4ServiceBindingProtocolGuid # PROTOCOL ALWAYS_CONSUMED
- gEfiDriverConfigurationProtocolGuid # PROTOCOL ALWAYS_CONSUMED
- gEfiSimpleNetworkProtocolGuid # PROTOCOL ALWAYS_CONSUMED
- gEfiDriverBindingProtocolGuid # PROTOCOL ALWAYS_CONSUMED
- gEfiUdp4ProtocolGuid # PROTOCOL ALWAYS_CONSUMED
- gEfiComponentNameProtocolGuid # PROTOCOL ALWAYS_CONSUMED
-
+ gEfiUdp4ProtocolGuid # PROTOCOL ALWAYS_CONSUMED \ No newline at end of file
diff --git a/MdeModulePkg/MdeModulePkg.dsc b/MdeModulePkg/MdeModulePkg.dsc
index b461ecc56b..4947db5851 100644
--- a/MdeModulePkg/MdeModulePkg.dsc
+++ b/MdeModulePkg/MdeModulePkg.dsc
@@ -401,6 +401,10 @@
$(WORKSPACE)/MdeModulePkg/Library/DxeUdpIoLib/DxeUdpIoLib.inf
$(WORKSPACE)/MdeModulePkg/Universal/Network/Ip4ConfigDxe/Ip4ConfigDxe.inf
+ $(WORKSPACE)/MdeModulePkg/Universal/Network/MnpDxe/MnpDxe.inf
+ $(WORKSPACE)/MdeModulePkg/Universal/Network/SnpDxe/SnpDxe.inf
+ $(WORKSPACE)/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Dxe.inf
+ $(WORKSPACE)/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Dxe.inf
$(WORKSPACE)/MdeModulePkg/Application/HelloWorld/HelloWorld.inf
diff --git a/MdeModulePkg/Universal/Network/MnpDxe/ComponentName.c b/MdeModulePkg/Universal/Network/MnpDxe/ComponentName.c
new file mode 100644
index 0000000000..209861c775
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/MnpDxe/ComponentName.c
@@ -0,0 +1,162 @@
+/** @file
+
+Copyright (c) 2005 - 2007, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+
+ ComponentName.c
+
+Abstract:
+
+
+**/
+
+
+#include "MnpDriver.h"
+
+//
+// EFI Component Name Functions
+//
+EFI_STATUS
+EFIAPI
+MnpComponentNameGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ );
+
+EFI_STATUS
+EFIAPI
+MnpComponentNameGetControllerName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle OPTIONAL,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ );
+
+//
+// EFI Component Name Protocol
+//
+EFI_COMPONENT_NAME_PROTOCOL gMnpComponentName = {
+ MnpComponentNameGetDriverName,
+ MnpComponentNameGetControllerName,
+ "eng"
+};
+
+STATIC EFI_UNICODE_STRING_TABLE mMnpDriverNameTable[] = {
+ {
+ "eng",
+ L"MNP Network Service Driver"
+ },
+ {
+ NULL,
+ NULL
+ }
+};
+
+EFI_STATUS
+EFIAPI
+MnpComponentNameGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ )
+/*++
+
+ Routine Description:
+ Retrieves a Unicode string that is the user readable name of the EFI Driver.
+
+ Arguments:
+ This - A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance.
+ Language - A pointer to a three character ISO 639-2 language identifier.
+ This is the language of the driver name that that the caller
+ is requesting, and it must match one of the languages specified
+ in SupportedLanguages. The number of languages supported by a
+ driver is up to the driver writer.
+ DriverName - A pointer to the Unicode string to return. This Unicode string
+ is the name of the driver specified by This in the language
+ specified by Language.
+
+ Returns:
+ EFI_SUCCES - The Unicode string for the Driver specified by This
+ and the language specified by Language was returned
+ in DriverName.
+ EFI_INVALID_PARAMETER - Language is NULL.
+ EFI_INVALID_PARAMETER - DriverName is NULL.
+ EFI_UNSUPPORTED - The driver specified by This does not support the
+ language specified by Language.
+
+--*/
+{
+ return LookupUnicodeString (
+ Language,
+ gMnpComponentName.SupportedLanguages,
+ mMnpDriverNameTable,
+ DriverName
+ );
+}
+
+EFI_STATUS
+EFIAPI
+MnpComponentNameGetControllerName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle OPTIONAL,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ )
+/*++
+
+ Routine Description:
+ Retrieves a Unicode string that is the user readable name of the controller
+ that is being managed by an EFI Driver.
+
+ Arguments:
+ This - A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance.
+ ControllerHandle - The handle of a controller that the driver specified by
+ This is managing. This handle specifies the controller
+ whose name is to be returned.
+ ChildHandle - The handle of the child controller to retrieve the name
+ of. This is an optional parameter that may be NULL. It
+ will be NULL for device drivers. It will also be NULL
+ for a bus drivers that wish to retrieve the name of the
+ bus controller. It will not be NULL for a bus driver
+ that wishes to retrieve the name of a child controller.
+ Language - A pointer to a three character ISO 639-2 language
+ identifier. This is the language of the controller name
+ that that the caller is requesting, and it must match one
+ of the languages specified in SupportedLanguages. The
+ number of languages supported by a driver is up to the
+ driver writer.
+ ControllerName - A pointer to the Unicode string to return. This Unicode
+ string is the name of the controller specified by
+ ControllerHandle and ChildHandle in the language specified
+ by Language from the point of view of the driver specified
+ by This.
+
+ Returns:
+ EFI_SUCCESS - The Unicode string for the user readable name in the
+ language specified by Language for the driver
+ specified by This was returned in DriverName.
+ EFI_INVALID_PARAMETER - ControllerHandle is not a valid EFI_HANDLE.
+ EFI_INVALID_PARAMETER - ChildHandle is not NULL and it is not a valid EFI_HANDLE.
+ EFI_INVALID_PARAMETER - Language is NULL.
+ EFI_INVALID_PARAMETER - ControllerName is NULL.
+ EFI_UNSUPPORTED - The driver specified by This is not currently managing
+ the controller specified by ControllerHandle and
+ ChildHandle.
+ EFI_UNSUPPORTED - The driver specified by This does not support the
+ language specified by Language.
+
+--*/
+{
+ return EFI_UNSUPPORTED;
+}
diff --git a/MdeModulePkg/Universal/Network/MnpDxe/MnpConfig.c b/MdeModulePkg/Universal/Network/MnpDxe/MnpConfig.c
new file mode 100644
index 0000000000..c842991f30
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/MnpDxe/MnpConfig.c
@@ -0,0 +1,1466 @@
+/** @file
+
+Copyright (c) 2005 - 2007, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+
+ MnpConfig.c
+
+Abstract:
+
+ Implementation of Managed Network Protocol private services.
+
+
+**/
+
+
+#include "MnpImpl.h"
+
+EFI_SERVICE_BINDING_PROTOCOL mMnpServiceBindingProtocol = {
+ MnpServiceBindingCreateChild,
+ MnpServiceBindingDestroyChild
+};
+
+EFI_MANAGED_NETWORK_PROTOCOL mMnpProtocolTemplate = {
+ MnpGetModeData,
+ MnpConfigure,
+ MnpMcastIpToMac,
+ MnpGroups,
+ MnpTransmit,
+ MnpReceive,
+ MnpCancel,
+ MnpPoll
+};
+
+EFI_MANAGED_NETWORK_CONFIG_DATA mMnpDefaultConfigData = {
+ 10000,
+ 10000,
+ 0,
+ FALSE,
+ FALSE,
+ FALSE,
+ FALSE,
+ FALSE,
+ FALSE,
+ FALSE
+};
+
+STATIC
+EFI_STATUS
+MnpAddFreeNbuf (
+ IN MNP_SERVICE_DATA *MnpServiceData,
+ IN UINTN Count
+ );
+
+STATIC
+EFI_STATUS
+MnpStartSnp (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *Snp
+ );
+
+STATIC
+EFI_STATUS
+MnpStopSnp (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *Snp
+ );
+
+STATIC
+EFI_STATUS
+MnpStart (
+ IN MNP_SERVICE_DATA *MnpServiceData,
+ IN BOOLEAN IsConfigUpdate,
+ IN BOOLEAN EnableSystemPoll
+ );
+
+STATIC
+EFI_STATUS
+MnpStop (
+ IN MNP_SERVICE_DATA *MnpServiceData
+ );
+
+STATIC
+EFI_STATUS
+MnpConfigReceiveFilters (
+ IN MNP_SERVICE_DATA *MnpServiceData
+ );
+
+STATIC
+EFI_STATUS
+MnpGroupOpAddCtrlBlk (
+ IN MNP_INSTANCE_DATA *Instance,
+ IN MNP_GROUP_CONTROL_BLOCK *CtrlBlk,
+ IN MNP_GROUP_ADDRESS *GroupAddress OPTIONAL,
+ IN EFI_MAC_ADDRESS *MacAddress,
+ IN UINT32 HwAddressSize
+ );
+
+STATIC
+BOOLEAN
+MnpGroupOpDelCtrlBlk (
+ IN MNP_INSTANCE_DATA *Instance,
+ IN MNP_GROUP_CONTROL_BLOCK *CtrlBlk
+ );
+
+
+/**
+ Add some NET_BUF into MnpServiceData->FreeNbufQue. The buffer length of
+ the NET_BUF is specified by MnpServiceData->BufferLength.
+
+ @param MnpServiceData Pointer to the MNP_SERVICE_DATA.
+ @param Count Number of NET_BUFFERs to add.
+
+ @retval EFI_SUCCESS The specified amount of NET_BUFs are allocated and
+ added into MnpServiceData->FreeNbufQue.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate a NET_BUF structure.
+
+**/
+STATIC
+EFI_STATUS
+MnpAddFreeNbuf (
+ IN MNP_SERVICE_DATA *MnpServiceData,
+ IN UINTN Count
+ )
+{
+ EFI_STATUS Status;
+ UINTN Index;
+ NET_BUF *Nbuf;
+
+ NET_CHECK_SIGNATURE (MnpServiceData, MNP_SERVICE_DATA_SIGNATURE);
+ ASSERT ((Count > 0) && (MnpServiceData->BufferLength > 0));
+
+ Status = EFI_SUCCESS;
+
+ for (Index = 0; Index < Count; Index++) {
+
+ Nbuf = NetbufAlloc (MnpServiceData->BufferLength);
+ if (Nbuf == NULL) {
+
+ MNP_DEBUG_ERROR (("MnpAddFreeNbuf: NetBufAlloc failed.\n"));
+ Status = EFI_OUT_OF_RESOURCES;
+ break;
+ }
+
+ NetbufQueAppend (&MnpServiceData->FreeNbufQue, Nbuf);
+ }
+
+ MnpServiceData->NbufCnt += Index;
+
+ return Status;
+}
+
+
+/**
+ Allocate a free NET_BUF from MnpServiceData->FreeNbufQue. If there is none
+ in the queue, first try to allocate some and add them into the queue, then
+ fetch the NET_BUF from the updated FreeNbufQue.
+
+ @param MnpServiceData Pointer to the MNP_SERVICE_DATA.
+
+ @return Pointer to the allocated free NET_BUF structure, if NULL the operation is failed.
+
+**/
+NET_BUF *
+MnpAllocNbuf (
+ IN MNP_SERVICE_DATA *MnpServiceData
+ )
+{
+ EFI_STATUS Status;
+ NET_BUF_QUEUE *FreeNbufQue;
+ NET_BUF *Nbuf;
+ EFI_TPL OldTpl;
+
+ NET_CHECK_SIGNATURE (MnpServiceData, MNP_SERVICE_DATA_SIGNATURE);
+
+ FreeNbufQue = &MnpServiceData->FreeNbufQue;
+
+ OldTpl = NET_RAISE_TPL (NET_TPL_RECYCLE);
+
+ //
+ // Check whether there are available buffers, or else try to add some.
+ //
+ if (FreeNbufQue->BufNum == 0) {
+
+ if ((MnpServiceData->NbufCnt + MNP_NET_BUFFER_INCREASEMENT) > MNP_MAX_NET_BUFFER_NUM) {
+
+ MNP_DEBUG_ERROR (
+ ("MnpAllocNbuf: The maximum NET_BUF size is reached for MNP driver instance %p.\n",
+ MnpServiceData)
+ );
+
+ Nbuf = NULL;
+ goto ON_EXIT;
+ }
+
+ Status = MnpAddFreeNbuf (MnpServiceData, MNP_NET_BUFFER_INCREASEMENT);
+ if (EFI_ERROR (Status)) {
+
+ MNP_DEBUG_ERROR (
+ ("MnpAllocNbuf: Failed to add NET_BUFs into the FreeNbufQue, %r.\n",
+ Status)
+ );
+ //
+ // Don't return NULL, perhaps MnpAddFreeNbuf does add some NET_BUFs but
+ // the amount is less than MNP_NET_BUFFER_INCREASEMENT.
+ //
+ }
+ }
+
+ Nbuf = NetbufQueRemove (FreeNbufQue);
+
+ //
+ // Increase the RefCnt.
+ //
+ if (Nbuf != NULL) {
+ NET_GET_REF (Nbuf);
+ }
+
+ON_EXIT:
+ NET_RESTORE_TPL (OldTpl);
+
+ return Nbuf;
+}
+
+
+/**
+ Try to reclaim the Nbuf into the buffer pool.
+
+ @param MnpServiceData Pointer to the mnp service context data.
+ @param Nbuf Pointer to the NET_BUF to free.
+
+ @return None.
+
+**/
+VOID
+MnpFreeNbuf (
+ IN MNP_SERVICE_DATA *MnpServiceData,
+ IN NET_BUF *Nbuf
+ )
+{
+ EFI_TPL OldTpl;
+
+ NET_CHECK_SIGNATURE (MnpServiceData, MNP_SERVICE_DATA_SIGNATURE);
+ ASSERT (Nbuf->RefCnt > 1);
+
+ OldTpl = NET_RAISE_TPL (NET_TPL_RECYCLE);
+
+ NET_PUT_REF (Nbuf);
+
+ if (Nbuf->RefCnt == 1) {
+ //
+ // Trim all buffer contained in the Nbuf, then append it to the NbufQue.
+ //
+ NetbufTrim (Nbuf, Nbuf->TotalSize, NET_BUF_TAIL);
+ NetbufQueAppend (&MnpServiceData->FreeNbufQue, Nbuf);
+ }
+
+ NET_RESTORE_TPL (OldTpl);
+}
+
+
+/**
+ Initialize the mnp service context data.
+
+ @param MnpServiceData Pointer to the mnp service context data.
+ @param Snp Pointer to the simple network protocol.
+
+ @retval EFI_SUCCESS The mnp service context is initialized.
+ @retval Other Some error occurs.
+
+**/
+EFI_STATUS
+MnpInitializeServiceData (
+ IN MNP_SERVICE_DATA *MnpServiceData,
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_HANDLE ControllerHandle
+ )
+{
+ EFI_STATUS Status;
+ EFI_SIMPLE_NETWORK_PROTOCOL *Snp;
+ EFI_SIMPLE_NETWORK_MODE *SnpMode;
+
+ MnpServiceData->Signature = MNP_SERVICE_DATA_SIGNATURE;
+
+ MnpServiceData->ControllerHandle = ControllerHandle;
+
+ //
+ // Copy the ServiceBinding structure.
+ //
+ MnpServiceData->ServiceBinding = mMnpServiceBindingProtocol;
+
+ //
+ // Open the Simple Network protocol.
+ //
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiSimpleNetworkProtocolGuid,
+ (VOID **) &Snp,
+ ImageHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+ if (EFI_ERROR (Status)) {
+ return EFI_UNSUPPORTED;
+ }
+
+ //
+ // Get MTU from Snp.
+ //
+ SnpMode = Snp->Mode;
+ MnpServiceData->Snp = Snp;
+ MnpServiceData->Mtu = SnpMode->MaxPacketSize;
+
+ //
+ // Initialize the lists.
+ //
+ NetListInit (&MnpServiceData->GroupAddressList);
+ NetListInit (&MnpServiceData->ChildrenList);
+
+ //
+ // Get the buffer length used to allocate NET_BUF to hold data received
+ // from SNP. Do this before fill the FreeNetBufQue.
+ //
+ MnpServiceData->BufferLength = MnpServiceData->Mtu + SnpMode->MediaHeaderSize + NET_ETHER_FCS_SIZE;
+
+ //
+ // Initialize the FreeNetBufQue and pre-allocate some NET_BUFs.
+ //
+ NetbufQueInit (&MnpServiceData->FreeNbufQue);
+ Status = MnpAddFreeNbuf (MnpServiceData, MNP_INIT_NET_BUFFER_NUM);
+ if (EFI_ERROR (Status)) {
+
+ MNP_DEBUG_ERROR (("MnpInitializeServiceData: MnpAddFreeNbuf failed, %r.\n", Status));
+ goto ERROR;
+ }
+ //
+ // Get one NET_BUF from the FreeNbufQue for rx cache.
+ //
+ MnpServiceData->RxNbufCache = MnpAllocNbuf (MnpServiceData);
+ NetbufAllocSpace (
+ MnpServiceData->RxNbufCache,
+ MnpServiceData->BufferLength,
+ NET_BUF_TAIL
+ );
+
+ //
+ // Allocate buffer pool for tx.
+ //
+ MnpServiceData->TxBuf = NetAllocatePool (MnpServiceData->Mtu + SnpMode->MediaHeaderSize);
+ if (MnpServiceData->TxBuf == NULL) {
+
+ MNP_DEBUG_ERROR (("MnpInitializeServiceData: NetAllocatePool failed.\n"));
+ Status = EFI_OUT_OF_RESOURCES;
+
+ goto ERROR;
+ }
+
+ //
+ // Create the system poll timer.
+ //
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL | EVT_TIMER,
+ NET_TPL_LOCK,
+ MnpSystemPoll,
+ MnpServiceData,
+ &MnpServiceData->PollTimer
+ );
+ if (EFI_ERROR (Status)) {
+
+ MNP_DEBUG_ERROR (("MnpInitializeServiceData: CreateEvent for poll timer failed.\n"));
+ goto ERROR;
+ }
+
+ //
+ // Create the timer for packet timeout check.
+ //
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL | EVT_TIMER,
+ NET_TPL_EVENT,
+ MnpCheckPacketTimeout,
+ MnpServiceData,
+ &MnpServiceData->TimeoutCheckTimer
+ );
+ if (EFI_ERROR (Status)) {
+
+ MNP_DEBUG_ERROR (("MnpInitializeServiceData: CreateEvent for packet timeout check failed.\n"));
+ goto ERROR;
+ }
+
+ //
+ // Create the timer for tx timeout check.
+ //
+ Status = gBS->CreateEvent (
+ EVT_TIMER,
+ NET_TPL_SLOW_TIMER,
+ NULL,
+ NULL,
+ &MnpServiceData->TxTimeoutEvent
+ );
+ if (EFI_ERROR (Status)) {
+
+ MNP_DEBUG_ERROR (("MnpInitializeServiceData: CreateEvent for tx timeout event failed.\n"));
+ }
+
+ERROR:
+
+ if (EFI_ERROR (Status)) {
+ //
+ // Free the dynamic allocated resources if necessary.
+ //
+ if (MnpServiceData->TimeoutCheckTimer != NULL) {
+
+ gBS->CloseEvent (MnpServiceData->TimeoutCheckTimer);
+ }
+
+ if (MnpServiceData->PollTimer != NULL) {
+
+ gBS->CloseEvent (MnpServiceData->PollTimer);
+ }
+
+ if (MnpServiceData->TxBuf != NULL) {
+
+ NetFreePool (MnpServiceData->TxBuf);
+ }
+
+ if (MnpServiceData->RxNbufCache != NULL) {
+
+ MnpFreeNbuf (MnpServiceData, MnpServiceData->RxNbufCache);
+ }
+
+ if (MnpServiceData->FreeNbufQue.BufNum != 0) {
+
+ NetbufQueFlush (&MnpServiceData->FreeNbufQue);
+ }
+ }
+
+ return Status;
+}
+
+
+/**
+ Flush the mnp service context data.
+
+ @param MnpServiceData Pointer to the mnp service context data.
+
+ @return None.
+
+**/
+VOID
+MnpFlushServiceData (
+ MNP_SERVICE_DATA *MnpServiceData
+ )
+{
+ NET_CHECK_SIGNATURE (MnpServiceData, MNP_SERVICE_DATA_SIGNATURE);
+
+ //
+ // The GroupAddressList must be empty.
+ //
+ ASSERT (NetListIsEmpty (&MnpServiceData->GroupAddressList));
+
+ //
+ // Close the event.
+ //
+ gBS->CloseEvent (&MnpServiceData->TxTimeoutEvent);
+ gBS->CloseEvent (&MnpServiceData->TimeoutCheckTimer);
+ gBS->CloseEvent (&MnpServiceData->PollTimer);
+
+ //
+ // Free the tx buffer.
+ //
+ NetFreePool (MnpServiceData->TxBuf);
+
+ //
+ // Free the RxNbufCache.
+ //
+ MnpFreeNbuf (MnpServiceData, MnpServiceData->RxNbufCache);
+
+ //
+ // Flush the FreeNbufQue.
+ //
+ MnpServiceData->NbufCnt -= MnpServiceData->FreeNbufQue.BufNum;
+ NetbufQueFlush (&MnpServiceData->FreeNbufQue);
+
+ DEBUG_CODE (
+
+ if (MnpServiceData->NbufCnt != 0) {
+
+ MNP_DEBUG_WARN (("MnpFlushServiceData: Memory leak, MnpServiceData->NbufCnt != 0.\n"));
+ }
+ );
+}
+
+
+/**
+ Initialize the mnp instance context data.
+
+ @param MnpServiceData Pointer to the mnp service context data.
+ @param Instance Pointer to the mnp instance context data to
+ initialize.
+
+ @return None.
+
+**/
+VOID
+MnpInitializeInstanceData (
+ IN MNP_SERVICE_DATA *MnpServiceData,
+ IN MNP_INSTANCE_DATA *Instance
+ )
+{
+ NET_CHECK_SIGNATURE (MnpServiceData, MNP_SERVICE_DATA_SIGNATURE);
+ ASSERT (Instance != NULL);
+
+ //
+ // Set the signature.
+ //
+ Instance->Signature = MNP_INSTANCE_DATA_SIGNATURE;
+
+ //
+ // Copy the MNP Protocol interfaces from the template.
+ //
+ Instance->ManagedNetwork = mMnpProtocolTemplate;
+
+ //
+ // Copy the default config data.
+ //
+ Instance->ConfigData = mMnpDefaultConfigData;
+
+ //
+ // Initialize the lists.
+ //
+ NetListInit (&Instance->GroupCtrlBlkList);
+ NetListInit (&Instance->RcvdPacketQueue);
+ NetListInit (&Instance->RxDeliveredPacketQueue);
+
+ //
+ // Initialize the RxToken Map.
+ //
+ NetMapInit (&Instance->RxTokenMap);
+
+ //
+ // Save the MnpServiceData info.
+ //
+ Instance->MnpServiceData = MnpServiceData;
+}
+
+
+/**
+ Check whether the token specified by Arg maches the token in Item.
+
+ @param Map Pointer to the NET_MAP.
+ @param Item Pointer to the NET_MAP_ITEM
+ @param Arg Pointer to the Arg, it's a pointer to the token to
+ check.
+
+ @retval EFI_SUCCESS The token specified by Arg is different from the
+ token in Item.
+ @retval EFI_ACCESS_DENIED The token specified by Arg is the same as that in
+ Item.
+
+**/
+EFI_STATUS
+MnpTokenExist (
+ IN NET_MAP *Map,
+ IN NET_MAP_ITEM *Item,
+ IN VOID *Arg
+ )
+{
+ EFI_MANAGED_NETWORK_COMPLETION_TOKEN *Token;
+ EFI_MANAGED_NETWORK_COMPLETION_TOKEN *TokenInItem;
+
+ Token = (EFI_MANAGED_NETWORK_COMPLETION_TOKEN *) Arg;
+ TokenInItem = (EFI_MANAGED_NETWORK_COMPLETION_TOKEN *) Item->Key;
+
+ if ((Token == TokenInItem) || (Token->Event == TokenInItem->Event)) {
+ //
+ // The token is the same either the two tokens equals or the Events in
+ // the two tokens are the same.
+ //
+ return EFI_ACCESS_DENIED;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Cancel the token specified by Arg if it matches the token in Item.
+
+ @param Map Pointer to the NET_MAP.
+ @param Item Pointer to the NET_MAP_ITEM
+ @param Arg Pointer to the Arg, it's a pointer to the token to
+ cancel.
+
+ @retval EFI_SUCCESS The Arg is NULL, and the token in Item is
+ cancelled, or the Arg isn't NULL, and the token in
+ Item is different from the Arg.
+ @retval EFI_ABORTED The Arg isn't NULL, the token in Item mathces the
+ Arg, and the token is cancelled.
+
+**/
+EFI_STATUS
+MnpCancelTokens (
+ IN NET_MAP *Map,
+ IN NET_MAP_ITEM *Item,
+ IN VOID *Arg
+ )
+{
+ EFI_MANAGED_NETWORK_COMPLETION_TOKEN *TokenToCancel;
+
+ if ((Arg != NULL) && (Item->Key != Arg)) {
+ //
+ // The token in Item is not the token specified by Arg.
+ //
+ return EFI_SUCCESS;
+ }
+
+ TokenToCancel = (EFI_MANAGED_NETWORK_COMPLETION_TOKEN *) Item->Key;
+
+ //
+ // Cancel this token with status set to EFI_ABORTED.
+ //
+ TokenToCancel->Status = EFI_ABORTED;
+ gBS->SignalEvent (TokenToCancel->Event);
+
+ //
+ // Remove the item from the map.
+ //
+ NetMapRemoveItem (Map, Item, NULL);
+
+ if (Arg != NULL) {
+ //
+ // Only abort the token specified by Arg if Arg isn't NULL.
+ //
+ return EFI_ABORTED;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Start and initialize the simple network.
+
+ @param Snp Pointer to the simple network protocol.
+
+ @retval EFI_SUCCESS The simple network protocol is started.
+ @retval Other Some error occurs.
+
+**/
+STATIC
+EFI_STATUS
+MnpStartSnp (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *Snp
+ )
+{
+ EFI_STATUS Status;
+
+ ASSERT (Snp != NULL);
+
+ //
+ // Start the simple network.
+ //
+ Status = Snp->Start (Snp);
+
+ if (!EFI_ERROR (Status)) {
+ //
+ // Initialize the simple network.
+ //
+ Status = Snp->Initialize (Snp, 0, 0);
+ }
+
+ return Status;
+}
+
+
+/**
+ Stop the simple network.
+
+ @param Snp Pointer to the simple network protocol.
+
+ @retval EFI_SUCCESS The simple network is stopped.
+ @retval Other Some error occurs.
+
+**/
+STATIC
+EFI_STATUS
+MnpStopSnp (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *Snp
+ )
+{
+ EFI_STATUS Status;
+
+ ASSERT (Snp != NULL);
+
+ //
+ // Shut down the simple network.
+ //
+ Status = Snp->Shutdown (Snp);
+
+ if (!EFI_ERROR (Status)) {
+ //
+ // Stop the simple network.
+ //
+ Status = Snp->Stop (Snp);
+ }
+
+ return Status;
+}
+
+
+/**
+ Start the managed network, this function is called when one instance is configured
+ or reconfigured.
+
+ @param MnpServiceData Pointer to the mnp service context data.
+ @param IsConfigUpdate The instance is reconfigured or it's the first time
+ the instanced is configured.
+ @param EnableSystemPoll Enable the system polling or not.
+
+ @retval EFI_SUCCESS The managed network is started and some
+ configuration is updated.
+ @retval Other Some error occurs.
+
+**/
+STATIC
+EFI_STATUS
+MnpStart (
+ IN MNP_SERVICE_DATA *MnpServiceData,
+ IN BOOLEAN IsConfigUpdate,
+ IN BOOLEAN EnableSystemPoll
+ )
+{
+ EFI_STATUS Status;
+ EFI_TIMER_DELAY TimerOpType;
+
+ NET_CHECK_SIGNATURE (MnpServiceData, MNP_SERVICE_DATA_SIGNATURE);
+
+ Status = EFI_SUCCESS;
+
+ if (!IsConfigUpdate) {
+ //
+ // If it's not a configuration update, increase the configured children number.
+ //
+ MnpServiceData->ConfiguredChildrenNumber++;
+
+ if (MnpServiceData->ConfiguredChildrenNumber == 1) {
+ //
+ // It's the first configured child, start the simple network.
+ //
+ Status = MnpStartSnp (MnpServiceData->Snp);
+ if (EFI_ERROR (Status)) {
+
+ MNP_DEBUG_ERROR (("MnpStart: MnpStartSnp failed, %r.\n", Status));
+ goto ErrorExit;
+ }
+
+ //
+ // Start the timeout timer.
+ //
+ Status = gBS->SetTimer (
+ MnpServiceData->TimeoutCheckTimer,
+ TimerPeriodic,
+ MNP_TIMEOUT_CHECK_INTERVAL
+ );
+ if (EFI_ERROR (Status)) {
+
+ MNP_DEBUG_ERROR (
+ ("MnpStart, gBS->SetTimer for TimeoutCheckTimer %r.\n",
+ Status)
+ );
+ goto ErrorExit;
+ }
+ }
+ }
+
+ if (MnpServiceData->EnableSystemPoll ^ EnableSystemPoll) {
+ //
+ // The EnableSystemPoll differs with the current state, disable or enable
+ // the system poll.
+ //
+ TimerOpType = EnableSystemPoll ? TimerPeriodic : TimerCancel;
+
+ Status = gBS->SetTimer (MnpServiceData->PollTimer, TimerOpType, MNP_SYS_POLL_INTERVAL);
+ if (EFI_ERROR (Status)) {
+
+ MNP_DEBUG_ERROR (("MnpStart: gBS->SetTimer for PollTimer failed, %r.\n", Status));
+ goto ErrorExit;
+ }
+
+ MnpServiceData->EnableSystemPoll = EnableSystemPoll;
+ }
+
+ //
+ // Change the receive filters if need.
+ //
+ Status = MnpConfigReceiveFilters (MnpServiceData);
+
+ErrorExit:
+
+ return Status;
+}
+
+
+/**
+ Stop the managed network.
+
+ @param MnpServiceData Pointer to the mnp service context data.
+
+ @retval EFI_SUCCESS The managed network is stopped.
+ @retval Other Some error occurs.
+
+**/
+STATIC
+EFI_STATUS
+MnpStop (
+ IN MNP_SERVICE_DATA *MnpServiceData
+ )
+{
+ EFI_STATUS Status;
+
+ NET_CHECK_SIGNATURE (MnpServiceData, MNP_SERVICE_DATA_SIGNATURE);
+ ASSERT (MnpServiceData->ConfiguredChildrenNumber > 0);
+
+ //
+ // Configure the receive filters.
+ //
+ MnpConfigReceiveFilters (MnpServiceData);
+
+ //
+ // Decrease the children number.
+ //
+ MnpServiceData->ConfiguredChildrenNumber--;
+
+ if (MnpServiceData->ConfiguredChildrenNumber > 0) {
+ //
+ // If there are other configured chilren, return and keep the timers and
+ // simple network unchanged.
+ //
+ return EFI_SUCCESS;
+ }
+
+ //
+ // No configured children now.
+ //
+
+ if (MnpServiceData->EnableSystemPoll) {
+ //
+ // The system poll in on, cancel the poll timer.
+ //
+ Status = gBS->SetTimer (MnpServiceData->PollTimer, TimerCancel, 0);
+ MnpServiceData->EnableSystemPoll = FALSE;
+ }
+
+ //
+ // Cancel the timeout timer.
+ //
+ Status = gBS->SetTimer (MnpServiceData->TimeoutCheckTimer, TimerCancel, 0);
+
+ //
+ // Stop the simple network.
+ //
+ Status = MnpStopSnp (MnpServiceData->Snp);
+
+ return Status;
+}
+
+
+/**
+ Flush the instance's received data.
+
+ @param Instance Pointer to the mnp instance context data.
+
+ @return None.
+
+**/
+VOID
+MnpFlushRcvdDataQueue (
+ IN MNP_INSTANCE_DATA *Instance
+ )
+{
+ EFI_TPL OldTpl;
+ MNP_RXDATA_WRAP *RxDataWrap;
+
+ NET_CHECK_SIGNATURE (Instance, MNP_INSTANCE_DATA_SIGNATURE);
+
+ OldTpl = NET_RAISE_TPL (NET_TPL_RECYCLE);
+
+ while (!NetListIsEmpty (&Instance->RcvdPacketQueue)) {
+ //
+ // Remove all the Wraps.
+ //
+ RxDataWrap = NET_LIST_HEAD (&Instance->RcvdPacketQueue, MNP_RXDATA_WRAP, WrapEntry);
+
+ //
+ // Recycle the RxDataWrap.
+ //
+ MnpRecycleRxData (NULL, (VOID *) RxDataWrap);
+ Instance->RcvdPacketQueueSize--;
+ }
+
+ ASSERT (Instance->RcvdPacketQueueSize == 0);
+
+ NET_RESTORE_TPL (OldTpl);
+}
+
+
+/**
+ Configure the Instance using ConfigData.
+
+ @param Instance Pointer to the mnp instance context data.
+ @param ConfigData Pointer to the configuration data used to configure
+ the isntance.
+
+ @retval EFI_SUCCESS The Instance is configured.
+ @retval EFI_UNSUPPORTED EnableReceiveTimestamps is on and the
+ implementation doesn't support it.
+ @retval Other Some error occurs.
+
+**/
+EFI_STATUS
+MnpConfigureInstance (
+ IN MNP_INSTANCE_DATA *Instance,
+ IN EFI_MANAGED_NETWORK_CONFIG_DATA *ConfigData OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ MNP_SERVICE_DATA *MnpServiceData;
+ EFI_MANAGED_NETWORK_CONFIG_DATA *OldConfigData;
+ EFI_MANAGED_NETWORK_CONFIG_DATA *NewConfigData;
+ BOOLEAN IsConfigUpdate;
+
+ NET_CHECK_SIGNATURE (Instance, MNP_INSTANCE_DATA_SIGNATURE);
+
+ if ((ConfigData != NULL) && ConfigData->EnableReceiveTimestamps) {
+ //
+ // Don't support timestamp.
+ //
+ return EFI_UNSUPPORTED;
+ }
+
+ Status = EFI_SUCCESS;
+
+ MnpServiceData = Instance->MnpServiceData;
+ NET_CHECK_SIGNATURE (MnpServiceData, MNP_SERVICE_DATA_SIGNATURE);
+
+ IsConfigUpdate = (BOOLEAN) ((Instance->Configured) && (ConfigData != NULL));
+
+ OldConfigData = &Instance->ConfigData;
+ NewConfigData = ConfigData;
+ if (NewConfigData == NULL) {
+ //
+ // Restore back the default config data if a reset of this instance
+ // is required.
+ //
+ NewConfigData = &mMnpDefaultConfigData;
+ }
+
+ //
+ // Reset the instance's receive filter.
+ //
+ Instance->ReceiveFilter = 0;
+
+ //
+ // Clear the receive counters according to the old ConfigData.
+ //
+ if (OldConfigData->EnableUnicastReceive) {
+ MnpServiceData->UnicastCount--;
+ }
+
+ if (OldConfigData->EnableMulticastReceive) {
+ MnpServiceData->MulticastCount--;
+ }
+
+ if (OldConfigData->EnableBroadcastReceive) {
+ MnpServiceData->BroadcastCount--;
+ }
+
+ if (OldConfigData->EnablePromiscuousReceive) {
+ MnpServiceData->PromiscuousCount--;
+ }
+
+ //
+ // Set the receive filter counters and the receive filter of the
+ // instance according to the new ConfigData.
+ //
+ if (NewConfigData->EnableUnicastReceive) {
+ MnpServiceData->UnicastCount++;
+ Instance->ReceiveFilter |= MNP_RECEIVE_UNICAST;
+ }
+
+ if (NewConfigData->EnableMulticastReceive) {
+ MnpServiceData->MulticastCount++;
+ }
+
+ if (NewConfigData->EnableBroadcastReceive) {
+ MnpServiceData->BroadcastCount++;
+ Instance->ReceiveFilter |= MNP_RECEIVE_BROADCAST;
+ }
+
+ if (NewConfigData->EnablePromiscuousReceive) {
+ MnpServiceData->PromiscuousCount++;
+ }
+
+ if (OldConfigData->FlushQueuesOnReset) {
+
+ MnpFlushRcvdDataQueue (Instance);
+ }
+
+ if (ConfigData == NULL) {
+
+ NetMapIterate (&Instance->RxTokenMap, MnpCancelTokens, NULL);
+ }
+
+ if (!NewConfigData->EnableMulticastReceive) {
+
+ MnpGroupOp (Instance, FALSE, NULL, NULL);
+ }
+
+ //
+ // Save the new configuration data.
+ //
+ *OldConfigData = *NewConfigData;
+
+ Instance->Configured = (BOOLEAN) (ConfigData != NULL);
+
+ if (Instance->Configured) {
+ //
+ // The instance is configured, start the Mnp.
+ //
+ Status = MnpStart (
+ MnpServiceData,
+ IsConfigUpdate,
+ !NewConfigData->DisableBackgroundPolling
+ );
+ } else {
+ //
+ // The instance is changed to the unconfigured state, stop the Mnp.
+ //
+ Status = MnpStop (MnpServiceData);
+ }
+
+ return Status;
+}
+
+
+/**
+ Configure the Snp receive filters according to the instances' receive filter
+ settings.
+
+ @param MnpServiceData Pointer to the mnp service context data.
+
+ @retval EFI_SUCCESS The receive filters is configured.
+ @retval EFI_OUT_OF_RESOURCES The receive filters can't be configured due to lack
+ of memory resource.
+
+**/
+STATIC
+EFI_STATUS
+MnpConfigReceiveFilters (
+ IN MNP_SERVICE_DATA *MnpServiceData
+ )
+{
+ EFI_STATUS Status;
+ EFI_SIMPLE_NETWORK_PROTOCOL *Snp;
+ EFI_MAC_ADDRESS *MCastFilter;
+ UINT32 MCastFilterCnt;
+ UINT32 EnableFilterBits;
+ UINT32 DisableFilterBits;
+ BOOLEAN ResetMCastFilters;
+ NET_LIST_ENTRY *Entry;
+ UINT32 Index;
+ MNP_GROUP_ADDRESS *GroupAddress;
+
+ NET_CHECK_SIGNATURE (MnpServiceData, MNP_SERVICE_DATA_SIGNATURE);
+
+ Snp = MnpServiceData->Snp;
+
+ //
+ // Initialize the enable filter and disable filter.
+ //
+ EnableFilterBits = 0;
+ DisableFilterBits = Snp->Mode->ReceiveFilterMask;
+
+ if (MnpServiceData->UnicastCount != 0) {
+ //
+ // Enable unicast if any instance wants to receive unicast.
+ //
+ EnableFilterBits |= EFI_SIMPLE_NETWORK_RECEIVE_UNICAST;
+ }
+
+ if (MnpServiceData->BroadcastCount != 0) {
+ //
+ // Enable broadcast if any instance wants to receive broadcast.
+ //
+ EnableFilterBits |= EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST;
+ }
+
+ MCastFilter = NULL;
+ MCastFilterCnt = 0;
+ ResetMCastFilters = TRUE;
+
+ if ((MnpServiceData->MulticastCount != 0) && (MnpServiceData->GroupAddressCount != 0)) {
+ //
+ // There are instances configured to receive multicast and already some group
+ // addresses are joined.
+ //
+
+ ResetMCastFilters = FALSE;
+
+ if (MnpServiceData->GroupAddressCount <= Snp->Mode->MaxMCastFilterCount) {
+ //
+ // The joind group address is less than simple network's maximum count.
+ // Just configure the snp to do the multicast filtering.
+ //
+
+ EnableFilterBits |= EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST;
+
+ //
+ // Allocate pool for the mulicast addresses.
+ //
+ MCastFilterCnt = MnpServiceData->GroupAddressCount;
+ MCastFilter = NetAllocatePool (sizeof (EFI_MAC_ADDRESS) * MCastFilterCnt);
+ if (MCastFilter == NULL) {
+
+ MNP_DEBUG_ERROR (("MnpConfigReceiveFilters: Failed to allocate memory resource for MCastFilter.\n"));
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Fill the multicast HW address buffer.
+ //
+ Index = 0;
+ NET_LIST_FOR_EACH (Entry, &MnpServiceData->GroupAddressList) {
+
+ GroupAddress = NET_LIST_USER_STRUCT (Entry, MNP_GROUP_ADDRESS, AddrEntry);
+ *(MCastFilter + Index) = GroupAddress->Address;
+ Index++;
+
+ ASSERT (Index <= MCastFilterCnt);
+ }
+ } else {
+ //
+ // The maximum multicast is reached, set the filter to be promiscuous
+ // multicast.
+ //
+
+ if (Snp->Mode->ReceiveFilterMask & EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST) {
+ EnableFilterBits |= EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST;
+ } else {
+ //
+ // Either MULTICAST or PROMISCUOUS_MULTICAST is not supported by Snp,
+ // set the NIC to be promiscuous although this will tremendously degrade
+ // the performance.
+ //
+ EnableFilterBits |= EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS;
+ }
+ }
+ }
+
+ if (MnpServiceData->PromiscuousCount != 0) {
+ //
+ // Enable promiscuous if any instance wants to receive promiscuous.
+ //
+ EnableFilterBits |= EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS;
+ }
+
+ //
+ // Set the disable filter.
+ //
+ DisableFilterBits ^= EnableFilterBits;
+
+ //
+ // Configure the receive filters of SNP.
+ //
+ Status = Snp->ReceiveFilters (
+ Snp,
+ EnableFilterBits,
+ DisableFilterBits,
+ ResetMCastFilters,
+ MCastFilterCnt,
+ MCastFilter
+ );
+ DEBUG_CODE (
+ if (EFI_ERROR (Status)) {
+
+ MNP_DEBUG_ERROR (
+ ("MnpConfigReceiveFilters: Snp->ReceiveFilters failed, %r.\n",
+ Status)
+ );
+ }
+ );
+
+ if (MCastFilter != NULL) {
+ //
+ // Free the buffer used to hold the group addresses.
+ //
+ NetFreePool (MCastFilter);
+ }
+
+ return Status;
+}
+
+
+/**
+ Add a group address control block which controls the MacAddress for
+ this instance.
+
+ @param Instance Pointer to the mnp instance context data.
+ @param CtrlBlk Pointer to the group address control block.
+ @param GroupAddress Pointer to the group adress.
+ @param MacAddress Pointer to the mac address.
+ @param HwAddressSize The hardware address size.
+
+ @retval EFI_SUCCESS The group address control block is added.
+ @retval EFI_OUT_OF_RESOURCE Failed due to lack of memory resources.
+
+**/
+STATIC
+EFI_STATUS
+MnpGroupOpAddCtrlBlk (
+ IN MNP_INSTANCE_DATA *Instance,
+ IN MNP_GROUP_CONTROL_BLOCK *CtrlBlk,
+ IN MNP_GROUP_ADDRESS *GroupAddress OPTIONAL,
+ IN EFI_MAC_ADDRESS *MacAddress,
+ IN UINT32 HwAddressSize
+ )
+{
+ MNP_SERVICE_DATA *MnpServiceData;
+
+ NET_CHECK_SIGNATURE (Instance, MNP_INSTANCE_DATA_SIGNATURE);
+
+ MnpServiceData = Instance->MnpServiceData;
+ NET_CHECK_SIGNATURE (MnpServiceData, MNP_SERVICE_DATA_SIGNATURE);
+
+ if (GroupAddress == NULL) {
+
+ ASSERT (MacAddress != NULL);
+
+ //
+ // Allocate a new GroupAddress to be added into MNP's GroupAddressList.
+ //
+ GroupAddress = NetAllocatePool (sizeof (MNP_GROUP_ADDRESS));
+ if (GroupAddress == NULL) {
+
+ MNP_DEBUG_ERROR (("MnpGroupOpFormCtrlBlk: Failed to allocate memory resource.\n"));
+
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ GroupAddress->Address = *MacAddress;
+ GroupAddress->RefCnt = 0;
+ NetListInsertTail (
+ &MnpServiceData->GroupAddressList,
+ &GroupAddress->AddrEntry
+ );
+ MnpServiceData->GroupAddressCount++;
+ }
+
+ //
+ // Increase the RefCnt.
+ //
+ GroupAddress->RefCnt++;
+
+ //
+ // Add the CtrlBlk into the instance's GroupCtrlBlkList.
+ //
+ CtrlBlk->GroupAddress = GroupAddress;
+ NetListInsertTail (&Instance->GroupCtrlBlkList, &CtrlBlk->CtrlBlkEntry);
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Delete a group control block from the instance. If the controlled group address's
+ reference count reaches zero, the group address is removed too.
+
+ @param Instance Pointer to the instance context data.
+ @param CtrlBlk Pointer to the group control block to delete.
+
+ @return The group address controlled by the control block is no longer used or not.
+
+**/
+STATIC
+BOOLEAN
+MnpGroupOpDelCtrlBlk (
+ IN MNP_INSTANCE_DATA *Instance,
+ IN MNP_GROUP_CONTROL_BLOCK *CtrlBlk
+ )
+{
+ MNP_SERVICE_DATA *MnpServiceData;
+ MNP_GROUP_ADDRESS *GroupAddress;
+
+ NET_CHECK_SIGNATURE (Instance, MNP_INSTANCE_DATA_SIGNATURE);
+
+ MnpServiceData = Instance->MnpServiceData;
+ NET_CHECK_SIGNATURE (MnpServiceData, MNP_SERVICE_DATA_SIGNATURE);
+
+ //
+ // Remove and free the CtrlBlk.
+ //
+ GroupAddress = CtrlBlk->GroupAddress;
+ NetListRemoveEntry (&CtrlBlk->CtrlBlkEntry);
+ NetFreePool (CtrlBlk);
+
+ ASSERT (GroupAddress->RefCnt > 0);
+
+ //
+ // Count down the RefCnt.
+ //
+ GroupAddress->RefCnt--;
+
+ if (GroupAddress->RefCnt == 0) {
+ //
+ // Free this GroupAddress entry if no instance uses it.
+ //
+ MnpServiceData->GroupAddressCount--;
+ NetListRemoveEntry (&GroupAddress->AddrEntry);
+ NetFreePool (GroupAddress);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+/**
+ Do the group operations for this instance.
+
+ @param Instance Pointer to the instance context data.
+ @param JoinFlag Set to TRUE to join a group. Set to TRUE to leave a
+ group/groups.
+ @param MacAddress Pointer to the group address to join or leave.
+ @param CtrlBlk Pointer to the group control block if JoinFlag if
+ FALSE.
+
+ @retval EFI_SUCCESS The group operation finished.
+ @retval Other Some error occurs.
+
+**/
+EFI_STATUS
+MnpGroupOp (
+ IN MNP_INSTANCE_DATA *Instance,
+ IN BOOLEAN JoinFlag,
+ IN EFI_MAC_ADDRESS *MacAddress OPTIONAL,
+ IN MNP_GROUP_CONTROL_BLOCK *CtrlBlk OPTIONAL
+ )
+{
+ MNP_SERVICE_DATA *MnpServiceData;
+ NET_LIST_ENTRY *Entry;
+ NET_LIST_ENTRY *NextEntry;
+ MNP_GROUP_ADDRESS *GroupAddress;
+ EFI_SIMPLE_NETWORK_MODE *SnpMode;
+ MNP_GROUP_CONTROL_BLOCK *NewCtrlBlk;
+ EFI_STATUS Status;
+ BOOLEAN AddressExist;
+ BOOLEAN NeedUpdate;
+
+ NET_CHECK_SIGNATURE (Instance, MNP_INSTANCE_DATA_SIGNATURE);
+
+ MnpServiceData = Instance->MnpServiceData;
+ SnpMode = MnpServiceData->Snp->Mode;
+
+ if (JoinFlag) {
+ //
+ // A new gropu address is to be added.
+ //
+
+ GroupAddress = NULL;
+ AddressExist = FALSE;
+
+ //
+ // Allocate memory for the control block.
+ //
+ NewCtrlBlk = NetAllocatePool (sizeof (MNP_GROUP_CONTROL_BLOCK));
+ if (NewCtrlBlk == NULL) {
+
+ MNP_DEBUG_ERROR (("MnpGroupOp: Failed to allocate memory resource.\n"));
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ NET_LIST_FOR_EACH (Entry, &MnpServiceData->GroupAddressList) {
+ //
+ // Check whether the MacAddress is already joined by other instances.
+ //
+ GroupAddress = NET_LIST_USER_STRUCT (Entry, MNP_GROUP_ADDRESS, AddrEntry);
+ if (0 == NetCompareMem (
+ MacAddress,
+ &GroupAddress->Address,
+ SnpMode->HwAddressSize
+ )) {
+
+ AddressExist = TRUE;
+ break;
+ }
+ }
+
+ if (!AddressExist) {
+ GroupAddress = NULL;
+ }
+
+ //
+ // Add the GroupAddress for this instance.
+ //
+ Status = MnpGroupOpAddCtrlBlk (
+ Instance,
+ NewCtrlBlk,
+ GroupAddress,
+ MacAddress,
+ SnpMode->HwAddressSize
+ );
+ if (EFI_ERROR (Status)) {
+
+ return Status;
+ }
+
+ NeedUpdate = TRUE;
+ } else {
+
+ if (MacAddress != NULL) {
+
+ ASSERT (CtrlBlk != NULL);
+
+ //
+ // Leave the specific multicast mac address.
+ //
+ NeedUpdate = MnpGroupOpDelCtrlBlk (Instance, CtrlBlk);
+ } else {
+ //
+ // Leave all multicast mac addresses.
+ //
+ NeedUpdate = FALSE;
+
+ NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Instance->GroupCtrlBlkList) {
+
+ NewCtrlBlk = NET_LIST_USER_STRUCT (
+ Entry,
+ MNP_GROUP_CONTROL_BLOCK,
+ CtrlBlkEntry
+ );
+ //
+ // Update is required if the group address left is no longer used
+ // by other instances.
+ //
+ NeedUpdate = MnpGroupOpDelCtrlBlk (Instance, NewCtrlBlk);
+ }
+ }
+ }
+
+ Status = EFI_SUCCESS;
+
+ if (NeedUpdate) {
+ //
+ // Reconfigure the receive filters if necessary.
+ //
+ Status = MnpConfigReceiveFilters (MnpServiceData);
+ }
+
+ return Status;
+}
diff --git a/MdeModulePkg/Universal/Network/MnpDxe/MnpDebug.h b/MdeModulePkg/Universal/Network/MnpDxe/MnpDebug.h
new file mode 100644
index 0000000000..72eb59b9b4
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/MnpDxe/MnpDebug.h
@@ -0,0 +1,28 @@
+/** @file
+
+Copyright (c) 2005 - 2006, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+
+ MnpDebug.h
+
+Abstract:
+
+
+**/
+
+#ifndef _MNP_DEBUG_H_
+#define _MNP_DEBUG_H_
+
+#define MNP_DEBUG_TRACE(PrintArg) NET_DEBUG_TRACE ("Mnp", PrintArg)
+#define MNP_DEBUG_WARN(PrintArg) NET_DEBUG_WARNING ("Mnp", PrintArg)
+#define MNP_DEBUG_ERROR(PrintArg) NET_DEBUG_ERROR ("Mnp", PrintArg)
+
+#endif
diff --git a/MdeModulePkg/Universal/Network/MnpDxe/MnpDriver.c b/MdeModulePkg/Universal/Network/MnpDxe/MnpDriver.c
new file mode 100644
index 0000000000..07be21dced
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/MnpDxe/MnpDriver.c
@@ -0,0 +1,560 @@
+/** @file
+
+Copyright (c) 2005 - 2007, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+
+ MnpDriver.c
+
+Abstract:
+
+
+**/
+
+#include "MnpDriver.h"
+#include "MnpDebug.h"
+#include "MnpImpl.h"
+
+
+EFI_DRIVER_BINDING_PROTOCOL gMnpDriverBinding = {
+ MnpDriverBindingSupported,
+ MnpDriverBindingStart,
+ MnpDriverBindingStop,
+ 0xa,
+ NULL,
+ NULL
+};
+
+
+/**
+ Test to see if this driver supports ControllerHandle.
+
+ @param This Protocol instance pointer.
+ @param ControllerHandle Handle of device to test.
+ @param RemainingDevicePath Optional parameter use to pick a specific child
+ device to start.
+
+ @retval EFI_SUCCES This driver supports this device.
+ @retval EFI_ALREADY_STARTED This driver is already running on this device.
+ @retval other This driver does not support this device.
+
+**/
+EFI_STATUS
+EFIAPI
+MnpDriverBindingSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+
+ //
+ // Test to see if MNP is already installed.
+ //
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiManagedNetworkServiceBindingProtocolGuid,
+ NULL,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+ if (!EFI_ERROR (Status)) {
+
+ return EFI_ALREADY_STARTED;
+ }
+
+ //
+ // Test to see if SNP is installed.
+ //
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiSimpleNetworkProtocolGuid,
+ NULL,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+
+ return Status;
+}
+
+
+/**
+ Start this driver on ControllerHandle.
+
+ @param This Protocol instance pointer.
+ @param ControllerHandle Handle of device to bind driver to.
+ @param RemainingDevicePath Optional parameter use to pick a specific child
+ device to start.
+
+ @retval EFI_SUCCES This driver is added to ControllerHandle.
+ @retval EFI_ALREADY_STARTED This driver is already running on
+ ControllerHandle.
+ @retval other This driver does not support this device.
+
+**/
+EFI_STATUS
+EFIAPI
+MnpDriverBindingStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ MNP_SERVICE_DATA *MnpServiceData;
+ BOOLEAN MnpInitialized;
+
+ MnpInitialized = FALSE;
+
+ MnpServiceData = NetAllocateZeroPool (sizeof (MNP_SERVICE_DATA));
+ if (MnpServiceData == NULL) {
+ MNP_DEBUG_ERROR (("MnpDriverBindingStart(): Failed to allocate the "
+ L"Mnp Service Data.\n"));
+
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Initialize the Mnp Service Data.
+ //
+ Status = MnpInitializeServiceData (MnpServiceData, This->DriverBindingHandle, ControllerHandle);
+ if (EFI_ERROR (Status)) {
+
+ MNP_DEBUG_ERROR (("MnpDriverBindingStart: MnpInitializeServiceData "
+ L"failed, %r.\n",Status));
+ goto ErrorExit;
+ }
+
+ MnpInitialized = TRUE;
+
+ //
+ // Install the MNP Service Binding Protocol.
+ //
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ &ControllerHandle,
+ &gEfiManagedNetworkServiceBindingProtocolGuid,
+ &MnpServiceData->ServiceBinding,
+ NULL
+ );
+
+ErrorExit:
+
+ if (EFI_ERROR (Status)) {
+
+ if (MnpInitialized) {
+ //
+ // Flush the Mnp Service Data.
+ //
+ MnpFlushServiceData (MnpServiceData);
+ }
+
+ //
+ // Close the Simple Network Protocol.
+ //
+ gBS->CloseProtocol (
+ ControllerHandle,
+ &gEfiSimpleNetworkProtocolGuid,
+ This->DriverBindingHandle,
+ ControllerHandle
+ );
+
+ NetFreePool (MnpServiceData);
+ }
+
+ return Status;
+}
+
+
+/**
+ Stop this driver on ControllerHandle.
+
+ @param This Protocol instance pointer.
+ @param ControllerHandle Handle of device to stop driver on.
+ @param NumberOfChildren Number of Handles in ChildHandleBuffer. If number
+ of children is zero stop the entire bus driver.
+ @param ChildHandleBuffer List of Child Handles to Stop.
+
+ @retval EFI_SUCCES This driver is removed ControllerHandle.
+ @retval other This driver was not removed from this device.
+
+**/
+EFI_STATUS
+EFIAPI
+MnpDriverBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer
+ )
+{
+ EFI_STATUS Status;
+ EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding;
+ MNP_SERVICE_DATA *MnpServiceData;
+ MNP_INSTANCE_DATA *Instance;
+
+ //
+ // Retrieve the MNP service binding protocol from the ControllerHandle.
+ //
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiManagedNetworkServiceBindingProtocolGuid,
+ (VOID **) &ServiceBinding,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+
+ MNP_DEBUG_ERROR (
+ ("MnpDriverBindingStop: Locate MNP Service Binding Protocol failed, %r.\n",
+ Status)
+ );
+ goto EXIT;
+ }
+
+ MnpServiceData = MNP_SERVICE_DATA_FROM_THIS (ServiceBinding);
+
+ while (!NetListIsEmpty (&MnpServiceData->ChildrenList)) {
+ //
+ // Don't use NetListRemoveHead here, the remove opreration will be done
+ // in ServiceBindingDestroyChild.
+ //
+ Instance = NET_LIST_HEAD (
+ &MnpServiceData->ChildrenList,
+ MNP_INSTANCE_DATA,
+ InstEntry
+ );
+
+ ServiceBinding->DestroyChild (ServiceBinding, Instance->Handle);
+ }
+
+ //
+ // Uninstall the MNP Service Binding Protocol.
+ //
+ Status = gBS->UninstallMultipleProtocolInterfaces (
+ ControllerHandle,
+ &gEfiManagedNetworkServiceBindingProtocolGuid,
+ ServiceBinding,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+
+ MNP_DEBUG_ERROR (("MnpDriverBindingStop: Uninstall MNP Service Binding Protocol failed, %r.\n"));
+ goto EXIT;
+ }
+
+ //
+ // Close the openned Snp protocol.
+ //
+ Status = gBS->CloseProtocol (
+ ControllerHandle,
+ &gEfiSimpleNetworkProtocolGuid,
+ This->DriverBindingHandle,
+ ControllerHandle
+ );
+ if (EFI_ERROR (Status)) {
+
+ MNP_DEBUG_ERROR (("MnpDriverBindingStop: Close SNP Protocol failed, %r.\n", Status));
+ goto EXIT;
+ }
+
+ //
+ // Flush the Mnp service data.
+ //
+ MnpFlushServiceData (MnpServiceData);
+
+ NetFreePool (MnpServiceData);
+
+EXIT:
+
+ return Status;
+}
+
+
+/**
+ Creates a child handle with a set of I/O services.
+
+ @param This Protocol instance pointer.
+ @param ChildHandle Pointer to the handle of the child to create. If
+ it is NULL, then a new handle is created. If it is
+ not NULL, then the I/O services are added to the
+ existing child handle.
+
+ @retval EFI_SUCCES The child handle was created with the I/O
+ services.
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources availabe to create
+ the child.
+ @retval other The child handle was not created.
+
+**/
+EFI_STATUS
+EFIAPI
+MnpServiceBindingCreateChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE *ChildHandle
+ )
+{
+ EFI_STATUS Status;
+ MNP_SERVICE_DATA *MnpServiceData;
+ MNP_INSTANCE_DATA *Instance;
+ VOID *Snp;
+ EFI_TPL OldTpl;
+
+ if ((This == NULL) || (ChildHandle == NULL)) {
+
+ return EFI_INVALID_PARAMETER;
+ }
+
+ MnpServiceData = MNP_SERVICE_DATA_FROM_THIS (This);
+
+ //
+ // Allocate buffer for the new instance.
+ //
+ Instance = NetAllocateZeroPool (sizeof (MNP_INSTANCE_DATA));
+ if (Instance == NULL) {
+
+ MNP_DEBUG_ERROR (("MnpServiceBindingCreateChild: Faild to allocate memory for the new instance.\n"));
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Init the instance data.
+ //
+ MnpInitializeInstanceData (MnpServiceData, Instance);
+
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ ChildHandle,
+ &gEfiManagedNetworkProtocolGuid,
+ &Instance->ManagedNetwork,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+
+ MNP_DEBUG_ERROR (
+ ("MnpServiceBindingCreateChild: Failed to install the MNP protocol, %r.\n",
+ Status)
+ );
+ goto ErrorExit;
+ }
+
+ //
+ // Save the instance's childhandle.
+ //
+ Instance->Handle = *ChildHandle;
+
+ Status = gBS->OpenProtocol (
+ MnpServiceData->ControllerHandle,
+ &gEfiSimpleNetworkProtocolGuid,
+ (VOID **) &Snp,
+ gMnpDriverBinding.DriverBindingHandle,
+ Instance->Handle,
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
+ );
+ if (EFI_ERROR (Status)) {
+ goto ErrorExit;
+ }
+
+ //
+ // Add the child instance into ChildrenList.
+ //
+ OldTpl = NET_RAISE_TPL (NET_TPL_LOCK);
+
+ NetListInsertTail (&MnpServiceData->ChildrenList, &Instance->InstEntry);
+ MnpServiceData->ChildrenNumber++;
+
+ NET_RESTORE_TPL (OldTpl);
+
+ErrorExit:
+
+ if (EFI_ERROR (Status)) {
+
+ if (Instance->Handle != NULL) {
+
+ gBS->UninstallMultipleProtocolInterfaces (
+ &gEfiManagedNetworkProtocolGuid,
+ &Instance->ManagedNetwork,
+ NULL
+ );
+ }
+
+ NetFreePool (Instance);
+ }
+
+ return Status;
+}
+
+
+/**
+ Destroys a child handle with a set of I/O services.
+
+ @param This Protocol instance pointer.
+ @param ChildHandle Handle of the child to destroy.
+
+ @retval EFI_SUCCES The I/O services were removed from the child
+ handle.
+ @retval EFI_UNSUPPORTED The child handle does not support the I/O services
+ that are being removed.
+ @retval EFI_INVALID_PARAMETER Child handle is not a valid EFI Handle.
+ @retval EFI_ACCESS_DENIED The child handle could not be destroyed because
+ its I/O services are being used.
+ @retval other The child handle was not destroyed.
+
+**/
+EFI_STATUS
+EFIAPI
+MnpServiceBindingDestroyChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ChildHandle
+ )
+{
+ EFI_STATUS Status;
+ MNP_SERVICE_DATA *MnpServiceData;
+ EFI_MANAGED_NETWORK_PROTOCOL *ManagedNetwork;
+ MNP_INSTANCE_DATA *Instance;
+ EFI_TPL OldTpl;
+
+ if ((This == NULL) || (ChildHandle == NULL)) {
+
+ return EFI_INVALID_PARAMETER;
+ }
+
+ MnpServiceData = MNP_SERVICE_DATA_FROM_THIS (This);
+
+ //
+ // Try to retrieve ManagedNetwork Protocol from ChildHandle.
+ //
+ Status = gBS->OpenProtocol (
+ ChildHandle,
+ &gEfiManagedNetworkProtocolGuid,
+ (VOID **) &ManagedNetwork,
+ gMnpDriverBinding.DriverBindingHandle,
+ ChildHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+
+ return EFI_UNSUPPORTED;
+ }
+
+ Instance = MNP_INSTANCE_DATA_FROM_THIS (ManagedNetwork);
+
+ //
+ // MnpServiceBindingDestroyChild may be called twice: first called by
+ // MnpServiceBindingStop, second called by uninstalling the MNP protocol
+ // in this ChildHandle. Use destroyed to make sure the resource clean code
+ // will only excecute once.
+ //
+ if (Instance->Destroyed) {
+
+ return EFI_SUCCESS;
+ }
+
+ Instance->Destroyed = TRUE;
+
+ //
+ // Close the Simple Network protocol.
+ //
+ gBS->CloseProtocol (
+ MnpServiceData->ControllerHandle,
+ &gEfiSimpleNetworkProtocolGuid,
+ gMnpDriverBinding.DriverBindingHandle,
+ ChildHandle
+ );
+
+ //
+ // Uninstall the ManagedNetwork protocol.
+ //
+ Status = gBS->UninstallMultipleProtocolInterfaces (
+ ChildHandle,
+ &gEfiManagedNetworkProtocolGuid,
+ &Instance->ManagedNetwork,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+
+ MNP_DEBUG_ERROR (
+ ("MnpServiceBindingDestroyChild: Failed to uninstall the ManagedNetwork protocol, %r.\n",
+ Status)
+ );
+
+ Instance->Destroyed = FALSE;
+ return Status;
+ }
+
+ OldTpl = NET_RAISE_TPL (NET_TPL_LOCK);
+
+ //
+ // Reset the configuration.
+ //
+ ManagedNetwork->Configure (ManagedNetwork, NULL);
+
+ //
+ // Try to flush the RcvdPacketQueue.
+ //
+ MnpFlushRcvdDataQueue (Instance);
+
+ //
+ // Clean the RxTokenMap.
+ //
+ NetMapClean (&Instance->RxTokenMap);
+
+ //
+ // Remove this instance from the ChildrenList.
+ //
+ NetListRemoveEntry (&Instance->InstEntry);
+ MnpServiceData->ChildrenNumber--;
+
+ NET_RESTORE_TPL (OldTpl);
+
+ NetFreePool (Instance);
+
+ return Status;
+}
+
+//@MT: EFI_DRIVER_ENTRY_POINT (MnpDriverEntryPoint)
+
+EFI_STATUS
+EFIAPI
+MnpDriverEntryPoint (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+/*++
+
+Routine Description:
+
+ The entry point for Mnp driver which installs the driver binding and component name
+ protocol on its ImageHandle.
+
+Arguments:
+
+ ImageHandle - The image handle of the driver.
+ SystemTable - The system table.
+
+Returns:
+
+ EFI_SUCCESS - If the driver binding and component name protocols are successfully
+ installed, otherwise if failed.
+
+--*/
+{
+ return NetLibInstallAllDriverProtocols (
+ ImageHandle,
+ SystemTable,
+ &gMnpDriverBinding,
+ ImageHandle,
+ &gMnpComponentName,
+ NULL,
+ NULL
+ );
+}
diff --git a/MdeModulePkg/Universal/Network/MnpDxe/MnpDriver.h b/MdeModulePkg/Universal/Network/MnpDxe/MnpDriver.h
new file mode 100644
index 0000000000..48ad0960cd
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/MnpDxe/MnpDriver.h
@@ -0,0 +1,136 @@
+/** @file
+
+Copyright (c) 2005 - 2007, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+
+ MnpDriver.h
+
+Abstract:
+
+
+**/
+
+#ifndef _MNP_DRIVER_H_
+#define _MNP_DRIVER_H_
+#include <PiDxe.h>
+
+#include <Protocol/ManagedNetwork.h>
+#include <Protocol/SimpleNetwork.h>
+#include <Protocol/ServiceBinding.h>
+
+#include <Library/DebugLib.h>
+#include <Library/UefiDriverEntryPoint.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiLib.h>
+#include <Library/NetLib.h>
+#include <Library/BaseLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/BaseMemoryLib.h>
+
+#include "MnpDebug.h"
+
+//
+// Required Global Variables
+//
+extern EFI_DRIVER_BINDING_PROTOCOL gMnpDriverBinding;
+extern EFI_COMPONENT_NAME_PROTOCOL gMnpComponentName;
+
+#define MNP_SERVICE_DATA_SIGNATURE EFI_SIGNATURE_32 ('M', 'n', 'p', 'S')
+
+typedef struct _MNP_SERVICE_DATA {
+ UINT32 Signature;
+
+ EFI_HANDLE ControllerHandle;
+
+ EFI_SERVICE_BINDING_PROTOCOL ServiceBinding;
+ EFI_SIMPLE_NETWORK_PROTOCOL *Snp;
+
+ UINT32 Mtu;
+
+ NET_LIST_ENTRY ChildrenList;
+ UINTN ChildrenNumber;
+ UINTN ConfiguredChildrenNumber;
+
+ NET_LIST_ENTRY GroupAddressList;
+ UINT32 GroupAddressCount;
+
+ EFI_EVENT TxTimeoutEvent;
+
+ NET_BUF_QUEUE FreeNbufQue;
+ INTN NbufCnt;
+
+ EFI_EVENT PollTimer;
+ BOOLEAN EnableSystemPoll;
+
+ EFI_EVENT TimeoutCheckTimer;
+
+ UINT32 UnicastCount;
+ UINT32 BroadcastCount;
+ UINT32 MulticastCount;
+ UINT32 PromiscuousCount;
+
+ //
+ // The size of the data buffer in the MNP_PACKET_BUFFER used to
+ // store a packet.
+ //
+ UINT32 BufferLength;
+ NET_BUF *RxNbufCache;
+ UINT8 *TxBuf;
+} MNP_SERVICE_DATA;
+
+#define MNP_SERVICE_DATA_FROM_THIS(a) \
+ CR ( \
+ (a), \
+ MNP_SERVICE_DATA, \
+ ServiceBinding, \
+ MNP_SERVICE_DATA_SIGNATURE \
+ )
+
+EFI_STATUS
+EFIAPI
+MnpDriverBindingSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL * This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL * RemainingDevicePath OPTIONAL
+ );
+
+EFI_STATUS
+EFIAPI
+MnpDriverBindingStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL * This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL * RemainingDevicePath OPTIONAL
+ );
+
+EFI_STATUS
+EFIAPI
+MnpDriverBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer
+ );
+
+EFI_STATUS
+EFIAPI
+MnpServiceBindingCreateChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE *ChildHandle
+ );
+
+EFI_STATUS
+EFIAPI
+MnpServiceBindingDestroyChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ChildHandle
+ );
+
+#endif
diff --git a/MdeModulePkg/Universal/Network/MnpDxe/MnpDxe.inf b/MdeModulePkg/Universal/Network/MnpDxe/MnpDxe.inf
new file mode 100644
index 0000000000..26512cd1da
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/MnpDxe/MnpDxe.inf
@@ -0,0 +1,63 @@
+#/** @file
+# Component name for module Mnp
+#
+# FIX ME!
+# Copyright (c) 2006, Intel Corporation. All right reserved.
+#
+# All rights reserved. This program and the accompanying materials
+# are licensed and made available under the terms and conditions of the BSD License
+# which accompanies this distribution. The full text of the license may be found at
+# http://opensource.org/licenses/bsd-license.php
+#
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+#
+#
+#**/
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = MnpDxe
+ FILE_GUID = 025BBFC7-E6A9-4b8b-82AD-6815A1AEAF4A
+ MODULE_TYPE = DXE_DRIVER
+ VERSION_STRING = 1.0
+ EDK_RELEASE_VERSION = 0x00020000
+ EFI_SPECIFICATION_VERSION = 0x00020000
+
+ ENTRY_POINT = MnpDriverEntryPoint
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 IPF EBC
+#
+
+[Sources.common]
+ MnpDebug.h
+ MnpMain.c
+ MnpIo.c
+ MnpDriver.h
+ ComponentName.c
+ MnpDriver.c
+ MnpConfig.c
+ MnpImpl.h
+
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+
+
+[LibraryClasses]
+ UefiLib
+ UefiBootServicesTableLib
+ UefiDriverEntryPoint
+ DebugLib
+ NetLib
+
+
+[Protocols]
+ gEfiManagedNetworkServiceBindingProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+ gEfiSimpleNetworkProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+ gEfiManagedNetworkProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+
diff --git a/MdeModulePkg/Universal/Network/MnpDxe/MnpDxe.msa b/MdeModulePkg/Universal/Network/MnpDxe/MnpDxe.msa
new file mode 100644
index 0000000000..3ef22ddc03
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/MnpDxe/MnpDxe.msa
@@ -0,0 +1,70 @@
+<ModuleSurfaceArea xmlns="http://www.TianoCore.org/2006/Edk2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <MsaHeader>
+ <ModuleName>MnpDxe</ModuleName>
+ <ModuleType>DXE_DRIVER</ModuleType>
+ <GuidValue>025BBFC7-E6A9-4b8b-82AD-6815A1AEAF4A</GuidValue>
+ <Version>1.0</Version>
+ <Abstract>Component name for module Mnp</Abstract>
+ <Description>FIX ME!</Description>
+ <Copyright>Copyright (c) 2006, Intel Corporation. All right reserved.</Copyright>
+ <License>All rights reserved. This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.</License>
+ <Specification>FRAMEWORK_BUILD_PACKAGING_SPECIFICATION 0x00000052</Specification>
+ </MsaHeader>
+ <ModuleDefinitions>
+ <SupportedArchitectures>IA32 X64 IPF EBC</SupportedArchitectures>
+ <BinaryModule>false</BinaryModule>
+ <OutputFileBasename>MnpDxe</OutputFileBasename>
+ </ModuleDefinitions>
+ <LibraryClassDefinitions>
+ <LibraryClass Usage="ALWAYS_CONSUMED">
+ <Keyword>DebugLib</Keyword>
+ </LibraryClass>
+ <LibraryClass Usage="ALWAYS_CONSUMED">
+ <Keyword>UefiDriverEntryPoint</Keyword>
+ </LibraryClass>
+ <LibraryClass Usage="ALWAYS_CONSUMED">
+ <Keyword>UefiBootServicesTableLib</Keyword>
+ </LibraryClass>
+ <LibraryClass Usage="ALWAYS_CONSUMED">
+ <Keyword>UefiLib</Keyword>
+ </LibraryClass>
+ </LibraryClassDefinitions>
+ <SourceFiles>
+ <Filename>MnpImpl.h</Filename>
+ <Filename>MnpConfig.c</Filename>
+ <Filename>MnpDriver.c</Filename>
+ <Filename>ComponentName.c</Filename>
+ <Filename>MnpDriver.h</Filename>
+ <Filename>MnpIo.c</Filename>
+ <Filename>MnpMain.c</Filename>
+ <Filename>MnpDebug.h</Filename>
+ </SourceFiles>
+ <PackageDependencies>
+ <Package PackageGuid="5e0e9358-46b6-4ae2-8218-4ab8b9bbdcec"/>
+ <Package PackageGuid="68169ab0-d41b-4009-9060-292c253ac43d"/>
+ </PackageDependencies>
+ <Protocols>
+ <Protocol Usage="ALWAYS_CONSUMED">
+ <ProtocolCName>gEfiManagedNetworkProtocolGuid</ProtocolCName>
+ </Protocol>
+ <Protocol Usage="ALWAYS_CONSUMED">
+ <ProtocolCName>gEfiSimpleNetworkProtocolGuid</ProtocolCName>
+ </Protocol>
+ <Protocol Usage="ALWAYS_CONSUMED">
+ <ProtocolCName>gEfiManagedNetworkServiceBindingProtocolGuid</ProtocolCName>
+ </Protocol>
+ </Protocols>
+ <Externs>
+ <Specification>EFI_SPECIFICATION_VERSION 0x00020000</Specification>
+ <Specification>EDK_RELEASE_VERSION 0x00020000</Specification>
+ <Extern>
+ <ModuleEntryPoint>MnpDriverEntryPoint</ModuleEntryPoint>
+ </Extern>
+ </Externs>
+</ModuleSurfaceArea> \ No newline at end of file
diff --git a/MdeModulePkg/Universal/Network/MnpDxe/MnpImpl.h b/MdeModulePkg/Universal/Network/MnpDxe/MnpImpl.h
new file mode 100644
index 0000000000..cdb081d2b4
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/MnpDxe/MnpImpl.h
@@ -0,0 +1,274 @@
+/** @file
+
+Copyright (c) 2005 - 2007, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+
+ MnpImpl.h
+
+Abstract:
+
+
+**/
+
+#ifndef _MNP_IMPL_H_
+#define _MNP_IMPL_H_
+
+#include "MnpDriver.h"
+#include "MnpDebug.h"
+
+#define NET_ETHER_FCS_SIZE 4
+
+#define MNP_SYS_POLL_INTERVAL (2 * TICKS_PER_MS) // 2 milliseconds
+#define MNP_TIMEOUT_CHECK_INTERVAL (50 * TICKS_PER_MS) // 50 milliseconds
+#define MNP_TX_TIMEOUT_TIME (500 * TICKS_PER_MS) // 500 milliseconds
+#define MNP_INIT_NET_BUFFER_NUM 512
+#define MNP_NET_BUFFER_INCREASEMENT 64
+#define MNP_MAX_NET_BUFFER_NUM 65536
+
+#define MNP_MAX_RCVD_PACKET_QUE_SIZE 256
+
+#define MNP_RECEIVE_UNICAST 0x01
+#define MNP_RECEIVE_BROADCAST 0x02
+
+#define UNICAST_PACKET MNP_RECEIVE_UNICAST
+#define BROADCAST_PACKET MNP_RECEIVE_BROADCAST
+
+#define MNP_INSTANCE_DATA_SIGNATURE EFI_SIGNATURE_32 ('M', 'n', 'p', 'I')
+
+#define MNP_INSTANCE_DATA_FROM_THIS(a) \
+ CR ( \
+ (a), \
+ MNP_INSTANCE_DATA, \
+ ManagedNetwork, \
+ MNP_INSTANCE_DATA_SIGNATURE \
+ )
+
+typedef struct _MNP_INSTANCE_DATA {
+ UINT32 Signature;
+
+ MNP_SERVICE_DATA *MnpServiceData;
+
+ EFI_HANDLE Handle;
+
+ NET_LIST_ENTRY InstEntry;
+
+ EFI_MANAGED_NETWORK_PROTOCOL ManagedNetwork;
+
+ BOOLEAN Configured;
+ BOOLEAN Destroyed;
+
+ NET_LIST_ENTRY GroupCtrlBlkList;
+
+ NET_MAP RxTokenMap;
+
+ NET_LIST_ENTRY RxDeliveredPacketQueue;
+ NET_LIST_ENTRY RcvdPacketQueue;
+ UINTN RcvdPacketQueueSize;
+
+ EFI_MANAGED_NETWORK_CONFIG_DATA ConfigData;
+
+ UINT8 ReceiveFilter;
+} MNP_INSTANCE_DATA;
+
+typedef struct _MNP_GROUP_ADDRESS {
+ NET_LIST_ENTRY AddrEntry;
+ EFI_MAC_ADDRESS Address;
+ INTN RefCnt;
+} MNP_GROUP_ADDRESS;
+
+typedef struct _MNP_GROUP_CONTROL_BLOCK {
+ NET_LIST_ENTRY CtrlBlkEntry;
+ MNP_GROUP_ADDRESS *GroupAddress;
+} MNP_GROUP_CONTROL_BLOCK;
+
+typedef struct _MNP_RXDATA_WRAP {
+ NET_LIST_ENTRY WrapEntry;
+ MNP_INSTANCE_DATA *Instance;
+ EFI_MANAGED_NETWORK_RECEIVE_DATA RxData;
+ NET_BUF *Nbuf;
+ UINT64 TimeoutTick;
+} MNP_RXDATA_WRAP;
+
+EFI_STATUS
+MnpInitializeServiceData (
+ IN MNP_SERVICE_DATA *MnpServiceData,
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_HANDLE ControllerHandle
+ );
+
+VOID
+MnpFlushServiceData (
+ MNP_SERVICE_DATA *MnpServiceData
+ );
+
+VOID
+MnpInitializeInstanceData (
+ IN MNP_SERVICE_DATA *MnpServiceData,
+ IN MNP_INSTANCE_DATA *Instance
+ );
+
+EFI_STATUS
+MnpTokenExist (
+ IN NET_MAP *Map,
+ IN NET_MAP_ITEM *Item,
+ IN VOID *Arg
+ );
+
+EFI_STATUS
+MnpCancelTokens (
+ IN NET_MAP *Map,
+ IN NET_MAP_ITEM *Item,
+ IN VOID *Arg
+ );
+
+VOID
+MnpFlushRcvdDataQueue (
+ IN MNP_INSTANCE_DATA *Instance
+ );
+
+EFI_STATUS
+MnpConfigureInstance (
+ IN MNP_INSTANCE_DATA *Instance,
+ IN EFI_MANAGED_NETWORK_CONFIG_DATA *ConfigData OPTIONAL
+ );
+
+EFI_STATUS
+MnpGroupOp (
+ IN MNP_INSTANCE_DATA *Instance,
+ IN BOOLEAN JoinFlag,
+ IN EFI_MAC_ADDRESS *MacAddr OPTIONAL,
+ IN MNP_GROUP_CONTROL_BLOCK *CtrlBlk OPTIONAL
+ );
+
+BOOLEAN
+MnpIsValidTxToken (
+ IN MNP_INSTANCE_DATA *Instance,
+ IN EFI_MANAGED_NETWORK_COMPLETION_TOKEN *Token
+ );
+
+VOID
+MnpBuildTxPacket (
+ IN MNP_SERVICE_DATA *MnpServiceData,
+ IN EFI_MANAGED_NETWORK_TRANSMIT_DATA *TxData,
+ OUT UINT8 **PktBuf,
+ OUT UINT32 *PktLen
+ );
+
+EFI_STATUS
+MnpSyncSendPacket (
+ IN MNP_SERVICE_DATA *MnpServiceData,
+ IN UINT8 *Packet,
+ IN UINT32 Length,
+ IN EFI_MANAGED_NETWORK_COMPLETION_TOKEN *Token
+ );
+
+EFI_STATUS
+MnpInstanceDeliverPacket (
+ IN MNP_INSTANCE_DATA *Instance
+ );
+
+VOID
+EFIAPI
+MnpRecycleRxData (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ );
+
+EFI_STATUS
+MnpReceivePacket (
+ IN MNP_SERVICE_DATA *MnpServiceData
+ );
+
+NET_BUF *
+MnpAllocNbuf (
+ IN MNP_SERVICE_DATA *MnpServiceData
+ );
+
+VOID
+MnpFreeNbuf (
+ IN MNP_SERVICE_DATA *MnpServiceData,
+ IN NET_BUF *Nbuf
+ );
+
+VOID
+EFIAPI
+MnpCheckPacketTimeout (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ );
+
+VOID
+EFIAPI
+MnpSystemPoll (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ );
+
+EFI_STATUS
+EFIAPI
+MnpGetModeData (
+ IN EFI_MANAGED_NETWORK_PROTOCOL *This,
+ OUT EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData OPTIONAL,
+ OUT EFI_SIMPLE_NETWORK_MODE *SnpModeData OPTIONAL
+ );
+
+EFI_STATUS
+EFIAPI
+MnpConfigure (
+ IN EFI_MANAGED_NETWORK_PROTOCOL *This,
+ IN EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData OPTIONAL
+ );
+
+EFI_STATUS
+EFIAPI
+MnpMcastIpToMac (
+ IN EFI_MANAGED_NETWORK_PROTOCOL *This,
+ IN BOOLEAN Ipv6Flag,
+ IN EFI_IP_ADDRESS *IpAddress,
+ OUT EFI_MAC_ADDRESS *MacAddress
+ );
+
+EFI_STATUS
+EFIAPI
+MnpGroups (
+ IN EFI_MANAGED_NETWORK_PROTOCOL *This,
+ IN BOOLEAN JoinFlag,
+ IN EFI_MAC_ADDRESS *MacAddress OPTIONAL
+ );
+
+EFI_STATUS
+EFIAPI
+MnpTransmit (
+ IN EFI_MANAGED_NETWORK_PROTOCOL *This,
+ IN EFI_MANAGED_NETWORK_COMPLETION_TOKEN *Token
+ );
+
+EFI_STATUS
+EFIAPI
+MnpCancel (
+ IN EFI_MANAGED_NETWORK_PROTOCOL *This,
+ IN EFI_MANAGED_NETWORK_COMPLETION_TOKEN *Token OPTIONAL
+ );
+
+EFI_STATUS
+EFIAPI
+MnpReceive (
+ IN EFI_MANAGED_NETWORK_PROTOCOL *This,
+ IN EFI_MANAGED_NETWORK_COMPLETION_TOKEN *Token
+ );
+
+EFI_STATUS
+EFIAPI
+MnpPoll (
+ IN EFI_MANAGED_NETWORK_PROTOCOL *This
+ );
+
+#endif
diff --git a/MdeModulePkg/Universal/Network/MnpDxe/MnpIo.c b/MdeModulePkg/Universal/Network/MnpDxe/MnpIo.c
new file mode 100644
index 0000000000..cde235912f
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/MnpDxe/MnpIo.c
@@ -0,0 +1,1087 @@
+/** @file
+
+Copyright (c) 2005 - 2007, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+
+ MnpIo.c
+
+Abstract:
+
+ Implementation of Managed Network Protocol I/O functions.
+
+
+**/
+
+#include <Library/NetLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/BaseLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include "MnpImpl.h"
+
+
+/**
+ Validates the Mnp transmit token.
+
+ @param Instance Pointer to the Mnp instance context data.
+ @param Token Pointer to the transmit token to check.
+
+ @return The Token is valid or not.
+
+**/
+BOOLEAN
+MnpIsValidTxToken (
+ IN MNP_INSTANCE_DATA *Instance,
+ IN EFI_MANAGED_NETWORK_COMPLETION_TOKEN *Token
+ )
+{
+ MNP_SERVICE_DATA *MnpServiceData;
+ EFI_SIMPLE_NETWORK_MODE *SnpMode;
+ EFI_MANAGED_NETWORK_TRANSMIT_DATA *TxData;
+ UINT32 Index;
+ UINT32 TotalLength;
+ EFI_MANAGED_NETWORK_FRAGMENT_DATA *FragmentTable;
+
+ MnpServiceData = Instance->MnpServiceData;
+ NET_CHECK_SIGNATURE (MnpServiceData, MNP_SERVICE_DATA_SIGNATURE);
+
+ SnpMode = MnpServiceData->Snp->Mode;
+ TxData = Token->Packet.TxData;
+
+ if ((Token->Event == NULL) || (TxData == NULL) || (TxData->FragmentCount == 0)) {
+ //
+ // The token is invalid if the Event is NULL, or the TxData is NULL, or
+ // the fragment count is zero.
+ //
+ MNP_DEBUG_WARN (("MnpIsValidTxToken: Invalid Token.\n"));
+ return FALSE;
+ }
+
+ if ((TxData->DestinationAddress != NULL) && (TxData->HeaderLength != 0)) {
+ //
+ // The token is invalid if the HeaderLength isn't zero while the DestinationAddress
+ // is NULL (The destination address is already put into the packet).
+ //
+ MNP_DEBUG_WARN (("MnpIsValidTxToken: DestinationAddress isn't NULL, HeaderLength must be 0.\n"));
+ return FALSE;
+ }
+
+ TotalLength = 0;
+ FragmentTable = TxData->FragmentTable;
+ for (Index = 0; Index < TxData->FragmentCount; Index++) {
+
+ if ((FragmentTable[Index].FragmentLength == 0) || (FragmentTable[Index].FragmentBuffer == NULL)) {
+ //
+ // The token is invalid if any FragmentLength is zero or any FragmentBuffer is NULL.
+ //
+ MNP_DEBUG_WARN (("MnpIsValidTxToken: Invalid FragmentLength or FragmentBuffer.\n"));
+ return FALSE;
+ }
+
+ TotalLength += FragmentTable[Index].FragmentLength;
+ }
+
+ if ((TxData->DestinationAddress == NULL) && (FragmentTable[0].FragmentLength < TxData->HeaderLength)) {
+ //
+ // Media header is split between fragments.
+ //
+ return FALSE;
+ }
+
+ if (TotalLength != (TxData->DataLength + TxData->HeaderLength)) {
+ //
+ // The length calculated from the fragment information doesn't equal to the
+ // sum of the DataLength and the HeaderLength.
+ //
+ MNP_DEBUG_WARN (("MnpIsValidTxData: Invalid Datalength compared with the sum of fragment length.\n"));
+ return FALSE;
+ }
+
+ if (TxData->DataLength > MnpServiceData->Mtu) {
+ //
+ // The total length is larger than the MTU.
+ //
+ MNP_DEBUG_WARN (("MnpIsValidTxData: TxData->DataLength exceeds Mtu.\n"));
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+/**
+ Build the packet to transmit from the TxData passed in.
+
+ @param MnpServiceData Pointer to the mnp service context data.
+ @param TxData Pointer to the transmit data containing the
+ information to build the packet.
+ @param PktBuf Pointer to record the address of the packet.
+ @param PktLen Pointer to a UINT32 variable used to record the
+ packet's length.
+
+ @return None.
+
+**/
+VOID
+MnpBuildTxPacket (
+ IN MNP_SERVICE_DATA *MnpServiceData,
+ IN EFI_MANAGED_NETWORK_TRANSMIT_DATA *TxData,
+ OUT UINT8 **PktBuf,
+ OUT UINT32 *PktLen
+ )
+{
+ EFI_SIMPLE_NETWORK_MODE *SnpMode;
+ UINT8 *DstPos;
+ UINT16 Index;
+
+ if ((TxData->DestinationAddress == NULL) && (TxData->FragmentCount == 1)) {
+ //
+ // Media header is in FragmentTable and there is only one fragment,
+ // use fragment buffer directly.
+ //
+ *PktBuf = TxData->FragmentTable[0].FragmentBuffer;
+ *PktLen = TxData->FragmentTable[0].FragmentLength;
+ } else {
+ //
+ // Either media header isn't in FragmentTable or there is more than
+ // one fragment, copy the data into the packet buffer. Reserve the
+ // media header space if necessary.
+ //
+ SnpMode = MnpServiceData->Snp->Mode;
+ DstPos = MnpServiceData->TxBuf;
+
+ *PktLen = 0;
+ if (TxData->DestinationAddress != NULL) {
+ //
+ // If dest address is not NULL, move DstPos to reserve space for the
+ // media header. Add the media header length to buflen.
+ //
+ DstPos += SnpMode->MediaHeaderSize;
+ *PktLen += SnpMode->MediaHeaderSize;
+ }
+
+ for (Index = 0; Index < TxData->FragmentCount; Index++) {
+ //
+ // Copy the data.
+ //
+ NetCopyMem (
+ DstPos,
+ TxData->FragmentTable[Index].FragmentBuffer,
+ TxData->FragmentTable[Index].FragmentLength
+ );
+ DstPos += TxData->FragmentTable[Index].FragmentLength;
+ }
+
+ //
+ // Set the buffer pointer and the buffer length.
+ //
+ *PktBuf = MnpServiceData->TxBuf;
+ *PktLen += TxData->DataLength + TxData->HeaderLength;
+ }
+}
+
+
+/**
+ Synchronously send out the packet.
+
+ @param MnpServiceData Pointer to the mnp service context data.
+ @param Packet Pointer to the pakcet buffer.
+ @param Length The length of the packet.
+ @param Token Pointer to the token the packet generated from.
+
+ @retval EFI_SUCCESS The packet is sent out.
+ @retval EFI_TIMEOUT Time out occurs, the packet isn't sent.
+ @retval EFI_DEVICE_ERROR An unexpected network error occurs.
+
+**/
+EFI_STATUS
+MnpSyncSendPacket (
+ IN MNP_SERVICE_DATA *MnpServiceData,
+ IN UINT8 *Packet,
+ IN UINT32 Length,
+ IN EFI_MANAGED_NETWORK_COMPLETION_TOKEN *Token
+ )
+{
+ EFI_STATUS Status;
+ EFI_SIMPLE_NETWORK_PROTOCOL *Snp;
+ EFI_MANAGED_NETWORK_TRANSMIT_DATA *TxData;
+ UINT32 HeaderSize;
+ UINT8 *TxBuf;
+
+ Snp = MnpServiceData->Snp;
+ TxData = Token->Packet.TxData;
+
+ HeaderSize = Snp->Mode->MediaHeaderSize - TxData->HeaderLength;
+
+ //
+ // Start the timeout event.
+ //
+ Status = gBS->SetTimer (
+ MnpServiceData->TxTimeoutEvent,
+ TimerRelative,
+ MNP_TX_TIMEOUT_TIME
+ );
+ if (EFI_ERROR (Status)) {
+
+ goto SIGNAL_TOKEN;
+ }
+
+ for (;;) {
+ //
+ // Transmit the packet through SNP.
+ //
+ Status = Snp->Transmit (
+ Snp,
+ HeaderSize,
+ Length,
+ Packet,
+ TxData->SourceAddress,
+ TxData->DestinationAddress,
+ &TxData->ProtocolType
+ );
+ if ((Status != EFI_SUCCESS) && (Status != EFI_NOT_READY)) {
+
+ Status = EFI_DEVICE_ERROR;
+ break;
+ }
+
+ //
+ // If Status is EFI_SUCCESS, the packet is put in the transmit queue.
+ // if Status is EFI_NOT_READY, the transmit engine of the network interface is busy.
+ // Both need to sync SNP.
+ //
+ TxBuf = NULL;
+ do {
+ //
+ // Get the recycled transmit buffer status.
+ //
+ Snp->GetStatus (Snp, NULL, &TxBuf);
+
+ if (!EFI_ERROR (gBS->CheckEvent (MnpServiceData->TxTimeoutEvent))) {
+
+ Status = EFI_TIMEOUT;
+ break;
+ }
+ } while (TxBuf == NULL);
+
+ if ((Status == EFI_SUCCESS) || (Status == EFI_TIMEOUT)) {
+
+ break;
+ } else {
+ //
+ // Status is EFI_NOT_READY. Restart the timer event and call Snp->Transmit again.
+ //
+ gBS->SetTimer (
+ MnpServiceData->TxTimeoutEvent,
+ TimerRelative,
+ MNP_TX_TIMEOUT_TIME
+ );
+ }
+ }
+
+ //
+ // Cancel the timer event.
+ //
+ gBS->SetTimer (MnpServiceData->TxTimeoutEvent, TimerCancel, 0);
+
+SIGNAL_TOKEN:
+
+ Token->Status = Status;
+ gBS->SignalEvent (Token->Event);
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Try to deliver the received packet to the instance.
+
+ @param Instance Pointer to the mnp instance context data.
+
+ @retval EFI_SUCCESS The received packet is delivered, or there is no
+ packet to deliver, or there is no available receive
+ token.
+ @retval EFI_OUT_OF_RESOURCES The deliver fails due to lack of memory resource.
+
+**/
+EFI_STATUS
+MnpInstanceDeliverPacket (
+ IN MNP_INSTANCE_DATA *Instance
+ )
+{
+ MNP_SERVICE_DATA *MnpServiceData;
+ MNP_RXDATA_WRAP *RxDataWrap;
+ NET_BUF *DupNbuf;
+ EFI_MANAGED_NETWORK_RECEIVE_DATA *RxData;
+ EFI_SIMPLE_NETWORK_MODE *SnpMode;
+ EFI_MANAGED_NETWORK_COMPLETION_TOKEN *RxToken;
+
+ MnpServiceData = Instance->MnpServiceData;
+ NET_CHECK_SIGNATURE (MnpServiceData, MNP_SERVICE_DATA_SIGNATURE);
+
+ if (NetMapIsEmpty (&Instance->RxTokenMap) || NetListIsEmpty (&Instance->RcvdPacketQueue)) {
+ //
+ // No pending received data or no available receive token, return.
+ //
+ return EFI_SUCCESS;
+ }
+
+ ASSERT (Instance->RcvdPacketQueueSize != 0);
+
+ RxDataWrap = NET_LIST_HEAD (&Instance->RcvdPacketQueue, MNP_RXDATA_WRAP, WrapEntry);
+ if (RxDataWrap->Nbuf->RefCnt > 2) {
+ //
+ // There are other instances share this Nbuf, duplicate to get a
+ // copy to allow the instance to do R/W operations.
+ //
+ DupNbuf = MnpAllocNbuf (MnpServiceData);
+ if (DupNbuf == NULL) {
+ MNP_DEBUG_WARN (("MnpDeliverPacket: Failed to allocate a free Nbuf.\n"));
+
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Duplicate the net buffer.
+ //
+ NetbufDuplicate (RxDataWrap->Nbuf, DupNbuf, 0);
+ MnpFreeNbuf (MnpServiceData, RxDataWrap->Nbuf);
+ RxDataWrap->Nbuf = DupNbuf;
+ }
+
+ //
+ // All resources are OK, remove the packet from the queue.
+ //
+ NetListRemoveHead (&Instance->RcvdPacketQueue);
+ Instance->RcvdPacketQueueSize--;
+
+ RxData = &RxDataWrap->RxData;
+ SnpMode = MnpServiceData->Snp->Mode;
+
+ //
+ // Set all the buffer pointers.
+ //
+ RxData->MediaHeader = NetbufGetByte (RxDataWrap->Nbuf, 0, NULL);
+ RxData->DestinationAddress = RxData->MediaHeader;
+ RxData->SourceAddress = (UINT8 *) RxData->MediaHeader + SnpMode->HwAddressSize;
+ RxData->PacketData = (UINT8 *) RxData->MediaHeader + SnpMode->MediaHeaderSize;
+
+ //
+ // Insert this RxDataWrap into the delivered queue.
+ //
+ NetListInsertTail (&Instance->RxDeliveredPacketQueue, &RxDataWrap->WrapEntry);
+
+ //
+ // Get the receive token from the RxTokenMap.
+ //
+ RxToken = NetMapRemoveHead (&Instance->RxTokenMap, NULL);
+
+ //
+ // Signal this token's event.
+ //
+ RxToken->Packet.RxData = &RxDataWrap->RxData;
+ RxToken->Status = EFI_SUCCESS;
+ gBS->SignalEvent (RxToken->Event);
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Deliver the received packet for the instances belonging to the MnpServiceData.
+
+ @param MnpServiceData Pointer to the mnp service context data.
+
+ @return None.
+
+**/
+STATIC
+VOID
+MnpDeliverPacket (
+ IN MNP_SERVICE_DATA *MnpServiceData
+ )
+{
+ NET_LIST_ENTRY *Entry;
+ MNP_INSTANCE_DATA *Instance;
+
+ NET_CHECK_SIGNATURE (MnpServiceData, MNP_SERVICE_DATA_SIGNATURE);
+
+ NET_LIST_FOR_EACH (Entry, &MnpServiceData->ChildrenList) {
+ Instance = NET_LIST_USER_STRUCT (Entry, MNP_INSTANCE_DATA, InstEntry);
+ NET_CHECK_SIGNATURE (Instance, MNP_INSTANCE_DATA_SIGNATURE);
+
+ //
+ // Try to deliver packet for this instance.
+ //
+ MnpInstanceDeliverPacket (Instance);
+ }
+}
+
+
+/**
+ Recycle the RxData and other resources used to hold and deliver the received
+ packet.
+
+ @param Event The event this notify function registered to.
+ @param Context Pointer to the context data registerd to the Event.
+
+ @return None.
+
+**/
+VOID
+EFIAPI
+MnpRecycleRxData (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ MNP_RXDATA_WRAP *RxDataWrap;
+ MNP_SERVICE_DATA *MnpServiceData;
+
+ ASSERT (Context != NULL);
+
+ RxDataWrap = (MNP_RXDATA_WRAP *) Context;
+ NET_CHECK_SIGNATURE (RxDataWrap->Instance, MNP_INSTANCE_DATA_SIGNATURE);
+
+ ASSERT (RxDataWrap->Nbuf != NULL);
+
+ MnpServiceData = RxDataWrap->Instance->MnpServiceData;
+ NET_CHECK_SIGNATURE (MnpServiceData, MNP_SERVICE_DATA_SIGNATURE);
+
+ //
+ // Free this Nbuf.
+ //
+ MnpFreeNbuf (MnpServiceData, RxDataWrap->Nbuf);
+ RxDataWrap->Nbuf = NULL;
+
+ //
+ // Close the recycle event.
+ //
+ gBS->CloseEvent (RxDataWrap->RxData.RecycleEvent);
+
+ //
+ // Remove this Wrap entry from the list.
+ //
+ NetListRemoveEntry (&RxDataWrap->WrapEntry);
+
+ NetFreePool (RxDataWrap);
+}
+
+
+/**
+ Queue the received packet into instance's receive queue.
+
+ @param Instance Pointer to the mnp instance context data.
+ @param RxDataWrap Pointer to the Wrap structure containing the
+ received data and other information.
+
+ @return None.
+
+**/
+STATIC
+VOID
+MnpQueueRcvdPacket (
+ IN MNP_INSTANCE_DATA *Instance,
+ IN MNP_RXDATA_WRAP *RxDataWrap
+ )
+{
+ MNP_RXDATA_WRAP *OldRxDataWrap;
+
+ NET_CHECK_SIGNATURE (Instance, MNP_INSTANCE_DATA_SIGNATURE);
+
+ //
+ // Check the queue size. If it exceeds the limit, drop one packet
+ // from the head.
+ //
+ if (Instance->RcvdPacketQueueSize == MNP_MAX_RCVD_PACKET_QUE_SIZE) {
+
+ MNP_DEBUG_WARN (("MnpQueueRcvdPacket: Drop one packet bcz queue size limit reached.\n"));
+
+ //
+ // Get the oldest packet.
+ //
+ OldRxDataWrap = NET_LIST_HEAD (
+ &Instance->RcvdPacketQueue,
+ MNP_RXDATA_WRAP,
+ WrapEntry
+ );
+
+ //
+ // Recycle this OldRxDataWrap, this entry will be removed by the callee.
+ //
+ MnpRecycleRxData (NULL, (VOID *) OldRxDataWrap);
+ Instance->RcvdPacketQueueSize--;
+ }
+
+ //
+ // Update the timeout tick using the configured parameter.
+ //
+ RxDataWrap->TimeoutTick = Instance->ConfigData.ReceivedQueueTimeoutValue;
+
+ //
+ // Insert this Wrap into the instance queue.
+ //
+ NetListInsertTail (&Instance->RcvdPacketQueue, &RxDataWrap->WrapEntry);
+ Instance->RcvdPacketQueueSize++;
+}
+
+
+/**
+ Match the received packet with the instance receive filters.
+
+ @param Instance Pointer to the mnp instance context data.
+ @param RxData Pointer to the EFI_MANAGED_NETWORK_RECEIVE_DATA.
+ @param GroupAddress Pointer to the GroupAddress, the GroupAddress is
+ non-NULL and it contains the destination multicast
+ mac address of the received packet if the packet
+ destinated to a multicast mac address.
+ @param PktAttr The received packets attribute.
+
+ @return The received packet matches the instance's receive filters or not.
+
+**/
+STATIC
+BOOLEAN
+MnpMatchPacket (
+ IN MNP_INSTANCE_DATA *Instance,
+ IN EFI_MANAGED_NETWORK_RECEIVE_DATA *RxData,
+ IN MNP_GROUP_ADDRESS *GroupAddress OPTIONAL,
+ IN UINT8 PktAttr
+ )
+{
+ EFI_MANAGED_NETWORK_CONFIG_DATA *ConfigData;
+ NET_LIST_ENTRY *Entry;
+ MNP_GROUP_CONTROL_BLOCK *GroupCtrlBlk;
+
+ NET_CHECK_SIGNATURE (Instance, MNP_INSTANCE_DATA_SIGNATURE);
+
+ ConfigData = &Instance->ConfigData;
+
+ if (ConfigData->EnablePromiscuousReceive) {
+ //
+ // Always match if this instance is configured to be promiscuous.
+ //
+ return TRUE;
+ }
+ //
+ // Check the protocol type.
+ //
+ if ((ConfigData->ProtocolTypeFilter != 0) && (ConfigData->ProtocolTypeFilter != RxData->ProtocolType)) {
+ return FALSE;
+ }
+
+ //
+ // The protocol type is matched, check receive filter, include unicast and broadcast.
+ //
+ if ((Instance->ReceiveFilter & PktAttr) != 0) {
+ return TRUE;
+ }
+
+ //
+ // Check multicast addresses.
+ //
+ if (ConfigData->EnableMulticastReceive && RxData->MulticastFlag) {
+
+ ASSERT (GroupAddress != NULL);
+
+ NET_LIST_FOR_EACH (Entry, &Instance->GroupCtrlBlkList) {
+
+ GroupCtrlBlk = NET_LIST_USER_STRUCT (Entry, MNP_GROUP_CONTROL_BLOCK, CtrlBlkEntry);
+ if (GroupCtrlBlk->GroupAddress == GroupAddress) {
+ //
+ // The instance is configured to receiveing packets destinated to this
+ // multicast address.
+ //
+ return TRUE;
+ }
+ }
+ }
+
+ //
+ // No match.
+ //
+ return FALSE;
+}
+
+
+/**
+ Analyse the received packets.
+
+ @param MnpServiceData Pointer to the mnp service context data.
+ @param Nbuf Pointer to the net buffer holding the received
+ packet.
+ @param RxData Pointer to the buffer used to save the analysed
+ result in EFI_MANAGED_NETWORK_RECEIVE_DATA.
+ @param GroupAddress Pointer to pointer to a MNP_GROUP_ADDRESS used to
+ pass out the address of the multicast address the
+ received packet destinated to.
+ @param PktAttr Pointer to the buffer used to save the analysed
+ packet attribute.
+
+ @return None.
+
+**/
+STATIC
+VOID
+MnpAnalysePacket (
+ IN MNP_SERVICE_DATA *MnpServiceData,
+ IN NET_BUF *Nbuf,
+ IN EFI_MANAGED_NETWORK_RECEIVE_DATA *RxData,
+ OUT MNP_GROUP_ADDRESS **GroupAddress,
+ OUT UINT8 *PktAttr
+ )
+{
+ EFI_SIMPLE_NETWORK_MODE *SnpMode;
+ UINT8 *BufPtr;
+ NET_LIST_ENTRY *Entry;
+
+ SnpMode = MnpServiceData->Snp->Mode;
+
+ //
+ // Get the packet buffer.
+ //
+ BufPtr = NetbufGetByte (Nbuf, 0, NULL);
+ ASSERT (BufPtr != NULL);
+
+ //
+ // Set the initial values.
+ //
+ RxData->BroadcastFlag = FALSE;
+ RxData->MulticastFlag = FALSE;
+ RxData->PromiscuousFlag = FALSE;
+ *PktAttr = UNICAST_PACKET;
+
+ if (!NET_MAC_EQUAL (&SnpMode->CurrentAddress, BufPtr, SnpMode->HwAddressSize)) {
+ //
+ // This packet isn't destinated to our current mac address, it't not unicast.
+ //
+ *PktAttr = 0;
+
+ if (NET_MAC_EQUAL (&SnpMode->BroadcastAddress, BufPtr, SnpMode->HwAddressSize)) {
+ //
+ // It's broadcast.
+ //
+ RxData->BroadcastFlag = TRUE;
+ *PktAttr = BROADCAST_PACKET;
+ } else if ((*BufPtr & 0x01) == 0x1) {
+ //
+ // It's multicast, try to match the multicast filters.
+ //
+ NET_LIST_FOR_EACH (Entry, &MnpServiceData->GroupAddressList) {
+
+ *GroupAddress = NET_LIST_USER_STRUCT (Entry, MNP_GROUP_ADDRESS, AddrEntry);
+ if (NET_MAC_EQUAL (BufPtr, &((*GroupAddress)->Address), SnpMode->HwAddressSize)) {
+ RxData->MulticastFlag = TRUE;
+ break;
+ }
+ }
+
+ if (!RxData->MulticastFlag) {
+ //
+ // No match, set GroupAddress to NULL. This multicast packet must
+ // be the result of PROMISUCOUS or PROMISUCOUS_MULTICAST flag is on.
+ //
+ *GroupAddress = NULL;
+ RxData->PromiscuousFlag = TRUE;
+
+ if (MnpServiceData->PromiscuousCount == 0) {
+ //
+ // Skip the below code, there is no receiver of this packet.
+ //
+ return ;
+ }
+ }
+ } else {
+ RxData->PromiscuousFlag = TRUE;
+ }
+ }
+
+ NetZeroMem (&RxData->Timestamp, sizeof (EFI_TIME));
+
+ //
+ // Fill the common parts of RxData.
+ //
+ RxData->PacketLength = Nbuf->TotalSize;
+ RxData->HeaderLength = SnpMode->MediaHeaderSize;
+ RxData->AddressLength = SnpMode->HwAddressSize;
+ RxData->DataLength = RxData->PacketLength - RxData->HeaderLength;
+ RxData->ProtocolType = NTOHS (*(UINT16 *) (BufPtr + 2 * SnpMode->HwAddressSize));
+}
+
+
+/**
+ Wrap the RxData.
+
+ @param Instance Pointer to the mnp instance context data.
+ @param RxData Pointer to the receive data to wrap.
+
+ @return Pointer to a MNP_RXDATA_WRAP which wraps the RxData.
+
+**/
+STATIC
+MNP_RXDATA_WRAP *
+MnpWrapRxData (
+ IN MNP_INSTANCE_DATA *Instance,
+ IN EFI_MANAGED_NETWORK_RECEIVE_DATA *RxData
+ )
+{
+ EFI_STATUS Status;
+ MNP_RXDATA_WRAP *RxDataWrap;
+
+ //
+ // Allocate memory.
+ //
+ RxDataWrap = NetAllocatePool (sizeof (MNP_RXDATA_WRAP));
+ if (RxDataWrap == NULL) {
+ MNP_DEBUG_ERROR (("MnpDispatchPacket: Failed to allocate a MNP_RXDATA_WRAP.\n"));
+ return NULL;
+ }
+
+ RxDataWrap->Instance = Instance;
+
+ //
+ // Fill the RxData in RxDataWrap,
+ //
+ RxDataWrap->RxData = *RxData;
+
+ //
+ // Create the recycle event.
+ //
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ NET_TPL_RECYCLE,
+ MnpRecycleRxData,
+ RxDataWrap,
+ &RxDataWrap->RxData.RecycleEvent
+ );
+ if (EFI_ERROR (Status)) {
+
+ MNP_DEBUG_ERROR (("MnpDispatchPacket: gBS->CreateEvent failed, %r.\n", Status));
+ NetFreePool (RxDataWrap);
+ return NULL;
+ }
+
+ return RxDataWrap;
+}
+
+
+/**
+ Enqueue the received the packets to the instances belonging to the
+ MnpServiceData.
+
+ @param MnpServiceData Pointer to the mnp service context data.
+ @param Nbuf Pointer to the net buffer representing the received
+ packet.
+
+ @return None.
+
+**/
+STATIC
+VOID
+MnpEnqueuePacket (
+ IN MNP_SERVICE_DATA *MnpServiceData,
+ IN NET_BUF *Nbuf
+ )
+{
+ NET_LIST_ENTRY *Entry;
+ MNP_INSTANCE_DATA *Instance;
+ EFI_MANAGED_NETWORK_RECEIVE_DATA RxData;
+ UINT8 PktAttr;
+ MNP_GROUP_ADDRESS *GroupAddress;
+ MNP_RXDATA_WRAP *RxDataWrap;
+
+ //
+ // First, analyse the packet header.
+ //
+ MnpAnalysePacket (MnpServiceData, Nbuf, &RxData, &GroupAddress, &PktAttr);
+
+ if (RxData.PromiscuousFlag && (MnpServiceData->PromiscuousCount == 0)) {
+ //
+ // No receivers, no more action need.
+ //
+ return ;
+ }
+
+ //
+ // Iterate the children to find match.
+ //
+ NET_LIST_FOR_EACH (Entry, &MnpServiceData->ChildrenList) {
+
+ Instance = NET_LIST_USER_STRUCT (Entry, MNP_INSTANCE_DATA, InstEntry);
+ NET_CHECK_SIGNATURE (Instance, MNP_INSTANCE_DATA_SIGNATURE);
+
+ if (!Instance->Configured) {
+ continue;
+ }
+
+ //
+ // Check the packet against the instance receive filters.
+ //
+ if (MnpMatchPacket (Instance, &RxData, GroupAddress, PktAttr)) {
+
+ //
+ // Wrap the RxData.
+ //
+ RxDataWrap = MnpWrapRxData (Instance, &RxData);
+ if (RxDataWrap == NULL) {
+ continue;
+ }
+
+ //
+ // Associate RxDataWrap with Nbuf and increase the RefCnt.
+ //
+ RxDataWrap->Nbuf = Nbuf;
+ NET_GET_REF (RxDataWrap->Nbuf);
+
+ //
+ // Queue the packet into the instance queue.
+ //
+ MnpQueueRcvdPacket (Instance, RxDataWrap);
+ }
+ }
+}
+
+
+/**
+ Try to receive a packet and deliver it.
+
+ @param MnpServiceData Pointer to the mnp service context data.
+
+ @retval EFI_SUCCESS add return value to function comment
+ @retval EFI_NOT_STARTED The simple network protocol is not started.
+ @retval EFI_NOT_READY No packet received.
+ @retval EFI_DEVICE_ERROR An unexpected error occurs.
+
+**/
+EFI_STATUS
+MnpReceivePacket (
+ IN MNP_SERVICE_DATA *MnpServiceData
+ )
+{
+ EFI_STATUS Status;
+ EFI_SIMPLE_NETWORK_PROTOCOL *Snp;
+ NET_BUF *Nbuf;
+ UINT8 *BufPtr;
+ UINTN BufLen;
+ UINTN HeaderSize;
+ UINT32 Trimmed;
+
+ NET_CHECK_SIGNATURE (MnpServiceData, MNP_SERVICE_DATA_SIGNATURE);
+
+ Snp = MnpServiceData->Snp;
+ if (Snp->Mode->State != EfiSimpleNetworkInitialized) {
+ //
+ // The simple network protocol is not started.
+ //
+ return EFI_NOT_STARTED;
+ }
+
+ if (NetListIsEmpty (&MnpServiceData->ChildrenList)) {
+ //
+ // There is no child, no need to receive packets.
+ //
+ return EFI_SUCCESS;
+ }
+
+ if (MnpServiceData->RxNbufCache == NULL) {
+ //
+ // Try to get a new buffer as there may be buffers recycled.
+ //
+ MnpServiceData->RxNbufCache = MnpAllocNbuf (MnpServiceData);
+
+ if (MnpServiceData->RxNbufCache == NULL) {
+ //
+ // No availabe buffer in the buffer pool.
+ //
+ return EFI_DEVICE_ERROR;
+ }
+
+ NetbufAllocSpace (
+ MnpServiceData->RxNbufCache,
+ MnpServiceData->BufferLength,
+ NET_BUF_TAIL
+ );
+ }
+
+ Nbuf = MnpServiceData->RxNbufCache;
+ BufLen = Nbuf->TotalSize;
+ BufPtr = NetbufGetByte (Nbuf, 0, NULL);
+ ASSERT (BufPtr != NULL);
+
+ //
+ // Receive packet through Snp.
+ //
+ Status = Snp->Receive (Snp, &HeaderSize, &BufLen, BufPtr, NULL, NULL, NULL);
+ if (EFI_ERROR (Status)) {
+
+ DEBUG_CODE (
+ if (Status != EFI_NOT_READY) {
+ MNP_DEBUG_ERROR (("MnpReceivePacket: Snp->Receive() = %r.\n", Status));
+ }
+ );
+
+ return Status;
+ }
+
+ //
+ // Sanity check.
+ //
+ if ((HeaderSize != Snp->Mode->MediaHeaderSize) || (BufLen < HeaderSize)) {
+
+ MNP_DEBUG_WARN (
+ ("MnpReceivePacket: Size error, HL:TL = %d:%d.\n",
+ HeaderSize,
+ BufLen)
+ );
+ return EFI_DEVICE_ERROR;
+ }
+
+ Trimmed = 0;
+ if (Nbuf->TotalSize != BufLen) {
+ //
+ // Trim the packet from tail.
+ //
+ Trimmed = NetbufTrim (Nbuf, Nbuf->TotalSize - (UINT32) BufLen, NET_BUF_TAIL);
+ ASSERT (Nbuf->TotalSize == BufLen);
+ }
+
+ //
+ // Enqueue the packet to the matched instances.
+ //
+ MnpEnqueuePacket (MnpServiceData, Nbuf);
+
+ if (Nbuf->RefCnt > 2) {
+ //
+ // RefCnt > 2 indicates there is at least one receiver of this packet.
+ // Free the current RxNbufCache and allocate a new one.
+ //
+ MnpFreeNbuf (MnpServiceData, Nbuf);
+
+ Nbuf = MnpAllocNbuf (MnpServiceData);
+ MnpServiceData->RxNbufCache = Nbuf;
+ if (Nbuf == NULL) {
+ MNP_DEBUG_ERROR (("MnpReceivePacket: Alloc packet for receiving cache failed.\n"));
+ return EFI_DEVICE_ERROR;
+ }
+
+ NetbufAllocSpace (Nbuf, MnpServiceData->BufferLength, NET_BUF_TAIL);
+ } else {
+ //
+ // No receiver for this packet.
+ //
+ NetbufAllocSpace (Nbuf, Trimmed, NET_BUF_TAIL);
+ goto EXIT;
+ }
+ //
+ // Deliver the queued packets.
+ //
+ MnpDeliverPacket (MnpServiceData);
+
+EXIT:
+
+ ASSERT (Nbuf->TotalSize == MnpServiceData->BufferLength);
+
+ return Status;
+}
+
+
+/**
+ Remove the received packets if timeout occurs.
+
+ @param Event The event this notify function registered to.
+ @param Context Pointer to the context data registered to the
+ event.
+
+ @return None.
+
+**/
+VOID
+EFIAPI
+MnpCheckPacketTimeout (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ MNP_SERVICE_DATA *MnpServiceData;
+ NET_LIST_ENTRY *Entry;
+ NET_LIST_ENTRY *RxEntry;
+ NET_LIST_ENTRY *NextEntry;
+ MNP_INSTANCE_DATA *Instance;
+ MNP_RXDATA_WRAP *RxDataWrap;
+ EFI_TPL OldTpl;
+
+ MnpServiceData = (MNP_SERVICE_DATA *) Context;
+ NET_CHECK_SIGNATURE (MnpServiceData, MNP_SERVICE_DATA_SIGNATURE);
+
+ NET_LIST_FOR_EACH (Entry, &MnpServiceData->ChildrenList) {
+
+ Instance = NET_LIST_USER_STRUCT (Entry, MNP_INSTANCE_DATA, InstEntry);
+ NET_CHECK_SIGNATURE (Instance, MNP_INSTANCE_DATA_SIGNATURE);
+
+ if (!Instance->Configured || (Instance->ConfigData.ReceivedQueueTimeoutValue == 0)) {
+ //
+ // This instance is not configured or there is no receive time out,
+ // just skip to the next instance.
+ //
+ continue;
+ }
+
+ OldTpl = NET_RAISE_TPL (NET_TPL_RECYCLE);
+
+ NET_LIST_FOR_EACH_SAFE (RxEntry, NextEntry, &Instance->RcvdPacketQueue) {
+
+ RxDataWrap = NET_LIST_USER_STRUCT (RxEntry, MNP_RXDATA_WRAP, WrapEntry);
+
+ if (RxDataWrap->TimeoutTick >= MNP_TIMEOUT_CHECK_INTERVAL) {
+
+ RxDataWrap->TimeoutTick -= MNP_TIMEOUT_CHECK_INTERVAL;
+ } else {
+ //
+ // Drop the timeout packet.
+ //
+ MNP_DEBUG_WARN (("MnpCheckPacketTimeout: Received packet timeout.\n"));
+ MnpRecycleRxData (NULL, RxDataWrap);
+ Instance->RcvdPacketQueueSize--;
+ }
+ }
+
+ NET_RESTORE_TPL (OldTpl);
+ }
+}
+
+
+/**
+ Poll to receive the packets from Snp. This function is either called by upperlayer
+ protocols/applications or the system poll timer notify mechanism.
+
+ @param Event The event this notify function registered to.
+ @param Context Pointer to the context data registered to the
+ event.
+
+ @return None.
+
+**/
+VOID
+EFIAPI
+MnpSystemPoll (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ MNP_SERVICE_DATA *MnpServiceData;
+
+ MnpServiceData = (MNP_SERVICE_DATA *) Context;
+ NET_CHECK_SIGNATURE (MnpServiceData, MNP_SERVICE_DATA_SIGNATURE);
+
+ //
+ // Try to receive packets from Snp.
+ //
+ MnpReceivePacket (MnpServiceData);
+}
diff --git a/MdeModulePkg/Universal/Network/MnpDxe/MnpMain.c b/MdeModulePkg/Universal/Network/MnpDxe/MnpMain.c
new file mode 100644
index 0000000000..02c0065c34
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/MnpDxe/MnpMain.c
@@ -0,0 +1,655 @@
+/** @file
+
+Copyright (c) 2005 - 2007, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+
+ MnpMain.c
+
+Abstract:
+
+ Implementation of Managed Network Protocol public services.
+
+
+**/
+
+#include <Library/BaseMemoryLib.h>
+#include <Library/BaseLib.h>
+
+#include "MnpImpl.h"
+
+
+/**
+ Get configuration data of this instance.
+
+ @param This Pointer to the Managed Network Protocol.
+ @param MnpConfigData Pointer to strorage for MNP operational
+ parameters.
+ @param SnpModeData Pointer to strorage for SNP operational
+ parameters.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+ @retval EFI_NOT_STARTED This MNP child driver instance has not been
+ configured The default values are returned in
+ MnpConfigData if it is not NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+MnpGetModeData (
+ IN EFI_MANAGED_NETWORK_PROTOCOL *This,
+ OUT EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData OPTIONAL,
+ OUT EFI_SIMPLE_NETWORK_MODE *SnpModeData OPTIONAL
+ )
+{
+ MNP_INSTANCE_DATA *Instance;
+ EFI_SIMPLE_NETWORK_PROTOCOL *Snp;
+ EFI_TPL OldTpl;
+ EFI_STATUS Status;
+
+ if (This == NULL) {
+
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = MNP_INSTANCE_DATA_FROM_THIS (This);
+
+ OldTpl = NET_RAISE_TPL (NET_TPL_LOCK);
+
+ if (MnpConfigData != NULL) {
+ //
+ // Copy the instance configuration data.
+ //
+ *MnpConfigData = Instance->ConfigData;
+ }
+
+ if (SnpModeData != NULL) {
+ //
+ // Copy the underlayer Snp mode data.
+ //
+ Snp = Instance->MnpServiceData->Snp;
+ *SnpModeData = *(Snp->Mode);
+ }
+
+ if (!Instance->Configured) {
+ Status = EFI_NOT_STARTED;
+ } else {
+ Status = EFI_SUCCESS;
+ }
+
+ NET_RESTORE_TPL (OldTpl);
+
+ return Status;
+}
+
+
+/**
+ Set or clear the operational parameters for the MNP child driver.
+
+ @param This Pointer to the Managed Network Protocol.
+ @param MnpConfigData Pointer to the configuration data that will be
+ assigned to the MNP child driver instance. If
+ NULL, the MNP child driver instance is reset to
+ startup defaults and all pending transmit and
+ receive requests are flushed.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_INVALID_PARAMETER One or more parameter is invalid.
+ @retval EFI_OUT_OF_RESOURCES Required system resources (usually memory) could
+ not be allocated.
+ @retval EFI_UNSUPPORTED EnableReceiveTimestamps is TRUE, this
+ implementation doesn't support it.
+ @retval EFI_DEVICE_ERROR An unexpected network or system error occurred.
+ @retval Other The MNP child driver instance has been reset to
+ startup defaults.
+
+**/
+EFI_STATUS
+EFIAPI
+MnpConfigure (
+ IN EFI_MANAGED_NETWORK_PROTOCOL *This,
+ IN EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData OPTIONAL
+ )
+{
+ MNP_INSTANCE_DATA *Instance;
+ EFI_TPL OldTpl;
+ EFI_STATUS Status;
+
+ if ((This == NULL) ||
+ ((MnpConfigData != NULL) &&
+ (MnpConfigData->ProtocolTypeFilter > 0) &&
+ (MnpConfigData->ProtocolTypeFilter <= 1500))) {
+
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = MNP_INSTANCE_DATA_FROM_THIS (This);
+
+ OldTpl = NET_RAISE_TPL (NET_TPL_LOCK);
+
+ if ((MnpConfigData == NULL) && (!Instance->Configured)) {
+ //
+ // If the instance is not configured and a reset is requested, just return.
+ //
+ Status = EFI_SUCCESS;
+ goto ON_EXIT;
+ }
+
+ //
+ // Configure the instance.
+ //
+ Status = MnpConfigureInstance (Instance, MnpConfigData);
+
+ON_EXIT:
+ NET_RESTORE_TPL (OldTpl);
+
+ return Status;
+}
+
+
+/**
+ Translate a multicast IP address to a multicast hardware (MAC) address.
+
+ @param This Pointer to the Managed Network Protocol.
+ @param Ipv6Flag Set to TRUE if IpAddress is an IPv6 multicast
+ address. Set to FALSE if IpAddress is an IPv4
+ multicast address.
+ @param IpAddress Pointer to the multicast IP address to convert.
+ @param MacAddress Pointer to the resulting multicast MAC address.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_INVALID_PARAMETER One or more parameter is invalid.
+ @retval EFI_NOT_STARTED This MNP child driver instance has not been
+ configured.
+ @retval EFI_UNSUPPORTED Ipv6Flag is TRUE, this implementation doesn't
+ supported it.
+ @retval EFI_DEVICE_ERROR An unexpected network or system error occurred.
+ @retval Other The address could not be converted.
+
+**/
+EFI_STATUS
+EFIAPI
+MnpMcastIpToMac (
+ IN EFI_MANAGED_NETWORK_PROTOCOL *This,
+ IN BOOLEAN Ipv6Flag,
+ IN EFI_IP_ADDRESS *IpAddress,
+ OUT EFI_MAC_ADDRESS *MacAddress
+ )
+{
+ EFI_STATUS Status;
+ MNP_INSTANCE_DATA *Instance;
+ EFI_SIMPLE_NETWORK_PROTOCOL *Snp;
+ EFI_TPL OldTpl;
+
+ if ((This == NULL) || (IpAddress == NULL) || (MacAddress == NULL)) {
+
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (Ipv6Flag) {
+ //
+ // Currently IPv6 isn't supported.
+ //
+ return EFI_UNSUPPORTED;
+ }
+
+ if (!IP4_IS_MULTICAST (EFI_NTOHL (*IpAddress))) {
+ //
+ // The IPv4 address passed in is not a multicast address.
+ //
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = MNP_INSTANCE_DATA_FROM_THIS (This);
+
+ OldTpl = NET_RAISE_TPL (NET_TPL_LOCK);
+
+ if (!Instance->Configured) {
+
+ Status = EFI_NOT_STARTED;
+ goto ON_EXIT;
+ }
+
+ Snp = Instance->MnpServiceData->Snp;
+ ASSERT (Snp != NULL);
+
+ if (Snp->Mode->IfType == NET_IFTYPE_ETHERNET) {
+ //
+ // Translate the IPv4 address into a multicast MAC address if the NIC is an
+ // ethernet NIC.
+ //
+ MacAddress->Addr[0] = 0x01;
+ MacAddress->Addr[1] = 0x00;
+ MacAddress->Addr[2] = 0x5E;
+ MacAddress->Addr[3] = IpAddress->v4.Addr[1] & 0x7F;
+ MacAddress->Addr[4] = IpAddress->v4.Addr[2];
+ MacAddress->Addr[5] = IpAddress->v4.Addr[3];
+
+ Status = EFI_SUCCESS;
+ } else {
+ //
+ // Invoke Snp to translate the multicast IP address.
+ //
+ Status = Snp->MCastIpToMac (
+ Snp,
+ Ipv6Flag,
+ IpAddress,
+ MacAddress
+ );
+ }
+
+ON_EXIT:
+ NET_RESTORE_TPL (OldTpl);
+
+ return Status;
+}
+
+
+/**
+ Enable or disable receie filters for multicast address.
+
+ @param This Pointer to the Managed Network Protocol.
+ @param JoinFlag Set to TRUE to join this multicast group. Set to
+ FALSE to leave this multicast group.
+ @param MacAddress Pointer to the multicast MAC group (address) to
+ join or leave.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_INVALID_PARAMETER One or more parameter is invalid
+ @retval EFI_NOT_STARTED This MNP child driver instance has not been
+ configured.
+ @retval EFI_ALREADY_STARTED The supplied multicast group is already joined.
+ @retval EFI_NOT_FOUND The supplied multicast group is not joined.
+ @retval EFI_DEVICE_ERROR An unexpected network or system error occurred.
+ @retval Other The requested operation could not be completed.
+ The MNP multicast group settings are unchanged.
+
+**/
+EFI_STATUS
+EFIAPI
+MnpGroups (
+ IN EFI_MANAGED_NETWORK_PROTOCOL *This,
+ IN BOOLEAN JoinFlag,
+ IN EFI_MAC_ADDRESS *MacAddress OPTIONAL
+ )
+{
+ MNP_INSTANCE_DATA *Instance;
+ EFI_SIMPLE_NETWORK_MODE *SnpMode;
+ MNP_GROUP_CONTROL_BLOCK *GroupCtrlBlk;
+ MNP_GROUP_ADDRESS *GroupAddress;
+ NET_LIST_ENTRY *ListEntry;
+ BOOLEAN AddressExist;
+ EFI_TPL OldTpl;
+ EFI_STATUS Status;
+
+ if (This == NULL || (JoinFlag && (MacAddress == NULL))) {
+ //
+ // This is NULL, or it's a join operation but MacAddress is NULL.
+ //
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = MNP_INSTANCE_DATA_FROM_THIS (This);
+ SnpMode = Instance->MnpServiceData->Snp->Mode;
+
+ OldTpl = NET_RAISE_TPL (NET_TPL_LOCK);
+
+ if (!Instance->Configured) {
+
+ Status = EFI_NOT_STARTED;
+ goto ON_EXIT;
+ }
+
+ if ((!Instance->ConfigData.EnableMulticastReceive) ||
+ ((MacAddress != NULL) && !NET_MAC_IS_MULTICAST (MacAddress, &SnpMode->BroadcastAddress, SnpMode->HwAddressSize))) {
+ //
+ // The instance isn't configured to do mulitcast receive. OR
+ // the passed in MacAddress is not a mutlticast mac address.
+ //
+ Status = EFI_INVALID_PARAMETER;
+ goto ON_EXIT;
+ }
+
+ Status = EFI_SUCCESS;
+ AddressExist = FALSE;
+ GroupCtrlBlk = NULL;
+
+ if (MacAddress != NULL) {
+ //
+ // Search the instance's GroupCtrlBlkList to find the specific address.
+ //
+ NET_LIST_FOR_EACH (ListEntry, &Instance->GroupCtrlBlkList) {
+
+ GroupCtrlBlk = NET_LIST_USER_STRUCT (
+ ListEntry,
+ MNP_GROUP_CONTROL_BLOCK,
+ CtrlBlkEntry
+ );
+ GroupAddress = GroupCtrlBlk->GroupAddress;
+ if (0 == NetCompareMem (
+ MacAddress,
+ &GroupAddress->Address,
+ SnpMode->HwAddressSize
+ )) {
+ //
+ // There is already the same multicast mac address configured.
+ //
+ AddressExist = TRUE;
+ break;
+ }
+ }
+
+ if (JoinFlag && AddressExist) {
+ //
+ // The multicast mac address to join already exists.
+ //
+ Status = EFI_ALREADY_STARTED;
+ }
+
+ if (!JoinFlag && !AddressExist) {
+ //
+ // The multicast mac address to leave doesn't exist in this instance.
+ //
+ Status = EFI_NOT_FOUND;
+ }
+
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+ } else if (NetListIsEmpty (&Instance->GroupCtrlBlkList)) {
+ //
+ // The MacAddress is NULL and there is no configured multicast mac address,
+ // just return.
+ //
+ goto ON_EXIT;
+ }
+
+ //
+ // OK, it is time to take action.
+ //
+ Status = MnpGroupOp (Instance, JoinFlag, MacAddress, GroupCtrlBlk);
+
+ON_EXIT:
+ NET_RESTORE_TPL (OldTpl);
+
+ return Status;
+}
+
+
+/**
+ Place an outgoing packet into the transmit queue.
+
+ @param This Pointer to the Managed Network Protocol.
+ @param Token Pointer to a token associated with the transmit
+ data descriptor.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_INVALID_PARAMETER One or more parameter is invalid
+ @retval EFI_NOT_STARTED This MNP child driver instance has not been
+ configured.
+ @retval EFI_ACCESS_DENIED The transmit completion token is already in the
+ transmit queue.
+ @retval EFI_OUT_OF_RESOURCES The transmit data could not be queued due to a
+ lack of system resources (usually memory).
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+ The MNP child driver instance has been reset to
+ startup defaults.
+ @retval EFI_NOT_READY The transmit request could not be queued because
+ the transmit queue is full.
+
+**/
+EFI_STATUS
+EFIAPI
+MnpTransmit (
+ IN EFI_MANAGED_NETWORK_PROTOCOL *This,
+ IN EFI_MANAGED_NETWORK_COMPLETION_TOKEN *Token
+ )
+{
+ EFI_STATUS Status;
+ MNP_INSTANCE_DATA *Instance;
+ MNP_SERVICE_DATA *MnpServiceData;
+ UINT8 *PktBuf;
+ UINT32 PktLen;
+ EFI_TPL OldTpl;
+
+ if ((This == NULL) || (Token == NULL)) {
+
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = MNP_INSTANCE_DATA_FROM_THIS (This);
+
+ OldTpl = NET_RAISE_TPL (NET_TPL_LOCK);
+
+ if (!Instance->Configured) {
+
+ Status = EFI_NOT_STARTED;
+ goto ON_EXIT;
+ }
+
+ if (!MnpIsValidTxToken (Instance, Token)) {
+ //
+ // The Token is invalid.
+ //
+ Status = EFI_INVALID_PARAMETER;
+ goto ON_EXIT;
+ }
+
+ MnpServiceData = Instance->MnpServiceData;
+ NET_CHECK_SIGNATURE (MnpServiceData, MNP_SERVICE_DATA_SIGNATURE);
+
+ //
+ // Build the tx packet
+ //
+ MnpBuildTxPacket (MnpServiceData, Token->Packet.TxData, &PktBuf, &PktLen);
+
+ //
+ // OK, send the packet synchronously.
+ //
+ Status = MnpSyncSendPacket (MnpServiceData, PktBuf, PktLen, Token);
+
+ON_EXIT:
+ NET_RESTORE_TPL (OldTpl);
+
+ return Status;
+}
+
+
+/**
+ Place an asynchronous receiving request into the receiving queue.
+
+ @param This Pointer to the EFI_MANAGED_NETWORK_PROTOCOL
+ instance.
+ @param Token Pointer to a token associated with the receive
+ data descriptor.
+
+ @retval EFI_SUCCESS The receive completion token was cached.
+ @retval EFI_NOT_STARTED This MNP child driver instance has not been
+ configured.
+ @retval EFI_INVALID_PARAMETER One or more parameter is invalid.
+ @retval EFI_OUT_OF_RESOURCES The transmit data could not be queued due to a
+ lack of system resources (usually memory).
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+ The MNP child driver instance has been reset to
+ startup defaults.
+ @retval EFI_ACCESS_DENIED The receive completion token was already in the
+ receive queue.
+ @retval EFI_NOT_READY The receive request could not be queued because
+ the receive queue is full.
+
+**/
+EFI_STATUS
+EFIAPI
+MnpReceive (
+ IN EFI_MANAGED_NETWORK_PROTOCOL *This,
+ IN EFI_MANAGED_NETWORK_COMPLETION_TOKEN *Token
+ )
+{
+ EFI_STATUS Status;
+ MNP_INSTANCE_DATA *Instance;
+ EFI_TPL OldTpl;
+
+ if ((This == NULL) || (Token == NULL) || (Token->Event == NULL)) {
+
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = MNP_INSTANCE_DATA_FROM_THIS (This);
+
+ OldTpl = NET_RAISE_TPL (NET_TPL_LOCK);
+
+ if (!Instance->Configured) {
+
+ Status = EFI_NOT_STARTED;
+ goto ON_EXIT;
+ }
+
+ //
+ // Check whether this token(event) is already in the rx token queue.
+ //
+ Status = NetMapIterate (&Instance->RxTokenMap, MnpTokenExist, (VOID *) Token);
+ if (EFI_ERROR (Status)) {
+
+ goto ON_EXIT;
+ }
+
+ //
+ // Insert the Token into the RxTokenMap.
+ //
+ Status = NetMapInsertTail (&Instance->RxTokenMap, (VOID *) Token, NULL);
+
+ if (!EFI_ERROR (Status)) {
+ //
+ // Try to deliver any buffered packets.
+ //
+ Status = MnpInstanceDeliverPacket (Instance);
+ }
+
+ON_EXIT:
+ NET_RESTORE_TPL (OldTpl);
+
+ return Status;
+}
+
+
+/**
+ Abort a pending transmit or receive request.
+
+ @param This Pointer to the EFI_MANAGED_NETWORK_PROTOCOL
+ instance.
+ @param Token Pointer to a token that has been issued by
+ EFI_MANAGED_NETWORK_PROTOCOL.Transmit() or
+ EFI_MANAGED_NETWORK_PROTOCOL.Receive(). If NULL,
+ all pending tokens are aborted.
+
+ @retval EFI_SUCCESS The asynchronous I/O request was aborted and
+ Token->Event was signaled.
+ @retval EFI_NOT_STARTED This MNP child driver instance has not been
+ configured.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+ @retval EFI_NOT_FOUND The asynchronous I/O request was not found in the
+ transmit or receive queue. It has either completed
+ or was not issued by Transmit() and Receive().
+
+**/
+EFI_STATUS
+EFIAPI
+MnpCancel (
+ IN EFI_MANAGED_NETWORK_PROTOCOL *This,
+ IN EFI_MANAGED_NETWORK_COMPLETION_TOKEN *Token OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ MNP_INSTANCE_DATA *Instance;
+ EFI_TPL OldTpl;
+
+ if (This == NULL) {
+
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = MNP_INSTANCE_DATA_FROM_THIS (This);
+
+ OldTpl = NET_RAISE_TPL (NET_TPL_LOCK);
+
+ if (!Instance->Configured) {
+
+ Status = EFI_NOT_STARTED;
+ goto ON_EXIT;
+ }
+
+ //
+ // Iterate the RxTokenMap to cancel the specified Token.
+ //
+ Status = NetMapIterate (&Instance->RxTokenMap, MnpCancelTokens, (VOID *) Token);
+
+ if (Token != NULL) {
+
+ Status = (Status == EFI_ABORTED) ? EFI_SUCCESS : EFI_NOT_FOUND;
+ }
+
+ON_EXIT:
+ NET_RESTORE_TPL (OldTpl);
+
+ return Status;
+}
+
+
+/**
+ Poll the network interface to do transmit/receive work.
+
+ @param This Pointer to the EFI_MANAGED_NETWORK_PROTOCOL
+ instance.
+
+ @retval EFI_SUCCESS Incoming or outgoing data was processed.
+ @retval EFI_NOT_STARTED This MNP child driver instance has not been
+ configured.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+ The MNP child driver instance has been reset to
+ startup defaults.
+ @retval EFI_NOT_READY No incoming or outgoing data was processed.
+ @retval EFI_TIMEOUT Data was dropped out of the transmit and/or
+ receive queue.
+
+**/
+EFI_STATUS
+EFIAPI
+MnpPoll (
+ IN EFI_MANAGED_NETWORK_PROTOCOL *This
+ )
+{
+ EFI_STATUS Status;
+ MNP_INSTANCE_DATA *Instance;
+ EFI_TPL OldTpl;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = MNP_INSTANCE_DATA_FROM_THIS (This);
+
+ OldTpl = NET_RAISE_TPL (NET_TPL_LOCK);
+
+ if (!Instance->Configured) {
+ Status = EFI_NOT_STARTED;
+ goto ON_EXIT;
+ }
+
+ //
+ // Try to receive packets.
+ //
+ Status = MnpReceivePacket (Instance->MnpServiceData);
+
+ON_EXIT:
+ NET_RESTORE_TPL (OldTpl);
+
+ return Status;
+}
diff --git a/MdeModulePkg/Universal/Network/SnpDxe/ComponentName.c b/MdeModulePkg/Universal/Network/SnpDxe/ComponentName.c
new file mode 100644
index 0000000000..6da17a3633
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/SnpDxe/ComponentName.c
@@ -0,0 +1,163 @@
+/** @file
+
+Copyright (c) 2004 - 2007, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+
+ ComponentName.c
+
+Abstract:
+
+
+**/
+
+
+
+#include "Snp.h"
+
+//
+// EFI Component Name Functions
+//
+EFI_STATUS
+EFIAPI
+SimpleNetworkComponentNameGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ );
+
+EFI_STATUS
+EFIAPI
+SimpleNetworkComponentNameGetControllerName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle OPTIONAL,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ );
+
+//
+// EFI Component Name Protocol
+//
+EFI_COMPONENT_NAME_PROTOCOL gSimpleNetworkComponentName = {
+ SimpleNetworkComponentNameGetDriverName,
+ SimpleNetworkComponentNameGetControllerName,
+ "eng"
+};
+
+static EFI_UNICODE_STRING_TABLE mSimpleNetworkDriverNameTable[] = {
+ {
+ "eng",
+ L"Simple Network Protocol Driver"
+ },
+ {
+ NULL,
+ NULL
+ }
+};
+
+EFI_STATUS
+EFIAPI
+SimpleNetworkComponentNameGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ )
+/*++
+
+ Routine Description:
+ Retrieves a Unicode string that is the user readable name of the EFI Driver.
+
+ Arguments:
+ This - A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance.
+ Language - A pointer to a three character ISO 639-2 language identifier.
+ This is the language of the driver name that that the caller
+ is requesting, and it must match one of the languages specified
+ in SupportedLanguages. The number of languages supported by a
+ driver is up to the driver writer.
+ DriverName - A pointer to the Unicode string to return. This Unicode string
+ is the name of the driver specified by This in the language
+ specified by Language.
+
+ Returns:
+ EFI_SUCCESS - The Unicode string for the Driver specified by This
+ and the language specified by Language was returned
+ in DriverName.
+ EFI_INVALID_PARAMETER - Language is NULL.
+ EFI_INVALID_PARAMETER - DriverName is NULL.
+ EFI_UNSUPPORTED - The driver specified by This does not support the
+ language specified by Language.
+
+--*/
+{
+ return LookupUnicodeString (
+ Language,
+ gSimpleNetworkComponentName.SupportedLanguages,
+ mSimpleNetworkDriverNameTable,
+ DriverName
+ );
+}
+
+EFI_STATUS
+EFIAPI
+SimpleNetworkComponentNameGetControllerName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle OPTIONAL,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ )
+/*++
+
+ Routine Description:
+ Retrieves a Unicode string that is the user readable name of the controller
+ that is being managed by an EFI Driver.
+
+ Arguments:
+ This - A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance.
+ ControllerHandle - The handle of a controller that the driver specified by
+ This is managing. This handle specifies the controller
+ whose name is to be returned.
+ ChildHandle - The handle of the child controller to retrieve the name
+ of. This is an optional parameter that may be NULL. It
+ will be NULL for device drivers. It will also be NULL
+ for a bus drivers that wish to retrieve the name of the
+ bus controller. It will not be NULL for a bus driver
+ that wishes to retrieve the name of a child controller.
+ Language - A pointer to a three character ISO 639-2 language
+ identifier. This is the language of the controller name
+ that that the caller is requesting, and it must match one
+ of the languages specified in SupportedLanguages. The
+ number of languages supported by a driver is up to the
+ driver writer.
+ ControllerName - A pointer to the Unicode string to return. This Unicode
+ string is the name of the controller specified by
+ ControllerHandle and ChildHandle in the language specified
+ by Language from the point of view of the driver specified
+ by This.
+
+ Returns:
+ EFI_SUCCESS - The Unicode string for the user readable name in the
+ language specified by Language for the driver
+ specified by This was returned in DriverName.
+ EFI_INVALID_PARAMETER - ControllerHandle is not a valid EFI_HANDLE.
+ EFI_INVALID_PARAMETER - ChildHandle is not NULL and it is not a valid EFI_HANDLE.
+ EFI_INVALID_PARAMETER - Language is NULL.
+ EFI_INVALID_PARAMETER - ControllerName is NULL.
+ EFI_UNSUPPORTED - The driver specified by This is not currently managing
+ the controller specified by ControllerHandle and
+ ChildHandle.
+ EFI_UNSUPPORTED - The driver specified by This does not support the
+ language specified by Language.
+
+--*/
+{
+ return EFI_UNSUPPORTED;
+}
diff --git a/MdeModulePkg/Universal/Network/SnpDxe/SnpDxe.inf b/MdeModulePkg/Universal/Network/SnpDxe/SnpDxe.inf
new file mode 100644
index 0000000000..34623da4c7
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/SnpDxe/SnpDxe.inf
@@ -0,0 +1,75 @@
+#/** @file
+# Component name for module SNP
+#
+# FIX ME!
+# Copyright (c) 2006, Intel Corporation. All right reserved.
+#
+# All rights reserved. This program and the accompanying materials
+# are licensed and made available under the terms and conditions of the BSD License
+# which accompanies this distribution. The full text of the license may be found at
+# http://opensource.org/licenses/bsd-license.php
+#
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+#
+#
+#**/
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = SnpDxe
+ FILE_GUID = A2f436EA-A127-4EF8-957C-8048606FF670
+ MODULE_TYPE = DXE_DRIVER
+ VERSION_STRING = 1.0
+ EDK_RELEASE_VERSION = 0x00020000
+ EFI_SPECIFICATION_VERSION = 0x00020000
+
+ ENTRY_POINT = InitializeSnpNiiDriver
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 IPF EBC
+#
+
+[Sources.common]
+ receive.c
+ snp.h
+ nvdata.c
+ get_status.c
+ start.c
+ snp.c
+ stop.c
+ statistics.c
+ reset.c
+ shutdown.c
+ mcast_ip_to_mac.c
+ transmit.c
+ WaitForPacket.c
+ receive_filters.c
+ initialize.c
+ ComponentName.c
+ callback.c
+ station_address.c
+
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+
+
+[LibraryClasses]
+ UefiLib
+ BaseLib
+ UefiBootServicesTableLib
+ UefiDriverEntryPoint
+ BaseMemoryLib
+ DebugLib
+
+
+[Protocols]
+ gEfiPciIoProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+ gEfiSimpleNetworkProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+ gEfiDevicePathProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+ gEfiNetworkInterfaceIdentifierProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+ gEfiNetworkInterfaceIdentifierProtocolGuid_31 # PROTOCOL ALWAYS_CONSUMED
diff --git a/MdeModulePkg/Universal/Network/SnpDxe/SnpDxe.msa b/MdeModulePkg/Universal/Network/SnpDxe/SnpDxe.msa
new file mode 100644
index 0000000000..748b5c5383
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/SnpDxe/SnpDxe.msa
@@ -0,0 +1,90 @@
+<ModuleSurfaceArea xmlns="http://www.TianoCore.org/2006/Edk2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <MsaHeader>
+ <ModuleName>SnpDxe</ModuleName>
+ <ModuleType>DXE_DRIVER</ModuleType>
+ <GuidValue>A2f436EA-A127-4EF8-957C-8048606FF670</GuidValue>
+ <Version>1.0</Version>
+ <Abstract>Component name for module SNP</Abstract>
+ <Description>FIX ME!</Description>
+ <Copyright>Copyright (c) 2006, Intel Corporation. All right reserved.</Copyright>
+ <License>All rights reserved. This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.</License>
+ <Specification>FRAMEWORK_BUILD_PACKAGING_SPECIFICATION 0x00000052</Specification>
+ </MsaHeader>
+ <ModuleDefinitions>
+ <SupportedArchitectures>IA32 X64 IPF EBC</SupportedArchitectures>
+ <BinaryModule>false</BinaryModule>
+ <OutputFileBasename>SnpDxe</OutputFileBasename>
+ </ModuleDefinitions>
+ <LibraryClassDefinitions>
+ <LibraryClass Usage="ALWAYS_CONSUMED">
+ <Keyword>DebugLib</Keyword>
+ </LibraryClass>
+ <LibraryClass Usage="ALWAYS_CONSUMED">
+ <Keyword>BaseMemoryLib</Keyword>
+ </LibraryClass>
+ <LibraryClass Usage="ALWAYS_CONSUMED">
+ <Keyword>UefiDriverEntryPoint</Keyword>
+ </LibraryClass>
+ <LibraryClass Usage="ALWAYS_CONSUMED">
+ <Keyword>UefiBootServicesTableLib</Keyword>
+ </LibraryClass>
+ <LibraryClass Usage="ALWAYS_CONSUMED">
+ <Keyword>BaseLib</Keyword>
+ </LibraryClass>
+ <LibraryClass Usage="ALWAYS_CONSUMED">
+ <Keyword>UefiLib</Keyword>
+ </LibraryClass>
+ </LibraryClassDefinitions>
+ <SourceFiles>
+ <Filename>station_address.c</Filename>
+ <Filename>SNPEntry.c</Filename>
+ <Filename>callback.c</Filename>
+ <Filename>ComponentName.c</Filename>
+ <Filename>initialize.c</Filename>
+ <Filename>receive_filters.c</Filename>
+ <Filename>WaitForPacket.c</Filename>
+ <Filename>transmit.c</Filename>
+ <Filename>mcast_ip_to_mac.c</Filename>
+ <Filename>shutdown.c</Filename>
+ <Filename>reset.c</Filename>
+ <Filename>statistics.c</Filename>
+ <Filename>stop.c</Filename>
+ <Filename>snp.c</Filename>
+ <Filename>start.c</Filename>
+ <Filename>get_status.c</Filename>
+ <Filename>nvdata.c</Filename>
+ <Filename>snp.h</Filename>
+ <Filename>receive.c</Filename>
+ </SourceFiles>
+ <PackageDependencies>
+ <Package PackageGuid="5e0e9358-46b6-4ae2-8218-4ab8b9bbdcec"/>
+ <Package PackageGuid="68169ab0-d41b-4009-9060-292c253ac43d"/>
+ </PackageDependencies>
+ <Protocols>
+ <Protocol Usage="ALWAYS_CONSUMED">
+ <ProtocolCName>gEfiNetworkInterfaceIdentifierProtocolGuid</ProtocolCName>
+ </Protocol>
+ <Protocol Usage="ALWAYS_CONSUMED">
+ <ProtocolCName>gEfiDevicePathProtocolGuid</ProtocolCName>
+ </Protocol>
+ <Protocol Usage="ALWAYS_CONSUMED">
+ <ProtocolCName>gEfiSimpleNetworkProtocolGuid</ProtocolCName>
+ </Protocol>
+ <Protocol Usage="ALWAYS_CONSUMED">
+ <ProtocolCName>gEfiPciIoProtocolGuid</ProtocolCName>
+ </Protocol>
+ </Protocols>
+ <Externs>
+ <Specification>EFI_SPECIFICATION_VERSION 0x00020000</Specification>
+ <Specification>EDK_RELEASE_VERSION 0x00020000</Specification>
+ <Extern>
+ <ModuleEntryPoint>InitializeSnpNiiDriver</ModuleEntryPoint>
+ </Extern>
+ </Externs>
+</ModuleSurfaceArea> \ No newline at end of file
diff --git a/MdeModulePkg/Universal/Network/SnpDxe/WaitForPacket.c b/MdeModulePkg/Universal/Network/SnpDxe/WaitForPacket.c
new file mode 100644
index 0000000000..57d82ea160
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/SnpDxe/WaitForPacket.c
@@ -0,0 +1,97 @@
+/** @file
+Copyright (c) 2004, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module name:
+ WaitForPacket.c
+
+Abstract:
+ Event handler to check for available packet.
+
+
+**/
+
+#include "Snp.h"
+
+
+/**
+
+
+
+**/
+VOID
+EFIAPI
+SnpWaitForPacketNotify (
+ EFI_EVENT Event,
+ VOID *SnpPtr
+ )
+{
+ PXE_DB_GET_STATUS PxeDbGetStatus;
+
+ //
+ // Do nothing if either parameter is a NULL pointer.
+ //
+ if (Event == NULL || SnpPtr == NULL) {
+ return ;
+ }
+ //
+ // Do nothing if the SNP interface is not initialized.
+ //
+ switch (((SNP_DRIVER *) SnpPtr)->mode.State) {
+ case EfiSimpleNetworkInitialized:
+ break;
+
+ case EfiSimpleNetworkStopped:
+ case EfiSimpleNetworkStarted:
+ default:
+ return ;
+ }
+ //
+ // Fill in CDB for UNDI GetStatus().
+ //
+ ((SNP_DRIVER *) SnpPtr)->cdb.OpCode = PXE_OPCODE_GET_STATUS;
+ ((SNP_DRIVER *) SnpPtr)->cdb.OpFlags = 0;
+ ((SNP_DRIVER *) SnpPtr)->cdb.CPBsize = PXE_CPBSIZE_NOT_USED;
+ ((SNP_DRIVER *) SnpPtr)->cdb.CPBaddr = PXE_CPBADDR_NOT_USED;
+ ((SNP_DRIVER *) SnpPtr)->cdb.DBsize = sizeof (UINT32) * 2;
+ ((SNP_DRIVER *) SnpPtr)->cdb.DBaddr = (UINT64)(UINTN) (((SNP_DRIVER *) SnpPtr)->db);
+ ((SNP_DRIVER *) SnpPtr)->cdb.StatCode = PXE_STATCODE_INITIALIZE;
+ ((SNP_DRIVER *) SnpPtr)->cdb.StatFlags = PXE_STATFLAGS_INITIALIZE;
+ ((SNP_DRIVER *) SnpPtr)->cdb.IFnum = ((SNP_DRIVER *) SnpPtr)->if_num;
+ ((SNP_DRIVER *) SnpPtr)->cdb.Control = PXE_CONTROL_LAST_CDB_IN_LIST;
+
+ //
+ // Clear contents of DB buffer.
+ //
+ ZeroMem (((SNP_DRIVER *) SnpPtr)->db, sizeof (UINT32) * 2);
+
+ //
+ // Issue UNDI command and check result.
+ //
+ (*((SNP_DRIVER *) SnpPtr)->issue_undi32_command) ((UINT64)(UINTN) &((SNP_DRIVER *) SnpPtr)->cdb);
+
+ if (((SNP_DRIVER *) SnpPtr)->cdb.StatCode != EFI_SUCCESS) {
+ return ;
+ }
+ //
+ // We might have a packet. Check the receive length and signal
+ // the event if the length is not zero.
+ //
+ CopyMem (
+ &PxeDbGetStatus,
+ ((SNP_DRIVER *) SnpPtr)->db,
+ sizeof (UINT32) * 2
+ );
+
+ if (PxeDbGetStatus.RxFrameLen != 0) {
+ gBS->SignalEvent (Event);
+ }
+}
+
+/* eof - WaitForPacket.c */
diff --git a/MdeModulePkg/Universal/Network/SnpDxe/callback.c b/MdeModulePkg/Universal/Network/SnpDxe/callback.c
new file mode 100644
index 0000000000..c246874917
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/SnpDxe/callback.c
@@ -0,0 +1,611 @@
+/*++
+Copyright (c) 2006, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module name:
+ callback.c
+
+Abstract:
+ This file contains two sets of callback routines for undi3.0 and undi3.1.
+ the callback routines for Undi3.1 have an extra parameter UniqueId which
+ stores the interface context for the NIC that snp is trying to talk..
+
+--*/
+
+
+#include "Snp.h"
+
+//
+// Global variables
+// these 2 global variables are used only for 3.0 undi. we could not place
+// them in the snp structure because we will not know which snp structure
+// in the callback context!
+//
+STATIC BOOLEAN mInitializeLock = TRUE;
+STATIC EFI_LOCK mLock;
+
+//
+// End Global variables
+//
+extern EFI_PCI_IO_PROTOCOL *mPciIoFncs;
+
+VOID
+snp_undi32_callback_v2p_30 (
+ IN UINT64 CpuAddr,
+ IN OUT UINT64 DeviceAddrPtr
+ )
+/*++
+
+Routine Description:
+ This is a callback routine supplied to UNDI at undi_start time.
+ UNDI call this routine with a virtual or CPU address that SNP provided
+ to convert it to a physical or device address. Since EFI uses the identical
+ mapping, this routine returns the physical address same as the virtual address
+ for most of the addresses. an address above 4GB cannot generally be used as a
+ device address, it needs to be mapped to a lower physical address. This routine
+ does not call the map routine itself, but it assumes that the mapping was done
+ at the time of providing the address to UNDI. This routine just looks up the
+ address in a map table (which is the v2p structure chain)
+
+Arguments:
+ CpuAddr - virtual address of a buffer
+ DeviceAddrPtr - pointer to the physical address
+
+Returns:
+ void - The DeviceAddrPtr will contain 0 in case of any error
+
+--*/
+{
+ struct s_v2p *v2p;
+ //
+ // Do nothing if virtual address is zero or physical pointer is NULL.
+ // No need to map if the virtual address is within 4GB limit since
+ // EFI uses identical mapping
+ //
+ if ((CpuAddr == 0) || (DeviceAddrPtr == 0)) {
+ DEBUG ((EFI_D_ERROR, "\nv2p: Null virtual address or physical pointer.\n"));
+ return ;
+ }
+
+ if (CpuAddr < FOUR_GIGABYTES) {
+ *(UINT64 *) (UINTN) DeviceAddrPtr = CpuAddr;
+ return ;
+ }
+ //
+ // SNP creates a vaddr tp paddr mapping at the time of calling undi with any
+ // big address, this callback routine just looks up in the v2p list and
+ // returns the physical address for any given virtual address.
+ //
+ if (find_v2p (&v2p, (VOID *) (UINTN) CpuAddr) != EFI_SUCCESS) {
+ *(UINT64 *) (UINTN) DeviceAddrPtr = CpuAddr;
+ } else {
+ *(UINT64 *) (UINTN) DeviceAddrPtr = v2p->paddr;
+ }
+}
+
+VOID
+snp_undi32_callback_block_30 (
+ IN UINT32 Enable
+ )
+/*++
+
+Routine Description:
+ This is a callback routine supplied to UNDI at undi_start time.
+ UNDI call this routine when it wants to have exclusive access to a critical
+ section of the code/data
+
+Arguments:
+ Enable - non-zero indicates acquire
+ zero indicates release
+
+Returns:
+ void
+--*/
+{
+ //
+ // tcpip was calling snp at tpl_notify and if we acquire a lock that was
+ // created at a lower level (TPL_CALLBACK) it gives an assert!
+ //
+ if (mInitializeLock) {
+ EfiInitializeLock (&mLock, TPL_NOTIFY);
+ mInitializeLock = FALSE;
+ }
+
+ if (Enable != 0) {
+ EfiAcquireLock (&mLock);
+ } else {
+ EfiReleaseLock (&mLock);
+ }
+}
+
+VOID
+snp_undi32_callback_delay_30 (
+ IN UINT64 MicroSeconds
+ )
+/*++
+
+Routine Description:
+ This is a callback routine supplied to UNDI at undi_start time.
+ UNDI call this routine with the number of micro seconds when it wants to
+ pause.
+
+Arguments:
+ MicroSeconds - number of micro seconds to pause, ususlly multiple of 10
+
+Returns:
+ void
+--*/
+{
+ if (MicroSeconds != 0) {
+ gBS->Stall ((UINTN) MicroSeconds);
+ }
+}
+
+VOID
+snp_undi32_callback_memio_30 (
+ IN UINT8 ReadOrWrite,
+ IN UINT8 NumBytes,
+ IN UINT64 Address,
+ IN OUT UINT64 BufferAddr
+ )
+/*++
+
+Routine Description:
+ This is a callback routine supplied to UNDI at undi_start time.
+ This is the IO routine for UNDI. This is not currently being used by UNDI3.0
+ because Undi3.0 uses io/mem offsets relative to the beginning of the device
+ io/mem address and so it needs to use the PCI_IO_FUNCTION that abstracts the
+ start of the device's io/mem addresses. Since SNP cannot retrive the context
+ of the undi3.0 interface it cannot use the PCI_IO_FUNCTION that specific for
+ that NIC and uses one global IO functions structure, this does not work.
+ This however works fine for EFI1.0 Undis because they use absolute addresses
+ for io/mem access.
+
+Arguments:
+ ReadOrWrite - indicates read or write, IO or Memory
+ NumBytes - number of bytes to read or write
+ Address - IO or memory address to read from or write to
+ BufferAddr - memory location to read into or that contains the bytes
+ to write
+
+Returns:
+
+--*/
+{
+ EFI_PCI_IO_PROTOCOL_WIDTH Width;
+
+ switch (NumBytes) {
+ case 2:
+ Width = (EFI_PCI_IO_PROTOCOL_WIDTH) 1;
+ break;
+
+ case 4:
+ Width = (EFI_PCI_IO_PROTOCOL_WIDTH) 2;
+ break;
+
+ case 8:
+ Width = (EFI_PCI_IO_PROTOCOL_WIDTH) 3;
+ break;
+
+ default:
+ Width = (EFI_PCI_IO_PROTOCOL_WIDTH) 0;
+ }
+
+ switch (ReadOrWrite) {
+ case PXE_IO_READ:
+ mPciIoFncs->Io.Read (
+ mPciIoFncs,
+ Width,
+ 1, // BAR 1, IO base address
+ Address,
+ 1, // count
+ (VOID *) (UINTN) BufferAddr
+ );
+ break;
+
+ case PXE_IO_WRITE:
+ mPciIoFncs->Io.Write (
+ mPciIoFncs,
+ Width,
+ 1, // BAR 1, IO base address
+ Address,
+ 1, // count
+ (VOID *) (UINTN) BufferAddr
+ );
+ break;
+
+ case PXE_MEM_READ:
+ mPciIoFncs->Mem.Read (
+ mPciIoFncs,
+ Width,
+ 0, // BAR 0, Memory base address
+ Address,
+ 1, // count
+ (VOID *) (UINTN) BufferAddr
+ );
+ break;
+
+ case PXE_MEM_WRITE:
+ mPciIoFncs->Mem.Write (
+ mPciIoFncs,
+ Width,
+ 0, // BAR 0, Memory base address
+ Address,
+ 1, // count
+ (VOID *) (UINTN) BufferAddr
+ );
+ break;
+ }
+
+ return ;
+}
+//
+// New callbacks for 3.1:
+// there won't be a virtual2physical callback for UNDI 3.1 because undi3.1 uses
+// the MemMap call to map the required address by itself!
+//
+VOID
+snp_undi32_callback_block (
+ IN UINT64 UniqueId,
+ IN UINT32 Enable
+ )
+/*++
+
+Routine Description:
+ This is a callback routine supplied to UNDI3.1 at undi_start time.
+ UNDI call this routine when it wants to have exclusive access to a critical
+ section of the code/data
+
+Arguments:
+ UniqueId - This was supplied to UNDI at Undi_Start, SNP uses this to store
+ Undi interface context (Undi does not read or write this variable)
+ Enable - non-zero indicates acquire
+ zero indicates release
+
+Returns:
+ void
+
+--*/
+{
+ SNP_DRIVER *snp;
+
+ snp = (SNP_DRIVER *) (UINTN) UniqueId;
+ //
+ // tcpip was calling snp at tpl_notify and when we acquire a lock that was
+ // created at a lower level (TPL_CALLBACK) it gives an assert!
+ //
+ if (Enable != 0) {
+ EfiAcquireLock (&snp->lock);
+ } else {
+ EfiReleaseLock (&snp->lock);
+ }
+}
+
+VOID
+snp_undi32_callback_delay (
+ IN UINT64 UniqueId,
+ IN UINT64 MicroSeconds
+ )
+/*++
+
+Routine Description:
+ This is a callback routine supplied to UNDI at undi_start time.
+ UNDI call this routine with the number of micro seconds when it wants to
+ pause.
+
+Arguments:
+ MicroSeconds - number of micro seconds to pause, ususlly multiple of 10
+
+Returns:
+ void
+--*/
+{
+ if (MicroSeconds != 0) {
+ gBS->Stall ((UINTN) MicroSeconds);
+ }
+}
+
+/*
+ * IO routine for UNDI start CPB.
+ */
+VOID
+snp_undi32_callback_memio (
+ UINT64 UniqueId,
+ UINT8 ReadOrWrite,
+ UINT8 NumBytes,
+ UINT64 Address,
+ UINT64 BufferAddr
+ )
+/*++
+
+Routine Description:
+ This is a callback routine supplied to UNDI at undi_start time.
+ This is the IO routine for UNDI3.1.
+
+Arguments:
+ ReadOrWrite - indicates read or write, IO or Memory
+ NumBytes - number of bytes to read or write
+ Address - IO or memory address to read from or write to
+ BufferAddr - memory location to read into or that contains the bytes
+ to write
+
+Returns:
+
+--*/
+{
+ SNP_DRIVER *snp;
+ EFI_PCI_IO_PROTOCOL_WIDTH Width;
+
+ snp = (SNP_DRIVER *) (UINTN) UniqueId;
+
+ Width = (EFI_PCI_IO_PROTOCOL_WIDTH) 0;
+ switch (NumBytes) {
+ case 2:
+ Width = (EFI_PCI_IO_PROTOCOL_WIDTH) 1;
+ break;
+
+ case 4:
+ Width = (EFI_PCI_IO_PROTOCOL_WIDTH) 2;
+ break;
+
+ case 8:
+ Width = (EFI_PCI_IO_PROTOCOL_WIDTH) 3;
+ break;
+ }
+
+ switch (ReadOrWrite) {
+ case PXE_IO_READ:
+ snp->IoFncs->Io.Read (
+ snp->IoFncs,
+ Width,
+ snp->IoBarIndex, // BAR 1 (for 32bit regs), IO base address
+ Address,
+ 1, // count
+ (VOID *) (UINTN) BufferAddr
+ );
+ break;
+
+ case PXE_IO_WRITE:
+ snp->IoFncs->Io.Write (
+ snp->IoFncs,
+ Width,
+ snp->IoBarIndex, // BAR 1 (for 32bit regs), IO base address
+ Address,
+ 1, // count
+ (VOID *) (UINTN) BufferAddr
+ );
+ break;
+
+ case PXE_MEM_READ:
+ snp->IoFncs->Mem.Read (
+ snp->IoFncs,
+ Width,
+ snp->MemoryBarIndex, // BAR 0, Memory base address
+ Address,
+ 1, // count
+ (VOID *) (UINTN) BufferAddr
+ );
+ break;
+
+ case PXE_MEM_WRITE:
+ snp->IoFncs->Mem.Write (
+ snp->IoFncs,
+ Width,
+ snp->MemoryBarIndex, // BAR 0, Memory base address
+ Address,
+ 1, // count
+ (VOID *) (UINTN) BufferAddr
+ );
+ break;
+ }
+
+ return ;
+}
+
+VOID
+snp_undi32_callback_map (
+ IN UINT64 UniqueId,
+ IN UINT64 CpuAddr,
+ IN UINT32 NumBytes,
+ IN UINT32 Direction,
+ IN OUT UINT64 DeviceAddrPtr
+ )
+/*++
+
+Routine Description:
+ This is a callback routine supplied to UNDI at undi_start time.
+ UNDI call this routine when it has to map a CPU address to a device
+ address.
+
+Arguments:
+ UniqueId - This was supplied to UNDI at Undi_Start, SNP uses this to store
+ Undi interface context (Undi does not read or write this variable)
+ CpuAddr - Virtual address to be mapped!
+ NumBytes - size of memory to be mapped
+ Direction - direction of data flow for this memory's usage:
+ cpu->device, device->cpu or both ways
+ DeviceAddrPtr - pointer to return the mapped device address
+
+Returns:
+ None
+
+--*/
+{
+ EFI_PHYSICAL_ADDRESS *DevAddrPtr;
+ EFI_PCI_IO_PROTOCOL_OPERATION DirectionFlag;
+ UINTN BuffSize;
+ SNP_DRIVER *snp;
+ UINTN Index;
+ EFI_STATUS Status;
+
+ BuffSize = (UINTN) NumBytes;
+ snp = (SNP_DRIVER *) (UINTN) UniqueId;
+ DevAddrPtr = (EFI_PHYSICAL_ADDRESS *) (UINTN) DeviceAddrPtr;
+
+ if (CpuAddr == 0) {
+ *DevAddrPtr = 0;
+ return ;
+ }
+
+ switch (Direction) {
+ case TO_AND_FROM_DEVICE:
+ DirectionFlag = EfiPciIoOperationBusMasterCommonBuffer;
+ break;
+
+ case FROM_DEVICE:
+ DirectionFlag = EfiPciIoOperationBusMasterWrite;
+ break;
+
+ case TO_DEVICE:
+ DirectionFlag = EfiPciIoOperationBusMasterRead;
+ break;
+
+ default:
+ *DevAddrPtr = 0;
+ //
+ // any non zero indicates error!
+ //
+ return ;
+ }
+ //
+ // find an unused map_list entry
+ //
+ for (Index = 0; Index < MAX_MAP_LENGTH; Index++) {
+ if (snp->map_list[Index].virt == 0) {
+ break;
+ }
+ }
+
+ if (Index >= MAX_MAP_LENGTH) {
+ DEBUG ((EFI_D_INFO, "SNP maplist is FULL\n"));
+ *DevAddrPtr = 0;
+ return ;
+ }
+
+ snp->map_list[Index].virt = (EFI_PHYSICAL_ADDRESS) CpuAddr;
+
+ Status = snp->IoFncs->Map (
+ snp->IoFncs,
+ DirectionFlag,
+ (VOID *) (UINTN) CpuAddr,
+ &BuffSize,
+ DevAddrPtr,
+ &(snp->map_list[Index].map_cookie)
+ );
+ if (Status != EFI_SUCCESS) {
+ *DevAddrPtr = 0;
+ snp->map_list[Index].virt = 0;
+ }
+
+ return ;
+}
+
+VOID
+snp_undi32_callback_unmap (
+ IN UINT64 UniqueId,
+ IN UINT64 CpuAddr,
+ IN UINT32 NumBytes,
+ IN UINT32 Direction,
+ IN UINT64 DeviceAddr
+ )
+/*++
+
+Routine Description:
+ This is a callback routine supplied to UNDI at undi_start time.
+ UNDI call this routine when it wants to unmap an address that was previously
+ mapped using map callback
+
+Arguments:
+ UniqueId - This was supplied to UNDI at Undi_Start, SNP uses this to store
+ Undi interface context (Undi does not read or write this variable)
+ CpuAddr - Virtual address that was mapped!
+ NumBytes - size of memory mapped
+ Direction- direction of data flow for this memory's usage:
+ cpu->device, device->cpu or both ways
+ DeviceAddr - the mapped device address
+
+Returns:
+
+--*/
+{
+ SNP_DRIVER *snp;
+ UINT16 Index;
+
+ snp = (SNP_DRIVER *) (UINTN) UniqueId;
+
+ for (Index = 0; Index < MAX_MAP_LENGTH; Index++) {
+ if (snp->map_list[Index].virt == CpuAddr) {
+ break;
+ }
+ }
+
+ if (Index >= MAX_MAP_LENGTH)
+ {
+ DEBUG ((EFI_D_ERROR, "SNP could not find a mapping, failed to unmap.\n"));
+ return ;
+ }
+
+ snp->IoFncs->Unmap (snp->IoFncs, snp->map_list[Index].map_cookie);
+ snp->map_list[Index].virt = 0;
+ snp->map_list[Index].map_cookie = NULL;
+ return ;
+}
+
+VOID
+snp_undi32_callback_sync (
+ UINT64 UniqueId,
+ UINT64 CpuAddr,
+ UINT32 NumBytes,
+ UINT32 Direction,
+ UINT64 DeviceAddr
+ )
+/*++
+
+Routine Description:
+ This is a callback routine supplied to UNDI at undi_start time.
+ UNDI call this routine when it wants synchronize the virtual buffer contents
+ with the mapped buffer contents. The virtual and mapped buffers need not
+ correspond to the same physical memory (especially if the virtual address is
+ > 4GB). Depending on the direction for which the buffer is mapped, undi will
+ need to synchronize their contents whenever it writes to/reads from the buffer
+ using either the cpu address or the device address.
+
+ EFI does not provide a sync call, since virt=physical, we sould just do
+ the synchronization ourself here!
+
+Arguments:
+ UniqueId - This was supplied to UNDI at Undi_Start, SNP uses this to store
+ Undi interface context (Undi does not read or write this variable)
+ CpuAddr - Virtual address that was mapped!
+ NumBytes - size of memory mapped
+ Direction- direction of data flow for this memory's usage:
+ cpu->device, device->cpu or both ways
+ DeviceAddr - the mapped device address
+
+Returns:
+
+--*/
+{
+ if ((CpuAddr == 0) || (DeviceAddr == 0) || (NumBytes == 0)) {
+ return ;
+
+ }
+
+ switch (Direction) {
+ case FROM_DEVICE:
+ CopyMem ((UINT8 *) (UINTN) CpuAddr, (UINT8 *) (UINTN) DeviceAddr, NumBytes);
+ break;
+
+ case TO_DEVICE:
+ CopyMem ((UINT8 *) (UINTN) DeviceAddr, (UINT8 *) (UINTN) CpuAddr, NumBytes);
+ break;
+ }
+
+ return ;
+}
diff --git a/MdeModulePkg/Universal/Network/SnpDxe/get_status.c b/MdeModulePkg/Universal/Network/SnpDxe/get_status.c
new file mode 100644
index 0000000000..0c1cd8a68e
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/SnpDxe/get_status.c
@@ -0,0 +1,195 @@
+/** @file
+Copyright (c) 2004 - 2007, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module name:
+ get_status.c
+
+Abstract:
+
+Revision history:
+ 2000-Feb-03 M(f)J Genesis.
+
+**/
+
+#include "Snp.h"
+
+STATIC
+/**
+ this routine calls undi to get the status of the interrupts, get the list of
+ transmit buffers that completed transmitting!
+
+ @param snp pointer to snp driver structure
+ @param InterruptStatusPtr a non null pointer gets the interrupt status
+ @param TransmitBufferListPtrs a non null ointer gets the list of pointers of
+ previously transmitted buffers whose
+ transmission was completed asynchrnously.
+
+
+**/
+EFI_STATUS
+pxe_getstatus (
+ SNP_DRIVER *snp,
+ UINT32 *InterruptStatusPtr,
+ VOID **TransmitBufferListPtr
+ )
+{
+ PXE_DB_GET_STATUS *db;
+ UINT16 InterruptFlags;
+ UINT64 TempData;
+
+ db = snp->db;
+ snp->cdb.OpCode = PXE_OPCODE_GET_STATUS;
+
+ snp->cdb.OpFlags = 0;
+
+ if (TransmitBufferListPtr != NULL) {
+ snp->cdb.OpFlags |= PXE_OPFLAGS_GET_TRANSMITTED_BUFFERS;
+ }
+
+ if (InterruptStatusPtr != NULL) {
+ snp->cdb.OpFlags |= PXE_OPFLAGS_GET_INTERRUPT_STATUS;
+ }
+
+ snp->cdb.CPBsize = PXE_CPBSIZE_NOT_USED;
+ snp->cdb.CPBaddr = PXE_CPBADDR_NOT_USED;
+
+ //
+ // size DB for return of one buffer
+ //
+ snp->cdb.DBsize = (UINT16) (((UINT16) (sizeof (PXE_DB_GET_STATUS)) - (UINT16) (sizeof db->TxBuffer)) + (UINT16) (sizeof db->TxBuffer[0]));
+
+ snp->cdb.DBaddr = (UINT64)(UINTN) db;
+
+ snp->cdb.StatCode = PXE_STATCODE_INITIALIZE;
+ snp->cdb.StatFlags = PXE_STATFLAGS_INITIALIZE;
+ snp->cdb.IFnum = snp->if_num;
+ snp->cdb.Control = PXE_CONTROL_LAST_CDB_IN_LIST;
+
+ //
+ // Issue UNDI command and check result.
+ //
+ DEBUG ((EFI_D_NET, "\nsnp->undi.get_status() "));
+
+ (*snp->issue_undi32_command) ((UINT64)(UINTN) &snp->cdb);
+
+ if (snp->cdb.StatCode != EFI_SUCCESS) {
+ DEBUG (
+ (EFI_D_NET,
+ "\nsnp->undi.get_status() %xh:%xh\n",
+ snp->cdb.StatFlags,
+ snp->cdb.StatFlags)
+ );
+
+ return EFI_DEVICE_ERROR;
+ }
+ //
+ // report the values back..
+ //
+ if (InterruptStatusPtr != NULL) {
+ InterruptFlags = (UINT16) (snp->cdb.StatFlags & PXE_STATFLAGS_GET_STATUS_INTERRUPT_MASK);
+
+ *InterruptStatusPtr = 0;
+
+ if (InterruptFlags & PXE_STATFLAGS_GET_STATUS_RECEIVE) {
+ *InterruptStatusPtr |= EFI_SIMPLE_NETWORK_RECEIVE_INTERRUPT;
+ }
+
+ if (InterruptFlags & PXE_STATFLAGS_GET_STATUS_TRANSMIT) {
+ *InterruptStatusPtr |= EFI_SIMPLE_NETWORK_TRANSMIT_INTERRUPT;
+ }
+
+ if (InterruptFlags & PXE_STATFLAGS_GET_STATUS_COMMAND) {
+ *InterruptStatusPtr |= EFI_SIMPLE_NETWORK_COMMAND_INTERRUPT;
+ }
+
+ if (InterruptFlags & PXE_STATFLAGS_GET_STATUS_SOFTWARE) {
+ *InterruptStatusPtr |= EFI_SIMPLE_NETWORK_COMMAND_INTERRUPT;
+ }
+
+ }
+
+ if (TransmitBufferListPtr != NULL) {
+ *TransmitBufferListPtr =
+ (
+ (snp->cdb.StatFlags & PXE_STATFLAGS_GET_STATUS_NO_TXBUFS_WRITTEN) ||
+ (snp->cdb.StatFlags & PXE_STATFLAGS_GET_STATUS_TXBUF_QUEUE_EMPTY)
+ ) ? 0 : (VOID *) (UINTN) db->TxBuffer[0];
+
+ TempData = (UINT64) (UINTN) (*TransmitBufferListPtr);
+ if (snp->IsOldUndi && (TempData >= FOUR_GIGABYTES)) {
+ del_v2p ((VOID *) (UINTN) (db->TxBuffer[0]));
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ This is the SNP interface routine for getting the status
+ This routine basically retrieves snp structure, checks the SNP state and
+ calls the pxe_getstatus routine to actually get the undi status
+
+ @param this context pointer
+ @param InterruptStatusPtr a non null pointer gets the interrupt status
+ @param TransmitBufferListPtrs a non null ointer gets the list of pointers of
+ previously transmitted buffers whose
+ transmission was completed asynchrnously.
+
+
+**/
+EFI_STATUS
+EFIAPI
+snp_undi32_get_status (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL * this,
+ OUT UINT32 *InterruptStatusPtr OPTIONAL,
+ OUT VOID **TransmitBufferListPtr OPTIONAL
+ )
+{
+ SNP_DRIVER *snp;
+ EFI_TPL OldTpl;
+ EFI_STATUS Status;
+
+ if (this == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (InterruptStatusPtr == NULL && TransmitBufferListPtr == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ snp = EFI_SIMPLE_NETWORK_DEV_FROM_THIS (this);
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ if (snp == NULL) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ switch (snp->mode.State) {
+ case EfiSimpleNetworkInitialized:
+ break;
+
+ case EfiSimpleNetworkStopped:
+ Status = EFI_NOT_STARTED;
+ goto ON_EXIT;
+
+ default:
+ Status = EFI_DEVICE_ERROR;
+ goto ON_EXIT;
+ }
+
+ Status = pxe_getstatus (snp, InterruptStatusPtr, TransmitBufferListPtr);
+
+ON_EXIT:
+ gBS->RestoreTPL (OldTpl);
+
+ return Status;
+}
diff --git a/MdeModulePkg/Universal/Network/SnpDxe/initialize.c b/MdeModulePkg/Universal/Network/SnpDxe/initialize.c
new file mode 100644
index 0000000000..69154fc0c7
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/SnpDxe/initialize.c
@@ -0,0 +1,245 @@
+/** @file
+Copyright (c) 2004 - 2007, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module name:
+ initialize.c
+
+Abstract:
+
+Revision history:
+ 2000-Feb-09 M(f)J Genesis.
+
+**/
+
+
+#include "Snp.h"
+
+VOID
+EFIAPI
+SnpWaitForPacketNotify (
+ IN EFI_EVENT Event,
+ IN VOID *SnpPtr
+ );
+
+
+/**
+ this routine calls undi to initialize the interface.
+
+ @param snp pointer to snp driver structure
+ @param CableDetectFlag Do/don't detect the cable (depending on what undi
+ supports)
+
+
+**/
+EFI_STATUS
+pxe_init (
+ SNP_DRIVER *snp,
+ UINT16 CableDetectFlag
+ )
+{
+ PXE_CPB_INITIALIZE *cpb;
+ VOID *addr;
+ EFI_STATUS Status;
+
+ cpb = snp->cpb;
+ if (snp->tx_rx_bufsize != 0) {
+ Status = snp->IoFncs->AllocateBuffer (
+ snp->IoFncs,
+ AllocateAnyPages,
+ EfiBootServicesData,
+ SNP_MEM_PAGES (snp->tx_rx_bufsize),
+ &addr,
+ 0
+ );
+
+ if (Status != EFI_SUCCESS) {
+ DEBUG (
+ (EFI_D_ERROR,
+ "\nsnp->pxe_init() AllocateBuffer %xh (%r)\n",
+ Status,
+ Status)
+ );
+
+ return Status;
+ }
+
+ ASSERT (addr);
+
+ snp->tx_rx_buffer = addr;
+ }
+
+ cpb->MemoryAddr = (UINT64)(UINTN) snp->tx_rx_buffer;
+
+ cpb->MemoryLength = snp->tx_rx_bufsize;
+
+ //
+ // let UNDI decide/detect these values
+ //
+ cpb->LinkSpeed = 0;
+ cpb->TxBufCnt = 0;
+ cpb->TxBufSize = 0;
+ cpb->RxBufCnt = 0;
+ cpb->RxBufSize = 0;
+
+ cpb->DuplexMode = PXE_DUPLEX_DEFAULT;
+
+ cpb->LoopBackMode = LOOPBACK_NORMAL;
+
+ snp->cdb.OpCode = PXE_OPCODE_INITIALIZE;
+ snp->cdb.OpFlags = CableDetectFlag;
+
+ snp->cdb.CPBsize = sizeof (PXE_CPB_INITIALIZE);
+ snp->cdb.DBsize = sizeof (PXE_DB_INITIALIZE);
+
+ snp->cdb.CPBaddr = (UINT64)(UINTN) snp->cpb;
+ snp->cdb.DBaddr = (UINT64)(UINTN) snp->db;
+
+ snp->cdb.StatCode = PXE_STATCODE_INITIALIZE;
+ snp->cdb.StatFlags = PXE_STATFLAGS_INITIALIZE;
+ snp->cdb.IFnum = snp->if_num;
+ snp->cdb.Control = PXE_CONTROL_LAST_CDB_IN_LIST;
+
+ DEBUG ((EFI_D_NET, "\nsnp->undi.initialize() "));
+
+ (*snp->issue_undi32_command) ((UINT64)(UINTN) &snp->cdb);
+
+ if (snp->cdb.StatCode == PXE_STATCODE_SUCCESS) {
+ snp->mode.State = EfiSimpleNetworkInitialized;
+
+ Status = EFI_SUCCESS;
+ } else {
+ DEBUG (
+ (EFI_D_WARN,
+ "\nsnp->undi.initialize() %xh:%xh\n",
+ snp->cdb.StatFlags,
+ snp->cdb.StatCode)
+ );
+
+ if (snp->tx_rx_buffer != NULL) {
+ snp->IoFncs->FreeBuffer (
+ snp->IoFncs,
+ SNP_MEM_PAGES (snp->tx_rx_bufsize),
+ (VOID *) snp->tx_rx_buffer
+ );
+ }
+
+ snp->tx_rx_buffer = NULL;
+
+ Status = EFI_DEVICE_ERROR;
+ }
+
+ return Status;
+}
+
+
+/**
+ This is the SNP interface routine for initializing the interface
+ This routine basically retrieves snp structure, checks the SNP state and
+ calls the pxe_initialize routine to actually do the undi initialization
+
+ @param this context pointer
+ @param extra_rx_buffer_size optional parameter, indicates extra space for
+ rx_buffers
+ @param extra_tx_buffer_size optional parameter, indicates extra space for
+ tx_buffers
+
+
+**/
+EFI_STATUS
+EFIAPI
+snp_undi32_initialize (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *this,
+ IN UINTN extra_rx_buffer_size OPTIONAL,
+ IN UINTN extra_tx_buffer_size OPTIONAL
+ )
+{
+ EFI_STATUS EfiStatus;
+ SNP_DRIVER *snp;
+ EFI_TPL OldTpl;
+
+ //
+ //
+ //
+ if (this == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ snp = EFI_SIMPLE_NETWORK_DEV_FROM_THIS (this);
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ if (snp == NULL) {
+ EfiStatus = EFI_INVALID_PARAMETER;
+ goto ON_EXIT;
+ }
+
+ switch (snp->mode.State) {
+ case EfiSimpleNetworkStarted:
+ break;
+
+ case EfiSimpleNetworkStopped:
+ EfiStatus = EFI_NOT_STARTED;
+ goto ON_EXIT;
+
+ default:
+ EfiStatus = EFI_DEVICE_ERROR;
+ goto ON_EXIT;
+ }
+
+ EfiStatus = gBS->CreateEvent (
+ EVT_NOTIFY_WAIT,
+ TPL_NOTIFY,
+ &SnpWaitForPacketNotify,
+ snp,
+ &snp->snp.WaitForPacket
+ );
+
+ if (EFI_ERROR (EfiStatus)) {
+ snp->snp.WaitForPacket = NULL;
+ EfiStatus = EFI_DEVICE_ERROR;
+ goto ON_EXIT;
+ }
+ //
+ //
+ //
+ snp->mode.MCastFilterCount = 0;
+ snp->mode.ReceiveFilterSetting = 0;
+ ZeroMem (snp->mode.MCastFilter, sizeof snp->mode.MCastFilter);
+ CopyMem (
+ &snp->mode.CurrentAddress,
+ &snp->mode.PermanentAddress,
+ sizeof (EFI_MAC_ADDRESS)
+ );
+
+ //
+ // Compute tx/rx buffer sizes based on UNDI init info and parameters.
+ //
+ snp->tx_rx_bufsize = (UINT32) (snp->init_info.MemoryRequired + extra_rx_buffer_size + extra_tx_buffer_size);
+
+ if (snp->mode.MediaPresentSupported) {
+ if (pxe_init (snp, PXE_OPFLAGS_INITIALIZE_DETECT_CABLE) == EFI_SUCCESS) {
+ snp->mode.MediaPresent = TRUE;
+ goto ON_EXIT;
+ }
+ }
+
+ snp->mode.MediaPresent = FALSE;
+
+ EfiStatus = pxe_init (snp, PXE_OPFLAGS_INITIALIZE_DO_NOT_DETECT_CABLE);
+
+ if (EFI_ERROR (EfiStatus)) {
+ gBS->CloseEvent (snp->snp.WaitForPacket);
+ }
+
+ON_EXIT:
+ gBS->RestoreTPL (OldTpl);
+
+ return EfiStatus;
+}
diff --git a/MdeModulePkg/Universal/Network/SnpDxe/mcast_ip_to_mac.c b/MdeModulePkg/Universal/Network/SnpDxe/mcast_ip_to_mac.c
new file mode 100644
index 0000000000..91b5e02fc1
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/SnpDxe/mcast_ip_to_mac.c
@@ -0,0 +1,165 @@
+/** @file
+Copyright (c) 2004 - 2007, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module name:
+ mcast_ip_to_mac.c
+
+Abstract:
+
+Revision history:
+ 2000-Feb-17 M(f)J Genesis.
+
+**/
+
+#include "Snp.h"
+
+/**
+ this routine calls undi to convert an multicast IP address to a MAC address
+
+ @param snp pointer to snp driver structure
+ @param IPv6 flag to indicate if this is an ipv6 address
+ @param IP multicast IP address
+ @param MAC pointer to hold the return MAC address
+
+
+**/
+STATIC
+EFI_STATUS
+pxe_ip2mac (
+ IN SNP_DRIVER *snp,
+ IN BOOLEAN IPv6,
+ IN EFI_IP_ADDRESS *IP,
+ IN OUT EFI_MAC_ADDRESS *MAC
+ )
+{
+ PXE_CPB_MCAST_IP_TO_MAC *cpb;
+ PXE_DB_MCAST_IP_TO_MAC *db;
+
+ cpb = snp->cpb;
+ db = snp->db;
+ snp->cdb.OpCode = PXE_OPCODE_MCAST_IP_TO_MAC;
+ snp->cdb.OpFlags = (UINT16) (IPv6 ? PXE_OPFLAGS_MCAST_IPV6_TO_MAC : PXE_OPFLAGS_MCAST_IPV4_TO_MAC);
+ snp->cdb.CPBsize = sizeof (PXE_CPB_MCAST_IP_TO_MAC);
+ snp->cdb.DBsize = sizeof (PXE_DB_MCAST_IP_TO_MAC);
+
+ snp->cdb.CPBaddr = (UINT64)(UINTN) cpb;
+ snp->cdb.DBaddr = (UINT64)(UINTN) db;
+
+ snp->cdb.StatCode = PXE_STATCODE_INITIALIZE;
+ snp->cdb.StatFlags = PXE_STATFLAGS_INITIALIZE;
+ snp->cdb.IFnum = snp->if_num;
+ snp->cdb.Control = PXE_CONTROL_LAST_CDB_IN_LIST;
+
+ CopyMem (&cpb->IP, IP, sizeof (PXE_IP_ADDR));
+
+ //
+ // Issue UNDI command and check result.
+ //
+ DEBUG ((EFI_D_NET, "\nsnp->undi.mcast_ip_to_mac() "));
+
+ (*snp->issue_undi32_command) ((UINT64)(UINTN) &snp->cdb);
+
+ switch (snp->cdb.StatCode) {
+ case PXE_STATCODE_SUCCESS:
+ break;
+
+ case PXE_STATCODE_INVALID_CPB:
+ return EFI_INVALID_PARAMETER;
+
+ case PXE_STATCODE_UNSUPPORTED:
+ DEBUG (
+ (EFI_D_NET,
+ "\nsnp->undi.mcast_ip_to_mac() %xh:%xh\n",
+ snp->cdb.StatFlags,
+ snp->cdb.StatCode)
+ );
+ return EFI_UNSUPPORTED;
+
+ default:
+ //
+ // UNDI command failed. Return EFI_DEVICE_ERROR
+ // to caller.
+ //
+ DEBUG (
+ (EFI_D_NET,
+ "\nsnp->undi.mcast_ip_to_mac() %xh:%xh\n",
+ snp->cdb.StatFlags,
+ snp->cdb.StatCode)
+ );
+
+ return EFI_DEVICE_ERROR;
+ }
+
+ CopyMem (MAC, &db->MAC, sizeof (PXE_MAC_ADDR));
+ return EFI_SUCCESS;
+}
+
+
+/**
+ This is the SNP interface routine for converting a multicast IP address to
+ a MAC address.
+ This routine basically retrieves snp structure, checks the SNP state and
+ calls the pxe_ip2mac routine to actually do the conversion
+
+ @param this context pointer
+ @param IPv6 flag to indicate if this is an ipv6 address
+ @param IP multicast IP address
+ @param MAC pointer to hold the return MAC address
+
+
+**/
+EFI_STATUS
+EFIAPI
+snp_undi32_mcast_ip_to_mac (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *this,
+ IN BOOLEAN IPv6,
+ IN EFI_IP_ADDRESS *IP,
+ OUT EFI_MAC_ADDRESS *MAC
+ )
+{
+ SNP_DRIVER *snp;
+ EFI_TPL OldTpl;
+ EFI_STATUS Status;
+
+ //
+ // Get pointer to SNP driver instance for *this.
+ //
+ if (this == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (IP == NULL || MAC == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ snp = EFI_SIMPLE_NETWORK_DEV_FROM_THIS (this);
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ switch (snp->mode.State) {
+ case EfiSimpleNetworkInitialized:
+ break;
+
+ case EfiSimpleNetworkStopped:
+ Status = EFI_NOT_STARTED;
+ goto ON_EXIT;
+
+ default:
+ Status = EFI_DEVICE_ERROR;
+ goto ON_EXIT;
+ }
+
+ Status = pxe_ip2mac (snp, IPv6, IP, MAC);
+
+ON_EXIT:
+ gBS->RestoreTPL (OldTpl);
+
+ return Status;
+}
diff --git a/MdeModulePkg/Universal/Network/SnpDxe/nvdata.c b/MdeModulePkg/Universal/Network/SnpDxe/nvdata.c
new file mode 100644
index 0000000000..531a4d6929
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/SnpDxe/nvdata.c
@@ -0,0 +1,185 @@
+/** @file
+Copyright (c) 2004 - 2007, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module name:
+ nvdata.c
+
+Abstract:
+
+Revision history:
+ 2000-Feb-03 M(f)J Genesis.
+
+**/
+
+#include "snp.h"
+
+
+/**
+ This routine calls Undi to read the desired number of eeprom bytes.
+
+ @param snp pointer to the snp driver structure
+ @param RegOffset eeprom register value relative to the base address
+ @param NumBytes number of bytes to read
+ @param BufferPtr pointer where to read into
+
+
+**/
+STATIC
+EFI_STATUS
+pxe_nvdata_read (
+ IN SNP_DRIVER *snp,
+ IN UINTN RegOffset,
+ IN UINTN NumBytes,
+ IN OUT VOID *BufferPtr
+ )
+{
+ PXE_DB_NVDATA *db;
+
+ db = snp->db;
+ snp->cdb.OpCode = PXE_OPCODE_NVDATA;
+
+ snp->cdb.OpFlags = PXE_OPFLAGS_NVDATA_READ;
+
+ snp->cdb.CPBsize = PXE_CPBSIZE_NOT_USED;
+ snp->cdb.CPBaddr = PXE_CPBADDR_NOT_USED;
+
+ snp->cdb.DBsize = sizeof (PXE_DB_NVDATA);
+ snp->cdb.DBaddr = (UINT64)(UINTN) db;
+
+ snp->cdb.StatCode = PXE_STATCODE_INITIALIZE;
+ snp->cdb.StatFlags = PXE_STATFLAGS_INITIALIZE;
+ snp->cdb.IFnum = snp->if_num;
+ snp->cdb.Control = PXE_CONTROL_LAST_CDB_IN_LIST;
+
+ //
+ // Issue UNDI command and check result.
+ //
+ DEBUG ((EFI_D_NET, "\nsnp->undi.nvdata () "));
+
+ (*snp->issue_undi32_command) ((UINT64)(UINTN) &snp->cdb);
+
+ switch (snp->cdb.StatCode) {
+ case PXE_STATCODE_SUCCESS:
+ break;
+
+ case PXE_STATCODE_UNSUPPORTED:
+ DEBUG (
+ (EFI_D_NET,
+ "\nsnp->undi.nvdata() %xh:%xh\n",
+ snp->cdb.StatFlags,
+ snp->cdb.StatCode)
+ );
+
+ return EFI_UNSUPPORTED;
+
+ default:
+ DEBUG (
+ (EFI_D_NET,
+ "\nsnp->undi.nvdata() %xh:%xh\n",
+ snp->cdb.StatFlags,
+ snp->cdb.StatCode)
+ );
+
+ return EFI_DEVICE_ERROR;
+ }
+
+ CopyMem (BufferPtr, db->Data.Byte + RegOffset, NumBytes);
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ This is an interface call provided by SNP.
+ It does the basic checking on the input parameters and retrieves snp structure
+ and then calls the read_nvdata() call which does the actual reading
+
+ @param this context pointer
+ @param ReadOrWrite true for reading and false for writing
+ @param RegOffset eeprom register relative to the base
+ @param NumBytes how many bytes to read
+ @param BufferPtr address of memory to read into
+
+
+**/
+EFI_STATUS
+EFIAPI
+snp_undi32_nvdata (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *this,
+ IN BOOLEAN ReadOrWrite,
+ IN UINTN RegOffset,
+ IN UINTN NumBytes,
+ IN OUT VOID *BufferPtr
+ )
+{
+ SNP_DRIVER *snp;
+ EFI_TPL OldTpl;
+ EFI_STATUS Status;
+
+ //
+ // Get pointer to SNP driver instance for *this.
+ //
+ if (this == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ snp = EFI_SIMPLE_NETWORK_DEV_FROM_THIS (this);
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ //
+ // Return error if the SNP is not initialized.
+ //
+ switch (snp->mode.State) {
+ case EfiSimpleNetworkInitialized:
+ break;
+
+ case EfiSimpleNetworkStopped:
+ Status = EFI_NOT_STARTED;
+ goto ON_EXIT;
+
+ default:
+ Status = EFI_DEVICE_ERROR;
+ goto ON_EXIT;
+ }
+ //
+ // Return error if non-volatile memory variables are not valid.
+ //
+ if (snp->mode.NvRamSize == 0 || snp->mode.NvRamAccessSize == 0) {
+ Status = EFI_UNSUPPORTED;
+ goto ON_EXIT;
+ }
+ //
+ // Check for invalid parameter combinations.
+ //
+ if ((NumBytes == 0) ||
+ (BufferPtr == NULL) ||
+ (RegOffset >= snp->mode.NvRamSize) ||
+ (RegOffset + NumBytes > snp->mode.NvRamSize) ||
+ (NumBytes % snp->mode.NvRamAccessSize != 0) ||
+ (RegOffset % snp->mode.NvRamAccessSize != 0)
+ ) {
+ Status = EFI_INVALID_PARAMETER;
+ goto ON_EXIT;
+ }
+ //
+ // check the implementation flags of undi if we can write the nvdata!
+ //
+ if (!ReadOrWrite) {
+ Status = EFI_UNSUPPORTED;
+ } else {
+ Status = pxe_nvdata_read (snp, RegOffset, NumBytes, BufferPtr);
+ }
+
+ON_EXIT:
+ gBS->RestoreTPL (OldTpl);
+
+ return Status;
+}
diff --git a/MdeModulePkg/Universal/Network/SnpDxe/receive.c b/MdeModulePkg/Universal/Network/SnpDxe/receive.c
new file mode 100644
index 0000000000..64ca2565d9
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/SnpDxe/receive.c
@@ -0,0 +1,255 @@
+/** @file
+Copyright (c) 2004 - 2007, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module name:
+ receive.c
+
+Abstract:
+
+Revision history:
+ 2000-Feb-03 M(f)J Genesis.
+
+**/
+
+
+#include "Snp.h"
+
+/**
+ this routine calls undi to receive a packet and fills in the data in the
+ input pointers!
+
+ @param snp pointer to snp driver structure
+ @param BufferPtr pointer to the memory for the received data
+ @param BuffSizePtr is a pointer to the length of the buffer on entry and
+ contains the length of the received data on return
+ @param HeaderSizePtr pointer to the header portion of the data received.
+ @param SourceAddrPtr optional parameter, is a pointer to contain the
+ source ethernet address on return
+ @param DestinationAddrPtr optional parameter, is a pointer to contain the
+ destination ethernet address on return
+ @param ProtocolPtr optional parameter, is a pointer to contain the
+ protocol type from the ethernet header on return
+
+
+**/
+STATIC
+EFI_STATUS
+pxe_receive (
+ SNP_DRIVER *snp,
+ VOID *BufferPtr,
+ UINTN *BuffSizePtr,
+ UINTN *HeaderSizePtr,
+ EFI_MAC_ADDRESS *SourceAddrPtr,
+ EFI_MAC_ADDRESS *DestinationAddrPtr,
+ UINT16 *ProtocolPtr
+ )
+{
+ PXE_CPB_RECEIVE *cpb;
+ PXE_DB_RECEIVE *db;
+ UINTN buf_size;
+ UINT64 TempData;
+
+ cpb = snp->cpb;
+ db = snp->db;
+ buf_size = *BuffSizePtr;
+ //
+ // IMPORTANT NOTE:
+ // In case of the older 3.0 UNDI, if the input buffer address is beyond 4GB,
+ // DO NOT call the map function on the given buffer, instead use
+ // a global buffer. The reason is that UNDI3.0 has some unnecessary check of
+ // making sure that all the addresses (whether or not they will be given
+ // to the NIC ) supplied to it are below 4GB. It may or may not use
+ // the mapped address after all (like in case of CPB and DB)!
+ // Instead of using the global buffer whose address is allocated within the
+ // 2GB limit if I start mapping the given buffer we lose the data, here is
+ // why!!!
+ // if our address is > 4GB, the map call creates another buffer below 2GB and
+ // copies data to/from the original buffer to the mapped buffer either at
+ // map time or unmap time depending on the map direction.
+ // UNDI will not complain since we already mapped the buffer to be
+ // within the 2GB limit but will not use (I know undi) the mapped address
+ // since it does not give the user buffers to the NIC's receive unit,
+ // It just copies the received packet into the user buffer using the virtual
+ // (CPU) address rather than the mapped (device or physical) address.
+ // When the UNDI call returns, if we then unmap the buffer, we will lose
+ // the contents because unmap copies the contents of the mapped buffer into
+ // the original buffer (since the direction is FROM_DEVICE) !!!
+ //
+ // this is not a problem in Undi 3.1 because this undi uses it's map callback
+ // routine to map a cpu address to device address and it does it only if
+ // it is giving the address to the device and unmaps it before using the cpu
+ // address!
+ //
+ TempData = (UINT64) (UINTN) BufferPtr;
+ if (snp->IsOldUndi && (TempData >= FOUR_GIGABYTES)) {
+ cpb->BufferAddr = (UINT64)(UINTN) snp->receive_buf;
+ cpb->BufferLen = (UINT32) (snp->init_info.MediaHeaderLen + snp->init_info.FrameDataLen);
+ } else {
+ cpb->BufferAddr = (UINT64)(UINTN) BufferPtr;
+ cpb->BufferLen = (UINT32) *BuffSizePtr;
+ }
+
+ cpb->reserved = 0;
+
+ snp->cdb.OpCode = PXE_OPCODE_RECEIVE;
+ snp->cdb.OpFlags = PXE_OPFLAGS_NOT_USED;
+
+ snp->cdb.CPBsize = sizeof (PXE_CPB_RECEIVE);
+ snp->cdb.CPBaddr = (UINT64)(UINTN) cpb;
+
+ snp->cdb.DBsize = sizeof (PXE_DB_RECEIVE);
+ snp->cdb.DBaddr = (UINT64)(UINTN) db;
+
+ snp->cdb.StatCode = PXE_STATCODE_INITIALIZE;
+ snp->cdb.StatFlags = PXE_STATFLAGS_INITIALIZE;
+ snp->cdb.IFnum = snp->if_num;
+ snp->cdb.Control = PXE_CONTROL_LAST_CDB_IN_LIST;
+
+ //
+ // Issue UNDI command and check result.
+ //
+ DEBUG ((EFI_D_INFO, "\nsnp->undi.receive () "));
+
+ (*snp->issue_undi32_command) ((UINT64)(UINTN) &snp->cdb);
+
+ switch (snp->cdb.StatCode) {
+ case PXE_STATCODE_SUCCESS:
+ break;
+
+ case PXE_STATCODE_NO_DATA:
+ DEBUG (
+ (EFI_D_INFO,
+ "\nsnp->undi.receive () %xh:%xh\n",
+ snp->cdb.StatFlags,
+ snp->cdb.StatCode)
+ );
+
+ return EFI_NOT_READY;
+
+ default:
+ DEBUG (
+ (EFI_D_ERROR,
+ "\nsnp->undi.receive() %xh:%xh\n",
+ snp->cdb.StatFlags,
+ snp->cdb.StatCode)
+ );
+
+ return EFI_DEVICE_ERROR;
+ }
+
+ *BuffSizePtr = db->FrameLen;
+
+ if (HeaderSizePtr != NULL) {
+ *HeaderSizePtr = db->MediaHeaderLen;
+ }
+
+ if (SourceAddrPtr != NULL) {
+ CopyMem (SourceAddrPtr, &db->SrcAddr, snp->mode.HwAddressSize);
+ }
+
+ if (DestinationAddrPtr != NULL) {
+ CopyMem (DestinationAddrPtr, &db->DestAddr, snp->mode.HwAddressSize);
+ }
+
+ if (ProtocolPtr != NULL) {
+ *ProtocolPtr = (UINT16) PXE_SWAP_UINT16 (db->Protocol); /* we need to do the byte swapping */
+ }
+
+ TempData = (UINT64) (UINTN) BufferPtr;
+ if (snp->IsOldUndi && (TempData >= FOUR_GIGABYTES)) {
+ CopyMem (BufferPtr, snp->receive_buf, snp->init_info.MediaHeaderLen + snp->init_info.FrameDataLen);
+ }
+
+ return (*BuffSizePtr <= buf_size) ? EFI_SUCCESS : EFI_BUFFER_TOO_SMALL;
+}
+
+
+/**
+ This is the SNP interface routine for receiving network data.
+ This routine basically retrieves snp structure, checks the SNP state and
+ calls the pxe_receive routine to actually do the receive!
+
+ @param this context pointer
+ @param HeaderSizePtr optional parameter and is a pointer to the header
+ portion of the data received.
+ @param BuffSizePtr is a pointer to the length of the buffer on entry and
+ contains the length of the received data on return
+ @param BufferPtr pointer to the memory for the received data
+ @param SourceAddrPtr optional parameter, is a pointer to contain the
+ source ethernet address on return
+ @param DestinationAddrPtr optional parameter, is a pointer to contain the
+ destination ethernet address on return
+ @param ProtocolPtr optional parameter, is a pointer to contain the
+ protocol type from the ethernet header on return
+
+
+**/
+EFI_STATUS
+EFIAPI
+snp_undi32_receive (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL * this,
+ OUT UINTN *HeaderSizePtr OPTIONAL,
+ IN OUT UINTN *BuffSizePtr,
+ OUT VOID *BufferPtr,
+ OUT EFI_MAC_ADDRESS * SourceAddrPtr OPTIONAL,
+ OUT EFI_MAC_ADDRESS * DestinationAddrPtr OPTIONAL,
+ OUT UINT16 *ProtocolPtr OPTIONAL
+ )
+{
+ SNP_DRIVER *snp;
+ EFI_TPL OldTpl;
+ EFI_STATUS Status;
+
+ if (this == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ snp = EFI_SIMPLE_NETWORK_DEV_FROM_THIS (this);
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ switch (snp->mode.State) {
+ case EfiSimpleNetworkInitialized:
+ break;
+
+ case EfiSimpleNetworkStopped:
+ Status = EFI_NOT_STARTED;
+ goto ON_EXIT;
+
+ default:
+ Status = EFI_DEVICE_ERROR;
+ goto ON_EXIT;
+ }
+
+ if ((BuffSizePtr == NULL) || (BufferPtr == NULL)) {
+ Status = EFI_INVALID_PARAMETER;
+ goto ON_EXIT;
+ }
+
+ if (!snp->mode.ReceiveFilterSetting) {
+ Status = EFI_DEVICE_ERROR;
+ goto ON_EXIT;
+ }
+
+ Status = pxe_receive (
+ snp,
+ BufferPtr,
+ BuffSizePtr,
+ HeaderSizePtr,
+ SourceAddrPtr,
+ DestinationAddrPtr,
+ ProtocolPtr
+ );
+
+ON_EXIT:
+ gBS->RestoreTPL (OldTpl);
+
+ return Status;
+}
diff --git a/MdeModulePkg/Universal/Network/SnpDxe/receive_filters.c b/MdeModulePkg/Universal/Network/SnpDxe/receive_filters.c
new file mode 100644
index 0000000000..3886d2078d
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/SnpDxe/receive_filters.c
@@ -0,0 +1,406 @@
+/** @file
+Copyright (c) 2004 - 2007, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module name:
+ receive_filters.c
+
+Abstract:
+
+Revision history:
+ 2000-Feb-17 M(f)J Genesis.
+
+**/
+
+
+
+#include "Snp.h"
+
+/**
+ this routine calls undi to enable the receive filters.
+
+ @param snp pointer to snp driver structure
+ @param EnableFlags bit mask for enabling the receive filters
+ @param MCastAddressCount multicast address count for a new multicast address
+ list
+ @param MCastAddressList list of new multicast addresses
+
+
+**/
+STATIC
+EFI_STATUS
+pxe_rcvfilter_enable (
+ SNP_DRIVER *snp,
+ UINT32 EnableFlags,
+ UINTN MCastAddressCount,
+ EFI_MAC_ADDRESS *MCastAddressList
+ )
+{
+ snp->cdb.OpCode = PXE_OPCODE_RECEIVE_FILTERS;
+ snp->cdb.OpFlags = PXE_OPFLAGS_RECEIVE_FILTER_ENABLE;
+ snp->cdb.CPBsize = PXE_CPBSIZE_NOT_USED;
+ snp->cdb.DBsize = PXE_DBSIZE_NOT_USED;
+ snp->cdb.CPBaddr = PXE_CPBADDR_NOT_USED;
+ snp->cdb.DBaddr = PXE_DBADDR_NOT_USED;
+ snp->cdb.StatCode = PXE_STATCODE_INITIALIZE;
+ snp->cdb.StatFlags = PXE_STATFLAGS_INITIALIZE;
+ snp->cdb.IFnum = snp->if_num;
+ snp->cdb.Control = PXE_CONTROL_LAST_CDB_IN_LIST;
+
+ if ((EnableFlags & EFI_SIMPLE_NETWORK_RECEIVE_UNICAST) != 0) {
+ snp->cdb.OpFlags |= PXE_OPFLAGS_RECEIVE_FILTER_UNICAST;
+ }
+
+ if ((EnableFlags & EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST) != 0) {
+ snp->cdb.OpFlags |= PXE_OPFLAGS_RECEIVE_FILTER_BROADCAST;
+ }
+
+ if ((EnableFlags & EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS) != 0) {
+ snp->cdb.OpFlags |= PXE_OPFLAGS_RECEIVE_FILTER_PROMISCUOUS;
+ }
+
+ if ((EnableFlags & EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST) != 0) {
+ snp->cdb.OpFlags |= PXE_OPFLAGS_RECEIVE_FILTER_ALL_MULTICAST;
+ }
+
+ if ((EnableFlags & EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST) != 0) {
+ snp->cdb.OpFlags |= PXE_OPFLAGS_RECEIVE_FILTER_FILTERED_MULTICAST;
+ }
+
+ if (MCastAddressCount != 0) {
+ snp->cdb.CPBsize = (UINT16) (MCastAddressCount * sizeof (EFI_MAC_ADDRESS));
+ snp->cdb.CPBaddr = (UINT64)(UINTN) snp->cpb;
+ CopyMem (snp->cpb, MCastAddressList, snp->cdb.CPBsize);
+ }
+ //
+ // Issue UNDI command and check result.
+ //
+ DEBUG ((EFI_D_NET, "\nsnp->undi.receive_filters() "));
+
+ (*snp->issue_undi32_command) ((UINT64)(UINTN) &snp->cdb);
+
+ if (snp->cdb.StatCode != EFI_SUCCESS) {
+ //
+ // UNDI command failed. Return UNDI status to caller.
+ //
+ DEBUG (
+ (EFI_D_ERROR,
+ "\nsnp->undi.receive_filters() %xh:%xh\n",
+ snp->cdb.StatFlags,
+ snp->cdb.StatCode)
+ );
+
+ switch (snp->cdb.StatCode) {
+ case PXE_STATCODE_INVALID_CDB:
+ case PXE_STATCODE_INVALID_CPB:
+ case PXE_STATCODE_INVALID_PARAMETER:
+ return EFI_INVALID_PARAMETER;
+
+ case PXE_STATCODE_UNSUPPORTED:
+ return EFI_UNSUPPORTED;
+ }
+
+ return EFI_DEVICE_ERROR;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ this routine calls undi to disable the receive filters.
+
+ @param snp pointer to snp driver structure
+ @param DisableFlags bit mask for disabling the receive filters
+ @param ResetMCastList boolean flag to reset/delete the multicast filter list
+
+
+**/
+STATIC
+EFI_STATUS
+pxe_rcvfilter_disable (
+ SNP_DRIVER *snp,
+ UINT32 DisableFlags,
+ BOOLEAN ResetMCastList
+ )
+{
+ snp->cdb.OpCode = PXE_OPCODE_RECEIVE_FILTERS;
+ snp->cdb.CPBsize = PXE_CPBSIZE_NOT_USED;
+ snp->cdb.DBsize = PXE_DBSIZE_NOT_USED;
+ snp->cdb.CPBaddr = PXE_CPBADDR_NOT_USED;
+ snp->cdb.DBaddr = PXE_DBADDR_NOT_USED;
+ snp->cdb.StatCode = PXE_STATCODE_INITIALIZE;
+ snp->cdb.StatFlags = PXE_STATFLAGS_INITIALIZE;
+ snp->cdb.IFnum = snp->if_num;
+ snp->cdb.Control = PXE_CONTROL_LAST_CDB_IN_LIST;
+
+ snp->cdb.OpFlags = (UINT16) (DisableFlags ? PXE_OPFLAGS_RECEIVE_FILTER_DISABLE : PXE_OPFLAGS_NOT_USED);
+
+ if (ResetMCastList) {
+ snp->cdb.OpFlags |= PXE_OPFLAGS_RECEIVE_FILTER_RESET_MCAST_LIST;
+ }
+
+ if ((DisableFlags & EFI_SIMPLE_NETWORK_RECEIVE_UNICAST) != 0) {
+ snp->cdb.OpFlags |= PXE_OPFLAGS_RECEIVE_FILTER_UNICAST;
+ }
+
+ if ((DisableFlags & EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST) != 0) {
+ snp->cdb.OpFlags |= PXE_OPFLAGS_RECEIVE_FILTER_BROADCAST;
+ }
+
+ if ((DisableFlags & EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS) != 0) {
+ snp->cdb.OpFlags |= PXE_OPFLAGS_RECEIVE_FILTER_PROMISCUOUS;
+ }
+
+ if ((DisableFlags & EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST) != 0) {
+ snp->cdb.OpFlags |= PXE_OPFLAGS_RECEIVE_FILTER_ALL_MULTICAST;
+ }
+
+ if ((DisableFlags & EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST) != 0) {
+ snp->cdb.OpFlags |= PXE_OPFLAGS_RECEIVE_FILTER_FILTERED_MULTICAST;
+ }
+ //
+ // Issue UNDI command and check result.
+ //
+ DEBUG ((EFI_D_NET, "\nsnp->undi.receive_filters() "));
+
+ (*snp->issue_undi32_command) ((UINT64)(UINTN) &snp->cdb);
+
+ if (snp->cdb.StatCode != EFI_SUCCESS) {
+ //
+ // UNDI command failed. Return UNDI status to caller.
+ //
+ DEBUG (
+ (EFI_D_ERROR,
+ "\nsnp->undi.receive_filters() %xh:%xh\n",
+ snp->cdb.StatFlags,
+ snp->cdb.StatCode)
+ );
+
+ return EFI_DEVICE_ERROR;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ this routine calls undi to read the receive filters.
+
+ @param snp pointer to snp driver structure
+
+
+**/
+STATIC
+EFI_STATUS
+pxe_rcvfilter_read (
+ SNP_DRIVER *snp
+ )
+{
+ snp->cdb.OpCode = PXE_OPCODE_RECEIVE_FILTERS;
+ snp->cdb.OpFlags = PXE_OPFLAGS_RECEIVE_FILTER_READ;
+ snp->cdb.CPBsize = PXE_CPBSIZE_NOT_USED;
+ snp->cdb.DBsize = (UINT16) (snp->mode.MaxMCastFilterCount * sizeof (EFI_MAC_ADDRESS));
+ snp->cdb.CPBaddr = PXE_CPBADDR_NOT_USED;
+ if (snp->cdb.DBsize == 0) {
+ snp->cdb.DBaddr = (UINT64)(UINTN) NULL;
+ } else {
+ snp->cdb.DBaddr = (UINT64)(UINTN) snp->db;
+ ZeroMem (snp->db, snp->cdb.DBsize);
+ }
+
+ snp->cdb.StatCode = PXE_STATCODE_INITIALIZE;
+ snp->cdb.StatFlags = PXE_STATFLAGS_INITIALIZE;
+ snp->cdb.IFnum = snp->if_num;
+ snp->cdb.Control = PXE_CONTROL_LAST_CDB_IN_LIST;
+
+ DEBUG ((EFI_D_NET, "\nsnp->undi.receive_filters() "));
+
+ (*snp->issue_undi32_command) ((UINT64)(UINTN) &snp->cdb);
+
+ if (snp->cdb.StatCode != EFI_SUCCESS) {
+ //
+ // UNDI command failed. Return UNDI status to caller.
+ //
+ DEBUG (
+ (EFI_D_ERROR,
+ "\nsnp->undi.receive_filters() %xh:%xh\n",
+ snp->cdb.StatFlags,
+ snp->cdb.StatCode)
+ );
+
+ return EFI_DEVICE_ERROR;
+ }
+ //
+ // Convert UNDI32 StatFlags to EFI SNP filter flags.
+ //
+ snp->mode.ReceiveFilterSetting = 0;
+
+ if ((snp->cdb.StatFlags & PXE_STATFLAGS_RECEIVE_FILTER_UNICAST) != 0) {
+ snp->mode.ReceiveFilterSetting |= EFI_SIMPLE_NETWORK_RECEIVE_UNICAST;
+ }
+
+ if ((snp->cdb.StatFlags & PXE_STATFLAGS_RECEIVE_FILTER_BROADCAST) != 0) {
+ snp->mode.ReceiveFilterSetting |= EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST;
+ }
+
+ if ((snp->cdb.StatFlags & PXE_STATFLAGS_RECEIVE_FILTER_PROMISCUOUS) != 0) {
+ snp->mode.ReceiveFilterSetting |= EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS;
+ }
+
+ if ((snp->cdb.StatFlags & PXE_STATFLAGS_RECEIVE_FILTER_ALL_MULTICAST) != 0) {
+ snp->mode.ReceiveFilterSetting |= EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST;
+ }
+
+ if ((snp->cdb.StatFlags & PXE_STATFLAGS_RECEIVE_FILTER_FILTERED_MULTICAST) != 0) {
+ snp->mode.ReceiveFilterSetting |= EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST;
+ }
+
+ CopyMem (snp->mode.MCastFilter, snp->db, snp->cdb.DBsize);
+
+ //
+ // Count number of active entries in multicast filter list.
+ //
+ {
+ EFI_MAC_ADDRESS ZeroMacAddr;
+
+ SetMem (&ZeroMacAddr, sizeof ZeroMacAddr, 0);
+
+ for (snp->mode.MCastFilterCount = 0;
+ snp->mode.MCastFilterCount < snp->mode.MaxMCastFilterCount;
+ snp->mode.MCastFilterCount++
+ ) {
+ if (CompareMem (
+ &snp->mode.MCastFilter[snp->mode.MCastFilterCount],
+ &ZeroMacAddr,
+ sizeof ZeroMacAddr
+ ) == 0) {
+ break;
+ }
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ This is the SNP interface routine for reading/enabling/disabling the
+ receive filters.
+ This routine basically retrieves snp structure, checks the SNP state and
+ checks the parameter validity, calls one of the above routines to actually
+ do the work
+
+ @param this context pointer
+ @param EnableFlags bit mask for enabling the receive filters
+ @param DisableFlags bit mask for disabling the receive filters
+ @param ResetMCastList boolean flag to reset/delete the multicast filter list
+ @param MCastAddressCount multicast address count for a new multicast address
+ list
+ @param MCastAddressList list of new multicast addresses
+
+
+**/
+EFI_STATUS
+EFIAPI
+snp_undi32_receive_filters (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL * this,
+ IN UINT32 EnableFlags,
+ IN UINT32 DisableFlags,
+ IN BOOLEAN ResetMCastList,
+ IN UINTN MCastAddressCount OPTIONAL,
+ IN EFI_MAC_ADDRESS * MCastAddressList OPTIONAL
+ )
+{
+ SNP_DRIVER *snp;
+ EFI_STATUS Status;
+ EFI_TPL OldTpl;
+
+ if (this == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ snp = EFI_SIMPLE_NETWORK_DEV_FROM_THIS (this);
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ switch (snp->mode.State) {
+ case EfiSimpleNetworkInitialized:
+ break;
+
+ case EfiSimpleNetworkStopped:
+ Status = EFI_NOT_STARTED;
+ goto ON_EXIT;
+
+ default:
+ Status = EFI_DEVICE_ERROR;
+ goto ON_EXIT;
+ }
+ //
+ // check if we are asked to enable or disable something that the UNDI
+ // does not even support!
+ //
+ if (((EnableFlags &~snp->mode.ReceiveFilterMask) != 0) ||
+ ((DisableFlags &~snp->mode.ReceiveFilterMask) != 0)) {
+ Status = EFI_INVALID_PARAMETER;
+ goto ON_EXIT;
+ }
+
+ if (ResetMCastList) {
+
+ DisableFlags |= EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST & snp->mode.ReceiveFilterMask;
+ MCastAddressCount = 0;
+ MCastAddressList = NULL;
+ } else {
+ if (MCastAddressCount != 0) {
+ if ((MCastAddressCount > snp->mode.MaxMCastFilterCount) ||
+ (MCastAddressList == NULL)) {
+
+ Status = EFI_INVALID_PARAMETER;
+ goto ON_EXIT;
+ }
+ }
+ }
+
+ if (EnableFlags == 0 && DisableFlags == 0 && !ResetMCastList && MCastAddressCount == 0) {
+ Status = EFI_SUCCESS;
+ goto ON_EXIT;
+ }
+
+ if ((EnableFlags & EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST) != 0 && MCastAddressCount == 0) {
+ Status = EFI_INVALID_PARAMETER;
+ goto ON_EXIT;
+ }
+
+ if ((EnableFlags != 0) || (MCastAddressCount != 0)) {
+ Status = pxe_rcvfilter_enable (
+ snp,
+ EnableFlags,
+ MCastAddressCount,
+ MCastAddressList
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+ }
+
+ if ((DisableFlags != 0) || ResetMCastList) {
+ Status = pxe_rcvfilter_disable (snp, DisableFlags, ResetMCastList);
+
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+ }
+
+ Status = pxe_rcvfilter_read (snp);
+
+ON_EXIT:
+ gBS->RestoreTPL (OldTpl);
+
+ return Status;
+}
diff --git a/MdeModulePkg/Universal/Network/SnpDxe/reset.c b/MdeModulePkg/Universal/Network/SnpDxe/reset.c
new file mode 100644
index 0000000000..0a08032fc4
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/SnpDxe/reset.c
@@ -0,0 +1,128 @@
+/** @file
+Copyright (c) 2004 - 2007, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module name:
+ reset.c
+
+Abstract:
+
+Revision history:
+ 2000-Feb-09 M(f)J Genesis.
+
+**/
+
+#include "Snp.h"
+
+
+/**
+ This routine calls undi to reset the nic.
+
+ @param snp pointer to the snp driver structure
+
+ @return EFI_SUCCESSFUL for a successful completion
+ @return other for failed calls
+
+**/
+STATIC
+EFI_STATUS
+pxe_reset (
+ SNP_DRIVER *snp
+ )
+{
+ snp->cdb.OpCode = PXE_OPCODE_RESET;
+ snp->cdb.OpFlags = PXE_OPFLAGS_NOT_USED;
+ snp->cdb.CPBsize = PXE_CPBSIZE_NOT_USED;
+ snp->cdb.DBsize = PXE_DBSIZE_NOT_USED;
+ snp->cdb.CPBaddr = PXE_CPBADDR_NOT_USED;
+ snp->cdb.DBaddr = PXE_DBADDR_NOT_USED;
+ snp->cdb.StatCode = PXE_STATCODE_INITIALIZE;
+ snp->cdb.StatFlags = PXE_STATFLAGS_INITIALIZE;
+ snp->cdb.IFnum = snp->if_num;
+ snp->cdb.Control = PXE_CONTROL_LAST_CDB_IN_LIST;
+
+ //
+ // Issue UNDI command and check result.
+ //
+ DEBUG ((EFI_D_NET, "\nsnp->undi.reset() "));
+
+ (*snp->issue_undi32_command) ((UINT64)(UINTN) &snp->cdb);
+
+ if (snp->cdb.StatCode != PXE_STATCODE_SUCCESS) {
+ DEBUG (
+ (EFI_D_WARN,
+ "\nsnp->undi32.reset() %xh:%xh\n",
+ snp->cdb.StatFlags,
+ snp->cdb.StatCode)
+ );
+
+ //
+ // UNDI could not be reset. Return UNDI error.
+ //
+ return EFI_DEVICE_ERROR;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ This is the SNP interface routine for resetting the NIC
+ This routine basically retrieves snp structure, checks the SNP state and
+ calls the pxe_reset routine to actually do the reset!
+
+ @param this context pointer
+ @param ExtendedVerification not implemented
+
+
+**/
+EFI_STATUS
+EFIAPI
+snp_undi32_reset (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *this,
+ IN BOOLEAN ExtendedVerification
+ )
+{
+ SNP_DRIVER *snp;
+ EFI_TPL OldTpl;
+ EFI_STATUS Status;
+
+ //
+ // Resolve Warning 4 unreferenced parameter problem
+ //
+ ExtendedVerification = 0;
+
+ if (this == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ snp = EFI_SIMPLE_NETWORK_DEV_FROM_THIS (this);
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ switch (snp->mode.State) {
+ case EfiSimpleNetworkInitialized:
+ break;
+
+ case EfiSimpleNetworkStopped:
+ Status = EFI_NOT_STARTED;
+ goto ON_EXIT;
+
+ default:
+ Status = EFI_DEVICE_ERROR;
+ goto ON_EXIT;
+ }
+
+ Status = pxe_reset (snp);
+
+ON_EXIT:
+ gBS->RestoreTPL (OldTpl);
+
+ return Status;
+}
diff --git a/MdeModulePkg/Universal/Network/SnpDxe/shutdown.c b/MdeModulePkg/Universal/Network/SnpDxe/shutdown.c
new file mode 100644
index 0000000000..8e0ae45503
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/SnpDxe/shutdown.c
@@ -0,0 +1,148 @@
+/** @file
+Copyright (c) 2004 - 2007, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module name:
+ shutdown.c
+
+Abstract:
+
+Revision history:
+ 2000-Feb-14 M(f)J Genesis.
+
+**/
+
+#include "Snp.h"
+
+
+/**
+ this routine calls undi to shut down the interface.
+
+ @param snp pointer to snp driver structure
+
+
+**/
+EFI_STATUS
+pxe_shutdown (
+ IN SNP_DRIVER *snp
+ )
+{
+ snp->cdb.OpCode = PXE_OPCODE_SHUTDOWN;
+ snp->cdb.OpFlags = PXE_OPFLAGS_NOT_USED;
+ snp->cdb.CPBsize = PXE_CPBSIZE_NOT_USED;
+ snp->cdb.DBsize = PXE_DBSIZE_NOT_USED;
+ snp->cdb.CPBaddr = PXE_CPBADDR_NOT_USED;
+ snp->cdb.DBaddr = PXE_DBADDR_NOT_USED;
+ snp->cdb.StatCode = PXE_STATCODE_INITIALIZE;
+ snp->cdb.StatFlags = PXE_STATFLAGS_INITIALIZE;
+ snp->cdb.IFnum = snp->if_num;
+ snp->cdb.Control = PXE_CONTROL_LAST_CDB_IN_LIST;
+
+ //
+ // Issue UNDI command and check result.
+ //
+ DEBUG ((EFI_D_NET, "\nsnp->undi.shutdown() "));
+
+ (*snp->issue_undi32_command) ((UINT64)(UINTN) &snp->cdb);
+
+ if (snp->cdb.StatCode != PXE_STATCODE_SUCCESS) {
+ //
+ // UNDI could not be shutdown. Return UNDI error.
+ //
+ DEBUG ((EFI_D_WARN, "\nsnp->undi.shutdown() %xh:%xh\n", snp->cdb.StatFlags, snp->cdb.StatCode));
+
+ return EFI_DEVICE_ERROR;
+ }
+ //
+ // Free allocated memory.
+ //
+ if (snp->tx_rx_buffer != NULL) {
+ snp->IoFncs->FreeBuffer (
+ snp->IoFncs,
+ SNP_MEM_PAGES (snp->tx_rx_bufsize),
+ (VOID *) snp->tx_rx_buffer
+ );
+ }
+
+ snp->tx_rx_buffer = NULL;
+ snp->tx_rx_bufsize = 0;
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ This is the SNP interface routine for shutting down the interface
+ This routine basically retrieves snp structure, checks the SNP state and
+ calls the pxe_shutdown routine to actually do the undi shutdown
+
+ @param this context pointer
+
+
+**/
+EFI_STATUS
+EFIAPI
+snp_undi32_shutdown (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *this
+ )
+{
+ SNP_DRIVER *snp;
+ EFI_STATUS Status;
+ EFI_TPL OldTpl;
+
+ //
+ //
+ //
+ if (this == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ snp = EFI_SIMPLE_NETWORK_DEV_FROM_THIS (this);
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ //
+ //
+ //
+ switch (snp->mode.State) {
+ case EfiSimpleNetworkInitialized:
+ break;
+
+ case EfiSimpleNetworkStopped:
+ Status = EFI_NOT_STARTED;
+ goto ON_EXIT;
+
+ default:
+ Status = EFI_DEVICE_ERROR;
+ goto ON_EXIT;
+ }
+ //
+ //
+ //
+ Status = pxe_shutdown (snp);
+
+ snp->mode.State = EfiSimpleNetworkStarted;
+ snp->mode.ReceiveFilterSetting = 0;
+
+ snp->mode.MCastFilterCount = 0;
+ snp->mode.ReceiveFilterSetting = 0;
+ ZeroMem (snp->mode.MCastFilter, sizeof snp->mode.MCastFilter);
+ CopyMem (
+ &snp->mode.CurrentAddress,
+ &snp->mode.PermanentAddress,
+ sizeof (EFI_MAC_ADDRESS)
+ );
+
+ gBS->CloseEvent (snp->snp.WaitForPacket);
+
+ON_EXIT:
+ gBS->RestoreTPL (OldTpl);
+
+ return Status;
+}
diff --git a/MdeModulePkg/Universal/Network/SnpDxe/snp.c b/MdeModulePkg/Universal/Network/SnpDxe/snp.c
new file mode 100644
index 0000000000..460cdfd472
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/SnpDxe/snp.c
@@ -0,0 +1,1269 @@
+/** @file
+Copyright (c) 2004 - 2005, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module name:
+ snp.c
+
+Abstract:
+
+
+**/
+
+#include "Snp.h"
+
+EFI_STATUS
+pxe_start (
+ SNP_DRIVER *snp
+ );
+EFI_STATUS
+pxe_stop (
+ SNP_DRIVER *snp
+ );
+EFI_STATUS
+pxe_init (
+ SNP_DRIVER *snp,
+ UINT16 OpFlags
+ );
+EFI_STATUS
+pxe_shutdown (
+ SNP_DRIVER *snp
+ );
+EFI_STATUS
+pxe_get_stn_addr (
+ SNP_DRIVER *snp
+ );
+
+EFI_STATUS
+EFIAPI
+InitializeSnpNiiDriver (
+ IN EFI_HANDLE image_handle,
+ IN EFI_SYSTEM_TABLE *system_table
+ );
+
+EFI_STATUS
+EFIAPI
+SimpleNetworkDriverSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
+ );
+
+EFI_STATUS
+EFIAPI
+SimpleNetworkDriverStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
+ );
+
+EFI_STATUS
+EFIAPI
+SimpleNetworkDriverStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer
+ );
+
+//
+// Simple Network Protocol Driver Global Variables
+//
+EFI_DRIVER_BINDING_PROTOCOL mSimpleNetworkDriverBinding = {
+ SimpleNetworkDriverSupported,
+ SimpleNetworkDriverStart,
+ SimpleNetworkDriverStop,
+ 0xa,
+ NULL,
+ NULL
+};
+
+//
+// Module global variables needed to support undi 3.0 interface
+//
+EFI_PCI_IO_PROTOCOL *mPciIoFncs;
+struct s_v2p *_v2p = NULL; // undi3.0 map_list head
+// End Global variables
+//
+
+/**
+ This routine maps the given CPU address to a Device address. It creates a
+ an entry in the map list with the virtual and physical addresses and the
+ un map cookie.
+
+ @param v2p pointer to return a map list node pointer.
+ @param type the direction in which the data flows from the given
+ virtual address device->cpu or cpu->device or both
+ ways.
+ @param vaddr virtual address (or CPU address) to be mapped
+ @param bsize size of the buffer to be mapped.
+
+ @retval EFI_SUCEESS routine has completed the mapping
+ @retval other error as indicated.
+
+**/
+EFI_STATUS
+add_v2p (
+ IN OUT struct s_v2p **v2p,
+ EFI_PCI_IO_PROTOCOL_OPERATION type,
+ VOID *vaddr,
+ UINTN bsize
+ )
+{
+ EFI_STATUS Status;
+
+ if ((v2p == NULL) || (vaddr == NULL) || (bsize == 0)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ *v2p = AllocatePool (sizeof (struct s_v2p));
+ if (*v2p != NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Status = mPciIoFncs->Map (
+ mPciIoFncs,
+ type,
+ vaddr,
+ &bsize,
+ &(*v2p)->paddr,
+ &(*v2p)->unmap
+ );
+ if (Status != EFI_SUCCESS) {
+ FreePool (*v2p);
+ return Status;
+ }
+ (*v2p)->vaddr = vaddr;
+ (*v2p)->bsize = bsize;
+ (*v2p)->next = _v2p;
+ _v2p = *v2p;
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ This routine searches the linked list of mapped address nodes (for undi3.0
+ interface) to find the node that corresponds to the given virtual address and
+ returns a pointer to that node.
+
+ @param v2p pointer to return a map list node pointer.
+ @param vaddr virtual address (or CPU address) to be searched in
+ the map list
+
+ @retval EFI_SUCEESS if a match found!
+ @retval Other match not found
+
+**/
+EFI_STATUS
+find_v2p (
+ struct s_v2p **v2p,
+ VOID *vaddr
+ )
+{
+ struct s_v2p *v;
+
+ if (v2p == NULL || vaddr == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ for (v = _v2p; v != NULL; v = v->next) {
+ if (v->vaddr == vaddr) {
+ *v2p = v;
+ return EFI_SUCCESS;
+ }
+ }
+
+ return EFI_NOT_FOUND;
+}
+
+
+/**
+ This routine unmaps the given virtual address and frees the memory allocated
+ for the map list node corresponding to that address.
+
+ @param vaddr virtual address (or CPU address) to be unmapped
+
+ @retval EFI_SUCEESS if successfully unmapped
+ @retval Other as indicated by the error
+
+**/
+EFI_STATUS
+del_v2p (
+ VOID *vaddr
+ )
+{
+ struct s_v2p *v;
+ struct s_v2p *t;
+ EFI_STATUS Status;
+
+ if (vaddr == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (_v2p == NULL) {
+ return EFI_NOT_FOUND;
+ }
+ //
+ // Is our node at the head of the list??
+ //
+ if ((v = _v2p)->vaddr == vaddr) {
+ _v2p = _v2p->next;
+
+ Status = mPciIoFncs->Unmap (mPciIoFncs, v->unmap);
+
+ FreePool (v);
+
+ if (Status) {
+ DEBUG ((EFI_D_ERROR, "Unmap failed with status = %x\n", Status));
+ }
+ return Status;
+ }
+
+ for (; v->next != NULL; v = t) {
+ if ((t = v->next)->vaddr == vaddr) {
+ v->next = t->next;
+ Status = mPciIoFncs->Unmap (mPciIoFncs, t->unmap);
+ FreePool (t);
+
+ if (Status) {
+ DEBUG ((EFI_D_ERROR, "Unmap failed with status = %x\n", Status));
+ }
+ return Status;
+ }
+ }
+
+ return EFI_NOT_FOUND;
+}
+
+STATIC
+EFI_STATUS
+issue_hwundi_command (
+ UINT64 cdb
+ )
+/*++
+
+Routine Description:
+
+Arguments:
+
+Returns:
+
+--*/
+{
+ DEBUG ((EFI_D_ERROR, "\nissue_hwundi_command() - This should not be called!"));
+
+ if (cdb == 0) {
+ return EFI_INVALID_PARAMETER;
+
+ }
+ //
+ // %%TBD - For now, nothing is done.
+ //
+ return EFI_UNSUPPORTED;
+}
+
+
+/**
+ Compute 8-bit checksum of a buffer.
+
+ @param ptr Pointer to buffer.
+ @param len Length of buffer in bytes.
+
+ @return 8-bit checksum of all bytes in buffer.
+ @return If ptr is NULL or len is zero, zero is returned.
+
+**/
+STATIC
+UINT8
+calc_8bit_cksum (
+ VOID *ptr,
+ UINTN len
+ )
+{
+ UINT8 *bptr;
+ UINT8 cksum;
+
+ bptr = ptr;
+ cksum = 0;
+
+ if (ptr == NULL || len == 0) {
+ return 0;
+ }
+
+ while (len--) {
+ cksum = (UINT8) (cksum +*bptr++);
+ }
+
+ return cksum;
+}
+
+
+/**
+ Test to see if this driver supports Controller. Any Controller
+ that contains a Nii protocol can be supported.
+
+ @param This Protocol instance pointer.
+ @param Controller Handle of device to test.
+ @param RemainingDevicePath Not used.
+
+ @retval EFI_SUCCESS This driver supports this device.
+ @retval EFI_ALREADY_STARTED This driver is already running on this device.
+ @retval other This driver does not support this device.
+
+**/
+EFI_STATUS
+EFIAPI
+SimpleNetworkDriverSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
+ )
+{
+ EFI_STATUS Status;
+ EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL *NiiProtocol;
+ PXE_UNDI *pxe;
+ BOOLEAN IsUndi31;
+
+ IsUndi31 = FALSE;
+ Status = gBS->OpenProtocol (
+ Controller,
+ &gEfiDevicePathProtocolGuid,
+ NULL,
+ This->DriverBindingHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = gBS->OpenProtocol (
+ Controller,
+ &gEfiNetworkInterfaceIdentifierProtocolGuid_31,
+ (VOID **) &NiiProtocol,
+ This->DriverBindingHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+ if (Status == EFI_ALREADY_STARTED)
+ {
+ DEBUG ((EFI_D_INFO, "Support(): Already Started. on handle %x\n", Controller));
+ return EFI_ALREADY_STARTED;
+ }
+
+ if (!EFI_ERROR (Status))
+ {
+ DEBUG ((EFI_D_INFO, "Support(): UNDI3.1 found on handle %x\n", Controller));
+ IsUndi31 = TRUE;
+ } else {
+ //
+ // try the older 3.0 driver
+ //
+ Status = gBS->OpenProtocol (
+ Controller,
+ &gEfiNetworkInterfaceIdentifierProtocolGuid,
+ (VOID **) &NiiProtocol,
+ This->DriverBindingHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ DEBUG ((EFI_D_INFO, "Support(): UNDI3.0 found on handle %x\n", Controller));
+ }
+ //
+ // check the version, we don't want to connect to the undi16
+ //
+ if (NiiProtocol->Type != EfiNetworkInterfaceUndi) {
+ Status = EFI_UNSUPPORTED;
+ goto Done;
+ }
+ //
+ // Check to see if !PXE structure is valid. Paragraph alignment of !PXE structure is required.
+ //
+ if (NiiProtocol->ID & 0x0F) {
+ DEBUG ((EFI_D_NET, "\n!PXE structure is not paragraph aligned.\n"));
+ Status = EFI_UNSUPPORTED;
+ goto Done;
+ }
+
+ pxe = (PXE_UNDI *) (UINTN) (NiiProtocol->ID);
+
+ //
+ // Verify !PXE revisions.
+ //
+ if (pxe->hw.Signature != PXE_ROMID_SIGNATURE) {
+ DEBUG ((EFI_D_NET, "\n!PXE signature is not valid.\n"));
+ Status = EFI_UNSUPPORTED;
+ goto Done;
+ }
+
+ if (pxe->hw.Rev < PXE_ROMID_REV) {
+ DEBUG ((EFI_D_NET, "\n!PXE.Rev is not supported.\n"));
+ Status = EFI_UNSUPPORTED;
+ goto Done;
+ }
+
+ if (pxe->hw.MajorVer < PXE_ROMID_MAJORVER) {
+
+ DEBUG ((EFI_D_NET, "\n!PXE.MajorVer is not supported.\n"));
+ Status = EFI_UNSUPPORTED;
+ goto Done;
+
+ } else if (pxe->hw.MajorVer == PXE_ROMID_MAJORVER && pxe->hw.MinorVer < PXE_ROMID_MINORVER) {
+ DEBUG ((EFI_D_NET, "\n!PXE.MinorVer is not supported."));
+ Status = EFI_UNSUPPORTED;
+ goto Done;
+ }
+ //
+ // Do S/W UNDI specific checks.
+ //
+ if ((pxe->hw.Implementation & PXE_ROMID_IMP_HW_UNDI) == 0) {
+ if (pxe->sw.EntryPoint < pxe->sw.Len) {
+ DEBUG ((EFI_D_NET, "\n!PXE S/W entry point is not valid."));
+ Status = EFI_UNSUPPORTED;
+ goto Done;
+ }
+
+ if (pxe->sw.BusCnt == 0) {
+ DEBUG ((EFI_D_NET, "\n!PXE.BusCnt is zero."));
+ Status = EFI_UNSUPPORTED;
+ goto Done;
+ }
+ }
+
+ Status = EFI_SUCCESS;
+ DEBUG ((EFI_D_INFO, "Support(): supported on %x\n", Controller));
+
+Done:
+ if (IsUndi31) {
+ gBS->CloseProtocol (
+ Controller,
+ &gEfiNetworkInterfaceIdentifierProtocolGuid_31,
+ This->DriverBindingHandle,
+ Controller
+ );
+
+ } else {
+ gBS->CloseProtocol (
+ Controller,
+ &gEfiNetworkInterfaceIdentifierProtocolGuid,
+ This->DriverBindingHandle,
+ Controller
+ );
+ }
+
+ return Status;
+}
+
+
+/**
+ called for any handle that we said "supported" in the above call!
+
+ @param This Protocol instance pointer.
+ @param Controller Handle of device to start
+ @param RemainingDevicePath Not used.
+
+ @retval EFI_SUCCESS This driver supports this device.
+ @retval other This driver failed to start this device.
+
+**/
+EFI_STATUS
+EFIAPI
+SimpleNetworkDriverStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
+ )
+{
+ EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL *Nii;
+ EFI_DEVICE_PATH_PROTOCOL *NiiDevicePath;
+ EFI_STATUS Status;
+ PXE_UNDI *pxe;
+ SNP_DRIVER *snp;
+ VOID *addr;
+ VOID *addrUnmap;
+ EFI_PHYSICAL_ADDRESS paddr;
+ EFI_HANDLE Handle;
+ UINTN Size;
+ BOOLEAN UndiNew;
+ PXE_PCI_CONFIG_INFO ConfigInfo;
+ PCI_TYPE00 *ConfigHeader;
+ UINT32 *TempBar;
+ UINT8 BarIndex;
+ PXE_STATFLAGS InitStatFlags;
+
+ DEBUG ((EFI_D_NET, "\nSnpNotifyNetworkInterfaceIdentifier() "));
+
+ Status = gBS->OpenProtocol (
+ Controller,
+ &gEfiDevicePathProtocolGuid,
+ (VOID **) &NiiDevicePath,
+ This->DriverBindingHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = gBS->LocateDevicePath (
+ &gEfiPciIoProtocolGuid,
+ &NiiDevicePath,
+ &Handle
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = gBS->OpenProtocol (
+ Handle,
+ &gEfiPciIoProtocolGuid,
+ (VOID **) &mPciIoFncs,
+ This->DriverBindingHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ //
+ // Get the NII interface. look for 3.1 undi first, if it is not there
+ // then look for 3.0, validate the interface.
+ //
+ Status = gBS->OpenProtocol (
+ Controller,
+ &gEfiNetworkInterfaceIdentifierProtocolGuid_31,
+ (VOID **) &Nii,
+ This->DriverBindingHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+ if (Status == EFI_ALREADY_STARTED) {
+ gBS->CloseProtocol (
+ Controller,
+ &gEfiDevicePathProtocolGuid,
+ This->DriverBindingHandle,
+ Controller
+ );
+ return Status;
+ }
+
+ if (!EFI_ERROR (Status)) {
+ //
+ // probably not a 3.1 UNDI
+ //
+ UndiNew = TRUE;
+ DEBUG ((EFI_D_INFO, "Start(): UNDI3.1 found\n"));
+
+ } else {
+ UndiNew = FALSE;
+ Status = gBS->OpenProtocol (
+ Controller,
+ &gEfiNetworkInterfaceIdentifierProtocolGuid,
+ (VOID **) &Nii,
+ This->DriverBindingHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+ if (EFI_ERROR (Status)) {
+ gBS->CloseProtocol (
+ Controller,
+ &gEfiDevicePathProtocolGuid,
+ This->DriverBindingHandle,
+ Controller
+ );
+
+ return Status;
+ }
+
+ DEBUG ((EFI_D_INFO, "Start(): UNDI3.0 found\n"));
+ }
+
+ pxe = (PXE_UNDI *) (UINTN) (Nii->ID);
+
+ if (calc_8bit_cksum (pxe, pxe->hw.Len) != 0) {
+ DEBUG ((EFI_D_NET, "\n!PXE checksum is not correct.\n"));
+ goto NiiError;
+ }
+
+ if ((pxe->hw.Implementation & PXE_ROMID_IMP_PROMISCUOUS_RX_SUPPORTED) != 0) {
+ //
+ // We can get any packets.
+ //
+ } else if ((pxe->hw.Implementation & PXE_ROMID_IMP_BROADCAST_RX_SUPPORTED) != 0) {
+ //
+ // We need to be able to get broadcast packets for DHCP.
+ // If we do not have promiscuous support, we must at least have
+ // broadcast support or we cannot do DHCP!
+ //
+ } else {
+ DEBUG ((EFI_D_NET, "\nUNDI does not have promiscuous or broadcast support."));
+ goto NiiError;
+ }
+ //
+ // OK, we like this UNDI, and we know snp is not already there on this handle
+ // Allocate and initialize a new simple network protocol structure.
+ //
+ Status = mPciIoFncs->AllocateBuffer (
+ mPciIoFncs,
+ AllocateAnyPages,
+ EfiBootServicesData,
+ SNP_MEM_PAGES (sizeof (SNP_DRIVER)),
+ &addr,
+ 0
+ );
+
+ if (Status != EFI_SUCCESS) {
+ DEBUG ((EFI_D_NET, "\nCould not allocate SNP_DRIVER structure.\n"));
+ goto NiiError;
+ }
+
+ snp = (SNP_DRIVER *) (UINTN) addr;
+
+ if (!UndiNew) {
+ Size = SNP_MEM_PAGES (sizeof (SNP_DRIVER));
+
+ Status = mPciIoFncs->Map (
+ mPciIoFncs,
+ EfiPciIoOperationBusMasterCommonBuffer,
+ addr,
+ &Size,
+ &paddr,
+ &addrUnmap
+ );
+
+ ASSERT (paddr);
+
+ DEBUG ((EFI_D_NET, "\nSNP_DRIVER @ %Xh, sizeof(SNP_DRIVER) == %d", addr, sizeof (SNP_DRIVER)));
+ snp = (SNP_DRIVER *) (UINTN) paddr;
+ snp->SnpDriverUnmap = addrUnmap;
+ }
+
+ ZeroMem (snp, sizeof (SNP_DRIVER));
+
+ snp->IoFncs = mPciIoFncs;
+ snp->IsOldUndi = (BOOLEAN) (!UndiNew);
+
+ snp->Signature = SNP_DRIVER_SIGNATURE;
+
+ EfiInitializeLock (&snp->lock, TPL_NOTIFY);
+
+ snp->snp.Revision = EFI_SIMPLE_NETWORK_PROTOCOL_REVISION;
+ snp->snp.Start = snp_undi32_start;
+ snp->snp.Stop = snp_undi32_stop;
+ snp->snp.Initialize = snp_undi32_initialize;
+ snp->snp.Reset = snp_undi32_reset;
+ snp->snp.Shutdown = snp_undi32_shutdown;
+ snp->snp.ReceiveFilters = snp_undi32_receive_filters;
+ snp->snp.StationAddress = snp_undi32_station_address;
+ snp->snp.Statistics = snp_undi32_statistics;
+ snp->snp.MCastIpToMac = snp_undi32_mcast_ip_to_mac;
+ snp->snp.NvData = snp_undi32_nvdata;
+ snp->snp.GetStatus = snp_undi32_get_status;
+ snp->snp.Transmit = snp_undi32_transmit;
+ snp->snp.Receive = snp_undi32_receive;
+ snp->snp.WaitForPacket = NULL;
+
+ snp->snp.Mode = &snp->mode;
+
+ snp->tx_rx_bufsize = 0;
+ snp->tx_rx_buffer = NULL;
+
+ snp->if_num = Nii->IfNum;
+
+ if ((pxe->hw.Implementation & PXE_ROMID_IMP_HW_UNDI) != 0) {
+ snp->is_swundi = FALSE;
+ snp->issue_undi32_command = &issue_hwundi_command;
+ } else {
+ snp->is_swundi = TRUE;
+
+ if ((pxe->sw.Implementation & PXE_ROMID_IMP_SW_VIRT_ADDR) != 0) {
+ snp->issue_undi32_command = (issue_undi32_command) (UINTN) pxe->sw.EntryPoint;
+ } else {
+ snp->issue_undi32_command = (issue_undi32_command) (UINTN) ((UINT8) (UINTN) pxe + pxe->sw.EntryPoint);
+ }
+ }
+ //
+ // Allocate a global CPB and DB buffer for this UNDI interface.
+ // we do this because:
+ //
+ // -UNDI 3.0 wants all the addresses passed to it (even the cpb and db) to be
+ // within 2GB limit, create them here and map them so that when undi calls
+ // v2p callback to check if the physical address is < 2gb, we will pass.
+ //
+ // -This is not a requirement for 3.1 or later UNDIs but the code looks
+ // simpler if we use the same cpb, db variables for both old and new undi
+ // interfaces from all the SNP interface calls (we don't map the buffers
+ // for the newer undi interfaces though)
+ // .
+ // -it is OK to allocate one global set of CPB, DB pair for each UNDI
+ // interface as EFI does not multi-task and so SNP will not be re-entered!
+ //
+ Status = mPciIoFncs->AllocateBuffer (
+ mPciIoFncs,
+ AllocateAnyPages,
+ EfiBootServicesData,
+ SNP_MEM_PAGES (4096),
+ &addr,
+ 0
+ );
+
+ if (Status != EFI_SUCCESS) {
+ DEBUG ((EFI_D_NET, "\nCould not allocate CPB and DB structures.\n"));
+ goto Error_DeleteSNP;
+ }
+
+ if (snp->IsOldUndi) {
+ Size = SNP_MEM_PAGES (4096);
+
+ Status = mPciIoFncs->Map (
+ mPciIoFncs,
+ EfiPciIoOperationBusMasterCommonBuffer,
+ addr,
+ &Size,
+ &paddr,
+ &snp->CpbUnmap
+ );
+
+ ASSERT (paddr);
+
+ snp->cpb = (VOID *) (UINTN) paddr;
+ snp->db = (VOID *) ((UINTN) paddr + 2048);
+ } else {
+ snp->cpb = (VOID *) (UINTN) addr;
+ snp->db = (VOID *) ((UINTN) addr + 2048);
+ }
+ //
+ // pxe_start call is going to give the callback functions to UNDI, these callback
+ // functions use the BarIndex values from the snp structure, so these must be initialized
+ // with default values before doing a pxe_start. The correct values can be obtained after
+ // getting the config information from UNDI
+ //
+ snp->MemoryBarIndex = 0;
+ snp->IoBarIndex = 1;
+
+ //
+ // we need the undi init information many times in this snp code, just get it
+ // once here and store it in the snp driver structure. to get Init Info
+ // from UNDI we have to start undi first.
+ //
+ Status = pxe_start (snp);
+
+ if (Status != EFI_SUCCESS) {
+ goto Error_DeleteCPBDB;
+ }
+
+ snp->cdb.OpCode = PXE_OPCODE_GET_INIT_INFO;
+ snp->cdb.OpFlags = PXE_OPFLAGS_NOT_USED;
+
+ snp->cdb.CPBsize = PXE_CPBSIZE_NOT_USED;
+ snp->cdb.CPBaddr = PXE_DBADDR_NOT_USED;
+
+ snp->cdb.DBsize = sizeof snp->init_info;
+ snp->cdb.DBaddr = (UINT64)(UINTN) &snp->init_info;
+
+ snp->cdb.StatCode = PXE_STATCODE_INITIALIZE;
+ snp->cdb.StatFlags = PXE_STATFLAGS_INITIALIZE;
+
+ snp->cdb.IFnum = snp->if_num;
+ snp->cdb.Control = PXE_CONTROL_LAST_CDB_IN_LIST;
+
+ DEBUG ((EFI_D_NET, "\nsnp->undi.get_init_info() "));
+
+ (*snp->issue_undi32_command) ((UINT64)(UINTN) &snp->cdb);
+
+ //
+ // Save the INIT Stat Code...
+ //
+ InitStatFlags = snp->cdb.StatFlags;
+
+ if (snp->cdb.StatCode != PXE_STATCODE_SUCCESS) {
+ DEBUG ((EFI_D_NET, "\nsnp->undi.init_info() %xh:%xh\n", snp->cdb.StatFlags, snp->cdb.StatCode));
+ pxe_stop (snp);
+ goto Error_DeleteCPBDB;
+ }
+
+ snp->cdb.OpCode = PXE_OPCODE_GET_CONFIG_INFO;
+ snp->cdb.OpFlags = PXE_OPFLAGS_NOT_USED;
+
+ snp->cdb.CPBsize = PXE_CPBSIZE_NOT_USED;
+ snp->cdb.CPBaddr = PXE_DBADDR_NOT_USED;
+
+ snp->cdb.DBsize = sizeof ConfigInfo;
+ snp->cdb.DBaddr = (UINT64)(UINTN) &ConfigInfo;
+
+ snp->cdb.StatCode = PXE_STATCODE_INITIALIZE;
+ snp->cdb.StatFlags = PXE_STATFLAGS_INITIALIZE;
+
+ snp->cdb.IFnum = snp->if_num;
+ snp->cdb.Control = PXE_CONTROL_LAST_CDB_IN_LIST;
+
+ DEBUG ((EFI_D_NET, "\nsnp->undi.get_config_info() "));
+
+ (*snp->issue_undi32_command) ((UINT64)(UINTN) &snp->cdb);
+
+ if (snp->cdb.StatCode != PXE_STATCODE_SUCCESS) {
+ DEBUG ((EFI_D_NET, "\nsnp->undi.config_info() %xh:%xh\n", snp->cdb.StatFlags, snp->cdb.StatCode));
+ pxe_stop (snp);
+ goto Error_DeleteCPBDB;
+ }
+ //
+ // Find the correct BAR to do IO.
+ //
+ //
+ // Enumerate through the PCI BARs for the device to determine which one is
+ // the IO BAR. Save the index of the BAR into the adapter info structure.
+ // for regular 32bit BARs, 0 is memory mapped, 1 is io mapped
+ //
+ ConfigHeader = (PCI_TYPE00 *) &ConfigInfo.Config.Byte[0];
+ TempBar = (UINT32 *) &ConfigHeader->Device.Bar[0];
+ for (BarIndex = 0; BarIndex <= 5; BarIndex++) {
+ if ((*TempBar & PCI_BAR_MEM_MASK) == PCI_BAR_MEM_64BIT) {
+ //
+ // This is a 64-bit memory bar, skip this and the
+ // next bar as well.
+ //
+ TempBar++;
+ }
+
+ if ((*TempBar & PCI_BAR_IO_MASK) == PCI_BAR_IO_MODE) {
+ snp->IoBarIndex = BarIndex;
+ break;
+ }
+
+ TempBar++;
+ }
+
+ //
+ // We allocate 2 more global buffers for undi 3.0 interface. We use these
+ // buffers to pass to undi when the user buffers are beyond 4GB.
+ // UNDI 3.0 wants all the addresses passed to it to be
+ // within 2GB limit, create them here and map them so that when undi calls
+ // v2p callback to check if the physical address is < 2gb, we will pass.
+ //
+ // For 3.1 and later UNDIs, we do not do this because undi is
+ // going to call the map() callback if and only if it wants to use the
+ // device address for any address it receives.
+ //
+ if (snp->IsOldUndi) {
+ //
+ // buffer for receive
+ //
+ Size = SNP_MEM_PAGES (snp->init_info.MediaHeaderLen + snp->init_info.FrameDataLen);
+ Status = mPciIoFncs->AllocateBuffer (
+ mPciIoFncs,
+ AllocateAnyPages,
+ EfiBootServicesData,
+ Size,
+ &addr,
+ 0
+ );
+
+ if (Status != EFI_SUCCESS) {
+ DEBUG ((EFI_D_ERROR, "\nCould not allocate receive buffer.\n"));
+ goto Error_DeleteCPBDB;
+ }
+
+ Status = mPciIoFncs->Map (
+ mPciIoFncs,
+ EfiPciIoOperationBusMasterCommonBuffer,
+ addr,
+ &Size,
+ &paddr,
+ &snp->ReceiveBufUnmap
+ );
+
+ ASSERT (paddr);
+
+ snp->receive_buf = (UINT8 *) (UINTN) paddr;
+
+ //
+ // buffer for fill_header
+ //
+ Size = SNP_MEM_PAGES (snp->init_info.MediaHeaderLen);
+ Status = mPciIoFncs->AllocateBuffer (
+ mPciIoFncs,
+ AllocateAnyPages,
+ EfiBootServicesData,
+ Size,
+ &addr,
+ 0
+ );
+
+ if (Status != EFI_SUCCESS) {
+ DEBUG ((EFI_D_ERROR, "\nCould not allocate fill_header buffer.\n"));
+ goto Error_DeleteRCVBuf;
+ }
+
+ Status = mPciIoFncs->Map (
+ mPciIoFncs,
+ EfiPciIoOperationBusMasterCommonBuffer,
+ addr,
+ &Size,
+ &paddr,
+ &snp->FillHdrBufUnmap
+ );
+
+ ASSERT (paddr);
+ snp->fill_hdr_buf = (UINT8 *) (UINTN) paddr;
+ }
+ //
+ // Initialize simple network protocol mode structure
+ //
+ snp->mode.State = EfiSimpleNetworkStopped;
+ snp->mode.HwAddressSize = snp->init_info.HWaddrLen;
+ snp->mode.MediaHeaderSize = snp->init_info.MediaHeaderLen;
+ snp->mode.MaxPacketSize = snp->init_info.FrameDataLen;
+ snp->mode.NvRamAccessSize = snp->init_info.NvWidth;
+ snp->mode.NvRamSize = snp->init_info.NvCount * snp->mode.NvRamAccessSize;
+ snp->mode.IfType = snp->init_info.IFtype;
+ snp->mode.MaxMCastFilterCount = snp->init_info.MCastFilterCnt;
+ snp->mode.MCastFilterCount = 0;
+
+ switch (InitStatFlags & PXE_STATFLAGS_CABLE_DETECT_MASK) {
+ case PXE_STATFLAGS_CABLE_DETECT_SUPPORTED:
+ snp->mode.MediaPresentSupported = TRUE;
+ break;
+
+ case PXE_STATFLAGS_CABLE_DETECT_NOT_SUPPORTED:
+ default:
+ snp->mode.MediaPresentSupported = FALSE;
+ }
+
+ if ((pxe->hw.Implementation & PXE_ROMID_IMP_STATION_ADDR_SETTABLE) != 0) {
+ snp->mode.MacAddressChangeable = TRUE;
+ } else {
+ snp->mode.MacAddressChangeable = FALSE;
+ }
+
+ if ((pxe->hw.Implementation & PXE_ROMID_IMP_MULTI_FRAME_SUPPORTED) != 0) {
+ snp->mode.MultipleTxSupported = TRUE;
+ } else {
+ snp->mode.MultipleTxSupported = FALSE;
+ }
+
+ snp->mode.ReceiveFilterMask = EFI_SIMPLE_NETWORK_RECEIVE_UNICAST;
+
+ if ((pxe->hw.Implementation & PXE_ROMID_IMP_PROMISCUOUS_MULTICAST_RX_SUPPORTED) != 0) {
+ snp->mode.ReceiveFilterMask |= EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST;
+
+ }
+
+ if ((pxe->hw.Implementation & PXE_ROMID_IMP_PROMISCUOUS_RX_SUPPORTED) != 0) {
+ snp->mode.ReceiveFilterMask |= EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS;
+
+ }
+
+ if ((pxe->hw.Implementation & PXE_ROMID_IMP_BROADCAST_RX_SUPPORTED) != 0) {
+ snp->mode.ReceiveFilterMask |= EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST;
+
+ }
+
+ if ((pxe->hw.Implementation & PXE_ROMID_IMP_FILTERED_MULTICAST_RX_SUPPORTED) != 0) {
+ snp->mode.ReceiveFilterMask |= EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST;
+
+ }
+
+ if (pxe->hw.Implementation & PXE_ROMID_IMP_PROMISCUOUS_MULTICAST_RX_SUPPORTED) {
+ snp->mode.ReceiveFilterMask |= EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST;
+
+ }
+
+ snp->mode.ReceiveFilterSetting = 0;
+
+ //
+ // need to get the station address to save in the mode structure. we need to
+ // initialize the UNDI first for this.
+ //
+ snp->tx_rx_bufsize = snp->init_info.MemoryRequired;
+ Status = pxe_init (snp, PXE_OPFLAGS_INITIALIZE_DO_NOT_DETECT_CABLE);
+
+ if (Status) {
+ pxe_stop (snp);
+ goto Error_DeleteHdrBuf;
+ }
+
+ Status = pxe_get_stn_addr (snp);
+
+ if (Status != EFI_SUCCESS) {
+ DEBUG ((EFI_D_ERROR, "\nsnp->undi.get_station_addr() failed.\n"));
+ pxe_shutdown (snp);
+ pxe_stop (snp);
+ goto Error_DeleteHdrBuf;
+ }
+
+ snp->mode.MediaPresent = FALSE;
+
+ //
+ // We should not leave UNDI started and initialized here. this DriverStart()
+ // routine must only find and attach the SNP interface to UNDI layer that it
+ // finds on the given handle!
+ // The UNDI layer will be started when upper layers call snp->start.
+ // How ever, this DriverStart() must fill up the snp mode structure which
+ // contains the MAC address of the NIC. For this reason we started and
+ // initialized UNDI here, now we are done, do a shutdown and stop of the
+ // UNDI interface!
+ //
+ pxe_shutdown (snp);
+ pxe_stop (snp);
+
+ //
+ // add SNP to the undi handle
+ //
+ Status = gBS->InstallProtocolInterface (
+ &Controller,
+ &gEfiSimpleNetworkProtocolGuid,
+ EFI_NATIVE_INTERFACE,
+ &(snp->snp)
+ );
+
+ if (!EFI_ERROR (Status)) {
+ return Status;
+ }
+
+Error_DeleteHdrBuf:
+ if (snp->IsOldUndi) {
+ Status = mPciIoFncs->Unmap (
+ mPciIoFncs,
+ snp->FillHdrBufUnmap
+ );
+ Size = SNP_MEM_PAGES (snp->init_info.MediaHeaderLen);
+ mPciIoFncs->FreeBuffer (
+ mPciIoFncs,
+ Size,
+ snp->fill_hdr_buf
+ );
+ }
+
+Error_DeleteRCVBuf:
+ if (snp->IsOldUndi) {
+ Status = mPciIoFncs->Unmap (
+ mPciIoFncs,
+ snp->ReceiveBufUnmap
+ );
+ Size = SNP_MEM_PAGES (snp->init_info.MediaHeaderLen + snp->init_info.FrameDataLen);
+ mPciIoFncs->FreeBuffer (
+ mPciIoFncs,
+ Size,
+ snp->receive_buf
+ );
+
+ }
+
+Error_DeleteCPBDB:
+ if (snp->IsOldUndi) {
+ Status = mPciIoFncs->Unmap (
+ mPciIoFncs,
+ snp->CpbUnmap
+ );
+ }
+
+ Status = mPciIoFncs->FreeBuffer (
+ mPciIoFncs,
+ SNP_MEM_PAGES (4096),
+ snp->cpb
+ );
+
+Error_DeleteSNP:
+ if (snp->IsOldUndi) {
+ Status = mPciIoFncs->Unmap (
+ mPciIoFncs,
+ snp->SnpDriverUnmap
+ );
+ }
+
+ mPciIoFncs->FreeBuffer (
+ mPciIoFncs,
+ SNP_MEM_PAGES (sizeof (SNP_DRIVER)),
+ snp
+ );
+NiiError:
+ if (!UndiNew) {
+ gBS->CloseProtocol (
+ Controller,
+ &gEfiNetworkInterfaceIdentifierProtocolGuid,
+ This->DriverBindingHandle,
+ Controller
+ );
+ } else {
+ gBS->CloseProtocol (
+ Controller,
+ &gEfiNetworkInterfaceIdentifierProtocolGuid_31,
+ This->DriverBindingHandle,
+ Controller
+ );
+ }
+
+ gBS->CloseProtocol (
+ Controller,
+ &gEfiDevicePathProtocolGuid,
+ This->DriverBindingHandle,
+ Controller
+ );
+
+ return Status;
+}
+
+
+/**
+
+
+
+**/
+EFI_STATUS
+EFIAPI
+SimpleNetworkDriverStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer
+ )
+{
+ EFI_STATUS Status;
+ EFI_SIMPLE_NETWORK_PROTOCOL *SnpProtocol;
+ SNP_DRIVER *Snp;
+
+ //
+ // Get our context back.
+ //
+ Status = gBS->OpenProtocol (
+ Controller,
+ &gEfiSimpleNetworkProtocolGuid,
+ (VOID **) &SnpProtocol,
+ This->DriverBindingHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+
+ if (EFI_ERROR (Status)) {
+ return EFI_UNSUPPORTED;
+ }
+
+ Snp = EFI_SIMPLE_NETWORK_DEV_FROM_THIS (SnpProtocol);
+
+ Status = gBS->UninstallProtocolInterface (
+ Controller,
+ &gEfiSimpleNetworkProtocolGuid,
+ &Snp->snp
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if (!Snp->IsOldUndi) {
+ Status = gBS->CloseProtocol (
+ Controller,
+ &gEfiNetworkInterfaceIdentifierProtocolGuid_31,
+ This->DriverBindingHandle,
+ Controller
+ );
+ } else {
+ Status = gBS->CloseProtocol (
+ Controller,
+ &gEfiNetworkInterfaceIdentifierProtocolGuid,
+ This->DriverBindingHandle,
+ Controller
+ );
+ }
+
+ Status = gBS->CloseProtocol (
+ Controller,
+ &gEfiDevicePathProtocolGuid,
+ This->DriverBindingHandle,
+ Controller
+ );
+
+ pxe_shutdown (Snp);
+ pxe_stop (Snp);
+
+ if (Snp->IsOldUndi) {
+ Status = mPciIoFncs->Unmap (
+ mPciIoFncs,
+ Snp->FillHdrBufUnmap
+ );
+
+ mPciIoFncs->FreeBuffer (
+ mPciIoFncs,
+ SNP_MEM_PAGES (Snp->init_info.MediaHeaderLen),
+ Snp->fill_hdr_buf
+ );
+ Status = mPciIoFncs->Unmap (
+ mPciIoFncs,
+ Snp->ReceiveBufUnmap
+ );
+
+ mPciIoFncs->FreeBuffer (
+ mPciIoFncs,
+ SNP_MEM_PAGES (Snp->init_info.MediaHeaderLen + Snp->init_info.FrameDataLen),
+ Snp->receive_buf
+ );
+
+ Status = mPciIoFncs->Unmap (
+ mPciIoFncs,
+ Snp->CpbUnmap
+ );
+ Status = mPciIoFncs->Unmap (
+ mPciIoFncs,
+ Snp->SnpDriverUnmap
+ );
+ }
+
+ mPciIoFncs->FreeBuffer (
+ mPciIoFncs,
+ SNP_MEM_PAGES (4096),
+ Snp->cpb
+ );
+
+ mPciIoFncs->FreeBuffer (
+ mPciIoFncs,
+ SNP_MEM_PAGES (sizeof (SNP_DRIVER)),
+ Snp
+ );
+
+ return Status;
+}
+
+
+/**
+ Install all the driver protocol
+
+ @param entry EFI_IMAGE_ENTRY_POINT)
+
+ @retval EFI_SUCEESS Initialization routine has found UNDI hardware,
+ loaded it's ROM, and installed a notify event for
+ the Network Indentifier Interface Protocol
+ successfully.
+ @retval Other Return value from HandleProtocol for
+ DeviceIoProtocol or LoadedImageProtocol
+
+**/
+EFI_STATUS
+EFIAPI
+InitializeSnpNiiDriver (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ return EfiLibInstallAllDriverProtocols (
+ ImageHandle,
+ SystemTable,
+ &mSimpleNetworkDriverBinding,
+ NULL,
+ COMPONENT_NAME,
+ NULL,
+ NULL
+ );
+}
diff --git a/MdeModulePkg/Universal/Network/SnpDxe/snp.h b/MdeModulePkg/Universal/Network/SnpDxe/snp.h
new file mode 100644
index 0000000000..624a44c971
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/SnpDxe/snp.h
@@ -0,0 +1,454 @@
+/** @file
+
+Copyright (c) 2004 - 2007, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module name:
+ snp.h
+
+Abstract:
+
+Revision history:
+
+
+**/
+#ifndef _SNP_H
+#define _SNP_H
+
+
+#include <PiDxe.h>
+
+#include <Protocol/SimpleNetwork.h>
+#include <Protocol/PciIo.h>
+#include <Protocol/NetworkInterfaceIdentifier.h>
+#include <Protocol/DevicePath.h>
+
+#include <Library/DebugLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/UefiDriverEntryPoint.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/BaseLib.h>
+#include <Library/UefiLib.h>
+#include <Library/MemoryAllocationLib.h>
+
+#include <IndustryStandard/Pci22.h>
+
+#define FOUR_GIGABYTES (UINT64) 0x100000000ULL
+
+//
+// Driver Consumed Protocol Prototypes
+//
+//@MT:#include EFI_PROTOCOL_DEFINITION (DevicePath)
+//@MT:#include EFI_PROTOCOL_DEFINITION (PciIo)
+//@MT:#include EFI_PROTOCOL_DEFINITION (EfiNetworkInterfaceIdentifier)
+
+//
+// Driver Produced Protocol Prototypes
+//
+//@MT:#include EFI_PROTOCOL_DEFINITION (DriverBinding)
+//@MT:#include EFI_PROTOCOL_DEFINITION (ComponentName)
+//@MT:#include EFI_PROTOCOL_DEFINITION (ComponentName2)
+//@MT:#include EFI_PROTOCOL_DEFINITION (SimpleNetwork)
+
+#define SNP_DRIVER_SIGNATURE EFI_SIGNATURE_32 ('s', 'n', 'd', 's')
+#define MAX_MAP_LENGTH 100
+
+#define PCI_BAR_IO_MASK 0x00000003
+#define PCI_BAR_IO_MODE 0x00000001
+
+#define PCI_BAR_MEM_MASK 0x0000000F
+#define PCI_BAR_MEM_MODE 0x00000000
+#define PCI_BAR_MEM_64BIT 0x00000004
+
+typedef struct {
+ UINT32 Signature;
+ EFI_LOCK lock;
+
+ EFI_SIMPLE_NETWORK_PROTOCOL snp;
+ EFI_SIMPLE_NETWORK_MODE mode;
+
+ EFI_HANDLE device_handle;
+ EFI_DEVICE_PATH_PROTOCOL *device_path;
+
+ //
+ // Local instance data needed by SNP driver
+ //
+ // Pointer to S/W UNDI API entry point
+ // This will be NULL for H/W UNDI
+ //
+ EFI_STATUS (*issue_undi32_command) (UINT64 cdb);
+
+ BOOLEAN is_swundi;
+
+ //
+ // undi interface number, if one undi manages more nics
+ //
+ PXE_IFNUM if_num;
+
+ //
+ // Allocated tx/rx buffer that was passed to UNDI Initialize.
+ //
+ UINT32 tx_rx_bufsize;
+ VOID *tx_rx_buffer;
+ //
+ // mappable buffers for receive and fill header for undi3.0
+ // these will be used if the user buffers are above 4GB limit (instead of
+ // mapping the user buffers)
+ //
+ UINT8 *receive_buf;
+ VOID *ReceiveBufUnmap;
+ UINT8 *fill_hdr_buf;
+ VOID *FillHdrBufUnmap;
+
+ EFI_PCI_IO_PROTOCOL *IoFncs;
+ UINT8 IoBarIndex;
+ UINT8 MemoryBarIndex;
+ BOOLEAN IsOldUndi; // true for EFI1.0 UNDI (3.0) drivers
+ //
+ // Buffers for command descriptor block, command parameter block
+ // and data block.
+ //
+ PXE_CDB cdb;
+ VOID *cpb;
+ VOID *CpbUnmap;
+ VOID *db;
+
+ //
+ // UNDI structure, we need to remember the init info for a long time!
+ //
+ PXE_DB_GET_INIT_INFO init_info;
+
+ VOID *SnpDriverUnmap;
+ //
+ // when ever we map an address, we must remember it's address and the un-map
+ // cookie so that we can unmap later
+ //
+ struct s_map_list {
+ EFI_PHYSICAL_ADDRESS virt;
+ VOID *map_cookie;
+ } map_list[MAX_MAP_LENGTH];
+}
+SNP_DRIVER;
+
+#define EFI_SIMPLE_NETWORK_DEV_FROM_THIS(a) CR (a, SNP_DRIVER, snp, SNP_DRIVER_SIGNATURE)
+
+//
+// Global Variables
+//
+extern EFI_COMPONENT_NAME_PROTOCOL gSimpleNetworkComponentName;
+
+//
+// Virtual to physical mapping for all UNDI 3.0s.
+//
+extern struct s_v2p {
+ struct s_v2p *next;
+ VOID *vaddr;
+ UINTN bsize;
+ EFI_PHYSICAL_ADDRESS paddr;
+ VOID *unmap;
+}
+*_v2p;
+
+EFI_STATUS
+add_v2p (
+ struct s_v2p **v2p,
+ EFI_PCI_IO_PROTOCOL_OPERATION type,
+ VOID *vaddr,
+ UINTN bsize
+ )
+;
+
+EFI_STATUS
+find_v2p (
+ struct s_v2p **v2p,
+ VOID *vaddr
+ )
+;
+
+EFI_STATUS
+del_v2p (
+ VOID *vaddr
+ )
+;
+
+extern
+VOID
+snp_undi32_callback_block_30 (
+ IN UINT32 Enable
+ )
+;
+
+extern
+VOID
+snp_undi32_callback_delay_30 (
+ IN UINT64 MicroSeconds
+ )
+;
+
+extern
+VOID
+snp_undi32_callback_memio_30 (
+ IN UINT8 ReadOrWrite,
+ IN UINT8 NumBytes,
+ IN UINT64 MemOrPortAddress,
+ IN OUT UINT64 BufferPtr
+ )
+;
+
+extern
+VOID
+snp_undi32_callback_v2p_30 (
+ IN UINT64 CpuAddr,
+ IN OUT UINT64 DeviceAddrPtr
+ )
+;
+
+extern
+VOID
+snp_undi32_callback_block (
+ IN UINT64 UniqueId,
+ IN UINT32 Enable
+ )
+;
+
+extern
+VOID
+snp_undi32_callback_delay (
+ IN UINT64 UniqueId,
+ IN UINT64 MicroSeconds
+ )
+;
+
+extern
+VOID
+snp_undi32_callback_memio (
+ IN UINT64 UniqueId,
+ IN UINT8 ReadOrWrite,
+ IN UINT8 NumBytes,
+ IN UINT64 MemOrPortAddr,
+ IN OUT UINT64 BufferPtr
+ )
+;
+
+extern
+VOID
+snp_undi32_callback_map (
+ IN UINT64 UniqueId,
+ IN UINT64 CpuAddr,
+ IN UINT32 NumBytes,
+ IN UINT32 Direction,
+ IN OUT UINT64 DeviceAddrPtr
+ )
+;
+
+extern
+VOID
+snp_undi32_callback_unmap (
+ IN UINT64 UniqueId,
+ IN UINT64 CpuAddr,
+ IN UINT32 NumBytes,
+ IN UINT32 Direction,
+ IN UINT64 DeviceAddr // not a pointer to device address
+ )
+;
+
+extern
+VOID
+snp_undi32_callback_sync (
+ IN UINT64 UniqueId,
+ IN UINT64 CpuAddr,
+ IN UINT32 NumBytes,
+ IN UINT32 Direction,
+ IN UINT64 DeviceAddr // not a pointer to device address
+ )
+;
+
+extern
+EFI_STATUS
+EFIAPI
+snp_undi32_start (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *this
+ )
+;
+
+extern
+EFI_STATUS
+EFIAPI
+snp_undi32_stop (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *this
+ )
+;
+
+extern
+EFI_STATUS
+EFIAPI
+snp_undi32_initialize (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *this,
+ IN UINTN extra_rx_buffer_size OPTIONAL,
+ IN UINTN extra_tx_buffer_size OPTIONAL
+ )
+;
+
+extern
+EFI_STATUS
+EFIAPI
+snp_undi32_reset (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *this,
+ IN BOOLEAN ExtendedVerification
+ )
+;
+
+extern
+EFI_STATUS
+EFIAPI
+snp_undi32_shutdown (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *this
+ )
+;
+
+extern
+EFI_STATUS
+EFIAPI
+snp_undi32_receive_filters (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL * this,
+ IN UINT32 enable,
+ IN UINT32 disable,
+ IN BOOLEAN reset_mcast_filter,
+ IN UINTN mcast_filter_count OPTIONAL,
+ IN EFI_MAC_ADDRESS * mcast_filter OPTIONAL
+ )
+;
+
+extern
+EFI_STATUS
+EFIAPI
+snp_undi32_station_address (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL * this,
+ IN BOOLEAN reset,
+ IN EFI_MAC_ADDRESS *new OPTIONAL
+ )
+;
+
+extern
+EFI_STATUS
+EFIAPI
+snp_undi32_statistics (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL * this,
+ IN BOOLEAN reset,
+ IN OUT UINTN *statistics_size OPTIONAL,
+ IN OUT EFI_NETWORK_STATISTICS * statistics_table OPTIONAL
+ )
+;
+
+extern
+EFI_STATUS
+EFIAPI
+snp_undi32_mcast_ip_to_mac (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *this,
+ IN BOOLEAN IPv6,
+ IN EFI_IP_ADDRESS *IP,
+ OUT EFI_MAC_ADDRESS *MAC
+ )
+;
+
+extern
+EFI_STATUS
+EFIAPI
+snp_undi32_nvdata (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *this,
+ IN BOOLEAN read_write,
+ IN UINTN offset,
+ IN UINTN buffer_size,
+ IN OUT VOID *buffer
+ )
+;
+
+extern
+EFI_STATUS
+EFIAPI
+snp_undi32_get_status (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL * this,
+ OUT UINT32 *interrupt_status OPTIONAL,
+ OUT VOID **tx_buffer OPTIONAL
+ )
+;
+
+extern
+EFI_STATUS
+EFIAPI
+snp_undi32_transmit (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL * this,
+ IN UINTN header_size,
+ IN UINTN buffer_size,
+ IN VOID *buffer,
+ IN EFI_MAC_ADDRESS * src_addr OPTIONAL,
+ IN EFI_MAC_ADDRESS * dest_addr OPTIONAL,
+ IN UINT16 *protocol OPTIONAL
+ )
+;
+
+extern
+EFI_STATUS
+EFIAPI
+snp_undi32_receive (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL * this,
+ OUT UINTN *header_size OPTIONAL,
+ IN OUT UINTN *buffer_size,
+ OUT VOID *buffer,
+ OUT EFI_MAC_ADDRESS * src_addr OPTIONAL,
+ OUT EFI_MAC_ADDRESS * dest_addr OPTIONAL,
+ OUT UINT16 *protocol OPTIONAL
+ )
+;
+
+typedef
+EFI_STATUS
+(*issue_undi32_command) (
+ UINT64 cdb
+ );
+typedef
+VOID
+(*ptr) (
+ VOID
+ );
+
+
+/**
+ Install all the driver protocol
+
+ @param ImageHandle Driver image handle
+ @param SystemTable System services table
+
+ @retval EFI_SUCEESS Initialization routine has found UNDI hardware, loaded it's
+ ROM, and installed a notify event for the Network
+ Indentifier Interface Protocol successfully.
+ @retval Other Return value from HandleProtocol for DeviceIoProtocol or
+ LoadedImageProtocol
+
+**/
+EFI_STATUS
+EFIAPI
+InitializeSnpNiiDriver (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+;
+
+#ifdef EFI_SIZE_REDUCTION_APPLIED
+ #define COMPONENT_NAME_CODE(code)
+ #define COMPONENT_NAME NULL
+#else
+ #define COMPONENT_NAME_CODE(code) code
+ #define COMPONENT_NAME &gSimpleNetworkComponentName
+#endif
+
+#define SNP_MEM_PAGES(x) (((x) - 1) / 4096 + 1)
+
+
+#endif /* _SNP_H */
diff --git a/MdeModulePkg/Universal/Network/SnpDxe/start.c b/MdeModulePkg/Universal/Network/SnpDxe/start.c
new file mode 100644
index 0000000000..0a43f8e60c
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/SnpDxe/start.c
@@ -0,0 +1,191 @@
+/** @file
+Copyright (c) 2004 - 2007, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module name:
+ start.c
+
+Abstract:
+
+Revision history:
+ 2000-Feb-07 M(f)J Genesis.
+
+**/
+
+#include "Snp.h"
+
+
+/**
+ this routine calls undi to start the interface and changes the snp state!
+
+ @param snp pointer to snp driver structure
+
+
+**/
+EFI_STATUS
+pxe_start (
+ SNP_DRIVER *snp
+ )
+{
+ PXE_CPB_START_30 *cpb;
+ PXE_CPB_START_31 *cpb_31;
+
+ cpb = snp->cpb;
+ cpb_31 = snp->cpb;
+ //
+ // Initialize UNDI Start CDB for H/W UNDI
+ //
+ snp->cdb.OpCode = PXE_OPCODE_START;
+ snp->cdb.OpFlags = PXE_OPFLAGS_NOT_USED;
+ snp->cdb.CPBsize = PXE_CPBSIZE_NOT_USED;
+ snp->cdb.DBsize = PXE_DBSIZE_NOT_USED;
+ snp->cdb.CPBaddr = PXE_CPBADDR_NOT_USED;
+ snp->cdb.DBaddr = PXE_DBADDR_NOT_USED;
+ snp->cdb.StatCode = PXE_STATCODE_INITIALIZE;
+ snp->cdb.StatFlags = PXE_STATFLAGS_INITIALIZE;
+ snp->cdb.IFnum = snp->if_num;
+ snp->cdb.Control = PXE_CONTROL_LAST_CDB_IN_LIST;
+
+ //
+ // Make changes to H/W UNDI Start CDB if this is
+ // a S/W UNDI.
+ //
+ if (snp->is_swundi) {
+ if (snp->IsOldUndi) {
+ snp->cdb.CPBsize = sizeof (PXE_CPB_START_30);
+ snp->cdb.CPBaddr = (UINT64)(UINTN) cpb;
+
+ cpb->Delay = (UINT64)(UINTN) &snp_undi32_callback_delay_30;
+ cpb->Block = (UINT64)(UINTN) &snp_undi32_callback_block_30;
+
+ //
+ // Virtual == Physical. This can be set to zero.
+ //
+ cpb->Virt2Phys = (UINT64)(UINTN) &snp_undi32_callback_v2p_30;
+ cpb->Mem_IO = (UINT64)(UINTN) &snp_undi32_callback_memio_30;
+ } else {
+ snp->cdb.CPBsize = sizeof (PXE_CPB_START_31);
+ snp->cdb.CPBaddr = (UINT64)(UINTN) cpb_31;
+
+ cpb_31->Delay = (UINT64)(UINTN) &snp_undi32_callback_delay;
+ cpb_31->Block = (UINT64)(UINTN) &snp_undi32_callback_block;
+
+ //
+ // Virtual == Physical. This can be set to zero.
+ //
+ cpb_31->Virt2Phys = (UINT64)(UINTN) 0;
+ cpb_31->Mem_IO = (UINT64)(UINTN) &snp_undi32_callback_memio;
+
+ cpb_31->Map_Mem = (UINT64)(UINTN) &snp_undi32_callback_map;
+ cpb_31->UnMap_Mem = (UINT64)(UINTN) &snp_undi32_callback_unmap;
+ cpb_31->Sync_Mem = (UINT64)(UINTN) &snp_undi32_callback_sync;
+
+ cpb_31->Unique_ID = (UINT64)(UINTN) snp;
+ }
+ }
+ //
+ // Issue UNDI command and check result.
+ //
+ DEBUG ((EFI_D_NET, "\nsnp->undi.start() "));
+
+ (*snp->issue_undi32_command) ((UINT64)(UINTN) &snp->cdb);
+
+ if (snp->cdb.StatCode != PXE_STATCODE_SUCCESS) {
+ //
+ // UNDI could not be started. Return UNDI error.
+ //
+ DEBUG (
+ (EFI_D_ERROR,
+ "\nsnp->undi.start() %xh:%xh\n",
+ snp->cdb.StatCode,
+ snp->cdb.StatFlags)
+ );
+
+ return EFI_DEVICE_ERROR;
+ }
+ //
+ // Set simple network state to Started and return success.
+ //
+ snp->mode.State = EfiSimpleNetworkStarted;
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ This is the SNP interface routine for starting the interface
+ This routine basically retrieves snp structure, checks the SNP state and
+ calls the pxe_start routine to actually do start undi interface
+
+ @param This context pointer
+
+ @retval EFI_INVALID_PARAMETER "This" is Null
+ @retval No SNP driver can be extracted from "This"
+ @retval EFI_ALREADY_STARTED The state of SNP is EfiSimpleNetworkStarted or
+ EfiSimpleNetworkInitialized
+ @retval EFI_DEVICE_ERROR The state of SNP is other than
+ EfiSimpleNetworkStarted,
+ EfiSimpleNetworkInitialized, and
+ EfiSimpleNetworkStopped
+ @retval EFI_SUCCESS UNDI interface is succesfully started
+ @retval Other Error occurs while calling pxe_start function.
+
+**/
+EFI_STATUS
+EFIAPI
+snp_undi32_start (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *This
+ )
+{
+ SNP_DRIVER *Snp;
+ EFI_STATUS Status;
+ UINTN Index;
+ EFI_TPL OldTpl;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Snp = EFI_SIMPLE_NETWORK_DEV_FROM_THIS (This);
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ switch (Snp->mode.State) {
+ case EfiSimpleNetworkStopped:
+ break;
+
+ case EfiSimpleNetworkStarted:
+ case EfiSimpleNetworkInitialized:
+ Status = EFI_ALREADY_STARTED;
+ goto ON_EXIT;
+
+ default:
+ Status = EFI_DEVICE_ERROR;
+ goto ON_EXIT;
+ }
+
+ Status = pxe_start (Snp);
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+ //
+ // clear the map_list in SNP structure
+ //
+ for (Index = 0; Index < MAX_MAP_LENGTH; Index++) {
+ Snp->map_list[Index].virt = 0;
+ Snp->map_list[Index].map_cookie = 0;
+ }
+
+ Snp->mode.MCastFilterCount = 0;
+
+ON_EXIT:
+ gBS->RestoreTPL (OldTpl);
+
+ return Status;
+}
diff --git a/MdeModulePkg/Universal/Network/SnpDxe/station_address.c b/MdeModulePkg/Universal/Network/SnpDxe/station_address.c
new file mode 100644
index 0000000000..b6e0728d71
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/SnpDxe/station_address.c
@@ -0,0 +1,237 @@
+/** @file
+Copyright (c) 2004 - 2007, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module name:
+ station_address.c
+
+Abstract:
+
+Revision history:
+ 2000-Feb-17 M(f)J Genesis.
+
+**/
+
+#include "Snp.h"
+
+
+/**
+ this routine calls undi to read the MAC address of the NIC and updates the
+ mode structure with the address.
+
+ @param snp pointer to snp driver structure
+
+
+**/
+EFI_STATUS
+pxe_get_stn_addr (
+ SNP_DRIVER *snp
+ )
+{
+ PXE_DB_STATION_ADDRESS *db;
+
+ db = snp->db;
+ snp->cdb.OpCode = PXE_OPCODE_STATION_ADDRESS;
+ snp->cdb.OpFlags = PXE_OPFLAGS_STATION_ADDRESS_READ;
+
+ snp->cdb.CPBaddr = PXE_CPBADDR_NOT_USED;
+ snp->cdb.CPBsize = PXE_CPBSIZE_NOT_USED;
+
+ snp->cdb.DBsize = sizeof (PXE_DB_STATION_ADDRESS);
+ snp->cdb.DBaddr = (UINT64)(UINTN) db;
+
+ snp->cdb.StatCode = PXE_STATCODE_INITIALIZE;
+ snp->cdb.StatFlags = PXE_STATFLAGS_INITIALIZE;
+ snp->cdb.IFnum = snp->if_num;
+ snp->cdb.Control = PXE_CONTROL_LAST_CDB_IN_LIST;
+
+ //
+ // Issue UNDI command and check result.
+ //
+ DEBUG ((EFI_D_NET, "\nsnp->undi.station_addr() "));
+
+ (*snp->issue_undi32_command) ((UINT64)(UINTN) &snp->cdb);
+
+ if (snp->cdb.StatCode != PXE_STATCODE_SUCCESS) {
+ DEBUG (
+ (EFI_D_ERROR,
+ "\nsnp->undi.station_addr() %xh:%xh\n",
+ snp->cdb.StatFlags,
+ snp->cdb.StatCode)
+ );
+
+ return EFI_DEVICE_ERROR;
+ }
+ //
+ // Set new station address in SNP->Mode structure and return success.
+ //
+ CopyMem (
+ &(snp->mode.CurrentAddress),
+ &db->StationAddr,
+ snp->mode.HwAddressSize
+ );
+
+ CopyMem (
+ &snp->mode.BroadcastAddress,
+ &db->BroadcastAddr,
+ snp->mode.HwAddressSize
+ );
+
+ CopyMem (
+ &snp->mode.PermanentAddress,
+ &db->PermanentAddr,
+ snp->mode.HwAddressSize
+ );
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ this routine calls undi to set a new MAC address for the NIC,
+
+ @param snp pointer to snp driver structure
+ @param NewMacAddr pointer to a mac address to be set for the nic, if this is
+ NULL then this routine resets the mac address to the NIC's
+ original address.
+
+
+**/
+STATIC
+EFI_STATUS
+pxe_set_stn_addr (
+ SNP_DRIVER *snp,
+ EFI_MAC_ADDRESS *NewMacAddr
+ )
+{
+ PXE_CPB_STATION_ADDRESS *cpb;
+ PXE_DB_STATION_ADDRESS *db;
+
+ cpb = snp->cpb;
+ db = snp->db;
+ snp->cdb.OpCode = PXE_OPCODE_STATION_ADDRESS;
+
+ if (NewMacAddr == NULL) {
+ snp->cdb.OpFlags = PXE_OPFLAGS_STATION_ADDRESS_RESET;
+ snp->cdb.CPBsize = PXE_CPBSIZE_NOT_USED;
+ snp->cdb.CPBaddr = PXE_CPBADDR_NOT_USED;
+ } else {
+ snp->cdb.OpFlags = PXE_OPFLAGS_STATION_ADDRESS_READ;
+ //
+ // even though the OPFLAGS are set to READ, supplying a new address
+ // in the CPB will make undi change the mac address to the new one.
+ //
+ CopyMem (&cpb->StationAddr, NewMacAddr, snp->mode.HwAddressSize);
+
+ snp->cdb.CPBsize = sizeof (PXE_CPB_STATION_ADDRESS);
+ snp->cdb.CPBaddr = (UINT64)(UINTN) cpb;
+ }
+
+ snp->cdb.DBsize = sizeof (PXE_DB_STATION_ADDRESS);
+ snp->cdb.DBaddr = (UINT64)(UINTN) db;
+
+ snp->cdb.StatCode = PXE_STATCODE_INITIALIZE;
+ snp->cdb.StatFlags = PXE_STATFLAGS_INITIALIZE;
+ snp->cdb.IFnum = snp->if_num;
+ snp->cdb.Control = PXE_CONTROL_LAST_CDB_IN_LIST;
+
+ //
+ // Issue UNDI command and check result.
+ //
+ DEBUG ((EFI_D_NET, "\nsnp->undi.station_addr() "));
+
+ (*snp->issue_undi32_command) ((UINT64)(UINTN) &snp->cdb);
+
+ if (snp->cdb.StatCode != PXE_STATCODE_SUCCESS) {
+ DEBUG (
+ (EFI_D_ERROR,
+ "\nsnp->undi.station_addr() %xh:%xh\n",
+ snp->cdb.StatFlags,
+ snp->cdb.StatCode)
+ );
+
+ //
+ // UNDI command failed. Return UNDI status to caller.
+ //
+ return EFI_DEVICE_ERROR;
+ }
+ //
+ // read the changed address and save it in SNP->Mode structure
+ //
+ pxe_get_stn_addr (snp);
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ This is the SNP interface routine for changing the NIC's mac address.
+ This routine basically retrieves snp structure, checks the SNP state and
+ calls the above routines to actually do the work
+
+ @param this context pointer
+ @param NewMacAddr pointer to a mac address to be set for the nic, if this is
+ NULL then this routine resets the mac address to the NIC's
+ original address.
+ @param ResetFlag If true, the mac address will change to NIC's original
+ address
+
+
+**/
+EFI_STATUS
+EFIAPI
+snp_undi32_station_address (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL * this,
+ IN BOOLEAN ResetFlag,
+ IN EFI_MAC_ADDRESS * NewMacAddr OPTIONAL
+ )
+{
+ SNP_DRIVER *snp;
+ EFI_STATUS Status;
+ EFI_TPL OldTpl;
+
+ //
+ // Check for invalid parameter combinations.
+ //
+ if ((this == NULL) ||
+ (!ResetFlag && (NewMacAddr == NULL))) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ snp = EFI_SIMPLE_NETWORK_DEV_FROM_THIS (this);
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ //
+ // Return error if the SNP is not initialized.
+ //
+ switch (snp->mode.State) {
+ case EfiSimpleNetworkInitialized:
+ break;
+
+ case EfiSimpleNetworkStopped:
+ Status = EFI_NOT_STARTED;
+ goto ON_EXIT;
+
+ default:
+ Status = EFI_DEVICE_ERROR;
+ goto ON_EXIT;
+ }
+
+ if (ResetFlag) {
+ Status = pxe_set_stn_addr (snp, NULL);
+ } else {
+ Status = pxe_set_stn_addr (snp, NewMacAddr);
+ }
+
+ON_EXIT:
+ gBS->RestoreTPL (OldTpl);
+
+ return Status;
+}
diff --git a/MdeModulePkg/Universal/Network/SnpDxe/statistics.c b/MdeModulePkg/Universal/Network/SnpDxe/statistics.c
new file mode 100644
index 0000000000..19f28239a5
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/SnpDxe/statistics.c
@@ -0,0 +1,201 @@
+/** @file
+Copyright (c) 2004 - 2007, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module name:
+ statistics.c
+
+Abstract:
+
+Revision history:
+ 2000-Feb-17 M(f)J Genesis.
+
+**/
+
+
+#include "Snp.h"
+
+
+/**
+ This is the SNP interface routine for getting the NIC's statistics.
+ This routine basically retrieves snp structure, checks the SNP state and
+ calls the pxe_ routine to actually do the
+
+ @param this context pointer
+ @param ResetFlag true to reset the NIC's statistics counters to zero.
+ @param StatTableSizePtr pointer to the statistics table size
+ @param StatTablePtr pointer to the statistics table
+
+
+**/
+EFI_STATUS
+EFIAPI
+snp_undi32_statistics (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL * this,
+ IN BOOLEAN ResetFlag,
+ IN OUT UINTN *StatTableSizePtr OPTIONAL,
+ IN OUT EFI_NETWORK_STATISTICS * StatTablePtr OPTIONAL
+ )
+{
+ SNP_DRIVER *snp;
+ PXE_DB_STATISTICS *db;
+ UINT64 *stp;
+ UINT64 mask;
+ UINTN size;
+ UINTN n;
+ EFI_TPL OldTpl;
+ EFI_STATUS Status;
+
+ //
+ // Get pointer to SNP driver instance for *this.
+ //
+ if (this == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ snp = EFI_SIMPLE_NETWORK_DEV_FROM_THIS (this);
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ //
+ // Return error if the SNP is not initialized.
+ //
+ switch (snp->mode.State) {
+ case EfiSimpleNetworkInitialized:
+ break;
+
+ case EfiSimpleNetworkStopped:
+ Status = EFI_NOT_STARTED;
+ goto ON_EXIT;
+
+ default:
+ Status = EFI_DEVICE_ERROR;
+ goto ON_EXIT;
+ }
+ //
+ // if we are not resetting the counters, we have to have a valid stat table
+ // with >0 size. if no reset, no table and no size, return success.
+ //
+ if (!ResetFlag && StatTableSizePtr == NULL) {
+ Status = StatTablePtr ? EFI_INVALID_PARAMETER : EFI_SUCCESS;
+ goto ON_EXIT;
+ }
+ //
+ // Initialize UNDI Statistics CDB
+ //
+ snp->cdb.OpCode = PXE_OPCODE_STATISTICS;
+ snp->cdb.CPBsize = PXE_CPBSIZE_NOT_USED;
+ snp->cdb.CPBaddr = PXE_CPBADDR_NOT_USED;
+ snp->cdb.StatCode = PXE_STATCODE_INITIALIZE;
+ snp->cdb.StatFlags = PXE_STATFLAGS_INITIALIZE;
+ snp->cdb.IFnum = snp->if_num;
+ snp->cdb.Control = PXE_CONTROL_LAST_CDB_IN_LIST;
+
+ if (ResetFlag) {
+ snp->cdb.OpFlags = PXE_OPFLAGS_STATISTICS_RESET;
+ snp->cdb.DBsize = PXE_DBSIZE_NOT_USED;
+ snp->cdb.DBaddr = PXE_DBADDR_NOT_USED;
+ db = snp->db;
+ } else {
+ snp->cdb.OpFlags = PXE_OPFLAGS_STATISTICS_READ;
+ snp->cdb.DBsize = sizeof (PXE_DB_STATISTICS);
+ snp->cdb.DBaddr = (UINT64)(UINTN) (db = snp->db);
+ }
+ //
+ // Issue UNDI command and check result.
+ //
+ DEBUG ((EFI_D_NET, "\nsnp->undi.statistics() "));
+
+ (*snp->issue_undi32_command) ((UINT64)(UINTN) &snp->cdb);
+
+ switch (snp->cdb.StatCode) {
+ case PXE_STATCODE_SUCCESS:
+ break;
+
+ case PXE_STATCODE_UNSUPPORTED:
+ DEBUG (
+ (EFI_D_ERROR,
+ "\nsnp->undi.statistics() %xh:%xh\n",
+ snp->cdb.StatFlags,
+ snp->cdb.StatCode)
+ );
+
+ Status = EFI_UNSUPPORTED;
+ goto ON_EXIT;
+
+ default:
+ DEBUG (
+ (EFI_D_ERROR,
+ "\nsnp->undi.statistics() %xh:%xh\n",
+ snp->cdb.StatFlags,
+ snp->cdb.StatCode)
+ );
+
+ Status = EFI_DEVICE_ERROR;
+ goto ON_EXIT;
+ }
+
+ if (ResetFlag) {
+ Status = EFI_SUCCESS;
+ goto ON_EXIT;
+ }
+
+ if (StatTablePtr == NULL) {
+ *StatTableSizePtr = sizeof (EFI_NETWORK_STATISTICS);
+ Status = EFI_BUFFER_TOO_SMALL;
+ goto ON_EXIT;
+ }
+ //
+ // Convert the UNDI statistics information to SNP statistics
+ // information.
+ //
+ ZeroMem (StatTablePtr, *StatTableSizePtr);
+ stp = (UINT64 *) StatTablePtr;
+ size = 0;
+
+ for (n = 0, mask = 1; n < 64; n++, mask = LShiftU64 (mask, 1), stp++) {
+ //
+ // There must be room for a full UINT64. Partial
+ // numbers will not be stored.
+ //
+ if ((n + 1) * sizeof (UINT64) > *StatTableSizePtr) {
+ break;
+ }
+
+ if (db->Supported & mask) {
+ *stp = db->Data[n];
+ size = n + 1;
+ } else {
+ SetMem (stp, sizeof (UINT64), 0xFF);
+ }
+ }
+ //
+ // Compute size up to last supported statistic.
+ //
+ while (++n < 64) {
+ if (db->Supported & (mask = LShiftU64 (mask, 1))) {
+ size = n;
+ }
+ }
+
+ size *= sizeof (UINT64);
+
+ if (*StatTableSizePtr >= size) {
+ *StatTableSizePtr = size;
+ Status = EFI_SUCCESS;
+ } else {
+ *StatTableSizePtr = size;
+ Status = EFI_BUFFER_TOO_SMALL;
+ }
+
+ON_EXIT:
+ gBS->RestoreTPL (OldTpl);
+
+ return Status;
+}
diff --git a/MdeModulePkg/Universal/Network/SnpDxe/stop.c b/MdeModulePkg/Universal/Network/SnpDxe/stop.c
new file mode 100644
index 0000000000..63725237b7
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/SnpDxe/stop.c
@@ -0,0 +1,118 @@
+/** @file
+Copyright (c) 2004 - 2007, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module name:
+ stop.c
+
+Abstract:
+
+Revision history:
+ 2000-Feb-09 M(f)J Genesis.
+
+**/
+
+#include "Snp.h"
+
+
+/**
+ this routine calls undi to stop the interface and changes the snp state
+
+ @param snp pointer to snp driver structure
+
+
+**/
+EFI_STATUS
+pxe_stop (
+ SNP_DRIVER *snp
+ )
+{
+ snp->cdb.OpCode = PXE_OPCODE_STOP;
+ snp->cdb.OpFlags = PXE_OPFLAGS_NOT_USED;
+ snp->cdb.CPBsize = PXE_CPBSIZE_NOT_USED;
+ snp->cdb.DBsize = PXE_DBSIZE_NOT_USED;
+ snp->cdb.CPBaddr = PXE_CPBADDR_NOT_USED;
+ snp->cdb.DBaddr = PXE_DBADDR_NOT_USED;
+ snp->cdb.StatCode = PXE_STATCODE_INITIALIZE;
+ snp->cdb.StatFlags = PXE_STATFLAGS_INITIALIZE;
+ snp->cdb.IFnum = snp->if_num;
+ snp->cdb.Control = PXE_CONTROL_LAST_CDB_IN_LIST;
+
+ //
+ // Issue UNDI command
+ //
+ DEBUG ((EFI_D_NET, "\nsnp->undi.stop() "));
+
+ (*snp->issue_undi32_command) ((UINT64)(UINTN) &snp->cdb);
+
+ if (snp->cdb.StatCode != PXE_STATCODE_SUCCESS) {
+ DEBUG (
+ (EFI_D_WARN,
+ "\nsnp->undi.stop() %xh:%xh\n",
+ snp->cdb.StatCode,
+ snp->cdb.StatFlags)
+ );
+
+ return EFI_DEVICE_ERROR;
+ }
+ //
+ // Set simple network state to Started and return success.
+ //
+ snp->mode.State = EfiSimpleNetworkStopped;
+ return EFI_SUCCESS;
+}
+
+
+/**
+ This is the SNP interface routine for stopping the interface.
+ This routine basically retrieves snp structure, checks the SNP state and
+ calls the pxe_stop routine to actually stop the undi interface
+
+ @param this context pointer
+
+
+**/
+EFI_STATUS
+EFIAPI
+snp_undi32_stop (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *this
+ )
+{
+ SNP_DRIVER *snp;
+ EFI_TPL OldTpl;
+ EFI_STATUS Status;
+
+ if (this == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ snp = EFI_SIMPLE_NETWORK_DEV_FROM_THIS (this);
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ switch (snp->mode.State) {
+ case EfiSimpleNetworkStarted:
+ break;
+
+ case EfiSimpleNetworkStopped:
+ Status = EFI_NOT_STARTED;
+ goto ON_EXIT;
+
+ default:
+ Status = EFI_DEVICE_ERROR;
+ goto ON_EXIT;
+ }
+
+ Status = pxe_stop (snp);
+
+ON_EXIT:
+ gBS->RestoreTPL (OldTpl);
+
+ return Status;
+}
diff --git a/MdeModulePkg/Universal/Network/SnpDxe/transmit.c b/MdeModulePkg/Universal/Network/SnpDxe/transmit.c
new file mode 100644
index 0000000000..d113edec96
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/SnpDxe/transmit.c
@@ -0,0 +1,399 @@
+/** @file
+Copyright (c) 2004 - 2007, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module name:
+
+ transmit.c
+
+Abstract:
+
+Revision history:
+ 2000-Feb-03 M(f)J Genesis.
+
+**/
+
+#include "Snp.h"
+
+
+/**
+ This routine calls undi to create the meadia header for the given data buffer.
+
+ @param snp pointer to SNP driver structure
+ @param MacHeaderPtr address where the media header will be filled in.
+ @param MacHeaderSize size of the memory at MacHeaderPtr
+ @param BufferPtr data buffer pointer
+ @param BufferLength Size of data in the BufferPtr
+ @param DestinationAddrPtr address of the destination mac address buffer
+ @param SourceAddrPtr address of the source mac address buffer
+ @param ProtocolPtr address of the protocol type
+
+ @retval EFI_SUCCESS if successfully completed the undi call
+ @retval Other error return from undi call.
+
+**/
+STATIC
+EFI_STATUS
+pxe_fillheader (
+ SNP_DRIVER *snp,
+ VOID *MacHeaderPtr,
+ UINTN MacHeaderSize,
+ VOID *BufferPtr,
+ UINTN BufferLength,
+ EFI_MAC_ADDRESS *DestinationAddrPtr,
+ EFI_MAC_ADDRESS *SourceAddrPtr,
+ UINT16 *ProtocolPtr
+ )
+{
+ PXE_CPB_FILL_HEADER_FRAGMENTED *cpb;
+ EFI_STATUS Status;
+ struct s_v2p *pkt_v2p;
+ UINT64 TempData;
+
+ cpb = snp->cpb;
+ if (SourceAddrPtr) {
+ CopyMem (
+ (VOID *) cpb->SrcAddr,
+ (VOID *) SourceAddrPtr,
+ snp->mode.HwAddressSize
+ );
+ } else {
+ CopyMem (
+ (VOID *) cpb->SrcAddr,
+ (VOID *) &(snp->mode.CurrentAddress),
+ snp->mode.HwAddressSize
+ );
+ }
+
+ CopyMem (
+ (VOID *) cpb->DestAddr,
+ (VOID *) DestinationAddrPtr,
+ snp->mode.HwAddressSize
+ );
+
+ //
+ // we need to do the byte swapping
+ //
+ cpb->Protocol = (UINT16) PXE_SWAP_UINT16 (*ProtocolPtr);
+
+ cpb->PacketLen = (UINT32) (BufferLength);
+ cpb->MediaHeaderLen = (UINT16) MacHeaderSize;
+
+ cpb->FragCnt = 2;
+ cpb->reserved = 0;
+
+ cpb->FragDesc[0].FragAddr = (UINT64)(UINTN) MacHeaderPtr;
+ cpb->FragDesc[0].FragLen = (UINT32) MacHeaderSize;
+ cpb->FragDesc[1].FragAddr = (UINT64)(UINTN) BufferPtr;
+ cpb->FragDesc[1].FragLen = (UINT32) BufferLength;
+
+ cpb->FragDesc[0].reserved = cpb->FragDesc[1].reserved = 0;
+
+ if (snp->IsOldUndi) {
+ TempData = (UINT64) (UINTN) MacHeaderPtr;
+ if (TempData >= FOUR_GIGABYTES) {
+ cpb->FragDesc[0].FragAddr = (UINT64) (UINTN) snp->fill_hdr_buf;
+ cpb->FragDesc[0].FragLen = (UINT32) snp->init_info.MediaHeaderLen;
+ }
+
+ TempData = (UINT64) (UINTN) (BufferPtr);
+ if (TempData >= FOUR_GIGABYTES) {
+ //
+ // Let the device just read this buffer
+ //
+ Status = add_v2p (
+ &pkt_v2p,
+ EfiPciIoOperationBusMasterRead,
+ BufferPtr,
+ BufferLength
+ );
+ if (Status != EFI_SUCCESS) {
+ return Status;
+ }
+ //
+ // give the virtual address to UNDI and it will call back on Virt2Phys
+ // to get the mapped address, if it needs it
+ //
+ cpb->FragDesc[1].FragLen = (UINT32) pkt_v2p->bsize;
+ }
+ }
+
+ snp->cdb.OpCode = PXE_OPCODE_FILL_HEADER;
+ snp->cdb.OpFlags = PXE_OPFLAGS_FILL_HEADER_FRAGMENTED;
+
+ snp->cdb.DBsize = PXE_DBSIZE_NOT_USED;
+ snp->cdb.DBaddr = PXE_DBADDR_NOT_USED;
+
+ snp->cdb.CPBsize = sizeof (PXE_CPB_FILL_HEADER_FRAGMENTED);
+ snp->cdb.CPBaddr = (UINT64)(UINTN) cpb;
+
+ snp->cdb.StatCode = PXE_STATCODE_INITIALIZE;
+ snp->cdb.StatFlags = PXE_STATFLAGS_INITIALIZE;
+ snp->cdb.IFnum = snp->if_num;
+ snp->cdb.Control = PXE_CONTROL_LAST_CDB_IN_LIST;
+
+ //
+ // Issue UNDI command and check result.
+ //
+ DEBUG ((EFI_D_NET, "\nsnp->undi.fill_header() "));
+
+ (*snp->issue_undi32_command) ((UINT64) (UINTN) &snp->cdb);
+
+ if (snp->IsOldUndi) {
+ TempData = (UINT64) (UINTN) (BufferPtr);
+ if (TempData >= FOUR_GIGABYTES) {
+ del_v2p (BufferPtr);
+ }
+ //
+ // if we used the global buffer for header, copy the contents
+ //
+ TempData = (UINT64) (UINTN) MacHeaderPtr;
+ if (TempData >= FOUR_GIGABYTES) {
+ CopyMem (
+ MacHeaderPtr,
+ snp->fill_hdr_buf,
+ snp->init_info.MediaHeaderLen
+ );
+ }
+ }
+
+ switch (snp->cdb.StatCode) {
+ case PXE_STATCODE_SUCCESS:
+ return EFI_SUCCESS;
+
+ case PXE_STATCODE_INVALID_PARAMETER:
+ DEBUG (
+ (EFI_D_ERROR,
+ "\nsnp->undi.fill_header() %xh:%xh\n",
+ snp->cdb.StatFlags,
+ snp->cdb.StatCode)
+ );
+
+ return EFI_INVALID_PARAMETER;
+
+ default:
+ DEBUG (
+ (EFI_D_ERROR,
+ "\nsnp->undi.fill_header() %xh:%xh\n",
+ snp->cdb.StatFlags,
+ snp->cdb.StatCode)
+ );
+
+ return EFI_DEVICE_ERROR;
+ }
+}
+
+
+/**
+ This routine calls undi to transmit the given data buffer
+
+ @param snp pointer to SNP driver structure
+ @param BufferPtr data buffer pointer
+ @param BufferLength Size of data in the BufferPtr
+
+ @retval EFI_SUCCESS if successfully completed the undi call
+ @retval Other error return from undi call.
+
+**/
+STATIC
+EFI_STATUS
+pxe_transmit (
+ SNP_DRIVER *snp,
+ VOID *BufferPtr,
+ UINTN BufferLength
+ )
+{
+ PXE_CPB_TRANSMIT *cpb;
+ EFI_STATUS Status;
+ struct s_v2p *v2p;
+ UINT64 TempData;
+
+ cpb = snp->cpb;
+ cpb->FrameAddr = (UINT64) (UINTN) BufferPtr;
+ cpb->DataLen = (UINT32) BufferLength;
+
+ TempData = (UINT64) (UINTN) BufferPtr;
+ if (snp->IsOldUndi && (TempData >= FOUR_GIGABYTES)) {
+ //
+ // we need to create a mapping now and give it to the undi when it calls
+ // the Virt2Phys on this address.
+ // this is a transmit, just map it for the device to READ
+ //
+ Status = add_v2p (
+ &v2p,
+ EfiPciIoOperationBusMasterRead,
+ BufferPtr,
+ BufferLength
+ );
+ if (Status != EFI_SUCCESS) {
+ return Status;
+ }
+
+ cpb->DataLen = (UINT32) v2p->bsize;
+ }
+
+ cpb->MediaheaderLen = 0;
+ cpb->reserved = 0;
+
+ snp->cdb.OpFlags = PXE_OPFLAGS_TRANSMIT_WHOLE;
+
+ snp->cdb.CPBsize = sizeof (PXE_CPB_TRANSMIT);
+ snp->cdb.CPBaddr = (UINT64)(UINTN) cpb;
+
+ snp->cdb.OpCode = PXE_OPCODE_TRANSMIT;
+ snp->cdb.DBsize = PXE_DBSIZE_NOT_USED;
+ snp->cdb.DBaddr = PXE_DBADDR_NOT_USED;
+
+ snp->cdb.StatCode = PXE_STATCODE_INITIALIZE;
+ snp->cdb.StatFlags = PXE_STATFLAGS_INITIALIZE;
+ snp->cdb.IFnum = snp->if_num;
+ snp->cdb.Control = PXE_CONTROL_LAST_CDB_IN_LIST;
+
+ //
+ // Issue UNDI command and check result.
+ //
+ DEBUG ((EFI_D_NET, "\nsnp->undi.transmit() "));
+ DEBUG ((EFI_D_NET, "\nsnp->cdb.OpCode == %x", snp->cdb.OpCode));
+ DEBUG ((EFI_D_NET, "\nsnp->cdb.CPBaddr == %X", snp->cdb.CPBaddr));
+ DEBUG ((EFI_D_NET, "\nsnp->cdb.DBaddr == %X", snp->cdb.DBaddr));
+ DEBUG ((EFI_D_NET, "\ncpb->FrameAddr == %X\n", cpb->FrameAddr));
+
+ (*snp->issue_undi32_command) ((UINT64) (UINTN) &snp->cdb);
+
+ DEBUG ((EFI_D_NET, "\nexit snp->undi.transmit() "));
+ DEBUG ((EFI_D_NET, "\nsnp->cdb.StatCode == %r", snp->cdb.StatCode));
+
+ //
+ // we will unmap the buffers in get_status call, not here
+ //
+ switch (snp->cdb.StatCode) {
+ case PXE_STATCODE_SUCCESS:
+ return EFI_SUCCESS;
+
+ case PXE_STATCODE_QUEUE_FULL:
+ case PXE_STATCODE_BUSY:
+ Status = EFI_NOT_READY;
+ break;
+
+ default:
+ Status = EFI_DEVICE_ERROR;
+ }
+
+ DEBUG (
+ (EFI_D_ERROR,
+ "\nsnp->undi.transmit() %xh:%xh\n",
+ snp->cdb.StatFlags,
+ snp->cdb.StatCode)
+ );
+
+ return Status;
+}
+
+
+/**
+ This is the snp interface routine for transmitting a packet. this routine
+ basically retrieves the snp structure, checks the snp state and calls
+ pxe_fill_header and pxe_transmit calls to complete the transmission.
+
+ @param this pointer to SNP driver context
+ @param MacHeaderSize size of the memory at MacHeaderPtr
+ @param BufferLength Size of data in the BufferPtr
+ @param BufferPtr data buffer pointer
+ @param SourceAddrPtr address of the source mac address buffer
+ @param DestinationAddrPtr address of the destination mac address buffer
+ @param ProtocolPtr address of the protocol type
+
+ @retval EFI_SUCCESS if successfully completed the undi call
+ @retval Other error return from undi call.
+
+**/
+EFI_STATUS
+EFIAPI
+snp_undi32_transmit (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL * this,
+ IN UINTN MacHeaderSize,
+ IN UINTN BufferLength,
+ IN VOID *BufferPtr,
+ IN EFI_MAC_ADDRESS * SourceAddrPtr OPTIONAL,
+ IN EFI_MAC_ADDRESS * DestinationAddrPtr OPTIONAL,
+ IN UINT16 *ProtocolPtr OPTIONAL
+ )
+{
+ SNP_DRIVER *snp;
+ EFI_STATUS Status;
+ EFI_TPL OldTpl;
+
+ if (this == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ snp = EFI_SIMPLE_NETWORK_DEV_FROM_THIS (this);
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ if (snp == NULL) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ switch (snp->mode.State) {
+ case EfiSimpleNetworkInitialized:
+ break;
+
+ case EfiSimpleNetworkStopped:
+ Status = EFI_NOT_STARTED;
+ goto ON_EXIT;
+
+ default:
+ Status = EFI_DEVICE_ERROR;
+ goto ON_EXIT;
+ }
+
+ if (BufferPtr == NULL) {
+ Status = EFI_INVALID_PARAMETER;
+ goto ON_EXIT;
+ }
+
+ if (BufferLength < snp->mode.MediaHeaderSize) {
+ Status = EFI_BUFFER_TOO_SMALL;
+ goto ON_EXIT;
+ }
+
+ //
+ // if the MacHeaderSize is non-zero, we need to fill up the header and for that
+ // we need the destination address and the protocol
+ //
+ if (MacHeaderSize != 0) {
+ if (MacHeaderSize != snp->mode.MediaHeaderSize || DestinationAddrPtr == 0 || ProtocolPtr == 0) {
+ Status = EFI_INVALID_PARAMETER;
+ goto ON_EXIT;
+ }
+
+ Status = pxe_fillheader (
+ snp,
+ BufferPtr,
+ MacHeaderSize,
+ (UINT8 *) BufferPtr + MacHeaderSize,
+ BufferLength - MacHeaderSize,
+ DestinationAddrPtr,
+ SourceAddrPtr,
+ ProtocolPtr
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+ }
+
+ Status = pxe_transmit (snp, BufferPtr, BufferLength);
+
+ON_EXIT:
+ gBS->RestoreTPL (OldTpl);
+
+ return Status;
+}
diff --git a/MdeModulePkg/Universal/Network/Tcp4Dxe/ComponentName.c b/MdeModulePkg/Universal/Network/Tcp4Dxe/ComponentName.c
new file mode 100644
index 0000000000..477af22e1e
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/Tcp4Dxe/ComponentName.c
@@ -0,0 +1,169 @@
+/** @file
+
+Copyright (c) 2005 - 2007, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+
+ ComponentName.c
+
+Abstract:
+
+
+**/
+
+#include "Tcp4Main.h"
+
+//
+// EFI Component Name Functions
+//
+EFI_STATUS
+EFIAPI
+TcpComponentNameGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ );
+
+EFI_STATUS
+EFIAPI
+TcpComponentNameGetControllerName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle OPTIONAL,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ );
+
+//
+// EFI Component Name Protocol
+//
+EFI_COMPONENT_NAME_PROTOCOL gTcp4ComponentName = {
+ TcpComponentNameGetDriverName,
+ TcpComponentNameGetControllerName,
+ "eng"
+};
+
+static EFI_UNICODE_STRING_TABLE mTcpDriverNameTable[] = {
+ {
+ "eng",
+ L"Tcp Network Service Driver"
+ },
+ {
+ NULL,
+ NULL
+ }
+};
+
+EFI_STATUS
+EFIAPI
+TcpComponentNameGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ )
+/*++
+
+Routine Description:
+
+ Retrieves a Unicode string that is the user readable name of the EFI Driver.
+
+Arguments:
+
+ This - A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance.
+ Language - A pointer to a three character ISO 639-2 language identifier.
+ This is the language of the driver name that that the caller
+ is requesting, and it must match one of the languages
+ specified in SupportedLanguages. The number of languages
+ supported by a driver is up to the driver writer.
+ DriverName - A pointer to the Unicode string to return. This Unicode
+ string is the name of the driver specified by This in the
+ language specified by Language.
+
+Returns:
+
+ EFI_SUCCES - The Unicode string for the Driver specified by
+ This and the language specified by Language was
+ returned in DriverName.
+ EFI_INVALID_PARAMETER - Language is NULL.
+ EFI_INVALID_PARAMETER - DriverName is NULL.
+ EFI_UNSUPPORTED - The driver specified by This does not support the
+ language specified by Language.
+
+--*/
+{
+ return LookupUnicodeString (
+ Language,
+ gTcp4ComponentName.SupportedLanguages,
+ mTcpDriverNameTable,
+ DriverName
+ );
+}
+
+EFI_STATUS
+EFIAPI
+TcpComponentNameGetControllerName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle OPTIONAL,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ )
+/*++
+
+Routine Description:
+
+ Retrieves a Unicode string that is the user readable name of the controller
+ that is being managed by an EFI Driver.
+
+Arguments:
+
+ This - A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance.
+ ControllerHandle - The handle of a controller that the driver specified by
+ This is managing. This handle specifies the controller
+ whose name is to be returned.
+ ChildHandle - The handle of the child controller to retrieve the name
+ of. This is an optional parameter that may be NULL.
+ It will be NULL for device drivers. It will also be
+ NULL for a bus drivers that wish to retrieve the name of
+ the bus controller. It will not be NULL for a bus
+ driver that wishes to retrieve the name of a child
+ controller.
+ Language - A pointer to a three character ISO 639-2 language
+ identifier. This is the language of the controller
+ name that that the caller is requesting, and it must
+ match one of the languages specified in supported
+ languages. The number of languages supported by a driver
+ is up to the driver writer.
+ ControllerName - A pointer to the Unicode string to return. This Unicode
+ string is the name of the controller specified by
+ ControllerHandle and ChildHandle in the language
+ specified by Language from the point of view of the
+ driver specified by This.
+
+Returns:
+
+ EFI_SUCCESS - The Unicode string for the user readable name in
+ the language specified by Language for the driver
+ specified by This was returned in DriverName.
+ EFI_INVALID_PARAMETER - ControllerHandle is not a valid EFI_HANDLE.
+ EFI_INVALID_PARAMETER - ChildHandle is not NULL and it is not a valid
+ EFI_HANDLE.
+ EFI_INVALID_PARAMETER - Language is NULL.
+ EFI_INVALID_PARAMETER - ControllerName is NULL.
+ EFI_UNSUPPORTED - The driver specified by This is not currently
+ managing the controller specified by
+ ControllerHandle and ChildHandle.
+ EFI_UNSUPPORTED - The driver specified by This does not support the
+ language specified by Language.
+
+--*/
+{
+ return EFI_UNSUPPORTED;
+}
diff --git a/MdeModulePkg/Universal/Network/Tcp4Dxe/SockImpl.c b/MdeModulePkg/Universal/Network/Tcp4Dxe/SockImpl.c
new file mode 100644
index 0000000000..1f81fc39d3
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/Tcp4Dxe/SockImpl.c
@@ -0,0 +1,1269 @@
+/** @file
+
+Copyright (c) 2005 - 2006, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+
+ SockImpl.c
+
+Abstract:
+
+
+**/
+
+#include "SockImpl.h"
+
+STATIC
+UINT32
+SockTcpDataToRcv (
+ IN SOCK_BUFFER *SockBuffer,
+ OUT BOOLEAN *IsOOB,
+ IN UINT32 BufLen
+ );
+
+STATIC
+VOID
+SockProcessSndToken (
+ IN SOCKET *Sock
+ );
+
+VOID
+SockFreeFoo (
+ IN EFI_EVENT Event
+ )
+{
+ return ;
+}
+
+
+/**
+ Get the length of the data that can be retrieved from the socket
+ receive buffer.
+
+ @param SockBuffer Pointer to the socket receive buffer.
+ @param IsUrg Pointer to a BOOLEAN variable. If TRUE the data is
+ OOB.
+ @param BufLen The maximum length of the data buffer to store the
+ received data in socket layer.
+
+ @return The length of the data can be retreived.
+
+**/
+STATIC
+UINT32
+SockTcpDataToRcv (
+ IN SOCK_BUFFER *SockBuffer,
+ OUT BOOLEAN *IsUrg,
+ IN UINT32 BufLen
+ )
+{
+ NET_BUF *RcvBufEntry;
+ UINT32 DataLen;
+ TCP_RSV_DATA *TcpRsvData;
+ BOOLEAN Urg;
+
+ ASSERT (SockBuffer && IsUrg && (BufLen > 0));
+
+ RcvBufEntry = SockBufFirst (SockBuffer);
+ ASSERT (RcvBufEntry);
+
+ TcpRsvData = (TCP_RSV_DATA *) RcvBufEntry->ProtoData;
+
+ *IsUrg = ((TcpRsvData->UrgLen > 0) ? TRUE : FALSE);
+
+ if (*IsUrg && TcpRsvData->UrgLen < RcvBufEntry->TotalSize) {
+
+ DataLen = NET_MIN (TcpRsvData->UrgLen, BufLen);
+
+ if (DataLen < TcpRsvData->UrgLen) {
+ TcpRsvData->UrgLen = TcpRsvData->UrgLen - DataLen;
+ } else {
+ TcpRsvData->UrgLen = 0;
+ }
+
+ return DataLen;
+
+ }
+
+ DataLen = RcvBufEntry->TotalSize;
+
+ RcvBufEntry = SockBufNext (SockBuffer, RcvBufEntry);
+
+ while ((BufLen > DataLen) && (RcvBufEntry != NULL)) {
+
+ TcpRsvData = (TCP_RSV_DATA *) RcvBufEntry->ProtoData;
+
+ Urg = ((TcpRsvData->UrgLen > 0) ? TRUE : FALSE);
+
+ if (*IsUrg != Urg) {
+ break;
+ }
+
+ if (*IsUrg && TcpRsvData->UrgLen < RcvBufEntry->TotalSize) {
+
+ if (TcpRsvData->UrgLen + DataLen < BufLen) {
+ TcpRsvData->UrgLen = 0;
+ } else {
+ TcpRsvData->UrgLen = TcpRsvData->UrgLen - (BufLen - DataLen);
+ }
+
+ return NET_MIN (TcpRsvData->UrgLen + DataLen, BufLen);
+
+ }
+
+ DataLen += RcvBufEntry->TotalSize;
+
+ RcvBufEntry = SockBufNext (SockBuffer, RcvBufEntry);
+ }
+
+ DataLen = NET_MIN (BufLen, DataLen);
+ return DataLen;
+}
+
+
+/**
+ Copy data from socket buffer to application provided receive buffer.
+
+ @param Sock Pointer to the socket.
+ @param TcpRxData Pointer to the application provided receive buffer.
+ @param RcvdBytes The maximum length of the data can be copied.
+ @param IsOOB If TURE the data is OOB, else the data is normal.
+
+ @return None.
+
+**/
+VOID
+SockSetTcpRxData (
+ IN SOCKET *Sock,
+ IN VOID *TcpRxData,
+ IN UINT32 RcvdBytes,
+ IN BOOLEAN IsOOB
+ )
+{
+ UINT32 Index;
+ UINT32 CopyBytes;
+ UINT32 OffSet;
+ EFI_TCP4_RECEIVE_DATA *RxData;
+ EFI_TCP4_FRAGMENT_DATA *Fragment;
+
+ RxData = (EFI_TCP4_RECEIVE_DATA *) TcpRxData;
+
+ OffSet = 0;
+
+ ASSERT (RxData->DataLength >= RcvdBytes);
+
+ RxData->DataLength = RcvdBytes;
+ RxData->UrgentFlag = IsOOB;
+
+ for (Index = 0; (Index < RxData->FragmentCount) && (RcvdBytes > 0); Index++) {
+
+ Fragment = &RxData->FragmentTable[Index];
+ CopyBytes = NET_MIN (Fragment->FragmentLength, RcvdBytes);
+
+ NetbufQueCopy (
+ Sock->RcvBuffer.DataQueue,
+ OffSet,
+ CopyBytes,
+ Fragment->FragmentBuffer
+ );
+
+ Fragment->FragmentLength = CopyBytes;
+ RcvdBytes -= CopyBytes;
+ OffSet += CopyBytes;
+ }
+}
+
+
+/**
+ Get received data from the socket layer to the receive token.
+
+ @param Sock Pointer to the socket.
+ @param RcvToken Pointer to the application provided receive token.
+
+ @return The length of data received in this token.
+
+**/
+UINT32
+SockProcessRcvToken (
+ IN SOCKET *Sock,
+ IN SOCK_IO_TOKEN *RcvToken
+ )
+{
+ UINT32 TokenRcvdBytes;
+ EFI_TCP4_RECEIVE_DATA *RxData;
+ BOOLEAN IsUrg;
+
+ ASSERT (Sock);
+
+ ASSERT (SOCK_STREAM == Sock->Type);
+
+ RxData = RcvToken->Packet.RxData;
+
+ TokenRcvdBytes = SockTcpDataToRcv (
+ &Sock->RcvBuffer,
+ &IsUrg,
+ RxData->DataLength
+ );
+
+ //
+ // Copy data from RcvBuffer of socket to user
+ // provided RxData and set the fields in TCP RxData
+ //
+ SockSetTcpRxData (Sock, RxData, TokenRcvdBytes, IsUrg);
+
+ SOCK_TRIM_RCV_BUFF (Sock, TokenRcvdBytes);
+ SIGNAL_TOKEN (&(RcvToken->Token), EFI_SUCCESS);
+
+ return TokenRcvdBytes;
+}
+
+
+/**
+ Process the TCP send data, buffer the tcp txdata and append
+ the buffer to socket send buffer,then try to send it.
+
+ @param Sock Pointer to the socket.
+ @param TcpTxData Pointer to the tcp txdata.
+
+ @retval EFI_SUCCESS The operation is completed successfully.
+ @retval EFI_OUT_OF_RESOURCES Failed due to resource limit.
+
+**/
+EFI_STATUS
+SockProcessTcpSndData (
+ IN SOCKET *Sock,
+ IN VOID *TcpTxData
+ )
+{
+ NET_BUF *SndData;
+ EFI_STATUS Status;
+ EFI_TCP4_TRANSMIT_DATA *TxData;
+
+ TxData = (EFI_TCP4_TRANSMIT_DATA *) TcpTxData;
+
+ //
+ // transform this TxData into a NET_BUFFER
+ // and insert it into Sock->SndBuffer
+ //
+ SndData = NetbufFromExt (
+ (NET_FRAGMENT *) TxData->FragmentTable,
+ TxData->FragmentCount,
+ 0,
+ 0,
+ SockFreeFoo,
+ NULL
+ );
+
+ if (NULL == SndData) {
+ SOCK_DEBUG_ERROR (("SockKProcessSndData: Failed to"
+ " call NetBufferFromExt\n"));
+
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ NetbufQueAppend (Sock->SndBuffer.DataQueue, SndData);
+
+ //
+ // notify the low layer protocol to handle this send token
+ //
+ if (TxData->Urgent) {
+ Status = Sock->ProtoHandler (Sock, SOCK_SNDURG, NULL);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+
+ if (TxData->Push) {
+ Status = Sock->ProtoHandler (Sock, SOCK_SNDPUSH, NULL);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+
+ //
+ // low layer protocol should really handle the sending
+ // process when catching SOCK_SND request
+ //
+ Status = Sock->ProtoHandler (Sock, SOCK_SND, NULL);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Flush the tokens in the specific token list.
+
+ @param Sock Pointer to the socket.
+ @param PendingTokenList Pointer to the token list to be flushed.
+
+ @return None.
+
+**/
+STATIC
+VOID
+SockFlushPendingToken (
+ IN SOCKET *Sock,
+ IN NET_LIST_ENTRY *PendingTokenList
+ )
+{
+ SOCK_TOKEN *SockToken;
+ SOCK_COMPLETION_TOKEN *Token;
+
+ ASSERT (Sock && PendingTokenList);
+
+ while (!NetListIsEmpty (PendingTokenList)) {
+ SockToken = NET_LIST_HEAD (
+ PendingTokenList,
+ SOCK_TOKEN,
+ TokenList
+ );
+
+ Token = SockToken->Token;
+ SIGNAL_TOKEN (Token, Sock->SockError);
+
+ NetListRemoveEntry (&(SockToken->TokenList));
+ NetFreePool (SockToken);
+ }
+}
+
+
+/**
+ Wake up the connection token while the connection is
+ successfully established, then try to process any
+ pending send token.
+
+ @param Sock Pointer to the socket.
+
+ @return None.
+
+**/
+STATIC
+VOID
+SockWakeConnToken (
+ IN SOCKET *Sock
+ )
+{
+ ASSERT (Sock->ConnectionToken != NULL);
+
+ SIGNAL_TOKEN (Sock->ConnectionToken, EFI_SUCCESS);
+ Sock->ConnectionToken = NULL;
+
+ //
+ // check to see if some pending send token existed?
+ //
+ SockProcessSndToken (Sock);
+ return ;
+}
+
+
+/**
+ Wake up the listen token while the connection is
+ established successfully.
+
+ @param Sock Pointer to the socket.
+
+ @return None.
+
+**/
+STATIC
+VOID
+SockWakeListenToken (
+ IN SOCKET *Sock
+ )
+{
+ SOCKET *Parent;
+ SOCK_TOKEN *SockToken;
+ EFI_TCP4_LISTEN_TOKEN *ListenToken;
+
+ Parent = Sock->Parent;
+
+ ASSERT (Parent && SOCK_IS_LISTENING (Parent) && SOCK_IS_CONNECTED (Sock));
+
+ if (!NetListIsEmpty (&Parent->ListenTokenList)) {
+ SockToken = NET_LIST_HEAD (
+ &Parent->ListenTokenList,
+ SOCK_TOKEN,
+ TokenList
+ );
+
+ ListenToken = (EFI_TCP4_LISTEN_TOKEN *) SockToken->Token;
+ ListenToken->NewChildHandle = Sock->SockHandle;
+
+ SIGNAL_TOKEN (&(ListenToken->CompletionToken), EFI_SUCCESS);
+
+ NetListRemoveEntry (&SockToken->TokenList);
+ NetFreePool (SockToken);
+
+ NetListRemoveEntry (&Sock->ConnectionList);
+
+ Parent->ConnCnt--;
+ SOCK_DEBUG_WARN (("SockWakeListenToken: accept a socket,"
+ "now conncnt is %d", Parent->ConnCnt));
+
+ Sock->Parent = NULL;
+ }
+}
+
+
+/**
+ Wake up the receive token while some data is received.
+
+ @param Sock Pointer to the socket.
+
+ @return None.
+
+**/
+STATIC
+VOID
+SockWakeRcvToken (
+ IN SOCKET *Sock
+ )
+{
+ UINT32 RcvdBytes;
+ UINT32 TokenRcvdBytes;
+ SOCK_TOKEN *SockToken;
+ SOCK_IO_TOKEN *RcvToken;
+
+ ASSERT (Sock->RcvBuffer.DataQueue);
+
+ RcvdBytes = (Sock->RcvBuffer.DataQueue)->BufSize;
+
+ ASSERT (RcvdBytes > 0);
+
+ while (RcvdBytes > 0 && !NetListIsEmpty (&Sock->RcvTokenList)) {
+
+ SockToken = NET_LIST_HEAD (
+ &Sock->RcvTokenList,
+ SOCK_TOKEN,
+ TokenList
+ );
+
+ RcvToken = (SOCK_IO_TOKEN *) SockToken->Token;
+ TokenRcvdBytes = SockProcessRcvToken (Sock, RcvToken);
+
+ if (0 == TokenRcvdBytes) {
+ return ;
+ }
+
+ NetListRemoveEntry (&(SockToken->TokenList));
+ NetFreePool (SockToken);
+ RcvdBytes -= TokenRcvdBytes;
+ }
+}
+
+
+/**
+ Process the send token.
+
+ @param Sock Pointer to the socket.
+
+ @return None.
+
+**/
+STATIC
+VOID
+SockProcessSndToken (
+ IN SOCKET *Sock
+ )
+{
+ UINT32 FreeSpace;
+ SOCK_TOKEN *SockToken;
+ UINT32 DataLen;
+ SOCK_IO_TOKEN *SndToken;
+ EFI_TCP4_TRANSMIT_DATA *TxData;
+ EFI_STATUS Status;
+
+ ASSERT (Sock && (SOCK_STREAM == Sock->Type));
+
+ FreeSpace = SockGetFreeSpace (Sock, SOCK_SND_BUF);
+
+ //
+ // to determine if process a send token using
+ // socket layer flow control policy
+ //
+ while ((FreeSpace >= Sock->SndBuffer.LowWater) &&
+ !NetListIsEmpty (&Sock->SndTokenList)) {
+
+ SockToken = NET_LIST_HEAD (
+ &(Sock->SndTokenList),
+ SOCK_TOKEN,
+ TokenList
+ );
+
+ //
+ // process this token
+ //
+ NetListRemoveEntry (&(SockToken->TokenList));
+ NetListInsertTail (
+ &(Sock->ProcessingSndTokenList),
+ &(SockToken->TokenList)
+ );
+
+ //
+ // Proceess it in the light of SockType
+ //
+ SndToken = (SOCK_IO_TOKEN *) SockToken->Token;
+ TxData = SndToken->Packet.TxData;
+
+ DataLen = TxData->DataLength;
+ Status = SockProcessTcpSndData (Sock, TxData);
+
+ if (EFI_ERROR (Status)) {
+ goto OnError;
+ }
+
+ if (DataLen >= FreeSpace) {
+ FreeSpace = 0;
+
+ } else {
+ FreeSpace -= DataLen;
+
+ }
+ }
+
+ return ;
+
+OnError:
+
+ NetListRemoveEntry (&SockToken->TokenList);
+ SIGNAL_TOKEN (SockToken->Token, Status);
+ NetFreePool (SockToken);
+}
+
+
+/**
+ Create a socket with initial data SockInitData.
+
+ @param SockInitData Pointer to the initial data of the socket.
+
+ @return Pointer to the newly created socket.
+
+**/
+SOCKET *
+SockCreate (
+ IN SOCK_INIT_DATA *SockInitData
+ )
+{
+ SOCKET *Sock;
+ SOCKET *Parent;
+ EFI_STATUS Status;
+
+ ASSERT (SockInitData && SockInitData->ProtoHandler);
+ ASSERT (SockInitData->Type == SOCK_STREAM);
+
+ Parent = SockInitData->Parent;
+
+ if (Parent && (Parent->ConnCnt == Parent->BackLog)) {
+ SOCK_DEBUG_ERROR (
+ ("SockCreate: Socket parent has "
+ "reached its connection limit with %d ConnCnt and %d BackLog\n",
+ Parent->ConnCnt,
+ Parent->BackLog)
+ );
+
+ return NULL;
+ }
+
+ Sock = NetAllocateZeroPool (sizeof (SOCKET));
+ if (NULL == Sock) {
+
+ SOCK_DEBUG_ERROR (("SockCreate: No resource to create a new socket\n"));
+ return NULL;
+ }
+
+ NetListInit (&Sock->ConnectionList);
+ NetListInit (&Sock->ListenTokenList);
+ NetListInit (&Sock->RcvTokenList);
+ NetListInit (&Sock->SndTokenList);
+ NetListInit (&Sock->ProcessingSndTokenList);
+
+ NET_LOCK_INIT (&(Sock->Lock));
+
+ Sock->SndBuffer.DataQueue = NetbufQueAlloc ();
+ if (NULL == Sock->SndBuffer.DataQueue) {
+ SOCK_DEBUG_ERROR (("SockCreate: No resource to allocate"
+ " SndBuffer for new socket\n"));
+
+ goto OnError;
+ }
+
+ Sock->RcvBuffer.DataQueue = NetbufQueAlloc ();
+ if (NULL == Sock->RcvBuffer.DataQueue) {
+ SOCK_DEBUG_ERROR (("SockCreate: No resource to allocate "
+ "RcvBuffer for new socket\n"));
+
+ goto OnError;
+ }
+
+ Sock->Signature = SOCK_SIGNATURE;
+
+ Sock->Parent = Parent;
+ Sock->BackLog = SockInitData->BackLog;
+ Sock->ProtoHandler = SockInitData->ProtoHandler;
+ Sock->SndBuffer.HighWater = SockInitData->SndBufferSize;
+ Sock->RcvBuffer.HighWater = SockInitData->RcvBufferSize;
+ Sock->Type = SockInitData->Type;
+ Sock->DriverBinding = SockInitData->DriverBinding;
+ Sock->State = SockInitData->State;
+
+ Sock->SockError = EFI_ABORTED;
+ Sock->SndBuffer.LowWater = SOCK_BUFF_LOW_WATER;
+ Sock->RcvBuffer.LowWater = SOCK_BUFF_LOW_WATER;
+
+ //
+ // Install protocol on Sock->SockHandle
+ //
+ NetCopyMem (
+ &(Sock->NetProtocol.TcpProtocol),
+ SockInitData->Protocol,
+ sizeof (EFI_TCP4_PROTOCOL)
+ );
+
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ &Sock->SockHandle,
+ &gEfiTcp4ProtocolGuid,
+ &(Sock->NetProtocol.TcpProtocol),
+ NULL
+ );
+
+ if (EFI_ERROR (Status)) {
+ SOCK_DEBUG_ERROR (("SockCreate: Install TCP protocol in "
+ "socket failed with %r\n", Status));
+
+ goto OnError;
+ }
+
+ if (Parent != NULL) {
+ ASSERT (Parent->BackLog > 0);
+ ASSERT (SOCK_IS_LISTENING (Parent));
+
+ //
+ // need to add it into Parent->ConnectionList
+ // if the Parent->ConnCnt < Parent->BackLog
+ //
+ Parent->ConnCnt++;
+
+ SOCK_DEBUG_WARN (("SockCreate: Create a new socket and"
+ "add to parent, now conncnt is %d\n", Parent->ConnCnt));
+
+ NetListInsertTail (&Parent->ConnectionList, &Sock->ConnectionList);
+ }
+
+ return Sock;
+
+OnError:
+ if (NULL != Sock) {
+
+ if (NULL != Sock->SndBuffer.DataQueue) {
+ NetbufQueFree (Sock->SndBuffer.DataQueue);
+ }
+
+ if (NULL != Sock->RcvBuffer.DataQueue) {
+ NetbufQueFree (Sock->RcvBuffer.DataQueue);
+ }
+
+ NetFreePool (Sock);
+ }
+
+ return NULL;
+}
+
+
+/**
+ Destroy a socket.
+
+ @param Sock Pointer to the socket.
+
+ @return None.
+
+**/
+VOID
+SockDestroy (
+ IN SOCKET *Sock
+ )
+{
+ VOID *SockProtocol;
+ EFI_GUID *ProtocolGuid;
+ EFI_STATUS Status;
+
+ ASSERT (SOCK_STREAM == Sock->Type);
+
+ //
+ // Flush the completion token buffered
+ // by sock and rcv, snd buffer
+ //
+ if (!SOCK_IS_UNCONFIGURED (Sock)) {
+
+ SockConnFlush (Sock);
+ SockSetState (Sock, SO_CLOSED);
+ Sock->ConfigureState = SO_UNCONFIGURED;
+
+ }
+ //
+ // Destory the RcvBuffer Queue and SendBuffer Queue
+ //
+ NetbufQueFree (Sock->RcvBuffer.DataQueue);
+ NetbufQueFree (Sock->SndBuffer.DataQueue);
+
+ //
+ // Remove it from parent connection list if needed
+ //
+ if (Sock->Parent) {
+
+ NetListRemoveEntry (&(Sock->ConnectionList));
+ (Sock->Parent->ConnCnt)--;
+
+ SOCK_DEBUG_WARN (("SockDestory: Delete a unaccepted socket from parent"
+ "now conncnt is %d\n", Sock->Parent->ConnCnt));
+
+ Sock->Parent = NULL;
+ }
+
+ //
+ // Set the protocol guid and driver binding handle
+ // in the light of Sock->SockType
+ //
+ ProtocolGuid = &gEfiTcp4ProtocolGuid;
+
+ //
+ // Retrieve the protocol installed on this sock
+ //
+ Status = gBS->OpenProtocol (
+ Sock->SockHandle,
+ ProtocolGuid,
+ &SockProtocol,
+ Sock->DriverBinding,
+ Sock->SockHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+
+ if (EFI_ERROR (Status)) {
+
+ SOCK_DEBUG_ERROR (("SockDestroy: Open protocol installed "
+ "on socket failed with %r\n", Status));
+
+ goto FreeSock;
+ }
+
+ //
+ // Uninstall the protocol installed on this sock
+ // in the light of Sock->SockType
+ //
+ gBS->UninstallMultipleProtocolInterfaces (
+ Sock->SockHandle,
+ ProtocolGuid,
+ SockProtocol,
+ NULL
+ );
+
+FreeSock:
+ NetFreePool (Sock);
+ return ;
+}
+
+
+/**
+ Flush the socket.
+
+ @param Sock Pointer to the socket.
+
+ @return None.
+
+**/
+VOID
+SockConnFlush (
+ IN SOCKET *Sock
+ )
+{
+ SOCKET *Child;
+
+ ASSERT (Sock);
+
+ //
+ // Clear the flag in this socket
+ //
+ Sock->Flag = 0;
+
+ //
+ // Flush the SndBuffer and RcvBuffer of Sock
+ //
+ NetbufQueFlush (Sock->SndBuffer.DataQueue);
+ NetbufQueFlush (Sock->RcvBuffer.DataQueue);
+
+ //
+ // Signal the pending token
+ //
+ if (Sock->ConnectionToken != NULL) {
+ SIGNAL_TOKEN (Sock->ConnectionToken, Sock->SockError);
+ Sock->ConnectionToken = NULL;
+ }
+
+ if (Sock->CloseToken != NULL) {
+ SIGNAL_TOKEN (Sock->CloseToken, Sock->SockError);
+ Sock->CloseToken = NULL;
+ }
+
+ SockFlushPendingToken (Sock, &(Sock->ListenTokenList));
+ SockFlushPendingToken (Sock, &(Sock->RcvTokenList));
+ SockFlushPendingToken (Sock, &(Sock->SndTokenList));
+ SockFlushPendingToken (Sock, &(Sock->ProcessingSndTokenList));
+
+ //
+ // Destroy the pending connection, if it is a listening socket
+ //
+ if (SOCK_IS_LISTENING (Sock)) {
+ while (!NetListIsEmpty (&Sock->ConnectionList)) {
+ Child = NET_LIST_HEAD (
+ &Sock->ConnectionList,
+ SOCKET,
+ ConnectionList
+ );
+
+ SockDestroyChild (Child);
+ }
+
+ Sock->ConnCnt = 0;
+ }
+
+ return ;
+}
+
+
+/**
+ Set the state of the socket.
+
+ @param Sock Pointer to the socket.
+ @param State The new state to be set.
+
+ @return None.
+
+**/
+VOID
+SockSetState (
+ IN SOCKET *Sock,
+ IN SOCK_STATE State
+ )
+{
+ Sock->State = State;
+}
+
+
+/**
+ Clone a new socket including its associated protocol control block.
+
+ @param Sock Pointer to the socket to be cloned.
+
+ @retval * Pointer to the newly cloned socket. If NULL, error
+ condition occurred.
+
+**/
+SOCKET *
+SockClone (
+ IN SOCKET *Sock
+ )
+{
+ SOCKET *ClonedSock;
+ SOCK_INIT_DATA InitData;
+
+ InitData.BackLog = Sock->BackLog;
+ InitData.Parent = Sock;
+ InitData.State = Sock->State;
+ InitData.ProtoHandler = Sock->ProtoHandler;
+ InitData.Type = Sock->Type;
+ InitData.RcvBufferSize = Sock->RcvBuffer.HighWater;
+ InitData.SndBufferSize = Sock->SndBuffer.HighWater;
+ InitData.DriverBinding = Sock->DriverBinding;
+ InitData.Protocol = &(Sock->NetProtocol);
+
+ ClonedSock = SockCreate (&InitData);
+
+ if (NULL == ClonedSock) {
+ SOCK_DEBUG_ERROR (("SockClone: no resource to create a cloned sock\n"));
+ return NULL;
+ }
+
+ NetCopyMem (
+ ClonedSock->ProtoReserved,
+ Sock->ProtoReserved,
+ PROTO_RESERVED_LEN
+ );
+
+ SockSetState (ClonedSock, SO_CONNECTING);
+ ClonedSock->ConfigureState = Sock->ConfigureState;
+
+ return ClonedSock;
+}
+
+
+/**
+ Called by the low layer protocol to indicate the socket
+ a connection is established. This function just changes
+ the socket's state to SO_CONNECTED and signals the token
+ used for connection establishment.
+
+ @param Sock Pointer to the socket associated with the
+ established connection.
+
+ @return None.
+
+**/
+VOID
+SockConnEstablished (
+ IN SOCKET *Sock
+ )
+{
+
+ ASSERT (SO_CONNECTING == Sock->State);
+
+ SockSetState (Sock, SO_CONNECTED);
+
+ if (NULL == Sock->Parent) {
+ SockWakeConnToken (Sock);
+ } else {
+ SockWakeListenToken (Sock);
+ }
+
+ return ;
+}
+
+
+/**
+ Called by the low layer protocol to indicate the connection
+ is closed. This function flushes the socket, sets the state
+ to SO_CLOSED and signals the close token.
+
+ @param Sock Pointer to the socket associated with the closed
+ connection.
+
+ @return None.
+
+**/
+VOID
+SockConnClosed (
+ IN SOCKET *Sock
+ )
+{
+ if (Sock->CloseToken) {
+ SIGNAL_TOKEN (Sock->CloseToken, EFI_SUCCESS);
+ Sock->CloseToken = NULL;
+ }
+
+ SockConnFlush (Sock);
+ SockSetState (Sock, SO_CLOSED);
+
+ if (Sock->Parent != NULL) {
+ SockDestroyChild (Sock);
+ }
+
+}
+
+
+/**
+ Called by low layer protocol to indicate that some
+ data is sent or processed. This function trims the
+ sent data in the socket send buffer, signals the
+ data token if proper
+
+ @param Sock Pointer to the socket.
+ @param Count The length of the data processed or sent, in bytes.
+
+ @return None.
+
+**/
+VOID
+SockDataSent (
+ IN SOCKET *Sock,
+ IN UINT32 Count
+ )
+{
+ SOCK_TOKEN *SockToken;
+ SOCK_COMPLETION_TOKEN *SndToken;
+
+ ASSERT (!NetListIsEmpty (&Sock->ProcessingSndTokenList));
+ ASSERT (Count <= (Sock->SndBuffer.DataQueue)->BufSize);
+
+ NetbufQueTrim (Sock->SndBuffer.DataQueue, Count);
+
+ //
+ // To check if we can signal some snd token in this socket
+ //
+ while (Count > 0) {
+ SockToken = NET_LIST_HEAD (
+ &(Sock->ProcessingSndTokenList),
+ SOCK_TOKEN,
+ TokenList
+ );
+
+ SndToken = SockToken->Token;
+
+ if (SockToken->RemainDataLen <= Count) {
+
+ NetListRemoveEntry (&(SockToken->TokenList));
+ SIGNAL_TOKEN (SndToken, EFI_SUCCESS);
+ Count -= SockToken->RemainDataLen;
+ NetFreePool (SockToken);
+ } else {
+
+ SockToken->RemainDataLen -= Count;
+ Count = 0;
+ }
+ }
+
+ //
+ // to judge if we can process some send token in
+ // Sock->SndTokenList, if so process those send token
+ //
+ SockProcessSndToken (Sock);
+ return ;
+}
+
+
+/**
+ Called by the low layer protocol to copy some data in socket send
+ buffer starting from the specific offset to a buffer provided by
+ the caller.
+
+ @param Sock Pointer to the socket.
+ @param Offset The start point of the data to be copied.
+ @param Len The length of the data to be copied.
+ @param Dest Pointer to the destination to copy the data.
+
+ @return The data size copied.
+
+**/
+UINT32
+SockGetDataToSend (
+ IN SOCKET *Sock,
+ IN UINT32 Offset,
+ IN UINT32 Len,
+ IN UINT8 *Dest
+ )
+{
+ ASSERT (Sock && SOCK_STREAM == Sock->Type);
+
+ return NetbufQueCopy (
+ Sock->SndBuffer.DataQueue,
+ Offset,
+ Len,
+ Dest
+ );
+}
+
+
+/**
+ Called by the low layer protocol to deliver received data
+ to socket layer. This function will append the data to the
+ socket receive buffer, set ther urgent data length and then
+ check if any receive token can be signaled.
+
+ @param Sock Pointer to the socket.
+ @param NetBuffer Pointer to the buffer that contains the received
+ data.
+ @param UrgLen The length of the urgent data in the received data.
+
+ @return None.
+
+**/
+VOID
+SockDataRcvd (
+ IN SOCKET *Sock,
+ IN NET_BUF *NetBuffer,
+ IN UINT32 UrgLen
+ )
+{
+ ASSERT (Sock && Sock->RcvBuffer.DataQueue &&
+ UrgLen <= NetBuffer->TotalSize);
+
+ NET_GET_REF (NetBuffer);
+
+ ((TCP_RSV_DATA *) (NetBuffer->ProtoData))->UrgLen = UrgLen;
+
+ NetbufQueAppend (Sock->RcvBuffer.DataQueue, NetBuffer);
+
+ SockWakeRcvToken (Sock);
+ return ;
+}
+
+
+/**
+ Get the length of the free space of the specific socket buffer.
+
+ @param Sock Pointer to the socket.
+ @param Which Flag to indicate which socket buffer to check,
+ either send buffer or receive buffer.
+
+ @return The length of the free space, in bytes.
+
+**/
+UINT32
+SockGetFreeSpace (
+ IN SOCKET *Sock,
+ IN UINT32 Which
+ )
+{
+ UINT32 BufferCC;
+ SOCK_BUFFER *SockBuffer;
+
+ ASSERT (Sock && ((SOCK_SND_BUF == Which) || (SOCK_RCV_BUF == Which)));
+
+ if (SOCK_SND_BUF == Which) {
+ SockBuffer = &(Sock->SndBuffer);
+ } else {
+ SockBuffer = &(Sock->RcvBuffer);
+ }
+
+ BufferCC = (SockBuffer->DataQueue)->BufSize;
+
+ if (BufferCC >= SockBuffer->HighWater) {
+
+ return 0;
+ }
+
+ return SockBuffer->HighWater - BufferCC;
+}
+
+
+/**
+ Signal the receive token with the specific error or
+ set socket error code after error is received.
+
+ @param Sock Pointer to the socket.
+ @param Error The error code received.
+
+ @return None.
+
+**/
+VOID
+SockRcvdErr (
+ IN SOCKET *Sock,
+ IN EFI_STATUS Error
+ )
+{
+ SOCK_TOKEN *SockToken;
+
+ if (!NetListIsEmpty (&Sock->RcvTokenList)) {
+
+ SockToken = NET_LIST_HEAD (
+ &Sock->RcvTokenList,
+ SOCK_TOKEN,
+ TokenList
+ );
+
+ NetListRemoveEntry (&SockToken->TokenList);
+
+ SIGNAL_TOKEN (SockToken->Token, Error);
+
+ NetFreePool (SockToken);
+ } else {
+
+ SOCK_ERROR (Sock, Error);
+ }
+}
+
+
+/**
+ Called by the low layer protocol to indicate that there
+ will be no more data from the communication peer. This
+ function set the socket's state to SO_NO_MORE_DATA and
+ signal all queued IO tokens with the error status
+ EFI_CONNECTION_FIN.
+
+ @param Sock Pointer to the socket.
+
+ @return None.
+
+**/
+VOID
+SockNoMoreData (
+ IN SOCKET *Sock
+ )
+{
+ EFI_STATUS Err;
+
+ SOCK_NO_MORE_DATA (Sock);
+
+ if (!NetListIsEmpty (&Sock->RcvTokenList)) {
+
+ ASSERT (0 == GET_RCV_DATASIZE (Sock));
+
+ Err = Sock->SockError;
+
+ SOCK_ERROR (Sock, EFI_CONNECTION_FIN);
+
+ SockFlushPendingToken (Sock, &Sock->RcvTokenList);
+
+ SOCK_ERROR (Sock, Err);
+
+ }
+
+}
+
+
+/**
+ Get the first buffer block in the specific socket buffer.
+
+ @param Sockbuf Pointer to the socket buffer.
+
+ @return Pointer to the first buffer in the queue. NULL if the queue is empty.
+
+**/
+NET_BUF *
+SockBufFirst (
+ IN SOCK_BUFFER *Sockbuf
+ )
+{
+ NET_LIST_ENTRY *NetbufList;
+
+ NetbufList = &(Sockbuf->DataQueue->BufList);
+
+ if (NetListIsEmpty (NetbufList)) {
+ return NULL;
+ }
+
+ return NET_LIST_HEAD (NetbufList, NET_BUF, List);
+}
+
+
+/**
+ Get the next buffer block in the specific socket buffer.
+
+ @param Sockbuf Pointer to the socket buffer.
+ @param SockEntry Pointer to the buffer block prior to the required
+ one.
+
+ @return Pointer to the buffer block next to SockEntry. NULL if SockEntry is the tail or head entry.
+
+**/
+NET_BUF *
+SockBufNext (
+ IN SOCK_BUFFER *Sockbuf,
+ IN NET_BUF *SockEntry
+ )
+{
+ NET_LIST_ENTRY *NetbufList;
+
+ NetbufList = &(Sockbuf->DataQueue->BufList);
+
+ if ((SockEntry->List.ForwardLink == NetbufList) ||
+ (SockEntry->List.BackLink == &SockEntry->List) ||
+ (SockEntry->List.ForwardLink == &SockEntry->List)
+ ) {
+
+ return NULL;
+ }
+
+ return NET_LIST_USER_STRUCT (SockEntry->List.ForwardLink, NET_BUF, List);
+}
diff --git a/MdeModulePkg/Universal/Network/Tcp4Dxe/SockImpl.h b/MdeModulePkg/Universal/Network/Tcp4Dxe/SockImpl.h
new file mode 100644
index 0000000000..69a1ac6222
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/Tcp4Dxe/SockImpl.h
@@ -0,0 +1,84 @@
+/** @file
+
+Copyright (c) 2005 - 2006, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+
+ SockImpl.h
+
+Abstract:
+
+
+**/
+
+#ifndef _SOCK_IMPL_H_
+#define _SOCK_IMPL_H_
+
+#include "Socket.h"
+
+#define SOCK_DEBUG_ERROR(PrintArg) NET_DEBUG_ERROR("Sock", PrintArg)
+#define SOCK_DEBUG_WARN(PrintArg) NET_DEBUG_WARNING("Sock", PrintArg)
+#define SOCK_DEBUG_TRACE(PrintArg) NET_DEBUG_TRACE("Sock", PrintArg)
+
+#define SOCK_TRIM_RCV_BUFF(Sock, Len) \
+ (NetbufQueTrim ((Sock)->RcvBuffer.DataQueue, (Len)))
+
+#define SIGNAL_TOKEN(Token, TokenStatus) \
+ do { \
+ (Token)->Status = (TokenStatus); \
+ gBS->SignalEvent ((Token)->Event); \
+ } while (0)
+
+#define SOCK_HEADER_SPACE (60 + 60 + 72)
+
+//
+// Supporting function for both SockImpl and SockInterface
+//
+VOID
+SockFreeFoo (
+ IN EFI_EVENT Event
+ );
+
+EFI_STATUS
+SockProcessTcpSndData (
+ IN SOCKET *Sock,
+ IN VOID *TcpTxData
+ );
+
+VOID
+SockSetTcpRxData (
+ IN SOCKET *Sock,
+ IN VOID *TcpRxData,
+ IN UINT32 RcvdBytes,
+ IN BOOLEAN IsOOB
+ );
+
+UINT32
+SockProcessRcvToken (
+ IN SOCKET *Sock,
+ IN SOCK_IO_TOKEN *RcvToken
+ );
+
+VOID
+SockConnFlush (
+ IN SOCKET *Sock
+ );
+
+SOCKET *
+SockCreate (
+ IN SOCK_INIT_DATA *SockInitData
+ );
+
+VOID
+SockDestroy (
+ IN SOCKET *Sock
+ );
+
+#endif
diff --git a/MdeModulePkg/Universal/Network/Tcp4Dxe/SockInterface.c b/MdeModulePkg/Universal/Network/Tcp4Dxe/SockInterface.c
new file mode 100644
index 0000000000..0b71996e14
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/Tcp4Dxe/SockInterface.c
@@ -0,0 +1,980 @@
+/** @file
+
+Copyright (c) 2005 - 2006, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+
+ SockInterface.c
+
+Abstract:
+
+
+**/
+
+
+#include "SockImpl.h"
+
+
+/**
+ Check whether the Event is in the List.
+
+ @param List Pointer to the token list to be searched.
+ @param Event The event to be checked.
+
+ @retval BOOLEAN If TRUE, the specific Event exists in the List. If
+ FALSE, the specific Event is not in the List.
+
+**/
+STATIC
+BOOLEAN
+SockTokenExistedInList (
+ IN NET_LIST_ENTRY *List,
+ IN EFI_EVENT Event
+ )
+{
+ NET_LIST_ENTRY *ListEntry;
+ SOCK_TOKEN *SockToken;
+
+ NET_LIST_FOR_EACH (ListEntry, List) {
+ SockToken = NET_LIST_USER_STRUCT (
+ ListEntry,
+ SOCK_TOKEN,
+ TokenList
+ );
+
+ if (Event == SockToken->Token->Event) {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+
+/**
+ Call SockTokenExistedInList() to check whether the Event is
+ in the related socket's lists.
+
+ @param Sock Pointer to the instance's socket.
+ @param Event The event to be checked.
+
+ @return The specific Event exists in one of socket's lists or not.
+
+**/
+BOOLEAN
+SockTokenExisted (
+ IN SOCKET *Sock,
+ IN EFI_EVENT Event
+ )
+{
+
+ if (SockTokenExistedInList (&Sock->SndTokenList, Event) ||
+ SockTokenExistedInList (&Sock->ProcessingSndTokenList, Event) ||
+ SockTokenExistedInList (&Sock->RcvTokenList, Event) ||
+ SockTokenExistedInList (&Sock->ListenTokenList, Event)
+ ) {
+
+ return TRUE;
+ }
+
+ if ((Sock->ConnectionToken != NULL) &&
+ (Sock->ConnectionToken->Event == Event)) {
+
+ return TRUE;
+ }
+
+ if ((Sock->CloseToken != NULL) && (Sock->CloseToken->Event == Event)) {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+/**
+ Buffer a token into the specific list of socket Sock.
+
+ @param Sock Pointer to the instance's socket.
+ @param List Pointer to the list to store the token.
+ @param Token Pointer to the token to be buffered.
+ @param DataLen The data length of the buffer contained in Token.
+
+ @return Pointer to the token that wraps Token. If NULL, error condition occurred.
+
+**/
+SOCK_TOKEN *
+SockBufferToken (
+ IN SOCKET *Sock,
+ IN NET_LIST_ENTRY *List,
+ IN VOID *Token,
+ IN UINT32 DataLen
+ )
+{
+ SOCK_TOKEN *SockToken;
+
+ SockToken = NetAllocatePool (sizeof (SOCK_TOKEN));
+ if (NULL == SockToken) {
+
+ SOCK_DEBUG_ERROR (("SockBufferIOToken: No Memory "
+ "to allocate SockToken\n"));
+
+ return NULL;
+ }
+
+ SockToken->Sock = Sock;
+ SockToken->Token = (SOCK_COMPLETION_TOKEN *) Token;
+ SockToken->RemainDataLen = DataLen;
+ NetListInsertTail (List, &SockToken->TokenList);
+
+ return SockToken;
+}
+
+
+/**
+ Destory the socket Sock and its associated protocol control block.
+
+ @param Sock The socket to be destroyed.
+
+ @retval EFI_SUCCESS The socket Sock is destroyed successfully.
+ @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket.
+
+**/
+EFI_STATUS
+SockDestroyChild (
+ IN SOCKET *Sock
+ )
+{
+ EFI_STATUS Status;
+
+ ASSERT (Sock && Sock->ProtoHandler);
+
+ if (Sock->IsDestroyed) {
+ return EFI_SUCCESS;
+ }
+
+ Sock->IsDestroyed = TRUE;
+
+ Status = NET_TRYLOCK (&(Sock->Lock));
+ if (EFI_ERROR (Status)) {
+
+ SOCK_DEBUG_ERROR (("SockDestroyChild: Get the lock to "
+ "access socket failed with %r\n", Status));
+
+ return EFI_ACCESS_DENIED;
+ }
+
+ //
+ // force protocol layer to detach the PCB
+ //
+ Status = Sock->ProtoHandler (Sock, SOCK_DETACH, NULL);
+
+ if (EFI_ERROR (Status)) {
+
+ SOCK_DEBUG_ERROR (("SockDestroyChild: Protocol detach socket"
+ " failed with %r\n", Status));
+
+ Sock->IsDestroyed = FALSE;
+ } else if (SOCK_IS_CONFIGURED (Sock)) {
+
+ SockConnFlush (Sock);
+ SockSetState (Sock, SO_CLOSED);
+
+ Sock->ConfigureState = SO_UNCONFIGURED;
+ }
+
+ NET_UNLOCK (&(Sock->Lock));
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ SockDestroy (Sock);
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Create a socket and its associated protocol control block
+ with the intial data SockInitData and protocol specific
+ data ProtoData.
+
+ @param SockInitData Inital data to setting the socket.
+ @param ProtoData Pointer to the protocol specific data.
+ @param Len Length of the protocol specific data.
+
+ @return Pointer to the newly created socket. If NULL, error condition occured.
+
+**/
+SOCKET *
+SockCreateChild (
+ IN SOCK_INIT_DATA *SockInitData,
+ IN VOID *ProtoData,
+ IN UINT32 Len
+ )
+{
+ SOCKET *Sock;
+ EFI_STATUS Status;
+
+ ASSERT (ProtoData && (Len <= PROTO_RESERVED_LEN));
+
+ //
+ // create a new socket
+ //
+ Sock = SockCreate (SockInitData);
+ if (NULL == Sock) {
+
+ SOCK_DEBUG_ERROR (("SockCreateChild: No resource to "
+ "create a new socket\n"));
+
+ return NULL;
+ }
+
+ //
+ // Open the
+ //
+
+ //
+ // copy the protodata into socket
+ //
+ NetCopyMem (Sock->ProtoReserved, ProtoData, Len);
+
+ Status = NET_TRYLOCK (&(Sock->Lock));
+ if (EFI_ERROR (Status)) {
+
+ SOCK_DEBUG_ERROR (("SockCreateChild: Get the lock to "
+ "access socket failed with %r\n", Status));
+
+ SockDestroy (Sock);
+ return NULL;
+ }
+ //
+ // inform the protocol layer to attach the socket
+ // with a new protocol control block
+ //
+ Status = Sock->ProtoHandler (Sock, SOCK_ATTACH, NULL);
+ if (EFI_ERROR (Status)) {
+
+ SOCK_DEBUG_ERROR (("SockCreateChild: Protocol failed to"
+ " attach a socket with %r\n", Status));
+
+ SockDestroy (Sock);
+ Sock = NULL;
+ }
+
+ NET_UNLOCK (&(Sock->Lock));
+ return Sock;
+}
+
+
+/**
+ Configure the specific socket Sock using configuration data
+ ConfigData.
+
+ @param Sock Pointer to the socket to be configured.
+ @param ConfigData Pointer to the configuration data.
+
+ @retval EFI_SUCCESS The socket is configured successfully.
+ @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket or the
+ socket is already configured.
+
+**/
+EFI_STATUS
+SockConfigure (
+ IN SOCKET *Sock,
+ IN VOID *ConfigData
+ )
+{
+ EFI_STATUS Status;
+
+ Status = NET_TRYLOCK (&(Sock->Lock));
+ if (EFI_ERROR (Status)) {
+
+ SOCK_DEBUG_ERROR (("SockConfigure: Get the access for "
+ "socket failed with %r", Status));
+
+ return EFI_ACCESS_DENIED;
+ }
+
+ if (SOCK_IS_CONFIGURED (Sock)) {
+ Status = EFI_ACCESS_DENIED;
+ goto OnExit;
+ }
+
+ ASSERT (Sock->State == SO_CLOSED);
+
+ Status = Sock->ProtoHandler (Sock, SOCK_CONFIGURE, ConfigData);
+
+OnExit:
+ NET_UNLOCK (&(Sock->Lock));
+
+ return Status;
+}
+
+
+/**
+ Initiate a connection establishment process.
+
+ @param Sock Pointer to the socket to initiate the initate the
+ connection.
+ @param Token Pointer to the token used for the connection
+ operation.
+
+ @retval EFI_SUCCESS The connection is initialized successfully.
+ @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket, or the
+ socket is closed, or the socket is not configured to
+ be an active one, or the token is already in one of
+ this socket's lists.
+ @retval EFI_NO_MAPPING The IP address configuration operation is not
+ finished.
+ @retval EFI_NOT_STARTED The socket is not configured.
+
+**/
+EFI_STATUS
+SockConnect (
+ IN SOCKET *Sock,
+ IN VOID *Token
+ )
+{
+ EFI_STATUS Status;
+ EFI_EVENT Event;
+
+ Status = NET_TRYLOCK (&(Sock->Lock));
+ if (EFI_ERROR (Status)) {
+
+ SOCK_DEBUG_ERROR (("SockConnect: Get the access for "
+ "socket failed with %r", Status));
+
+ return EFI_ACCESS_DENIED;
+ }
+
+ if (SOCK_IS_NO_MAPPING (Sock)) {
+ Status = EFI_NO_MAPPING;
+ goto OnExit;
+ }
+
+ if (SOCK_IS_UNCONFIGURED (Sock)) {
+
+ Status = EFI_NOT_STARTED;
+ goto OnExit;
+ }
+
+ if (!SOCK_IS_CLOSED (Sock) || !SOCK_IS_CONFIGURED_ACTIVE (Sock)) {
+
+ Status = EFI_ACCESS_DENIED;
+ goto OnExit;
+ }
+
+ Event = ((SOCK_COMPLETION_TOKEN *) Token)->Event;
+
+ if (SockTokenExisted (Sock, Event)) {
+
+ Status = EFI_ACCESS_DENIED;
+ goto OnExit;
+ }
+
+ Sock->ConnectionToken = (SOCK_COMPLETION_TOKEN *) Token;
+ SockSetState (Sock, SO_CONNECTING);
+ Status = Sock->ProtoHandler (Sock, SOCK_CONNECT, NULL);
+
+OnExit:
+ NET_UNLOCK (&(Sock->Lock));
+ return Status;
+}
+
+
+/**
+ Issue a listen token to get an existed connected network instance
+ or wait for a connection if there is none.
+
+ @param Sock Pointer to the socket to accept connections.
+ @param Token The token to accept a connection.
+
+ @retval EFI_SUCCESS Either a connection is accpeted or the Token is
+ buffered for further acception.
+ @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket, or the
+ socket is closed, or the socket is not configured to
+ be a passive one, or the token is already in one of
+ this socket's lists.
+ @retval EFI_NO_MAPPING The IP address configuration operation is not
+ finished.
+ @retval EFI_NOT_STARTED The socket is not configured.
+ @retval EFI_OUT_OF_RESOURCE Failed to buffer the Token due to memory limit.
+
+**/
+EFI_STATUS
+SockAccept (
+ IN SOCKET *Sock,
+ IN VOID *Token
+ )
+{
+ EFI_TCP4_LISTEN_TOKEN *ListenToken;
+ NET_LIST_ENTRY *ListEntry;
+ EFI_STATUS Status;
+ SOCKET *Socket;
+ EFI_EVENT Event;
+
+ ASSERT (SOCK_STREAM == Sock->Type);
+
+ Status = NET_TRYLOCK (&(Sock->Lock));
+ if (EFI_ERROR (Status)) {
+
+ SOCK_DEBUG_ERROR (("SockAccept: Get the access for socket"
+ " failed with %r", Status));
+
+ return EFI_ACCESS_DENIED;
+ }
+
+ if (SOCK_IS_NO_MAPPING (Sock)) {
+ Status = EFI_NO_MAPPING;
+ goto Exit;
+ }
+
+ if (SOCK_IS_UNCONFIGURED (Sock)) {
+
+ Status = EFI_NOT_STARTED;
+ goto Exit;
+ }
+
+ if (!SOCK_IS_LISTENING (Sock)) {
+
+ Status = EFI_ACCESS_DENIED;
+ goto Exit;
+ }
+
+ Event = ((SOCK_COMPLETION_TOKEN *) Token)->Event;
+
+ if (SockTokenExisted (Sock, Event)) {
+
+ Status = EFI_ACCESS_DENIED;
+ goto Exit;
+ }
+
+ ListenToken = (EFI_TCP4_LISTEN_TOKEN *) Token;
+
+ //
+ // Check if a connection has already in this Sock->ConnectionList
+ //
+ NET_LIST_FOR_EACH (ListEntry, &Sock->ConnectionList) {
+
+ Socket = NET_LIST_USER_STRUCT (ListEntry, SOCKET, ConnectionList);
+
+ if (SOCK_IS_CONNECTED (Socket)) {
+ ListenToken->NewChildHandle = Socket->SockHandle;
+ SIGNAL_TOKEN (&(ListenToken->CompletionToken), EFI_SUCCESS);
+
+ NetListRemoveEntry (ListEntry);
+
+ ASSERT (Socket->Parent);
+
+ Socket->Parent->ConnCnt--;
+
+ SOCK_DEBUG_WARN (("SockAccept: Accept a socket,"
+ "now conncount is %d", Socket->Parent->ConnCnt)
+ );
+ Socket->Parent = NULL;
+
+ goto Exit;
+ }
+ }
+
+ //
+ // Buffer this token for latter incoming connection request
+ //
+ if (NULL == SockBufferToken (Sock, &(Sock->ListenTokenList), Token, 0)) {
+
+ Status = EFI_OUT_OF_RESOURCES;
+ }
+
+Exit:
+ NET_UNLOCK (&(Sock->Lock));
+
+ return Status;
+}
+
+
+/**
+ Issue a token with data to the socket to send out.
+
+ @param Sock Pointer to the socket to process the token with
+ data.
+ @param Token The token with data that needs to send out.
+
+ @retval EFI_SUCCESS The token is processed successfully.
+ @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket, or the
+ socket is closed, or the socket is not in a
+ synchronized state , or the token is already in one
+ of this socket's lists.
+ @retval EFI_NO_MAPPING The IP address configuration operation is not
+ finished.
+ @retval EFI_NOT_STARTED The socket is not configured.
+ @retval EFI_OUT_OF_RESOURCE Failed to buffer the token due to memory limit.
+
+**/
+EFI_STATUS
+SockSend (
+ IN SOCKET *Sock,
+ IN VOID *Token
+ )
+{
+ SOCK_IO_TOKEN *SndToken;
+ EFI_EVENT Event;
+ UINT32 FreeSpace;
+ EFI_TCP4_TRANSMIT_DATA *TxData;
+ EFI_STATUS Status;
+ SOCK_TOKEN *SockToken;
+ UINT32 DataLen;
+
+ ASSERT (SOCK_STREAM == Sock->Type);
+
+ Status = NET_TRYLOCK (&(Sock->Lock));
+ if (EFI_ERROR (Status)) {
+
+ SOCK_DEBUG_ERROR (("SockSend: Get the access for socket"
+ " failed with %r", Status));
+
+ return EFI_ACCESS_DENIED;
+ }
+
+ if (SOCK_IS_NO_MAPPING (Sock)) {
+ Status = EFI_NO_MAPPING;
+ goto Exit;
+ }
+
+ SndToken = (SOCK_IO_TOKEN *) Token;
+ TxData = (EFI_TCP4_TRANSMIT_DATA *) SndToken->Packet.TxData;
+
+ if (SOCK_IS_UNCONFIGURED (Sock)) {
+ Status = EFI_NOT_STARTED;
+ goto Exit;
+ }
+
+ if (!(SOCK_IS_CONNECTING (Sock) || SOCK_IS_CONNECTED (Sock))) {
+
+ Status = EFI_ACCESS_DENIED;
+ goto Exit;
+ }
+
+ //
+ // check if a token is already in the token buffer
+ //
+ Event = SndToken->Token.Event;
+
+ if (SockTokenExisted (Sock, Event)) {
+ Status = EFI_ACCESS_DENIED;
+ goto Exit;
+ }
+
+ DataLen = TxData->DataLength;
+
+ //
+ // process this sending token now or buffer it only?
+ //
+ FreeSpace = SockGetFreeSpace (Sock, SOCK_SND_BUF);
+
+ if ((FreeSpace < Sock->SndBuffer.LowWater) || !SOCK_IS_CONNECTED (Sock)) {
+
+ SockToken = SockBufferToken (
+ Sock,
+ &Sock->SndTokenList,
+ SndToken,
+ DataLen
+ );
+
+ if (NULL == SockToken) {
+ Status = EFI_OUT_OF_RESOURCES;
+ }
+ } else {
+
+ SockToken = SockBufferToken (
+ Sock,
+ &Sock->ProcessingSndTokenList,
+ SndToken,
+ DataLen
+ );
+
+ if (NULL == SockToken) {
+ SOCK_DEBUG_ERROR (("SockSend: Failed to buffer IO token into"
+ " socket processing SndToken List\n", Status));
+
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Exit;
+ }
+
+ Status = SockProcessTcpSndData (Sock, TxData);
+
+ if (EFI_ERROR (Status)) {
+ SOCK_DEBUG_ERROR (("SockSend: Failed to process "
+ "Snd Data\n", Status));
+
+ NetListRemoveEntry (&(SockToken->TokenList));
+ NetFreePool (SockToken);
+ }
+ }
+
+Exit:
+ NET_UNLOCK (&(Sock->Lock));
+ return Status;
+}
+
+
+/**
+ Issue a token to get data from the socket.
+
+ @param Sock Pointer to the socket to get data from.
+ @param Token The token to store the received data from the
+ socket.
+
+ @retval EFI_SUCCESS The token is processed successfully.
+ @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket, or the
+ socket is closed, or the socket is not in a
+ synchronized state , or the token is already in one
+ of this socket's lists.
+ @retval EFI_NO_MAPPING The IP address configuration operation is not
+ finished.
+ @retval EFI_NOT_STARTED The socket is not configured.
+ @retval EFI_CONNECTION_FIN The connection is closed and there is no more data.
+ @retval EFI_OUT_OF_RESOURCE Failed to buffer the token due to memory limit.
+
+**/
+EFI_STATUS
+SockRcv (
+ IN SOCKET *Sock,
+ IN VOID *Token
+ )
+{
+ SOCK_IO_TOKEN *RcvToken;
+ UINT32 RcvdBytes;
+ EFI_STATUS Status;
+ EFI_EVENT Event;
+
+ ASSERT (SOCK_STREAM == Sock->Type);
+
+ Status = NET_TRYLOCK (&(Sock->Lock));
+ if (EFI_ERROR (Status)) {
+
+ SOCK_DEBUG_ERROR (("SockRcv: Get the access for socket"
+ " failed with %r", Status));
+
+ return EFI_ACCESS_DENIED;
+ }
+
+ if (SOCK_IS_NO_MAPPING (Sock)) {
+
+ Status = EFI_NO_MAPPING;
+ goto Exit;
+ }
+
+ if (SOCK_IS_UNCONFIGURED (Sock)) {
+
+ Status = EFI_NOT_STARTED;
+ goto Exit;
+ }
+
+ if (!(SOCK_IS_CONNECTED (Sock) || SOCK_IS_CONNECTING (Sock))) {
+
+ Status = EFI_ACCESS_DENIED;
+ goto Exit;
+ }
+
+ RcvToken = (SOCK_IO_TOKEN *) Token;
+
+ //
+ // check if a token is already in the token buffer of this socket
+ //
+ Event = RcvToken->Token.Event;
+ if (SockTokenExisted (Sock, Event)) {
+ Status = EFI_ACCESS_DENIED;
+ goto Exit;
+ }
+
+ RcvToken = (SOCK_IO_TOKEN *) Token;
+ RcvdBytes = GET_RCV_DATASIZE (Sock);
+
+ //
+ // check whether an error has happened before
+ //
+ if (EFI_ABORTED != Sock->SockError) {
+
+ SIGNAL_TOKEN (&(RcvToken->Token), Sock->SockError);
+ Sock->SockError = EFI_ABORTED;
+ goto Exit;
+ }
+
+ //
+ // check whether can not receive and there is no any
+ // data buffered in Sock->RcvBuffer
+ //
+ if (SOCK_IS_NO_MORE_DATA (Sock) && (0 == RcvdBytes)) {
+
+ Status = EFI_CONNECTION_FIN;
+ goto Exit;
+ }
+
+ if (RcvdBytes != 0) {
+ Status = SockProcessRcvToken (Sock, RcvToken);
+
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+
+ Status = Sock->ProtoHandler (Sock, SOCK_CONSUMED, NULL);
+ } else {
+
+ if (NULL == SockBufferToken (Sock, &Sock->RcvTokenList, RcvToken, 0)) {
+ Status = EFI_OUT_OF_RESOURCES;
+ }
+ }
+
+Exit:
+ NET_UNLOCK (&(Sock->Lock));
+ return Status;
+}
+
+
+/**
+ Reset the socket and its associated protocol control block.
+
+ @param Sock Pointer to the socket to be flushed.
+
+ @retval EFI_SUCCESS The socket is flushed successfully.
+ @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket.
+
+**/
+EFI_STATUS
+SockFlush (
+ IN SOCKET *Sock
+ )
+{
+ EFI_STATUS Status;
+
+ ASSERT (SOCK_STREAM == Sock->Type);
+
+ Status = NET_TRYLOCK (&(Sock->Lock));
+ if (EFI_ERROR (Status)) {
+
+ SOCK_DEBUG_ERROR (("SockFlush: Get the access for socket"
+ " failed with %r", Status));
+
+ return EFI_ACCESS_DENIED;
+ }
+
+ if (!SOCK_IS_CONFIGURED (Sock)) {
+ goto Exit;
+ }
+
+ Status = Sock->ProtoHandler (Sock, SOCK_FLUSH, NULL);
+ if (EFI_ERROR (Status)) {
+
+ SOCK_DEBUG_ERROR (("SockFlush: Protocol failed handling"
+ " SOCK_FLUSH with %r", Status));
+
+ goto Exit;
+ }
+
+ SOCK_ERROR (Sock, EFI_ABORTED);
+ SockConnFlush (Sock);
+ SockSetState (Sock, SO_CLOSED);
+
+ Sock->ConfigureState = SO_UNCONFIGURED;
+
+Exit:
+ NET_UNLOCK (&(Sock->Lock));
+ return Status;
+}
+
+
+/**
+ Close or abort the socket associated connection.
+
+ @param Sock Pointer to the socket of the connection to close or
+ abort.
+ @param Token The token for close operation.
+ @param OnAbort TRUE for aborting the connection, FALSE to close it.
+
+ @retval EFI_SUCCESS The close or abort operation is initialized
+ successfully.
+ @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket, or the
+ socket is closed, or the socket is not in a
+ synchronized state , or the token is already in one
+ of this socket's lists.
+ @retval EFI_NO_MAPPING The IP address configuration operation is not
+ finished.
+ @retval EFI_NOT_STARTED The socket is not configured.
+
+**/
+EFI_STATUS
+SockClose (
+ IN SOCKET *Sock,
+ IN VOID *Token,
+ IN BOOLEAN OnAbort
+ )
+{
+ EFI_STATUS Status;
+ EFI_EVENT Event;
+
+ ASSERT (SOCK_STREAM == Sock->Type);
+
+ Status = NET_TRYLOCK (&(Sock->Lock));
+ if (EFI_ERROR (Status)) {
+ SOCK_DEBUG_ERROR (("SockClose: Get the access for socket"
+ " failed with %r", Status));
+
+ return EFI_ACCESS_DENIED;
+ }
+
+ if (SOCK_IS_NO_MAPPING (Sock)) {
+ Status = EFI_NO_MAPPING;
+ goto Exit;
+ }
+
+ if (SOCK_IS_UNCONFIGURED (Sock)) {
+ Status = EFI_NOT_STARTED;
+ goto Exit;
+ }
+
+ if (SOCK_IS_DISCONNECTING (Sock)) {
+ Status = EFI_ACCESS_DENIED;
+ goto Exit;
+ }
+
+ Event = ((SOCK_COMPLETION_TOKEN *) Token)->Event;
+
+ if (SockTokenExisted (Sock, Event)) {
+ Status = EFI_ACCESS_DENIED;
+ goto Exit;
+ }
+
+ Sock->CloseToken = Token;
+ SockSetState (Sock, SO_DISCONNECTING);
+
+ if (OnAbort) {
+ Status = Sock->ProtoHandler (Sock, SOCK_ABORT, NULL);
+ } else {
+ Status = Sock->ProtoHandler (Sock, SOCK_CLOSE, NULL);
+ }
+
+Exit:
+ NET_UNLOCK (&(Sock->Lock));
+ return Status;
+}
+
+
+/**
+ Get the mode data of the low layer protocol.
+
+ @param Sock Pointer to the socket to get mode data from.
+ @param Mode Pointer to the data to store the low layer mode
+ information.
+
+ @retval EFI_SUCCESS The mode data is got successfully.
+ @retval EFI_NOT_STARTED The socket is not configured.
+
+**/
+EFI_STATUS
+SockGetMode (
+ IN SOCKET *Sock,
+ IN VOID *Mode
+ )
+{
+ return Sock->ProtoHandler (Sock, SOCK_MODE, Mode);
+}
+
+
+/**
+ Configure the low level protocol to join a multicast group for
+ this socket's connection.
+
+ @param Sock Pointer to the socket of the connection to join the
+ specific multicast group.
+ @param GroupInfo Pointer to the multicast group info.
+
+ @retval EFI_SUCCESS The configuration is done successfully.
+ @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket.
+ @retval EFI_NOT_STARTED The socket is not configured.
+
+**/
+EFI_STATUS
+SockGroup (
+ IN SOCKET *Sock,
+ IN VOID *GroupInfo
+ )
+{
+ EFI_STATUS Status;
+
+ Status = NET_TRYLOCK (&(Sock->Lock));
+
+ if (EFI_ERROR (Status)) {
+
+ SOCK_DEBUG_ERROR (("SockGroup: Get the access for socket"
+ " failed with %r", Status));
+
+ return EFI_ACCESS_DENIED;
+ }
+
+ if (SOCK_IS_UNCONFIGURED (Sock)) {
+ Status = EFI_NOT_STARTED;
+ goto Exit;
+ }
+
+ Status = Sock->ProtoHandler (Sock, SOCK_GROUP, GroupInfo);
+
+Exit:
+ NET_UNLOCK (&(Sock->Lock));
+ return Status;
+}
+
+
+/**
+ Add or remove route information in IP route table associated
+ with this socket.
+
+ @param Sock Pointer to the socket associated with the IP route
+ table to operate on.
+ @param RouteInfo Pointer to the route information to be processed.
+
+ @retval EFI_SUCCESS The route table is updated successfully.
+ @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket.
+ @retval EFI_NO_MAPPING The IP address configuration operation is not
+ finished.
+ @retval EFI_NOT_STARTED The socket is not configured.
+
+**/
+EFI_STATUS
+SockRoute (
+ IN SOCKET *Sock,
+ IN VOID *RouteInfo
+ )
+{
+ EFI_STATUS Status;
+
+ Status = NET_TRYLOCK (&(Sock->Lock));
+ if (EFI_ERROR (Status)) {
+ SOCK_DEBUG_ERROR (("SockRoute: Get the access for socket"
+ " failed with %r", Status));
+
+ return EFI_ACCESS_DENIED;
+ }
+
+ if (SOCK_IS_NO_MAPPING (Sock)) {
+ Status = EFI_NO_MAPPING;
+ goto Exit;
+ }
+
+ if (SOCK_IS_UNCONFIGURED (Sock)) {
+ Status = EFI_NOT_STARTED;
+ goto Exit;
+ }
+
+ Status = Sock->ProtoHandler (Sock, SOCK_ROUTE, RouteInfo);
+
+Exit:
+ NET_UNLOCK (&(Sock->Lock));
+ return Status;
+}
diff --git a/MdeModulePkg/Universal/Network/Tcp4Dxe/Socket.h b/MdeModulePkg/Universal/Network/Tcp4Dxe/Socket.h
new file mode 100644
index 0000000000..f3978541fb
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/Tcp4Dxe/Socket.h
@@ -0,0 +1,518 @@
+/** @file
+
+Copyright (c) 2005 - 2006, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+
+ Socket.h
+
+Abstract:
+
+
+**/
+
+#ifndef _SOCKET_H_
+#define _SOCKET_H_
+
+#include <PiDxe.h>
+
+#include <Protocol/IP4.h>
+#include <Protocol/Tcp4.h>
+#include <Protocol/Udp4.h>
+
+#include <Library/NetLib.h>
+#include <Library/DebugLib.h>
+#include <Library/UefiRuntimeServicesTableLib.h>
+#include <Library/UefiDriverEntryPoint.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/BaseLib.h>
+#include <Library/UefiLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/BaseMemoryLib.h>
+
+#define SOCK_SND_BUF 0
+#define SOCK_RCV_BUF 1
+
+#define SOCK_BUFF_LOW_WATER 2 * 1024
+#define SOCK_RCV_BUFF_SIZE 8 * 1024
+#define SOCK_SND_BUFF_SIZE 8 * 1024
+#define SOCK_BACKLOG 5
+
+#define PROTO_RESERVED_LEN 20
+
+#define SO_NO_MORE_DATA 0x0001
+
+//
+//
+//
+// When a socket is created it enters into SO_UNCONFIGURED,
+// no actions can be taken on this socket, only after calling
+// SockConfigure. The state transition diagram of socket is
+// as following:
+//
+// SO_UNCONFIGURED --- SO_CONFIGURED --- SO_CONNECTING
+// ^ | |
+// | ---> SO_LISTENING |
+// | |
+// |------------------SO_DISCONNECTING<-- SO_CONNECTED
+//
+// A passive socket can only go into SO_LISTENING and
+// SO_UNCONFIGURED state. SO_XXXING state is a middle state
+// when a socket is undergoing a protocol procedure such
+// as requesting a TCP connection.
+//
+//
+//
+typedef enum {
+ SO_CLOSED = 0,
+ SO_LISTENING,
+ SO_CONNECTING,
+ SO_CONNECTED,
+ SO_DISCONNECTING
+} SOCK_STATE;
+
+typedef enum {
+ SO_UNCONFIGURED = 0,
+ SO_CONFIGURED_ACTIVE,
+ SO_CONFIGURED_PASSIVE,
+ SO_NO_MAPPING
+} SOCK_CONFIGURE_STATE;
+
+#define SOCK_NO_MORE_DATA(Sock) ((Sock)->Flag |= SO_NO_MORE_DATA)
+
+#define SOCK_IS_UNCONFIGURED(Sock) ((Sock)->ConfigureState == SO_UNCONFIGURED)
+
+#define SOCK_IS_CONFIGURED(Sock) \
+ (((Sock)->ConfigureState == SO_CONFIGURED_ACTIVE) || \
+ ((Sock)->ConfigureState == SO_CONFIGURED_PASSIVE))
+
+#define SOCK_IS_CONFIGURED_ACTIVE(Sock) \
+ ((Sock)->ConfigureState == SO_CONFIGURED_ACTIVE)
+
+#define SOCK_IS_CONNECTED_PASSIVE(Sock) \
+ ((Sock)->ConfigureState == SO_CONFIGURED_PASSIVE)
+
+#define SOCK_IS_NO_MAPPING(Sock) \
+ ((Sock)->ConfigureState == SO_NO_MAPPING)
+
+#define SOCK_IS_CLOSED(Sock) ((Sock)->State == SO_CLOSED)
+
+#define SOCK_IS_LISTENING(Sock) ((Sock)->State == SO_LISTENING)
+
+#define SOCK_IS_CONNECTING(Sock) ((Sock)->State == SO_CONNECTING)
+
+#define SOCK_IS_CONNECTED(Sock) ((Sock)->State == SO_CONNECTED)
+
+#define SOCK_IS_DISCONNECTING(Sock) ((Sock)->State == SO_DISCONNECTING)
+
+#define SOCK_IS_NO_MORE_DATA(Sock) (0 != ((Sock)->Flag & SO_NO_MORE_DATA))
+
+#define SOCK_SIGNATURE EFI_SIGNATURE_32 ('S', 'O', 'C', 'K')
+
+#define SOCK_FROM_THIS(a) CR ((a), SOCKET, NetProtocol, SOCK_SIGNATURE)
+
+#define SET_RCV_BUFFSIZE(Sock, Size) ((Sock)->RcvBuffer.HighWater = (Size))
+
+#define GET_RCV_BUFFSIZE(Sock) ((Sock)->RcvBuffer.HighWater)
+
+#define GET_RCV_DATASIZE(Sock) (((Sock)->RcvBuffer.DataQueue)->BufSize)
+
+#define SET_SND_BUFFSIZE(Sock, Size) ((Sock)->SndBuffer.HighWater = (Size))
+
+#define GET_SND_BUFFSIZE(Sock) ((Sock)->SndBuffer.HighWater)
+
+#define GET_SND_DATASIZE(Sock) (((Sock)->SndBuffer.DataQueue)->BufSize)
+
+#define SET_BACKLOG(Sock, Value) ((Sock)->BackLog = (Value))
+
+#define GET_BACKLOG(Sock) ((Sock)->BackLog)
+
+#define SOCK_ERROR(Sock, Error) ((Sock)->SockError = (Error))
+
+#define SND_BUF_HDR_LEN(Sock) \
+ ((SockBufFirst (&((Sock)->SndBuffer)))->TotalSize)
+
+#define RCV_BUF_HDR_LEN(Sock) \
+ ((SockBufFirst (&((Sock)->RcvBuffer)))->TotalSize)
+
+#define SOCK_FROM_TOKEN(Token) (((SOCK_TOKEN *) (Token))->Sock)
+
+#define PROTO_TOKEN_FORM_SOCK(SockToken, Type) \
+ ((Type *) (((SOCK_TOKEN *) (SockToken))->Token))
+
+typedef struct _SOCKET SOCKET;
+
+typedef struct _SOCK_COMPLETION_TOKEN {
+ EFI_EVENT Event;
+ EFI_STATUS Status;
+} SOCK_COMPLETION_TOKEN;
+
+typedef struct _SOCK_IO_TOKEN {
+ SOCK_COMPLETION_TOKEN Token;
+ union {
+ VOID *RxData;
+ VOID *TxData;
+ } Packet;
+} SOCK_IO_TOKEN;
+
+//
+// the request issued from socket layer to protocol layer
+//
+typedef enum {
+ SOCK_ATTACH, // attach current socket to a new PCB
+ SOCK_DETACH, // detach current socket from the PCB
+ SOCK_CONFIGURE, // configure attached PCB
+ SOCK_FLUSH, // flush attached PCB
+ SOCK_SND, // need protocol to send something
+ SOCK_SNDPUSH, // need protocol to send pushed data
+ SOCK_SNDURG, // need protocol to send urgent data
+ SOCK_CONSUMED, // application has retrieved data from socket
+ SOCK_CONNECT, // need to connect to a peer
+ SOCK_CLOSE, // need to close the protocol process
+ SOCK_ABORT, // need to reset the protocol process
+ SOCK_POLL, // need to poll to the protocol layer
+ SOCK_ROUTE, // need to add a route information
+ SOCK_MODE, // need to get the mode data of the protocol
+ SOCK_GROUP // need to join a mcast group
+} SOCK_REQUEST;
+
+//
+// the socket type
+//
+typedef enum {
+ SOCK_DGRAM, // this socket providing datagram service
+ SOCK_STREAM // this socket providing stream service
+} SOCK_TYPE;
+
+//
+// the handler of protocol for request from socket
+//
+typedef
+EFI_STATUS
+(*SOCK_PROTO_HANDLER) (
+ IN SOCKET * Socket, // the socket issuing the request to protocol
+ IN SOCK_REQUEST Request, // the request issued by socket
+ IN VOID *RequestData // the request related data
+ );
+
+//
+// the buffer structure of rcvd data and send data used by socket
+//
+typedef struct _SOCK_BUFFER {
+ UINT32 HighWater; // the buffersize upper limit of sock_buffer
+ UINT32 LowWater; // the low warter mark of sock_buffer
+ NET_BUF_QUEUE *DataQueue; // the queue to buffer data
+} SOCK_BUFFER;
+
+//
+// the initialize data for create a new socket
+//
+typedef struct _SOCK_INIT_DATA {
+ SOCK_TYPE Type;
+ SOCK_STATE State;
+
+ SOCKET *Parent; // the parent of this socket
+ UINT32 BackLog; // the connection limit for listening socket
+ UINT32 SndBufferSize; // the high warter mark of send buffer
+ UINT32 RcvBufferSize; // the high warter mark of receive buffer
+ VOID *Protocol; // the pointer to protocol function template
+ // wanted to install on socket
+
+ SOCK_PROTO_HANDLER ProtoHandler;
+
+ EFI_HANDLE DriverBinding; // the driver binding handle
+} SOCK_INIT_DATA;
+
+//
+// socket provided oprerations for low layer protocol
+//
+
+//
+// socket provided operations for user interface
+//
+VOID
+SockSetState (
+ IN SOCKET *Sock,
+ IN SOCK_STATE State
+ );
+
+//
+// when the connection establishment process for a Sock
+// is finished low layer protocol calling this function
+// to notify socket layer
+//
+VOID
+SockConnEstablished (
+ IN SOCKET *Sock
+ );
+
+VOID
+SockConnClosed (
+ IN SOCKET *Sock
+ );
+
+//
+// called by low layer protocol to trim send buffer of
+// Sock, when Count data is sent out completely
+//
+VOID
+SockDataSent (
+ IN SOCKET *Sock,
+ IN UINT32 Count
+ );
+
+//
+// called by low layer protocol to get Len of data from
+// socket to send and copy it in Dest
+//
+UINT32
+SockGetDataToSend (
+ IN SOCKET *Sock,
+ IN UINT32 Offset,
+ IN UINT32 Len,
+ IN UINT8 *Dest
+ );
+
+//
+// called by low layer protocol to notify socket no more data can be
+// received
+//
+VOID
+SockNoMoreData (
+ IN SOCKET *Sock
+ );
+
+//
+// called by low layer protocol to append a NetBuffer
+// to rcv buffer of sock
+//
+VOID
+SockDataRcvd (
+ IN SOCKET *Sock,
+ IN NET_BUF *NetBuffer,
+ IN UINT32 UrgLen
+ );
+
+UINT32
+SockGetFreeSpace (
+ IN SOCKET *Sock,
+ IN UINT32 Which
+ );
+
+SOCKET *
+SockClone (
+ IN SOCKET *Sock
+ );
+
+VOID
+SockRcvdErr (
+ IN SOCKET *Sock,
+ IN EFI_STATUS Error
+ );
+
+//
+// the socket structure representing a network service access point
+//
+typedef struct _SOCKET {
+
+ //
+ // socket description information
+ //
+ UINT32 Signature;
+ EFI_HANDLE SockHandle; // the virtual handle of the socket
+ EFI_HANDLE DriverBinding; // socket't driver binding protocol
+ SOCK_CONFIGURE_STATE ConfigureState;
+ SOCK_TYPE Type;
+ SOCK_STATE State;
+ UINT16 Flag;
+ NET_LOCK Lock; // the lock of socket
+ SOCK_BUFFER SndBuffer; // send buffer of application's data
+ SOCK_BUFFER RcvBuffer; // receive buffer of received data
+ EFI_STATUS SockError; // the error returned by low layer protocol
+ BOOLEAN IsDestroyed;
+
+ //
+ // fields used to manage the connection request
+ //
+ UINT32 BackLog; // the limit of connection to this socket
+ UINT32 ConnCnt; // the current count of connections to it
+ SOCKET *Parent; // listening parent that accept the connection
+ NET_LIST_ENTRY ConnectionList; // the connections maintained by this socket
+ //
+ // the queue to buffer application's asynchronous token
+ //
+ NET_LIST_ENTRY ListenTokenList;
+ NET_LIST_ENTRY RcvTokenList;
+ NET_LIST_ENTRY SndTokenList;
+ NET_LIST_ENTRY ProcessingSndTokenList;
+
+ SOCK_COMPLETION_TOKEN *ConnectionToken; // app's token to signal if connected
+ SOCK_COMPLETION_TOKEN *CloseToken; // app's token to signal if closed
+
+ //
+ // interface for low level protocol
+ //
+ SOCK_PROTO_HANDLER ProtoHandler; // the request handler of protocol
+ UINT8 ProtoReserved[PROTO_RESERVED_LEN]; // Data fields reserved for protocol
+ union {
+ EFI_TCP4_PROTOCOL TcpProtocol;
+ EFI_UDP4_PROTOCOL UdpProtocol;
+ } NetProtocol;
+} SOCKET;
+
+//
+// the token structure buffered in socket layer
+//
+typedef struct _SOCK_TOKEN {
+ NET_LIST_ENTRY TokenList; // the entry to add in the token list
+ SOCK_COMPLETION_TOKEN *Token; // The application's token
+ UINT32 RemainDataLen; // unprocessed data length
+ SOCKET *Sock; // the poninter to the socket this token
+ // belongs to
+} SOCK_TOKEN;
+
+//
+// reserved data to access the NET_BUF delivered by UDP driver
+//
+typedef struct _UDP_RSV_DATA {
+ EFI_TIME TimeStamp;
+ EFI_UDP4_SESSION_DATA Session;
+} UDP_RSV_DATA;
+
+//
+// reserved data to access the NET_BUF delivered by TCP driver
+//
+typedef struct _TCP_RSV_DATA {
+ UINT32 UrgLen;
+} TCP_RSV_DATA;
+
+//
+// call it to creat a socket and attach it to a PCB
+//
+SOCKET *
+SockCreateChild (
+ IN SOCK_INIT_DATA *SockInitData,
+ IN VOID *ProtoData,
+ IN UINT32 Len
+ );
+
+//
+// call it to destroy a socket and its related PCB
+//
+EFI_STATUS
+SockDestroyChild (
+ IN SOCKET *Sock
+ );
+
+//
+// call it to configure a socket and its related PCB
+//
+EFI_STATUS
+SockConfigure (
+ IN SOCKET *Sock,
+ IN VOID *ConfigData
+ );
+
+//
+// call it to connect a socket to the peer
+//
+EFI_STATUS
+SockConnect (
+ IN SOCKET *Sock,
+ IN VOID *Token
+ );
+
+//
+// call it to issue an asynchronous listen token to the socket
+//
+EFI_STATUS
+SockAccept (
+ IN SOCKET *Sock,
+ IN VOID *Token
+ );
+
+//
+// Call it to send data using this socket
+//
+EFI_STATUS
+SockSend (
+ IN SOCKET *Sock,
+ IN VOID *Token
+ );
+
+//
+// Call it to receive data from this socket
+//
+EFI_STATUS
+SockRcv (
+ IN SOCKET *Sock,
+ IN VOID *Token
+ );
+
+//
+// Call it to flush a socket
+//
+EFI_STATUS
+SockFlush (
+ IN SOCKET *Sock
+ );
+
+//
+// Call it to close a socket in the light of policy in Token
+//
+EFI_STATUS
+SockClose (
+ IN SOCKET *Sock,
+ IN VOID *Token,
+ IN BOOLEAN OnAbort
+ );
+
+//
+// Call it to get the mode data of low layer protocol
+//
+EFI_STATUS
+SockGetMode (
+ IN SOCKET *Sock,
+ IN VOID *Mode
+ );
+
+//
+// call it to add this socket instance into a group
+//
+EFI_STATUS
+SockGroup (
+ IN SOCKET *Sock,
+ IN VOID *GroupInfo
+ );
+
+//
+// call it to add a route entry for this socket instance
+//
+EFI_STATUS
+SockRoute (
+ IN SOCKET *Sock,
+ IN VOID *RouteInfo
+ );
+
+//
+// Supporting function to operate on socket buffer
+//
+NET_BUF *
+SockBufFirst (
+ IN SOCK_BUFFER *Sockbuf
+ );
+
+NET_BUF *
+SockBufNext (
+ IN SOCK_BUFFER *Sockbuf,
+ IN NET_BUF *SockEntry
+ );
+
+#endif
diff --git a/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Dispatcher.c b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Dispatcher.c
new file mode 100644
index 0000000000..9039905be6
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Dispatcher.c
@@ -0,0 +1,680 @@
+/** @file
+
+Copyright (c) 2005 - 2006, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+
+ Tcp4Dispatcher.c
+
+Abstract:
+
+
+**/
+
+#include "Tcp4Main.h"
+
+#define TCP_COMP_VAL(Min, Max, Default, Val) \
+ ((((Val) <= (Max)) && ((Val) >= (Min))) ? (Val) : (Default))
+
+STATIC
+EFI_STATUS
+Tcp4Route (
+ IN TCP_CB *Tcb,
+ IN TCP4_ROUTE_INFO *RouteInfo
+ )
+/*++
+
+Routine Description:
+
+ Add or remove a route entry in the IP route table associated
+ with this TCP instance.
+
+Arguments:
+
+ Tcb - Pointer to the TCP_CB of this TCP instance.
+ RouteInfo - Pointer to the route info to be processed.
+
+Returns:
+
+ EFI_SUCCESS - The operation completed successfully.
+ EFI_NOT_STARTED - The driver instance has not been started.
+ EFI_NO_MAPPING - When using the default address, configuration(DHCP,
+ BOOTP, RARP, etc.) is not finished yet.
+ EFI_OUT_OF_RESOURCES - Could not add the entry to the routing table.
+ EFI_NOT_FOUND - This route is not in the routing table
+ (when RouteInfo->DeleteRoute is TRUE).
+ EFI_ACCESS_DENIED - The route is already defined in the routing table
+ (when RouteInfo->DeleteRoute is FALSE).
+
+--*/
+{
+ EFI_IP4_PROTOCOL *Ip;
+
+ Ip = Tcb->IpInfo->Ip;
+
+ ASSERT (Ip);
+
+ return Ip->Routes (
+ Ip,
+ RouteInfo->DeleteRoute,
+ RouteInfo->SubnetAddress,
+ RouteInfo->SubnetMask,
+ RouteInfo->GatewayAddress
+ );
+
+}
+
+
+/**
+ Get the operational settings of this TCP instance.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+ @param Mode Pointer to the buffer to store the operational
+ settings.
+
+ @retval EFI_SUCCESS The mode data is read.
+ @retval EFI_NOT_STARTED No configuration data is available because this
+ instance hasn't been started.
+
+**/
+STATIC
+EFI_STATUS
+Tcp4GetMode (
+ IN TCP_CB *Tcb,
+ IN TCP4_MODE_DATA *Mode
+ )
+{
+ SOCKET *Sock;
+ EFI_TCP4_CONFIG_DATA *ConfigData;
+ EFI_TCP4_ACCESS_POINT *AccessPoint;
+ EFI_TCP4_OPTION *Option;
+ EFI_IP4_PROTOCOL *Ip;
+
+ Sock = Tcb->Sk;
+
+ if (!SOCK_IS_CONFIGURED (Sock) && (Mode->Tcp4ConfigData != NULL)) {
+ return EFI_NOT_STARTED;
+ }
+
+ if (Mode->Tcp4State) {
+ *(Mode->Tcp4State) = Tcb->State;
+ }
+
+ if (Mode->Tcp4ConfigData) {
+
+ ConfigData = Mode->Tcp4ConfigData;
+ AccessPoint = &(ConfigData->AccessPoint);
+ Option = ConfigData->ControlOption;
+
+ ConfigData->TypeOfService = Tcb->TOS;
+ ConfigData->TimeToLive = Tcb->TTL;
+
+ AccessPoint->UseDefaultAddress = Tcb->UseDefaultAddr;
+
+ EFI_IP4 (AccessPoint->StationAddress) = Tcb->LocalEnd.Ip;
+ AccessPoint->SubnetMask = Tcb->SubnetMask;
+ AccessPoint->StationPort = NTOHS (Tcb->LocalEnd.Port);
+
+ EFI_IP4 (AccessPoint->RemoteAddress) = Tcb->RemoteEnd.Ip;
+ AccessPoint->RemotePort = NTOHS (Tcb->RemoteEnd.Port);
+ AccessPoint->ActiveFlag = (BOOLEAN) (Tcb->State != TCP_LISTEN);
+
+ if (Option != NULL) {
+ Option->ReceiveBufferSize = GET_RCV_BUFFSIZE (Tcb->Sk);
+ Option->SendBufferSize = GET_SND_BUFFSIZE (Tcb->Sk);
+ Option->MaxSynBackLog = GET_BACKLOG (Tcb->Sk);
+
+ Option->ConnectionTimeout = Tcb->ConnectTimeout / TCP_TICK_HZ;
+ Option->DataRetries = Tcb->MaxRexmit;
+ Option->FinTimeout = Tcb->FinWait2Timeout / TCP_TICK_HZ;
+ Option->TimeWaitTimeout = Tcb->TimeWaitTimeout / TCP_TICK_HZ;
+ Option->KeepAliveProbes = Tcb->MaxKeepAlive;
+ Option->KeepAliveTime = Tcb->KeepAliveIdle / TCP_TICK_HZ;
+ Option->KeepAliveInterval = Tcb->KeepAlivePeriod / TCP_TICK_HZ;
+
+ Option->EnableNagle = !TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_NAGLE);
+ Option->EnableTimeStamp = !TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_TS);
+ Option->EnableWindowScaling = !TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_WS);
+
+ Option->EnableSelectiveAck = FALSE;
+ Option->EnablePathMtuDiscovery = FALSE;
+ }
+ }
+
+ Ip = Tcb->IpInfo->Ip;
+ ASSERT (Ip);
+
+ return Ip->GetModeData (Ip, Mode->Ip4ModeData, Mode->MnpConfigData, Mode->SnpModeData);
+}
+
+
+/**
+ If AP->StationPort isn't zero, check whether the access point
+ is registered, else generate a random station port for this
+ access point.
+
+ @param AP Pointer to the access point.
+
+ @retval EFI_SUCCESS The check is passed or the port is assigned.
+ @retval EFI_INVALID_PARAMETER The non-zero station port is already used.
+ @retval EFI_OUT_OF_RESOURCES No port can be allocated.
+
+**/
+STATIC
+EFI_STATUS
+Tcp4Bind (
+ IN EFI_TCP4_ACCESS_POINT *AP
+ )
+{
+ BOOLEAN Cycle;
+
+ if (0 != AP->StationPort) {
+ //
+ // check if a same endpoint is bound
+ //
+ if (TcpFindTcbByPeer (&AP->StationAddress, AP->StationPort)) {
+
+ return EFI_INVALID_PARAMETER;
+ }
+ } else {
+ //
+ // generate a random port
+ //
+ Cycle = FALSE;
+
+ if (TCP4_PORT_USER_RESERVED == mTcp4RandomPort) {
+ mTcp4RandomPort = TCP4_PORT_KNOWN;
+ }
+
+ mTcp4RandomPort++;
+
+ while (TcpFindTcbByPeer (&AP->StationAddress, mTcp4RandomPort)) {
+
+ mTcp4RandomPort++;
+
+ if (mTcp4RandomPort <= TCP4_PORT_KNOWN) {
+
+ if (Cycle) {
+ TCP4_DEBUG_ERROR (("Tcp4Bind: no port can be allocated "
+ "for this pcb\n"));
+
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ mTcp4RandomPort = TCP4_PORT_KNOWN + 1;
+
+ Cycle = TRUE;
+ }
+
+ }
+
+ AP->StationPort = mTcp4RandomPort;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Flush the Tcb add its associated protocols..
+
+ @param Tcb Pointer to the TCP_CB to be flushed.
+
+ @retval EFI_SUCCESS The operation is completed successfully.
+
+**/
+STATIC
+VOID
+Tcp4FlushPcb (
+ IN TCP_CB *Tcb
+ )
+{
+ SOCKET *Sock;
+ TCP4_PROTO_DATA *TcpProto;
+
+ IpIoConfigIp (Tcb->IpInfo, NULL);
+
+ Sock = Tcb->Sk;
+ TcpProto = (TCP4_PROTO_DATA *) Sock->ProtoReserved;
+
+ if (SOCK_IS_CONFIGURED (Sock)) {
+ NetListRemoveEntry (&Tcb->List);
+
+ TcpSetVariableData (TcpProto->TcpService);
+ }
+
+ NetbufFreeList (&Tcb->SndQue);
+ NetbufFreeList (&Tcb->RcvQue);
+}
+
+STATIC
+EFI_STATUS
+Tcp4AttachPcb (
+ IN SOCKET *Sk
+ )
+{
+ TCP_CB *Tcb;
+ TCP4_PROTO_DATA *ProtoData;
+ IP_IO *IpIo;
+
+ Tcb = NetAllocateZeroPool (sizeof (TCP_CB));
+
+ if (Tcb == NULL) {
+
+ TCP4_DEBUG_ERROR (("Tcp4ConfigurePcb: failed to allocate a TCB\n"));
+
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ ProtoData = (TCP4_PROTO_DATA *) Sk->ProtoReserved;
+ IpIo = ProtoData->TcpService->IpIo;
+
+ //
+ // Create an IpInfo for this Tcb.
+ //
+ Tcb->IpInfo = IpIoAddIp (IpIo);
+ if (Tcb->IpInfo == NULL) {
+
+ NetFreePool (Tcb);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ NetListInit (&Tcb->List);
+ NetListInit (&Tcb->SndQue);
+ NetListInit (&Tcb->RcvQue);
+
+ Tcb->State = TCP_CLOSED;
+ Tcb->Sk = Sk;
+ ProtoData->TcpPcb = Tcb;
+
+ return EFI_SUCCESS;
+}
+
+STATIC
+VOID
+Tcp4DetachPcb (
+ IN SOCKET *Sk
+ )
+{
+ TCP4_PROTO_DATA *ProtoData;
+ TCP_CB *Tcb;
+
+ ProtoData = (TCP4_PROTO_DATA *) Sk->ProtoReserved;
+ Tcb = ProtoData->TcpPcb;
+
+ ASSERT (Tcb != NULL);
+
+ Tcp4FlushPcb (Tcb);
+
+ IpIoRemoveIp (ProtoData->TcpService->IpIo, Tcb->IpInfo);
+
+ NetFreePool (Tcb);
+
+ ProtoData->TcpPcb = NULL;
+}
+
+
+/**
+ Configure the Tcb using CfgData.
+
+ @param Sk Pointer to the socket of this TCP instance.
+ @param SkTcb Pointer to the TCP_CB of this TCP instance.
+ @param CfgData Pointer to the TCP configuration data.
+
+ @retval EFI_SUCCESS The operation is completed successfully.
+ @retval EFI_INVALID_PARAMETER A same access point has been configured in
+ another TCP instance.
+ @retval EFI_OUT_OF_RESOURCES Failed due to resource limit.
+
+**/
+STATIC
+EFI_STATUS
+Tcp4ConfigurePcb (
+ IN SOCKET *Sk,
+ IN EFI_TCP4_CONFIG_DATA *CfgData
+ )
+{
+ IP_IO *IpIo;
+ EFI_IP4_CONFIG_DATA IpCfgData;
+ EFI_STATUS Status;
+ EFI_TCP4_OPTION *Option;
+ TCP4_PROTO_DATA *TcpProto;
+ TCP_CB *Tcb;
+
+ ASSERT (CfgData && Sk && Sk->SockHandle);
+
+ TcpProto = (TCP4_PROTO_DATA *) Sk->ProtoReserved;
+ Tcb = TcpProto->TcpPcb;
+ IpIo = TcpProto->TcpService->IpIo;
+
+ ASSERT (Tcb != NULL);
+
+ //
+ // Add Ip for send pkt to the peer
+ //
+ IpCfgData = mIpIoDefaultIpConfigData;
+ IpCfgData.DefaultProtocol = EFI_IP_PROTO_TCP;
+ IpCfgData.UseDefaultAddress = CfgData->AccessPoint.UseDefaultAddress;
+ IpCfgData.StationAddress = CfgData->AccessPoint.StationAddress;
+ IpCfgData.SubnetMask = CfgData->AccessPoint.SubnetMask;
+ IpCfgData.ReceiveTimeout = (UINT32) (-1);
+
+ //
+ // Configure the IP instance this Tcb consumes.
+ //
+ Status = IpIoConfigIp (Tcb->IpInfo, &IpCfgData);
+ if (EFI_ERROR (Status)) {
+ goto OnExit;
+ }
+
+ //
+ // Get the default address info if the instance is configured to use default address.
+ //
+ if (CfgData->AccessPoint.UseDefaultAddress) {
+ CfgData->AccessPoint.StationAddress = IpCfgData.StationAddress;
+ CfgData->AccessPoint.SubnetMask = IpCfgData.SubnetMask;
+ }
+
+ //
+ // check if we can bind this endpoint in CfgData
+ //
+ Status = Tcp4Bind (&(CfgData->AccessPoint));
+
+ if (EFI_ERROR (Status)) {
+ TCP4_DEBUG_ERROR (("Tcp4ConfigurePcb: Bind endpoint failed "
+ "with %r\n", Status));
+
+ goto OnExit;
+ }
+
+ //
+ // Initalize the operating information in this Tcb
+ //
+ ASSERT (Tcb->State == TCP_CLOSED &&
+ NetListIsEmpty (&Tcb->SndQue) &&
+ NetListIsEmpty (&Tcb->RcvQue));
+
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_NO_KEEPALIVE);
+ Tcb->State = TCP_CLOSED;
+
+ Tcb->SndMss = 536;
+ Tcb->RcvMss = TcpGetRcvMss (Sk);
+
+ Tcb->SRtt = 0;
+ Tcb->Rto = 3 * TCP_TICK_HZ;
+
+ Tcb->CWnd = Tcb->SndMss;
+ Tcb->Ssthresh = 0xffffffff;
+
+ Tcb->CongestState = TCP_CONGEST_OPEN;
+
+ Tcb->KeepAliveIdle = TCP_KEEPALIVE_IDLE_MIN;
+ Tcb->KeepAlivePeriod = TCP_KEEPALIVE_PERIOD;
+ Tcb->MaxKeepAlive = TCP_MAX_KEEPALIVE;
+ Tcb->MaxRexmit = TCP_MAX_LOSS;
+ Tcb->FinWait2Timeout = TCP_FIN_WAIT2_TIME;
+ Tcb->TimeWaitTimeout = TCP_TIME_WAIT_TIME;
+ Tcb->ConnectTimeout = TCP_CONNECT_TIME;
+
+ //
+ // initialize Tcb in the light of CfgData
+ //
+ Tcb->TTL = CfgData->TimeToLive;
+ Tcb->TOS = CfgData->TypeOfService;
+
+ Tcb->LocalEnd.Ip = EFI_IP4 (CfgData->AccessPoint.StationAddress);
+ Tcb->LocalEnd.Port = HTONS (CfgData->AccessPoint.StationPort);
+ Tcb->SubnetMask = CfgData->AccessPoint.SubnetMask;
+
+ Tcb->RemoteEnd.Ip = EFI_IP4 (CfgData->AccessPoint.RemoteAddress);
+ Tcb->RemoteEnd.Port = HTONS (CfgData->AccessPoint.RemotePort);
+
+ Option = CfgData->ControlOption;
+
+ if (Option != NULL) {
+ SET_RCV_BUFFSIZE (
+ Sk,
+ TCP_COMP_VAL (TCP_RCV_BUF_SIZE_MIN,
+ TCP_RCV_BUF_SIZE,
+ TCP_RCV_BUF_SIZE,
+ Option->ReceiveBufferSize)
+ );
+ SET_SND_BUFFSIZE (
+ Sk,
+ TCP_COMP_VAL (TCP_SND_BUF_SIZE_MIN,
+ TCP_SND_BUF_SIZE,
+ TCP_SND_BUF_SIZE,
+ Option->SendBufferSize)
+ );
+
+ SET_BACKLOG (
+ Sk,
+ TCP_COMP_VAL (TCP_BACKLOG_MIN,
+ TCP_BACKLOG,
+ TCP_BACKLOG,
+ Option->MaxSynBackLog)
+ );
+
+ Tcb->MaxRexmit = (UINT16) TCP_COMP_VAL (
+ TCP_MAX_LOSS_MIN,
+ TCP_MAX_LOSS,
+ TCP_MAX_LOSS,
+ Option->DataRetries
+ );
+ Tcb->FinWait2Timeout = TCP_COMP_VAL (
+ TCP_FIN_WAIT2_TIME,
+ TCP_FIN_WAIT2_TIME_MAX,
+ TCP_FIN_WAIT2_TIME,
+ Option->FinTimeout * TCP_TICK_HZ
+ );
+
+ if (Option->TimeWaitTimeout != 0) {
+ Tcb->TimeWaitTimeout = TCP_COMP_VAL (
+ TCP_TIME_WAIT_TIME,
+ TCP_TIME_WAIT_TIME_MAX,
+ TCP_TIME_WAIT_TIME,
+ Option->TimeWaitTimeout * TCP_TICK_HZ
+ );
+ } else {
+ Tcb->TimeWaitTimeout = 0;
+ }
+
+ if (Option->KeepAliveProbes != 0) {
+ TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_NO_KEEPALIVE);
+
+ Tcb->MaxKeepAlive = (UINT8) TCP_COMP_VAL (
+ TCP_MAX_KEEPALIVE_MIN,
+ TCP_MAX_KEEPALIVE,
+ TCP_MAX_KEEPALIVE,
+ Option->KeepAliveProbes
+ );
+ Tcb->KeepAliveIdle = TCP_COMP_VAL (
+ TCP_KEEPALIVE_IDLE_MIN,
+ TCP_KEEPALIVE_IDLE_MAX,
+ TCP_KEEPALIVE_IDLE_MIN,
+ Option->KeepAliveTime * TCP_TICK_HZ
+ );
+ Tcb->KeepAlivePeriod = TCP_COMP_VAL (
+ TCP_KEEPALIVE_PERIOD_MIN,
+ TCP_KEEPALIVE_PERIOD,
+ TCP_KEEPALIVE_PERIOD,
+ Option->KeepAliveInterval * TCP_TICK_HZ
+ );
+ }
+
+ Tcb->ConnectTimeout = TCP_COMP_VAL (
+ TCP_CONNECT_TIME_MIN,
+ TCP_CONNECT_TIME,
+ TCP_CONNECT_TIME,
+ Option->ConnectionTimeout * TCP_TICK_HZ
+ );
+
+ if (Option->EnableNagle == FALSE) {
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_NO_NAGLE);
+ }
+
+ if (Option->EnableTimeStamp == FALSE) {
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_NO_TS);
+ }
+
+ if (Option->EnableWindowScaling == FALSE) {
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_NO_WS);
+ }
+ }
+
+ //
+ // update state of Tcb and socket
+ //
+ if (CfgData->AccessPoint.ActiveFlag == FALSE) {
+
+ TcpSetState (Tcb, TCP_LISTEN);
+ SockSetState (Sk, SO_LISTENING);
+
+ Sk->ConfigureState = SO_CONFIGURED_PASSIVE;
+ } else {
+
+ Sk->ConfigureState = SO_CONFIGURED_ACTIVE;
+ }
+
+ TcpInsertTcb (Tcb);
+
+OnExit:
+
+ return Status;
+}
+
+
+/**
+ The procotol handler provided to the socket layer, used to
+ dispatch the socket level requests by calling the corresponding
+ TCP layer functions.
+
+ @param Sock Pointer to the socket of this TCP instance.
+ @param Request The code of this operation request.
+ @param Data Pointer to the operation specific data passed in
+ together with the operation request.
+
+ @retval EFI_SUCCESS The socket request is completed successfully.
+ @retval other The error status returned by the corresponding TCP
+ layer function.
+
+**/
+EFI_STATUS
+Tcp4Dispatcher (
+ IN SOCKET *Sock,
+ IN SOCK_REQUEST Request,
+ IN VOID *Data OPTIONAL
+ )
+{
+ TCP_CB *Tcb;
+ TCP4_PROTO_DATA *ProtoData;
+ EFI_IP4_PROTOCOL *Ip;
+
+ ProtoData = (TCP4_PROTO_DATA *) Sock->ProtoReserved;
+ Tcb = ProtoData->TcpPcb;
+
+ switch (Request) {
+ case SOCK_POLL:
+ Ip = ProtoData->TcpService->IpIo->Ip;
+ Ip->Poll (Ip);
+ break;
+
+ case SOCK_CONSUMED:
+ //
+ // After user received data from socket buffer, socket will
+ // notify TCP using this message to give it a chance to send out
+ // window update information
+ //
+ ASSERT (Tcb);
+ TcpOnAppConsume (Tcb);
+ break;
+
+ case SOCK_SND:
+
+ ASSERT (Tcb);
+ TcpOnAppSend (Tcb);
+ break;
+
+ case SOCK_CLOSE:
+
+ TcpOnAppClose (Tcb);
+
+ break;
+
+ case SOCK_ABORT:
+
+ TcpOnAppAbort (Tcb);
+
+ break;
+
+ case SOCK_SNDPUSH:
+ Tcb->SndPsh = TcpGetMaxSndNxt (Tcb) + GET_SND_DATASIZE (Tcb->Sk);
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_SND_PSH);
+
+ break;
+
+ case SOCK_SNDURG:
+ Tcb->SndUp = TcpGetMaxSndNxt (Tcb) + GET_SND_DATASIZE (Tcb->Sk) - 1;
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_SND_URG);
+
+ break;
+
+ case SOCK_CONNECT:
+
+ TcpOnAppConnect (Tcb);
+
+ break;
+
+ case SOCK_ATTACH:
+
+ return Tcp4AttachPcb (Sock);
+
+ break;
+
+ case SOCK_FLUSH:
+
+ Tcp4FlushPcb (Tcb);
+
+ break;
+
+ case SOCK_DETACH:
+
+ Tcp4DetachPcb (Sock);
+
+ break;
+
+ case SOCK_CONFIGURE:
+
+ return Tcp4ConfigurePcb (
+ Sock,
+ (EFI_TCP4_CONFIG_DATA *) Data
+ );
+
+ break;
+
+ case SOCK_MODE:
+
+ ASSERT (Data && Tcb);
+
+ return Tcp4GetMode (Tcb, (TCP4_MODE_DATA *) Data);
+
+ break;
+
+ case SOCK_ROUTE:
+
+ ASSERT (Data && Tcb);
+
+ return Tcp4Route (Tcb, (TCP4_ROUTE_INFO *) Data);
+
+ }
+
+ return EFI_SUCCESS;
+
+}
diff --git a/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Driver.c b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Driver.c
new file mode 100644
index 0000000000..2927849285
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Driver.c
@@ -0,0 +1,669 @@
+/** @file
+
+Copyright (c) 2005 - 2007, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+
+ Tcp4Driver.c
+
+Abstract:
+
+
+**/
+
+#include "Tcp4Main.h"
+
+
+UINT16 mTcp4RandomPort;
+extern EFI_COMPONENT_NAME_PROTOCOL gTcp4ComponentName;
+
+TCP4_HEARTBEAT_TIMER mTcp4Timer = {
+ NULL,
+ 0
+};
+
+EFI_TCP4_PROTOCOL mTcp4ProtocolTemplate = {
+ Tcp4GetModeData,
+ Tcp4Configure,
+ Tcp4Routes,
+ Tcp4Connect,
+ Tcp4Accept,
+ Tcp4Transmit,
+ Tcp4Receive,
+ Tcp4Close,
+ Tcp4Cancel,
+ Tcp4Poll
+};
+
+SOCK_INIT_DATA mTcp4DefaultSockData = {
+ SOCK_STREAM,
+ 0,
+ NULL,
+ TCP_BACKLOG,
+ TCP_SND_BUF_SIZE,
+ TCP_RCV_BUF_SIZE,
+ &mTcp4ProtocolTemplate,
+ Tcp4Dispatcher,
+ NULL,
+};
+
+EFI_DRIVER_BINDING_PROTOCOL mTcp4DriverBinding = {
+ Tcp4DriverBindingSupported,
+ Tcp4DriverBindingStart,
+ Tcp4DriverBindingStop,
+ 0xa,
+ NULL,
+ NULL
+};
+
+EFI_SERVICE_BINDING_PROTOCOL mTcp4ServiceBinding = {
+ Tcp4ServiceBindingCreateChild,
+ Tcp4ServiceBindingDestroyChild
+};
+
+
+/**
+ Create and start the heartbeat timer for TCP driver.
+
+ None.
+
+ @retval EFI_SUCCESS The timer is successfully created and started.
+ @retval other The timer is not created.
+
+**/
+STATIC
+EFI_STATUS
+Tcp4CreateTimer (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+
+ Status = EFI_SUCCESS;
+
+ if (mTcp4Timer.RefCnt == 0) {
+
+ Status = gBS->CreateEvent (
+ EVT_TIMER | EVT_NOTIFY_SIGNAL,
+ NET_TPL_TIMER,
+ TcpTicking,
+ NULL,
+ &mTcp4Timer.TimerEvent
+ );
+ if (!EFI_ERROR (Status)) {
+
+ Status = gBS->SetTimer (
+ mTcp4Timer.TimerEvent,
+ TimerPeriodic,
+ (UINT64) (TICKS_PER_SECOND / TCP_TICK_HZ)
+ );
+ }
+ }
+
+ if (!EFI_ERROR (Status)) {
+
+ mTcp4Timer.RefCnt++;
+ }
+
+ return Status;
+}
+
+
+/**
+ Stop and destroy the heartbeat timer for TCP driver.
+
+ None.
+
+ @return None.
+
+**/
+STATIC
+VOID
+Tcp4DestroyTimer (
+ VOID
+ )
+{
+ ASSERT (mTcp4Timer.RefCnt > 0);
+
+ mTcp4Timer.RefCnt--;
+
+ if (mTcp4Timer.RefCnt > 0) {
+ return;
+ }
+
+ gBS->SetTimer (mTcp4Timer.TimerEvent, TimerCancel, 0);
+ gBS->CloseEvent (mTcp4Timer.TimerEvent);
+ mTcp4Timer.TimerEvent = NULL;
+}
+
+//@MT: EFI_DRIVER_ENTRY_POINT (Tcp4DriverEntryPoint)
+
+EFI_STATUS
+EFIAPI
+Tcp4DriverEntryPoint (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+/*++
+
+Routine Description:
+
+ The entry point for Tcp4 driver. used to install
+ Tcp4 driver on the ImageHandle.
+
+Arguments:
+
+ ImageHandle - The firmware allocated handle for this
+ driver image.
+ SystemTable - Pointer to the EFI system table.
+
+Returns:
+
+ EFI_SUCCESS - Driver loaded.
+ other - Driver not loaded.
+
+--*/
+{
+ EFI_STATUS Status;
+ UINT32 Seed;
+
+ //
+ // Install the TCP4 Driver Binding Protocol
+ //
+ Status = NetLibInstallAllDriverProtocols (
+ ImageHandle,
+ SystemTable,
+ &mTcp4DriverBinding,
+ ImageHandle,
+ &gTcp4ComponentName,
+ NULL,
+ NULL
+ );
+
+ //
+ // Initialize ISS and random port.
+ //
+ Seed = NetRandomInitSeed ();
+ mTcpGlobalIss = NET_RANDOM (Seed) % mTcpGlobalIss;
+ mTcp4RandomPort = TCP4_PORT_KNOWN +
+ (UINT16) (NET_RANDOM(Seed) % TCP4_PORT_KNOWN);
+
+ return Status;
+}
+
+
+/**
+ Test to see if this driver supports ControllerHandle.
+
+ @param This Protocol instance pointer.
+ @param ControllerHandle Handle of device to test.
+ @param RemainingDevicePath Optional parameter use to pick a specific child
+ device to start.
+
+ @retval EFI_SUCCESS This driver supports this device.
+ @retval EFI_ALREADY_STARTED This driver is already running on this device.
+ @retval other This driver does not support this device.
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp4DriverBindingSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL * This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL * RemainingDevicePath OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+
+ //
+ // Test for the Tcp4ServiceBinding Protocol
+ //
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiTcp4ServiceBindingProtocolGuid,
+ NULL,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+ if (!EFI_ERROR (Status)) {
+ return EFI_ALREADY_STARTED;
+ }
+
+ //
+ // Test for the Ip4 Protocol
+ //
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiIp4ServiceBindingProtocolGuid,
+ NULL,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+
+ return Status;
+}
+
+
+/**
+ Start this driver on ControllerHandle.
+
+ @param This Protocol instance pointer.
+ @param ControllerHandle Handle of device to bind driver to.
+ @param RemainingDevicePath Optional parameter use to pick a specific child
+ device to start.
+
+ @retval EFI_SUCCESS The driver is added to ControllerHandle.
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources to start the
+ driver.
+ @retval other The driver cannot be added to ControllerHandle.
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp4DriverBindingStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL * This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL * RemainingDevicePath OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ TCP4_SERVICE_DATA *TcpServiceData;
+ IP_IO_OPEN_DATA OpenData;
+
+ TcpServiceData = NetAllocateZeroPool (sizeof (TCP4_SERVICE_DATA));
+
+ if (NULL == TcpServiceData) {
+ TCP4_DEBUG_ERROR (("Tcp4DriverBindingStart: Have no enough"
+ " resource to create a Tcp Servcie Data!\n"));
+
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Create a new IP IO to Consume it
+ //
+ TcpServiceData->IpIo = IpIoCreate (This->DriverBindingHandle, ControllerHandle);
+ if (NULL == TcpServiceData->IpIo) {
+
+ TCP4_DEBUG_ERROR (("Tcp4DriverBindingStart: Have no enough"
+ " resource to create an Ip Io!\n"));
+
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ReleaseServiceData;
+ }
+
+ //
+ // Configure and start IpIo.
+ //
+ NetZeroMem (&OpenData, sizeof (IP_IO_OPEN_DATA));
+
+ OpenData.IpConfigData = mIpIoDefaultIpConfigData;
+ OpenData.IpConfigData.DefaultProtocol = EFI_IP_PROTO_TCP;
+
+ OpenData.PktRcvdNotify = Tcp4RxCallback;
+ Status = IpIoOpen (TcpServiceData->IpIo, &OpenData);
+
+ if (EFI_ERROR (Status)) {
+ goto ReleaseServiceData;
+ }
+
+ //
+ // Create the timer event used by TCP driver
+ //
+ Status = Tcp4CreateTimer ();
+ if (EFI_ERROR (Status)) {
+
+ TCP4_DEBUG_ERROR (("Tcp4DriverBindingStart: Create TcpTimer"
+ " Event failed with %r\n", Status));
+
+ goto ReleaseIpIo;
+ }
+
+ //
+ // Install the Tcp4ServiceBinding Protocol on the
+ // controller handle
+ //
+ TcpServiceData->Tcp4ServiceBinding = mTcp4ServiceBinding;
+
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ &ControllerHandle,
+ &gEfiTcp4ServiceBindingProtocolGuid,
+ &TcpServiceData->Tcp4ServiceBinding,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+
+ TCP4_DEBUG_ERROR (("Tcp4DriverBindingStart: Install Tcp4 Service Binding"
+ " Protocol failed for %r\n", Status));
+
+ goto ReleaseTimer;
+ }
+
+ //
+ // Initialize member in TcpServiceData
+ //
+ TcpServiceData->ControllerHandle = ControllerHandle;
+ TcpServiceData->Signature = TCP4_DRIVER_SIGNATURE;
+ TcpServiceData->DriverBindingHandle = This->DriverBindingHandle;
+
+ TcpSetVariableData (TcpServiceData);
+
+ return EFI_SUCCESS;
+
+ReleaseTimer:
+
+ Tcp4DestroyTimer ();
+
+ReleaseIpIo:
+
+ IpIoDestroy (TcpServiceData->IpIo);
+
+ReleaseServiceData:
+
+ NetFreePool (TcpServiceData);
+
+ return Status;
+}
+
+
+/**
+ Stop this driver on ControllerHandle.
+
+ @param This Protocol instance pointer.
+ @param ControllerHandle Handle of device to stop driver on.
+ @param NumberOfChildren Number of Handles in ChildHandleBuffer. If number
+ of children is zero stop the entire bus driver.
+ @param ChildHandleBuffer List of Child Handles to Stop.
+
+ @retval EFI_SUCCESS This driver is removed from ControllerHandle.
+ @retval other This driver is not removed from ControllerHandle.
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp4DriverBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer
+ )
+{
+ EFI_STATUS Status;
+ EFI_HANDLE NicHandle;
+ EFI_SERVICE_BINDING_PROTOCOL *Tcp4ServiceBinding;
+ TCP4_SERVICE_DATA *TcpServiceData;
+ TCP_CB *TcpPcb;
+ SOCKET *Sock;
+ TCP4_PROTO_DATA *TcpProto;
+ NET_LIST_ENTRY *Entry;
+ NET_LIST_ENTRY *NextEntry;
+
+ // Find the NicHandle where Tcp4 ServiceBinding Protocol is installed.
+ //
+ NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiIp4ProtocolGuid);
+ if (NicHandle == NULL) {
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Retrieve the TCP driver Data Structure
+ //
+ Status = gBS->OpenProtocol (
+ NicHandle,
+ &gEfiTcp4ServiceBindingProtocolGuid,
+ (VOID **) &Tcp4ServiceBinding,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+
+ TCP4_DEBUG_ERROR (("Tcp4DriverBindingStop: Locate Tcp4 Service "
+ " Binding Protocol failed with %r\n", Status));
+
+ return Status;
+ }
+
+ TcpServiceData = TCP4_FROM_THIS (Tcp4ServiceBinding);
+
+ //
+ // Kill TCP driver
+ //
+ NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &mTcpRunQue) {
+ TcpPcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);
+
+ //
+ // Try to destroy this child
+ //
+ Sock = TcpPcb->Sk;
+ TcpProto = (TCP4_PROTO_DATA *) Sock->ProtoReserved;
+
+ if (TcpProto->TcpService == TcpServiceData) {
+ Status = SockDestroyChild (Sock);
+
+ if (EFI_ERROR (Status)) {
+
+ TCP4_DEBUG_ERROR (("Tcp4DriverBindingStop: Destroy Tcp "
+ "instance failed with %r\n", Status));
+ return Status;
+ }
+ }
+ }
+
+ NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &mTcpListenQue) {
+ TcpPcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);
+
+ //
+ // Try to destroy this child
+ //
+ Sock = TcpPcb->Sk;
+ TcpProto = (TCP4_PROTO_DATA *) Sock->ProtoReserved;
+
+ if (TcpProto->TcpService == TcpServiceData) {
+ Status = SockDestroyChild (TcpPcb->Sk);
+ if (EFI_ERROR (Status)) {
+
+ TCP4_DEBUG_ERROR (("Tcp4DriverBindingStop: Destroy Tcp "
+ "instance failed with %r\n", Status));
+ return Status;
+ }
+ }
+ }
+
+ //
+ // Uninstall TCP servicebinding protocol
+ //
+ Status = gBS->UninstallMultipleProtocolInterfaces (
+ NicHandle,
+ &gEfiTcp4ServiceBindingProtocolGuid,
+ Tcp4ServiceBinding,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+
+ TCP4_DEBUG_ERROR (("Tcp4DriverBindingStop: Uninstall TCP service "
+ "binding protocol failed with %r\n", Status));
+ return Status;
+ }
+
+ //
+ // Destroy the IpIO consumed by TCP driver
+ //
+ Status = IpIoDestroy (TcpServiceData->IpIo);
+
+ //
+ // Destroy the heartbeat timer.
+ //
+ Tcp4DestroyTimer ();
+
+ //
+ // Clear the variable.
+ //
+ TcpClearVariableData (TcpServiceData);
+
+ //
+ // Release the TCP service data
+ //
+ NetFreePool (TcpServiceData);
+
+ return Status;
+}
+
+
+/**
+ Creates a child handle with a set of TCP4 services.
+
+ @param This Protocol instance pointer.
+ @param ChildHandle Pointer to the handle of the child to create. If
+ it is NULL, then a new handle is created. If it is
+ not NULL, then the I/O services are added to the
+ existing child handle.
+
+ @retval EFI_SUCCESS The child handle is created.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources to create the
+ child.
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp4ServiceBindingCreateChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE *ChildHandle
+ )
+{
+ SOCKET *Sock;
+ TCP4_SERVICE_DATA *TcpServiceData;
+ TCP4_PROTO_DATA TcpProto;
+ EFI_STATUS Status;
+ VOID *Ip4;
+ EFI_TPL OldTpl;
+
+ if (NULL == This || NULL == ChildHandle) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ OldTpl = NET_RAISE_TPL (NET_TPL_LOCK);
+ TcpServiceData = TCP4_FROM_THIS (This);
+ TcpProto.TcpService = TcpServiceData;
+ TcpProto.TcpPcb = NULL;
+
+ //
+ // Create a tcp instance with defualt Tcp default
+ // sock init data and TcpProto
+ //
+ mTcp4DefaultSockData.DriverBinding = TcpServiceData->DriverBindingHandle;
+
+ Sock = SockCreateChild (&mTcp4DefaultSockData, &TcpProto, sizeof (TCP4_PROTO_DATA));
+ if (NULL == Sock) {
+ TCP4_DEBUG_ERROR (("Tcp4DriverBindingCreateChild: "
+ "No resource to create a Tcp Child\n"));
+
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+
+ *ChildHandle = Sock->SockHandle;
+
+ //
+ // Open the default Ip4 protocol of IP_IO BY_DRIVER.
+ //
+ Status = gBS->OpenProtocol (
+ TcpServiceData->IpIo->ChildHandle,
+ &gEfiIp4ProtocolGuid,
+ (VOID **) &Ip4,
+ TcpServiceData->DriverBindingHandle,
+ Sock->SockHandle,
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
+ );
+ if (EFI_ERROR (Status)) {
+ SockDestroyChild (Sock);
+ }
+
+ON_EXIT:
+ NET_RESTORE_TPL (OldTpl);
+ return Status;
+}
+
+
+/**
+ Destroys a child handle with a set of UDP4 services.
+
+ @param This Protocol instance pointer.
+ @param ChildHandle Handle of the child to be destroyed.
+
+ @retval EFI_SUCCESS The TCP4 services are removed from the child
+ handle.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+ @retval other The child handle is not destroyed.
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp4ServiceBindingDestroyChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ChildHandle
+ )
+{
+ EFI_STATUS Status;
+ EFI_TCP4_PROTOCOL *Tcp4;
+ SOCKET *Sock;
+ TCP4_PROTO_DATA *TcpProtoData;
+ TCP4_SERVICE_DATA *TcpServiceData;
+ EFI_TPL OldTpl;
+
+ if (NULL == This || NULL == ChildHandle) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ OldTpl = NET_RAISE_TPL (NET_TPL_LOCK);
+
+ //
+ // retrieve the Tcp4 protocol from ChildHandle
+ //
+ Status = gBS->OpenProtocol (
+ ChildHandle,
+ &gEfiTcp4ProtocolGuid,
+ (VOID **) &Tcp4,
+ mTcp4DriverBinding.DriverBindingHandle,
+ ChildHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ Status = EFI_UNSUPPORTED;
+ goto ON_EXIT;
+ }
+
+ //
+ // destroy this sock and related Tcp protocol control
+ // block
+ //
+ Sock = SOCK_FROM_THIS (Tcp4);
+ TcpProtoData = (TCP4_PROTO_DATA *) Sock->ProtoReserved;
+ TcpServiceData = TcpProtoData->TcpService;
+
+ Status = SockDestroyChild (Sock);
+
+ //
+ // Close the Ip4 protocol.
+ //
+ gBS->CloseProtocol (
+ TcpServiceData->IpIo->ChildHandle,
+ &gEfiIp4ProtocolGuid,
+ TcpServiceData->DriverBindingHandle,
+ ChildHandle
+ );
+
+ON_EXIT:
+ NET_RESTORE_TPL (OldTpl);
+ return Status;
+}
diff --git a/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Driver.h b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Driver.h
new file mode 100644
index 0000000000..af3444ef58
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Driver.h
@@ -0,0 +1,141 @@
+/** @file
+
+Copyright (c) 2005 - 2006, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+
+ Tcp4Driver.h
+
+Abstract:
+
+
+**/
+
+#ifndef _TCP4_DRIVER_H_
+#define _TCP4_DRIVER_H_
+
+#include <Protocol/ServiceBinding.h>
+#include <Library/IpIoLib.h>
+
+#define TCP4_DRIVER_SIGNATURE EFI_SIGNATURE_32 ('T', 'C', 'P', '4')
+
+#define TCP4_PORT_KNOWN 1024
+#define TCP4_PORT_USER_RESERVED 65535
+
+typedef struct _TCP4_HEARTBEAT_TIMER {
+ EFI_EVENT TimerEvent;
+ INTN RefCnt;
+} TCP4_HEARTBEAT_TIMER;
+
+typedef struct _TCP4_SERVICE_DATA {
+ UINT32 Signature;
+ EFI_HANDLE ControllerHandle;
+ IP_IO *IpIo; // IP Io consumed by TCP4
+ EFI_SERVICE_BINDING_PROTOCOL Tcp4ServiceBinding;
+ EFI_HANDLE DriverBindingHandle;
+ CHAR16 *MacString;
+} TCP4_SERVICE_DATA;
+
+//
+// Prototype for TCP4 driver Rcv callback function registered to IP_IO
+//
+VOID
+Tcp4RxCallback (
+ IN EFI_STATUS Status,
+ IN ICMP_ERROR IcmpErr,
+ IN EFI_NET_SESSION_DATA *NetSession,
+ IN NET_BUF *Pkt,
+ IN VOID *Context OPTIONAL
+ );
+
+INTN
+TcpSendIpPacket (
+ IN TCP_CB *Tcb,
+ IN NET_BUF *Nbuf,
+ IN UINT32 Src,
+ IN UINT32 Dest
+ );
+
+EFI_STATUS
+Tcp4Dispatcher (
+ IN SOCKET *Sock,
+ IN SOCK_REQUEST Request,
+ IN VOID *Data OPTIONAL
+ );
+
+typedef struct _TCP4_PROTO_DATA {
+ TCP4_SERVICE_DATA *TcpService;
+ TCP_CB *TcpPcb;
+} TCP4_PROTO_DATA;
+
+#define TCP4_FROM_THIS(a) \
+ CR ( \
+ (a), \
+ TCP4_SERVICE_DATA, \
+ Tcp4ServiceBinding, \
+ TCP4_DRIVER_SIGNATURE \
+ )
+
+//
+// Function prototype for the driver's entry point
+//
+EFI_STATUS
+EFIAPI
+Tcp4DriverEntryPoint (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ );
+
+//
+// Function prototypes for the Drivr Binding Protocol
+//
+EFI_STATUS
+EFIAPI
+Tcp4DriverBindingSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL * This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL * RemainingDevicePath OPTIONAL
+ );
+
+EFI_STATUS
+EFIAPI
+Tcp4DriverBindingStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL * This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL * RemainingDevicePath OPTIONAL
+ );
+
+EFI_STATUS
+EFIAPI
+Tcp4DriverBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer
+ );
+
+//
+// Function ptototypes for the ServiceBinding Prococol
+//
+EFI_STATUS
+EFIAPI
+Tcp4ServiceBindingCreateChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE *ChildHandle
+ );
+
+EFI_STATUS
+EFIAPI
+Tcp4ServiceBindingDestroyChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ChildHandle
+ );
+
+#endif
diff --git a/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Dxe.inf b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Dxe.inf
new file mode 100644
index 0000000000..9baea4e388
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Dxe.inf
@@ -0,0 +1,77 @@
+#/** @file
+# Component name for module Tcp4
+#
+# FIX ME!
+# Copyright (c) 2006, Intel Corporation. All right reserved.
+#
+# All rights reserved. This program and the accompanying materials
+# are licensed and made available under the terms and conditions of the BSD License
+# which accompanies this distribution. The full text of the license may be found at
+# http://opensource.org/licenses/bsd-license.php
+#
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+#
+#
+#**/
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = Tcp4Dxe
+ FILE_GUID = 6d6963ab-906d-4a65-a7ca-bd40e5d6af4d
+ MODULE_TYPE = DXE_DRIVER
+ VERSION_STRING = 1.0
+ EDK_RELEASE_VERSION = 0x00020000
+ EFI_SPECIFICATION_VERSION = 0x00020000
+
+ ENTRY_POINT = Tcp4DriverEntryPoint
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 IPF EBC
+#
+
+[Sources.common]
+ SockImpl.c
+ SockInterface.c
+ Tcp4Proto.h
+ Tcp4Main.h
+ SockImpl.h
+ Tcp4Output.c
+ Tcp4Timer.c
+ Tcp4Option.h
+ Tcp4Dispatcher.c
+ Tcp4Input.c
+ Tcp4Misc.c
+ Tcp4Main.c
+ Socket.h
+ ComponentName.c
+ Tcp4Driver.h
+ Tcp4Io.c
+ Tcp4Driver.c
+ Tcp4Func.h
+ Tcp4Option.c
+
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+
+
+[LibraryClasses]
+ UefiLib
+ BaseLib
+ UefiBootServicesTableLib
+ UefiDriverEntryPoint
+ UefiRuntimeServicesTableLib
+ DebugLib
+ NetLib
+ IpIoLib
+
+[Protocols]
+ gEfiIp4ProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+ gEfiTcp4ServiceBindingProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+ gEfiIp4ServiceBindingProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+ gEfiTcp4ProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+
diff --git a/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Dxe.msa b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Dxe.msa
new file mode 100644
index 0000000000..fec1934bb7
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Dxe.msa
@@ -0,0 +1,90 @@
+<ModuleSurfaceArea xmlns="http://www.TianoCore.org/2006/Edk2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <MsaHeader>
+ <ModuleName>Tcp4Dxe</ModuleName>
+ <ModuleType>DXE_DRIVER</ModuleType>
+ <GuidValue>6d6963ab-906d-4a65-a7ca-bd40e5d6af4d</GuidValue>
+ <Version>1.0</Version>
+ <Abstract>Component name for module Tcp4</Abstract>
+ <Description>FIX ME!</Description>
+ <Copyright>Copyright (c) 2006, Intel Corporation. All right reserved.</Copyright>
+ <License>All rights reserved. This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.</License>
+ <Specification>FRAMEWORK_BUILD_PACKAGING_SPECIFICATION 0x00000052</Specification>
+ </MsaHeader>
+ <ModuleDefinitions>
+ <SupportedArchitectures>IA32 X64 IPF EBC</SupportedArchitectures>
+ <BinaryModule>false</BinaryModule>
+ <OutputFileBasename>Tcp4Dxe</OutputFileBasename>
+ </ModuleDefinitions>
+ <LibraryClassDefinitions>
+ <LibraryClass Usage="ALWAYS_CONSUMED">
+ <Keyword>DebugLib</Keyword>
+ </LibraryClass>
+ <LibraryClass Usage="ALWAYS_CONSUMED">
+ <Keyword>UefiRuntimeServicesTableLib</Keyword>
+ </LibraryClass>
+ <LibraryClass Usage="ALWAYS_CONSUMED">
+ <Keyword>UefiDriverEntryPoint</Keyword>
+ </LibraryClass>
+ <LibraryClass Usage="ALWAYS_CONSUMED">
+ <Keyword>UefiBootServicesTableLib</Keyword>
+ </LibraryClass>
+ <LibraryClass Usage="ALWAYS_CONSUMED">
+ <Keyword>BaseLib</Keyword>
+ </LibraryClass>
+ <LibraryClass Usage="ALWAYS_CONSUMED">
+ <Keyword>UefiLib</Keyword>
+ </LibraryClass>
+ </LibraryClassDefinitions>
+ <SourceFiles>
+ <Filename>Tcp4Option.c</Filename>
+ <Filename>Tcp4Func.h</Filename>
+ <Filename>Tcp4Driver.c</Filename>
+ <Filename>Tcp4Io.c</Filename>
+ <Filename>Tcp4Driver.h</Filename>
+ <Filename>ComponentName.c</Filename>
+ <Filename>Socket.h</Filename>
+ <Filename>Tcp4Main.c</Filename>
+ <Filename>Tcp4Misc.c</Filename>
+ <Filename>Tcp4Input.c</Filename>
+ <Filename>Tcp4Dispatcher.c</Filename>
+ <Filename>Tcp4Option.h</Filename>
+ <Filename>Tcp4Timer.c</Filename>
+ <Filename>Tcp4Output.c</Filename>
+ <Filename>SockImpl.h</Filename>
+ <Filename>Tcp4Main.h</Filename>
+ <Filename>Tcp4Proto.h</Filename>
+ <Filename>SockInterface.c</Filename>
+ <Filename>SockImpl.c</Filename>
+ </SourceFiles>
+ <PackageDependencies>
+ <Package PackageGuid="5e0e9358-46b6-4ae2-8218-4ab8b9bbdcec"/>
+ <Package PackageGuid="68169ab0-d41b-4009-9060-292c253ac43d"/>
+ </PackageDependencies>
+ <Protocols>
+ <Protocol Usage="ALWAYS_CONSUMED">
+ <ProtocolCName>gEfiTcp4ProtocolGuid</ProtocolCName>
+ </Protocol>
+ <Protocol Usage="ALWAYS_CONSUMED">
+ <ProtocolCName>gEfiIp4ServiceBindingProtocolGuid</ProtocolCName>
+ </Protocol>
+ <Protocol Usage="ALWAYS_CONSUMED">
+ <ProtocolCName>gEfiTcp4ServiceBindingProtocolGuid</ProtocolCName>
+ </Protocol>
+ <Protocol Usage="ALWAYS_CONSUMED">
+ <ProtocolCName>gEfiIp4ProtocolGuid</ProtocolCName>
+ </Protocol>
+ </Protocols>
+ <Externs>
+ <Specification>EFI_SPECIFICATION_VERSION 0x00020000</Specification>
+ <Specification>EDK_RELEASE_VERSION 0x00020000</Specification>
+ <Extern>
+ <ModuleEntryPoint>Tcp4DriverEntryPoint</ModuleEntryPoint>
+ </Extern>
+ </Externs>
+</ModuleSurfaceArea> \ No newline at end of file
diff --git a/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Func.h b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Func.h
new file mode 100644
index 0000000000..6441c13c64
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Func.h
@@ -0,0 +1,353 @@
+/** @file
+
+Copyright (c) 2005 - 2006, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+
+ Tcp4Func.h
+
+Abstract:
+
+
+**/
+
+#ifndef _TCP4_FUNC_H_
+#define _TCP4_FUNC_H_
+
+//
+// Declaration of all the functions in TCP
+// protocol. It is intended to keep tcp.h
+// clear.
+//
+
+//
+// Functions in tcp.c
+//
+BOOLEAN
+TcpFindTcbByPeer (
+ IN EFI_IPv4_ADDRESS *Addr,
+ IN TCP_PORTNO Port
+ );
+
+TCP_CB *
+TcpLocateTcb (
+ IN TCP_PORTNO LocalPort,
+ IN UINT32 LocalIp,
+ IN TCP_PORTNO RemotePort,
+ IN UINT32 RemoteIp,
+ IN BOOLEAN Syn
+ );
+
+INTN
+TcpInsertTcb (
+ IN TCP_CB *Tcb
+ );
+
+TCP_CB *
+TcpCloneTcb (
+ IN TCP_CB *Tcb
+ );
+
+TCP_SEQNO
+TcpGetIss (
+ VOID
+ );
+
+VOID
+TcpInitTcbLocal (
+ IN TCP_CB *Tcb
+ );
+
+VOID
+TcpInitTcbPeer (
+ IN TCP_CB *Tcb,
+ IN TCP_SEG *Seg,
+ IN TCP_OPTION *Opt
+ );
+
+UINT16
+TcpGetRcvMss (
+ IN SOCKET *Sock
+ );
+
+VOID
+TcpSetState (
+ IN TCP_CB *Tcb,
+ IN UINT8 State
+ );
+
+//
+// Functions in Tcp4Output.c
+//
+INTN
+TcpSendIpPacket (
+ IN TCP_CB *Tcb,
+ IN NET_BUF *Nbuf,
+ IN UINT32 Src,
+ IN UINT32 Dst
+ );
+
+INTN
+TcpToSendData (
+ IN TCP_CB *Tcb,
+ IN INTN Force
+ );
+
+VOID
+TcpToSendAck (
+ IN TCP_CB *Tcb
+ );
+
+VOID
+TcpSendAck (
+ IN TCP_CB *Tcb
+ );
+
+INTN
+TcpSendZeroProbe (
+ IN TCP_CB *Tcb
+ );
+
+INTN
+TcpDeliverData (
+ IN TCP_CB *Tcb
+ );
+
+INTN
+TcpSendReset (
+ IN TCP_CB *Tcb,
+ IN TCP_HEAD *Head,
+ IN INT32 Len,
+ IN UINT32 Local,
+ IN UINT32 Remote
+ );
+
+UINT32
+TcpRcvWinOld (
+ IN TCP_CB *Tcb
+ );
+
+UINT32
+TcpRcvWinNow (
+ IN TCP_CB *Tcb
+ );
+
+INTN
+TcpRetransmit (
+ IN TCP_CB *Tcb,
+ IN TCP_SEQNO Seq
+ );
+
+UINT32
+TcpDataToSend (
+ IN TCP_CB *Tcb,
+ IN INTN Force
+ );
+
+INTN
+TcpVerifySegment (
+ IN NET_BUF *Nbuf
+ );
+
+INTN
+TcpCheckSndQue (
+ IN NET_LIST_ENTRY *Head
+ );
+
+NET_BUF *
+TcpGetSegmentSndQue (
+ IN TCP_CB *Tcb,
+ IN TCP_SEQNO Seq,
+ IN UINT32 Len
+ );
+
+NET_BUF *
+TcpGetSegmentSock (
+ IN TCP_CB *Tcb,
+ IN TCP_SEQNO Seq,
+ IN UINT32 Len
+ );
+
+NET_BUF *
+TcpGetSegment (
+ IN TCP_CB *Tcb,
+ IN TCP_SEQNO Seq,
+ IN UINT32 Len
+ );
+
+TCP_SEQNO
+TcpGetMaxSndNxt (
+ IN TCP_CB *Tcb
+ );
+
+//
+// Functions from Tcp4Input.c
+//
+VOID
+TcpIcmpInput (
+ IN NET_BUF *Nbuf,
+ IN ICMP_ERROR IcmpErr,
+ IN UINT32 Src,
+ IN UINT32 Dst
+ );
+
+INTN
+TcpInput (
+ IN NET_BUF *Nbuf,
+ IN UINT32 Src,
+ IN UINT32 Dst
+ );
+
+INTN
+TcpSeqAcceptable (
+ IN TCP_CB *Tcb,
+ IN TCP_SEG *Seg
+ );
+
+VOID
+TcpFastRecover (
+ IN TCP_CB *Tcb,
+ IN TCP_SEG *Seg
+ );
+
+VOID
+TcpFastLossRecover (
+ IN TCP_CB *Tcb,
+ IN TCP_SEG *Seg
+ );
+
+VOID
+TcpComputeRtt (
+ IN TCP_CB *Tcb,
+ IN UINT32 Measure
+ );
+
+INTN
+TcpTrimInWnd (
+ IN TCP_CB *Tcb,
+ IN NET_BUF *Buf
+ );
+
+VOID
+TcpQueueData (
+ IN TCP_CB *Tcb,
+ IN NET_BUF *Nbuf
+ );
+
+VOID
+TcpAdjustSndQue (
+ IN TCP_CB *Tcb,
+ IN TCP_SEQNO Ack
+ );
+
+//
+// Functions from Tcp4Misc.c
+//
+UINT16
+TcpChecksum (
+ IN NET_BUF *Buf,
+ IN UINT16 HeadChecksum
+ );
+
+TCP_SEG *
+TcpFormatNetbuf (
+ IN TCP_CB *Tcb,
+ IN NET_BUF *Nbuf
+ );
+
+VOID
+TcpOnAppConnect (
+ IN TCP_CB *Tcb
+ );
+
+INTN
+TcpOnAppConsume (
+ IN TCP_CB *Tcb
+ );
+
+VOID
+TcpOnAppClose (
+ IN TCP_CB *Tcb
+ );
+
+INTN
+TcpOnAppSend (
+ IN TCP_CB *Tcb
+ );
+
+VOID
+TcpOnAppAbort (
+ IN TCP_CB *Tcb
+ );
+
+VOID
+TcpResetConnection (
+ IN TCP_CB *Tcb
+ );
+
+//
+// Functions in Tcp4Timer.c
+//
+VOID
+TcpClose (
+ IN TCP_CB *Tcb
+ );
+
+VOID
+EFIAPI
+TcpTicking (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ );
+
+VOID
+TcpSetTimer (
+ IN TCP_CB *Tcb,
+ IN UINT16 Timer,
+ IN UINT32 TimeOut
+ );
+
+VOID
+TcpClearTimer (
+ IN TCP_CB *Tcb,
+ IN UINT16 Timer
+ );
+
+VOID
+TcpClearAllTimer (
+ IN TCP_CB *Tcb
+ );
+
+VOID
+TcpSetProbeTimer (
+ IN TCP_CB *Tcb
+ );
+
+VOID
+TcpSetKeepaliveTimer (
+ IN TCP_CB *Tcb
+ );
+
+VOID
+TcpBackoffRto (
+ IN TCP_CB *Tcb
+ );
+
+EFI_STATUS
+TcpSetVariableData (
+ IN TCP4_SERVICE_DATA *Tcp4Service
+ );
+
+VOID
+TcpClearVariableData (
+ IN TCP4_SERVICE_DATA *Tcp4Service
+ );
+
+#endif
diff --git a/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Input.c b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Input.c
new file mode 100644
index 0000000000..b636016207
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Input.c
@@ -0,0 +1,1486 @@
+/** @file
+
+Copyright (c) 2005 - 2007, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+
+ Tcp4Input.c
+
+Abstract:
+
+ TCP input process routines.
+
+
+**/
+
+#include "Tcp4Main.h"
+
+
+/**
+ Check whether the sequence number of the incoming segment
+ is acceptable.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+ @param Seg Pointer to the incoming segment.
+
+ @return 1 if the sequence number is acceptable, otherwise 0.
+
+**/
+INTN
+TcpSeqAcceptable (
+ IN TCP_CB *Tcb,
+ IN TCP_SEG *Seg
+ )
+{
+ return (TCP_SEQ_LEQ (Tcb->RcvWl2, Seg->End) &&
+ TCP_SEQ_LEQ (Seg->Seq, Tcb->RcvWl2 + Tcb->RcvWnd));
+}
+
+
+/**
+ NewReno fast recovery, RFC3782.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+ @param Seg Segment that triggers the fast recovery.
+
+ @return None.
+
+**/
+VOID
+TcpFastRecover (
+ IN TCP_CB *Tcb,
+ IN TCP_SEG *Seg
+ )
+{
+ UINT32 FlightSize;
+ UINT32 Acked;
+
+ //
+ // Step 1: Three duplicate ACKs and not in fast recovery
+ //
+ if (Tcb->CongestState != TCP_CONGEST_RECOVER) {
+
+ //
+ // Step 1A: Invoking fast retransmission.
+ //
+ FlightSize = TCP_SUB_SEQ (Tcb->SndNxt, Tcb->SndUna);
+
+ Tcb->Ssthresh = NET_MAX (FlightSize >> 1, (UINT32) (2 * Tcb->SndMss));
+ Tcb->Recover = Tcb->SndNxt;
+
+ Tcb->CongestState = TCP_CONGEST_RECOVER;
+ TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_RTT_ON);
+
+ //
+ // Step 2: Entering fast retransmission
+ //
+ TcpRetransmit (Tcb, Tcb->SndUna);
+ Tcb->CWnd = Tcb->Ssthresh + 3 * Tcb->SndMss;
+
+ TCP4_DEBUG_TRACE (("TcpFastRecover: enter fast retransmission"
+ " for TCB %x, recover point is %d\n", Tcb, Tcb->Recover));
+ return;
+ }
+
+ //
+ // During fast recovery, execute Step 3, 4, 5 of RFC3782
+ //
+ if (Seg->Ack == Tcb->SndUna) {
+
+ //
+ // Step 3: Fast Recovery,
+ // If this is a duplicated ACK, increse Cwnd by SMSS.
+ //
+
+ // Step 4 is skipped here only to be executed later
+ // by TcpToSendData
+ //
+ Tcb->CWnd += Tcb->SndMss;
+ TCP4_DEBUG_TRACE (("TcpFastRecover: received another"
+ " duplicated ACK (%d) for TCB %x\n", Seg->Ack, Tcb));
+
+ } else {
+
+ //
+ // New data is ACKed, check whether it is a
+ // full ACK or partial ACK
+ //
+ if (TCP_SEQ_GEQ (Seg->Ack, Tcb->Recover)) {
+
+ //
+ // Step 5 - Full ACK:
+ // deflate the congestion window, and exit fast recovery
+ //
+ FlightSize = TCP_SUB_SEQ (Tcb->SndNxt, Tcb->SndUna);
+
+ Tcb->CWnd = NET_MIN (Tcb->Ssthresh, FlightSize + Tcb->SndMss);
+
+ Tcb->CongestState = TCP_CONGEST_OPEN;
+ TCP4_DEBUG_TRACE (("TcpFastRecover: received a full ACK(%d)"
+ " for TCB %x, exit fast recovery\n", Seg->Ack, Tcb));
+
+ } else {
+
+ //
+ // Step 5 - Partial ACK:
+ // fast retransmit the first unacknowledge field
+ // , then deflate the CWnd
+ //
+ TcpRetransmit (Tcb, Seg->Ack);
+ Acked = TCP_SUB_SEQ (Seg->Ack, Tcb->SndUna);
+
+ //
+ // Deflate the CWnd by the amount of new data
+ // ACKed by SEG.ACK. If more than one SMSS data
+ // is ACKed, add back SMSS byte to CWnd after
+ //
+ if (Acked >= Tcb->SndMss) {
+ Acked -= Tcb->SndMss;
+
+ }
+
+ Tcb->CWnd -= Acked;
+
+ TCP4_DEBUG_TRACE (("TcpFastRecover: received a partial"
+ " ACK(%d) for TCB %x\n", Seg->Ack, Tcb));
+
+ }
+ }
+}
+
+
+/**
+ NewReno fast loss recovery, RFC3792.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+ @param Seg Segment that triggers the fast loss recovery.
+
+ @return None.
+
+**/
+VOID
+TcpFastLossRecover (
+ IN TCP_CB *Tcb,
+ IN TCP_SEG *Seg
+ )
+{
+ if (TCP_SEQ_GT (Seg->Ack, Tcb->SndUna)) {
+
+ //
+ // New data is ACKed, check whether it is a
+ // full ACK or partial ACK
+ //
+ if (TCP_SEQ_GEQ (Seg->Ack, Tcb->LossRecover)) {
+
+ //
+ // Full ACK: exit the loss recovery.
+ //
+ Tcb->LossTimes = 0;
+ Tcb->CongestState = TCP_CONGEST_OPEN;
+
+ TCP4_DEBUG_TRACE (("TcpFastLossRecover: received a "
+ "full ACK(%d) for TCB %x\n", Seg->Ack, Tcb));
+
+ } else {
+
+ //
+ // Partial ACK:
+ // fast retransmit the first unacknowledge field.
+ //
+ TcpRetransmit (Tcb, Seg->Ack);
+ TCP4_DEBUG_TRACE (("TcpFastLossRecover: received a "
+ "partial ACK(%d) for TCB %x\n", Seg->Ack, Tcb));
+ }
+ }
+}
+
+
+/**
+ Compute the RTT as specified in RFC2988
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+ @param Measure Currently measured RTT in heart beats.
+
+ @return None.
+
+**/
+VOID
+TcpComputeRtt (
+ IN TCP_CB *Tcb,
+ IN UINT32 Measure
+ )
+{
+ INT32 Var;
+
+ //
+ // Step 2.3: Compute the RTO for subsequent RTT measurement.
+ //
+ if (Tcb->SRtt != 0) {
+
+ Var = Tcb->SRtt - (Measure << TCP_RTT_SHIFT);
+
+ if (Var < 0) {
+ Var = -Var;
+ }
+
+ Tcb->RttVar = (3 * Tcb->RttVar + Var) >> 2;
+ Tcb->SRtt = 7 * (Tcb->SRtt >> 3) + Measure;
+
+ } else {
+ //
+ // Step 2.2: compute the first RTT measure
+ //
+ Tcb->SRtt = Measure << TCP_RTT_SHIFT;
+ Tcb->RttVar = Measure << (TCP_RTT_SHIFT - 1);
+ }
+
+ Tcb->Rto = (Tcb->SRtt + NET_MAX (8, 4 * Tcb->RttVar)) >> TCP_RTT_SHIFT;
+
+ //
+ // Step 2.4: Limit the RTO to at least 1 second
+ // Step 2.5: Limit the RTO to a maxium value that
+ // is at least 60 second
+ //
+ if (Tcb->Rto < TCP_RTO_MIN) {
+ Tcb->Rto = TCP_RTO_MIN;
+
+ } else if (Tcb->Rto > TCP_RTO_MAX) {
+ Tcb->Rto = TCP_RTO_MAX;
+
+ }
+
+ TCP4_DEBUG_TRACE (("TcpComputeRtt: new RTT for TCB %x"
+ " computed SRTT: %d RTTVAR: %d RTO: %d\n",
+ Tcb, Tcb->SRtt, Tcb->RttVar, Tcb->Rto));
+
+}
+
+
+/**
+ Trim the data, SYN and FIN to fit into the window defined by
+ Left and Right.
+
+ @param Nbuf Buffer that contains received TCP segment without IP header.
+ @param Left The sequence number of the window's left edge.
+ @param Right The sequence number of the window's right edge.
+
+ @return 0, the data is successfully trimmed.
+
+**/
+STATIC
+INTN
+TcpTrimSegment (
+ IN NET_BUF *Nbuf,
+ IN TCP_SEQNO Left,
+ IN TCP_SEQNO Right
+ )
+{
+ TCP_SEG *Seg;
+ TCP_SEQNO Urg;
+ UINT32 Drop;
+
+ Seg = TCPSEG_NETBUF (Nbuf);
+
+ //
+ // If the segment is completely out of window,
+ // truncate every thing, include SYN and FIN.
+ //
+ if (TCP_SEQ_LEQ (Seg->End, Left) || TCP_SEQ_LEQ (Right, Seg->Seq)) {
+
+ TCP_CLEAR_FLG (Seg->Flag, TCP_FLG_SYN);
+ TCP_CLEAR_FLG (Seg->Flag, TCP_FLG_FIN);
+
+ Seg->Seq = Seg->End;
+ NetbufTrim (Nbuf, Nbuf->TotalSize, NET_BUF_HEAD);
+ return 0;
+ }
+
+ //
+ // Adjust the buffer header
+ //
+ if (TCP_SEQ_LT (Seg->Seq, Left)) {
+
+ Drop = TCP_SUB_SEQ (Left, Seg->Seq);
+ Urg = Seg->Seq + Seg->Urg;
+ Seg->Seq = Left;
+
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) {
+ TCP_CLEAR_FLG (Seg->Flag, TCP_FLG_SYN);
+ Drop--;
+ }
+
+ //
+ // Adjust the urgent point
+ //
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_URG)) {
+
+ if (TCP_SEQ_LT (Urg, Seg->Seq)) {
+
+ TCP_CLEAR_FLG (Seg->Flag, TCP_FLG_URG);
+ } else {
+ Seg->Urg = (UINT16) TCP_SUB_SEQ (Urg, Seg->Seq);
+ }
+ }
+
+ if (Drop) {
+ NetbufTrim (Nbuf, Drop, NET_BUF_HEAD);
+ }
+ }
+
+ //
+ // Adjust the buffer tail
+ //
+ if (TCP_SEQ_GT (Seg->End, Right)) {
+
+ Drop = TCP_SUB_SEQ (Seg->End, Right);
+ Seg->End = Right;
+
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_FIN)) {
+ TCP_CLEAR_FLG (Seg->Flag, TCP_FLG_FIN);
+ Drop--;
+ }
+
+ if (Drop) {
+ NetbufTrim (Nbuf, Drop, NET_BUF_TAIL);
+ }
+ }
+
+ ASSERT (TcpVerifySegment (Nbuf));
+ return 0;
+}
+
+
+/**
+ Trim off the data outside the tcb's receive window.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+ @param Nbuf Pointer to the NET_BUF containing the received tcp segment.
+
+ @return 0, the data is trimmed.
+
+**/
+INTN
+TcpTrimInWnd (
+ IN TCP_CB *Tcb,
+ IN NET_BUF *Nbuf
+ )
+{
+ return TcpTrimSegment (Nbuf, Tcb->RcvNxt, Tcb->RcvWl2 + Tcb->RcvWnd);
+}
+
+
+/**
+ Process the data and FIN flag, check whether to deliver
+ data to the socket layer.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+
+ @retval 0 No error occurred to deliver data.
+ @retval -1 Error condition occurred. Proper response is to reset the
+ connection.
+
+**/
+INTN
+TcpDeliverData (
+ IN TCP_CB *Tcb
+ )
+{
+ NET_LIST_ENTRY *Entry;
+ NET_BUF *Nbuf;
+ TCP_SEQNO Seq;
+ TCP_SEG *Seg;
+ UINT32 Urgent;
+
+ ASSERT (Tcb && Tcb->Sk);
+
+ //
+ // make sure there is some data queued,
+ // and TCP is in a proper state
+ //
+ if (NetListIsEmpty (&Tcb->RcvQue) || !TCP_CONNECTED (Tcb->State)) {
+
+ return 0;
+ }
+
+ //
+ // Deliver data to the socket layer
+ //
+ Entry = Tcb->RcvQue.ForwardLink;
+ Seq = Tcb->RcvNxt;
+
+ while (Entry != &Tcb->RcvQue) {
+ Nbuf = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);
+ Seg = TCPSEG_NETBUF (Nbuf);
+
+ ASSERT (TcpVerifySegment (Nbuf));
+ ASSERT (Nbuf->Tcp == NULL);
+
+ if (TCP_SEQ_GT (Seg->Seq, Seq)) {
+ break;
+ }
+
+ Entry = Entry->ForwardLink;
+ Seq = Seg->End;
+ Tcb->RcvNxt = Seq;
+
+ NetListRemoveEntry (&Nbuf->List);
+
+ //
+ // RFC793 Eighth step: process FIN in sequence
+ //
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_FIN)) {
+
+ //
+ // The peer sends to us junky data after FIN,
+ // reset the connection.
+ //
+ if (!NetListIsEmpty (&Tcb->RcvQue)) {
+ TCP4_DEBUG_ERROR (("TcpDeliverData: data received after"
+ " FIN from peer of TCB %x, reset connection\n", Tcb));
+
+ NetbufFree (Nbuf);
+ return -1;
+ }
+
+ TCP4_DEBUG_TRACE (("TcpDeliverData: processing FIN "
+ "from peer of TCB %x\n", Tcb));
+
+ switch (Tcb->State) {
+ case TCP_SYN_RCVD:
+ case TCP_ESTABLISHED:
+
+ TcpSetState (Tcb, TCP_CLOSE_WAIT);
+ break;
+
+ case TCP_FIN_WAIT_1:
+
+ if (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_FIN_ACKED)) {
+
+ TcpSetState (Tcb, TCP_CLOSING);
+ break;
+ }
+
+ //
+ // fall through
+ //
+ case TCP_FIN_WAIT_2:
+
+ TcpSetState (Tcb, TCP_TIME_WAIT);
+ TcpClearAllTimer (Tcb);
+
+ if (Tcb->TimeWaitTimeout != 0) {
+
+ TcpSetTimer (Tcb, TCP_TIMER_2MSL, Tcb->TimeWaitTimeout);
+ } else {
+
+ TCP4_DEBUG_WARN (("Connection closed immediately "
+ "because app disables TIME_WAIT timer for %x\n", Tcb));
+
+ TcpSendAck (Tcb);
+ TcpClose (Tcb);
+ }
+ break;
+
+ case TCP_CLOSE_WAIT:
+ case TCP_CLOSING:
+ case TCP_LAST_ACK:
+ case TCP_TIME_WAIT:
+ //
+ // The peer sends to us junk FIN byte. Discard
+ // the buffer then reset the connection
+ //
+ NetbufFree (Nbuf);
+ return -1;
+ break;
+ }
+
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW);
+
+ Seg->End--;
+ }
+
+ //
+ // Don't delay the ack if PUSH flag is on.
+ //
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_PSH)) {
+
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW);
+ }
+
+ if (Nbuf->TotalSize) {
+ Urgent = 0;
+
+ if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RCVD_URG) &&
+ TCP_SEQ_LEQ (Seg->Seq, Tcb->RcvUp)) {
+
+ if (TCP_SEQ_LEQ (Seg->End, Tcb->RcvUp)) {
+ Urgent = Nbuf->TotalSize;
+ } else {
+ Urgent = TCP_SUB_SEQ (Tcb->RcvUp, Seg->Seq) + 1;
+ }
+ }
+
+ SockDataRcvd (Tcb->Sk, Nbuf, Urgent);
+ }
+
+ if (TCP_FIN_RCVD (Tcb->State)) {
+
+ SockNoMoreData (Tcb->Sk);
+ }
+
+ NetbufFree (Nbuf);
+ }
+
+ return 0;
+}
+
+
+/**
+ Store the data into the reassemble queue.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+ @param Nbuf Pointer to the buffer containing the data to be queued.
+
+ @return None.
+
+**/
+VOID
+TcpQueueData (
+ IN TCP_CB *Tcb,
+ IN NET_BUF *Nbuf
+ )
+{
+ TCP_SEG *Seg;
+ NET_LIST_ENTRY *Head;
+ NET_LIST_ENTRY *Prev;
+ NET_LIST_ENTRY *Cur;
+ NET_BUF *Node;
+
+ ASSERT (Tcb && Nbuf && (Nbuf->Tcp == NULL));
+
+ NET_GET_REF (Nbuf);
+
+ Seg = TCPSEG_NETBUF (Nbuf);
+ Head = &Tcb->RcvQue;
+
+ //
+ // Fast path to process normal case. That is,
+ // no out-of-order segments are received.
+ //
+ if (NetListIsEmpty (Head)) {
+
+ NetListInsertTail (Head, &Nbuf->List);
+ return ;
+ }
+
+ //
+ // Find the point to insert the buffer
+ //
+ for (Prev = Head, Cur = Head->ForwardLink;
+ Cur != Head;
+ Prev = Cur, Cur = Cur->ForwardLink) {
+
+ Node = NET_LIST_USER_STRUCT (Cur, NET_BUF, List);
+
+ if (TCP_SEQ_LT (Seg->Seq, TCPSEG_NETBUF (Node)->Seq)) {
+ break;
+ }
+ }
+
+ //
+ // Check whether the current segment overlaps with the
+ // previous segment.
+ //
+ if (Prev != Head) {
+ Node = NET_LIST_USER_STRUCT (Prev, NET_BUF, List);
+
+ if (TCP_SEQ_LT (Seg->Seq, TCPSEG_NETBUF (Node)->End)) {
+
+ if (TCP_SEQ_LEQ (Seg->End, TCPSEG_NETBUF (Node)->End)) {
+
+ NetbufFree (Nbuf);
+ return ;
+ }
+
+ TcpTrimSegment (Nbuf, TCPSEG_NETBUF (Node)->End, Seg->End);
+ }
+ }
+
+ NetListInsertHead (Prev, &Nbuf->List);
+
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW);
+
+ //
+ // Check the segments after the insert point.
+ //
+ while (Cur != Head) {
+ Node = NET_LIST_USER_STRUCT (Cur, NET_BUF, List);
+
+ if (TCP_SEQ_LEQ (TCPSEG_NETBUF (Node)->End, Seg->End)) {
+
+ Cur = Cur->ForwardLink;
+
+ NetListRemoveEntry (&Node->List);
+ NetbufFree (Node);
+ continue;
+ }
+
+ if (TCP_SEQ_LT (TCPSEG_NETBUF (Node)->Seq, Seg->End)) {
+
+ if (TCP_SEQ_LEQ (TCPSEG_NETBUF (Node)->Seq, Seg->Seq)) {
+
+ NetListRemoveEntry (&Nbuf->List);
+ NetbufFree (Nbuf);
+ return ;
+ }
+
+ TcpTrimSegment (Nbuf, Seg->Seq, TCPSEG_NETBUF (Node)->Seq);
+ break;
+ }
+
+ Cur = Cur->ForwardLink;
+ }
+}
+
+
+/**
+ Ajust the send queue or the retransmit queue.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+ @param Ack The acknowledge seuqence number of the received segment.
+
+ @return None.
+
+**/
+VOID
+TcpAdjustSndQue (
+ IN TCP_CB *Tcb,
+ IN TCP_SEQNO Ack
+ )
+{
+ NET_LIST_ENTRY *Head;
+ NET_LIST_ENTRY *Cur;
+ NET_BUF *Node;
+ TCP_SEG *Seg;
+
+ Head = &Tcb->SndQue;
+ Cur = Head->ForwardLink;
+
+ while (Cur != Head) {
+ Node = NET_LIST_USER_STRUCT (Cur, NET_BUF, List);
+ Seg = TCPSEG_NETBUF (Node);
+
+ if (TCP_SEQ_GEQ (Seg->Seq, Ack)) {
+ break;
+ }
+
+ //
+ // Remove completely ACKed segments
+ //
+ if (TCP_SEQ_LEQ (Seg->End, Ack)) {
+ Cur = Cur->ForwardLink;
+
+ NetListRemoveEntry (&Node->List);
+ NetbufFree (Node);
+ continue;
+ }
+
+ TcpTrimSegment (Node, Ack, Seg->End);
+ break;
+ }
+}
+
+
+/**
+ Process the received TCP segments.
+
+ @param Nbuf Buffer that contains received TCP segment without IP header.
+ @param Src Source address of the segment, or the peer's IP address.
+ @param Dst Destination address of the segment, or the local end's IP
+ address.
+
+ @retval 0 Segment is processed successfully. It is either accepted or
+ discarded. But no connection is reset by the segment.
+ @retval -1 A connection is reset by the segment.
+
+**/
+INTN
+TcpInput (
+ IN NET_BUF *Nbuf,
+ IN UINT32 Src,
+ IN UINT32 Dst
+ )
+{
+ TCP_CB *Tcb;
+ TCP_CB *Parent;
+ TCP_OPTION Option;
+ TCP_HEAD *Head;
+ INT32 Len;
+ TCP_SEG *Seg;
+ TCP_SEQNO Right;
+ TCP_SEQNO Urg;
+
+ NET_CHECK_SIGNATURE (Nbuf, NET_BUF_SIGNATURE);
+
+ Parent = NULL;
+ Tcb = NULL;
+
+ Head = (TCP_HEAD *) NetbufGetByte (Nbuf, 0, NULL);
+ Len = Nbuf->TotalSize - (Head->HeadLen << 2);
+
+ if ((Head->HeadLen < 5) || (Len < 0) ||
+ TcpChecksum (Nbuf, NetPseudoHeadChecksum (Src, Dst, 6, 0))) {
+
+ TCP4_DEBUG_TRACE (("TcpInput: received an mal-formated packet\n"));
+ goto DISCARD;
+ }
+
+ if (TCP_FLG_ON (Head->Flag, TCP_FLG_SYN)) {
+ Len++;
+ }
+
+ if (TCP_FLG_ON (Head->Flag, TCP_FLG_FIN)) {
+ Len++;
+ }
+
+ Tcb = TcpLocateTcb (
+ Head->DstPort,
+ Dst,
+ Head->SrcPort,
+ Src,
+ (BOOLEAN) TCP_FLG_ON (Head->Flag, TCP_FLG_SYN)
+ );
+
+ if ((Tcb == NULL) || (Tcb->State == TCP_CLOSED)) {
+ TCP4_DEBUG_TRACE (("TcpInput: send reset because no TCB find\n"));
+
+ Tcb = NULL;
+ goto SEND_RESET;
+ }
+
+ Seg = TcpFormatNetbuf (Tcb, Nbuf);
+
+ //
+ // RFC1122 recommended reaction to illegal option
+ // (in fact, an illegal option length) is reset.
+ //
+ if (TcpParseOption (Nbuf->Tcp, &Option) == -1) {
+ TCP4_DEBUG_ERROR (("TcpInput: reset the peer because"
+ " of mal-format option for Tcb %x\n", Tcb));
+
+ goto SEND_RESET;
+ }
+
+ //
+ // From now on, the segment is headless
+ //
+ NetbufTrim (Nbuf, (Head->HeadLen << 2), NET_BUF_HEAD);
+ Nbuf->Tcp = NULL;
+
+ //
+ // TODO: add fast path process here
+ //
+
+ //
+ // Process the segment in LISTEN state.
+ //
+ if (Tcb->State == TCP_LISTEN) {
+ //
+ // First step: Check RST
+ //
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_RST)) {
+ TCP4_DEBUG_WARN (("TcpInput: discard a reset segment "
+ "for TCB %x in listening\n", Tcb));
+
+ goto DISCARD;
+ }
+
+ //
+ // Second step: Check ACK.
+ // Any ACK sent to TCP in LISTEN is reseted.
+ //
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_ACK)) {
+ TCP4_DEBUG_WARN (("TcpInput: send reset because of"
+ " segment with ACK for TCB %x in listening\n", Tcb));
+
+ goto SEND_RESET;
+ }
+
+ //
+ // Third step: Check SYN
+ //
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) {
+ //
+ // create a child TCB to handle the data
+ //
+ Parent = Tcb;
+
+ Tcb = TcpCloneTcb (Parent);
+ if (Tcb == NULL) {
+ TCP4_DEBUG_ERROR (("TcpInput: discard a segment because"
+ "failed to clone a child for TCB%x\n", Tcb));
+
+ goto DISCARD;
+ }
+
+ TCP4_DEBUG_TRACE (("TcpInput: create a child for TCB %x"
+ " in listening\n", Tcb));
+
+ //
+ // init the TCB structure
+ //
+ Tcb->LocalEnd.Ip = Dst;
+ Tcb->LocalEnd.Port = Head->DstPort;
+ Tcb->RemoteEnd.Ip = Src;
+ Tcb->RemoteEnd.Port = Head->SrcPort;
+
+ TcpInitTcbLocal (Tcb);
+ TcpInitTcbPeer (Tcb, Seg, &Option);
+
+ TcpSetState (Tcb, TCP_SYN_RCVD);
+ TcpSetTimer (Tcb, TCP_TIMER_CONNECT, Tcb->ConnectTimeout);
+ TcpTrimInWnd (Tcb, Nbuf);
+
+ goto StepSix;
+ }
+
+ goto DISCARD;
+
+ } else if (Tcb->State == TCP_SYN_SENT) {
+ //
+ // First step: Check ACK bit
+ //
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_ACK) && (Seg->Ack != Tcb->Iss + 1)) {
+
+ TCP4_DEBUG_WARN (("TcpInput: send reset because of "
+ "wrong ACK received for TCB %x in SYN_SENT\n", Tcb));
+
+ goto SEND_RESET;
+ }
+
+ //
+ // Second step: Check RST bit
+ //
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_RST)) {
+
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_ACK)) {
+
+ TCP4_DEBUG_WARN (("TcpInput: connection reset by"
+ " peer for TCB%x in SYN_SENT\n", Tcb));
+
+ SOCK_ERROR (Tcb->Sk, EFI_CONNECTION_RESET);
+ goto DROP_CONNECTION;
+ } else {
+
+ TCP4_DEBUG_WARN (("TcpInput: discard a reset segment "
+ "because of no ACK for TCB%x in SYN_SENT\n", Tcb));
+
+ goto DISCARD;
+ }
+ }
+
+ //
+ // Third step: Check security and precedence. Skipped
+ //
+
+ //
+ // Fourth step: Check SYN. Pay attention to sitimulatous open
+ //
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) {
+
+ TcpInitTcbPeer (Tcb, Seg, &Option);
+
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_ACK)) {
+
+ Tcb->SndUna = Seg->Ack;
+ }
+
+ TcpClearTimer (Tcb, TCP_TIMER_REXMIT);
+
+ if (TCP_SEQ_GT (Tcb->SndUna, Tcb->Iss)) {
+
+ TcpSetState (Tcb, TCP_ESTABLISHED);
+
+ TcpClearTimer (Tcb, TCP_TIMER_CONNECT);
+ TcpDeliverData (Tcb);
+
+ if ((Tcb->CongestState == TCP_CONGEST_OPEN) &&
+ TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RTT_ON)) {
+
+ TcpComputeRtt (Tcb, Tcb->RttMeasure);
+ TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_RTT_ON);
+ }
+
+ TcpTrimInWnd (Tcb, Nbuf);
+
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW);
+
+ TCP4_DEBUG_TRACE (("TcpInput: connection established"
+ " for TCB %x in SYN_SENT\n", Tcb));
+
+ goto StepSix;
+ } else {
+ //
+ // Received a SYN segment without ACK, simultanous open.
+ //
+ TcpSetState (Tcb, TCP_SYN_RCVD);
+
+ ASSERT (Tcb->SndNxt == Tcb->Iss + 1);
+ TcpAdjustSndQue (Tcb, Tcb->SndNxt);
+
+ TcpTrimInWnd (Tcb, Nbuf);
+
+ TCP4_DEBUG_WARN (("TcpInput: simultanous open "
+ "for TCB %x in SYN_SENT\n", Tcb));
+
+ goto StepSix;
+ }
+ }
+
+ goto DISCARD;
+ }
+
+ //
+ // Process segment in SYN_RCVD or TCP_CONNECTED states
+ //
+
+ //
+ // First step: Check whether SEG.SEQ is acceptable
+ //
+ if (!TcpSeqAcceptable (Tcb, Seg)) {
+ TCP4_DEBUG_WARN (("TcpInput: sequence acceptance"
+ " test failed for segment of TCB %x\n", Tcb));
+
+ if (!TCP_FLG_ON (Seg->Flag, TCP_FLG_RST)) {
+ TcpSendAck (Tcb);
+ }
+
+ goto DISCARD;
+ }
+
+ if ((TCP_SEQ_LT (Seg->Seq, Tcb->RcvWl2)) &&
+ (Tcb->RcvWl2 == Seg->End) &&
+ !TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN | TCP_FLG_FIN)) {
+
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW);
+ }
+
+ //
+ // Second step: Check the RST
+ //
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_RST)) {
+
+ TCP4_DEBUG_WARN (("TcpInput: connection reset for TCB %x\n", Tcb));
+
+ if (Tcb->State == TCP_SYN_RCVD) {
+
+ SOCK_ERROR (Tcb->Sk, EFI_CONNECTION_REFUSED);
+
+ //
+ // This TCB comes from either a LISTEN TCB,
+ // or active open TCB with simultanous open.
+ // Do NOT signal user CONNECTION refused
+ // if it comes from a LISTEN TCB.
+ //
+ } else if ((Tcb->State == TCP_ESTABLISHED) ||
+ (Tcb->State == TCP_FIN_WAIT_1) ||
+ (Tcb->State == TCP_FIN_WAIT_2) ||
+ (Tcb->State == TCP_CLOSE_WAIT)
+ ) {
+
+ SOCK_ERROR (Tcb->Sk, EFI_CONNECTION_RESET);
+
+ } else {
+ //
+ // TODO: set socket error to CLOSED
+ //
+ }
+
+ goto DROP_CONNECTION;
+ }
+
+ //
+ // Trim the data and flags.
+ //
+ TcpTrimInWnd (Tcb, Nbuf);
+
+ //
+ // Third step: Check security and precedence, Ignored
+ //
+
+ //
+ // Fourth step: Check the SYN bit.
+ //
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) {
+
+ TCP4_DEBUG_WARN (("TcpInput: connection reset "
+ "because received extra SYN for TCB %x\n", Tcb));
+
+ SOCK_ERROR (Tcb->Sk, EFI_CONNECTION_RESET);
+ goto RESET_THEN_DROP;
+ }
+
+ //
+ // Fifth step: Check the ACK
+ //
+ if (!TCP_FLG_ON (Seg->Flag, TCP_FLG_ACK)) {
+ TCP4_DEBUG_WARN (("TcpInput: segment discard because"
+ " of no ACK for connected TCB %x\n", Tcb));
+
+ goto DISCARD;
+
+ }
+
+ if (Tcb->State == TCP_SYN_RCVD) {
+
+ if (TCP_SEQ_LT (Tcb->SndUna, Seg->Ack) &&
+ TCP_SEQ_LEQ (Seg->Ack, Tcb->SndNxt)) {
+
+ Tcb->SndWnd = Seg->Wnd;
+ Tcb->SndWndMax = NET_MAX (Tcb->SndWnd, Tcb->SndWndMax);
+ Tcb->SndWl1 = Seg->Seq;
+ Tcb->SndWl2 = Seg->Ack;
+ TcpSetState (Tcb, TCP_ESTABLISHED);
+
+ TcpClearTimer (Tcb, TCP_TIMER_CONNECT);
+ TcpDeliverData (Tcb);
+
+ TCP4_DEBUG_TRACE (("TcpInput: connection established "
+ " for TCB %x in SYN_RCVD\n", Tcb));
+
+ //
+ // Continue the process as ESTABLISHED state
+ //
+ } else {
+ TCP4_DEBUG_WARN (("TcpInput: send reset because of"
+ " wrong ACK for TCB %x in SYN_RCVD\n", Tcb));
+
+ goto SEND_RESET;
+ }
+ }
+
+ if (TCP_SEQ_LT (Seg->Ack, Tcb->SndUna)) {
+
+ TCP4_DEBUG_WARN (("TcpInput: ignore the out-of-data"
+ " ACK for connected TCB %x\n", Tcb));
+
+ goto StepSix;
+
+ } else if (TCP_SEQ_GT (Seg->Ack, Tcb->SndNxt)) {
+
+ TCP4_DEBUG_WARN (("TcpInput: discard segment for "
+ "future ACK for connected TCB %x\n", Tcb));
+
+ TcpSendAck (Tcb);
+ goto DISCARD;
+ }
+
+ //
+ // From now on: SND.UNA <= SEG.ACK <= SND.NXT.
+ //
+ if (TCP_FLG_ON (Option.Flag, TCP_OPTION_RCVD_TS)) {
+ //
+ // update TsRecent as specified in page 16 RFC1323.
+ // RcvWl2 equals to the variable "LastAckSent"
+ // defined there.
+ //
+ if (TCP_SEQ_LEQ (Seg->Seq, Tcb->RcvWl2) &&
+ TCP_SEQ_LT (Tcb->RcvWl2, Seg->End)) {
+
+ Tcb->TsRecent = Option.TSVal;
+ Tcb->TsRecentAge = mTcpTick;
+ }
+
+ TcpComputeRtt (Tcb, TCP_SUB_TIME (mTcpTick, Option.TSEcr));
+
+ } else if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RTT_ON)) {
+
+ ASSERT (Tcb->CongestState == TCP_CONGEST_OPEN);
+
+ TcpComputeRtt (Tcb, Tcb->RttMeasure);
+ TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_RTT_ON);
+ }
+
+ if (Seg->Ack == Tcb->SndNxt) {
+
+ TcpClearTimer (Tcb, TCP_TIMER_REXMIT);
+ } else {
+
+ TcpSetTimer (Tcb, TCP_TIMER_REXMIT, Tcb->Rto);
+ }
+
+ //
+ // Count duplicate acks.
+ //
+ if ((Seg->Ack == Tcb->SndUna) &&
+ (Tcb->SndUna != Tcb->SndNxt) &&
+ (Seg->Wnd == Tcb->SndWnd) &&
+ (0 == Len)) {
+
+ Tcb->DupAck++;
+ } else {
+
+ Tcb->DupAck = 0;
+ }
+
+ //
+ // Congestion avoidance, fast recovery and fast retransmission.
+ //
+ if (((Tcb->CongestState == TCP_CONGEST_OPEN) && (Tcb->DupAck < 3)) ||
+ (Tcb->CongestState == TCP_CONGEST_LOSS)) {
+
+ if (TCP_SEQ_GT (Seg->Ack, Tcb->SndUna)) {
+
+ if (Tcb->CWnd < Tcb->Ssthresh) {
+
+ Tcb->CWnd += Tcb->SndMss;
+ } else {
+
+ Tcb->CWnd += NET_MAX (Tcb->SndMss * Tcb->SndMss / Tcb->CWnd, 1);
+ }
+
+ Tcb->CWnd = NET_MIN (Tcb->CWnd, TCP_MAX_WIN << Tcb->SndWndScale);
+ }
+
+ if (Tcb->CongestState == TCP_CONGEST_LOSS) {
+ TcpFastLossRecover (Tcb, Seg);
+ }
+ } else {
+
+ TcpFastRecover (Tcb, Seg);
+ }
+
+ if (TCP_SEQ_GT (Seg->Ack, Tcb->SndUna)) {
+
+ TcpAdjustSndQue (Tcb, Seg->Ack);
+ Tcb->SndUna = Seg->Ack;
+
+ if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_SND_URG) &&
+ (TCP_SEQ_LT (Tcb->SndUp, Seg->Ack))) {
+
+ TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_SND_URG);
+ }
+ }
+
+ //
+ // Update window info
+ //
+ if (TCP_SEQ_LT (Tcb->SndWl1, Seg->Seq) ||
+ ((Tcb->SndWl1 == Seg->Seq) && TCP_SEQ_LEQ (Tcb->SndWl2, Seg->Ack))) {
+
+ Right = Seg->Ack + Seg->Wnd;
+
+ if (TCP_SEQ_LT (Right, Tcb->SndWl2 + Tcb->SndWnd)) {
+
+ if ((Tcb->SndWl1 == Seg->Seq) &&
+ (Tcb->SndWl2 == Seg->Ack) &&
+ (Len == 0)) {
+
+ goto NO_UPDATE;
+ }
+
+ TCP4_DEBUG_WARN (("TcpInput: peer shrinks the"
+ " window for connected TCB %x\n", Tcb));
+
+ if ((Tcb->CongestState == TCP_CONGEST_RECOVER) &&
+ (TCP_SEQ_LT (Right, Tcb->Recover))) {
+
+ Tcb->Recover = Right;
+ }
+
+ if ((Tcb->CongestState == TCP_CONGEST_LOSS) &&
+ (TCP_SEQ_LT (Right, Tcb->LossRecover))) {
+
+ Tcb->LossRecover = Right;
+ }
+
+ if (TCP_SEQ_LT (Right, Tcb->SndNxt)) {
+
+ Tcb->SndNxt = Right;
+
+ if (Right == Tcb->SndUna) {
+
+ TcpClearTimer (Tcb, TCP_TIMER_REXMIT);
+ TcpSetProbeTimer (Tcb);
+ }
+ }
+ }
+
+ Tcb->SndWnd = Seg->Wnd;
+ Tcb->SndWndMax = NET_MAX (Tcb->SndWnd, Tcb->SndWndMax);
+ Tcb->SndWl1 = Seg->Seq;
+ Tcb->SndWl2 = Seg->Ack;
+ }
+
+NO_UPDATE:
+
+ if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_FIN_SENT) &&
+ (Tcb->SndUna == Tcb->SndNxt)) {
+
+ TCP4_DEBUG_TRACE (("TcpInput: local FIN is ACKed by"
+ " peer for connected TCB %x\n", Tcb));
+
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_FIN_ACKED);
+ }
+
+ //
+ // Transit the state if proper.
+ //
+ switch (Tcb->State) {
+ case TCP_FIN_WAIT_1:
+
+ if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_FIN_ACKED)) {
+
+ TcpSetState (Tcb, TCP_FIN_WAIT_2);
+
+ TcpClearAllTimer (Tcb);
+ TcpSetTimer (Tcb, TCP_TIMER_FINWAIT2, Tcb->FinWait2Timeout);
+ }
+
+ case TCP_FIN_WAIT_2:
+
+ break;
+
+ case TCP_CLOSE_WAIT:
+ break;
+
+ case TCP_CLOSING:
+
+ if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_FIN_ACKED)) {
+
+ TcpSetState (Tcb, TCP_TIME_WAIT);
+
+ TcpClearAllTimer (Tcb);
+
+ if (Tcb->TimeWaitTimeout != 0) {
+
+ TcpSetTimer (Tcb, TCP_TIMER_2MSL, Tcb->TimeWaitTimeout);
+ } else {
+
+ TCP4_DEBUG_WARN (("Connection closed immediately "
+ "because app disables TIME_WAIT timer for %x\n", Tcb));
+
+ TcpClose (Tcb);
+ }
+ }
+ break;
+
+ case TCP_LAST_ACK:
+
+ if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_FIN_ACKED)) {
+
+ TcpSetState (Tcb, TCP_CLOSED);
+ }
+
+ break;
+
+ case TCP_TIME_WAIT:
+
+ TcpSendAck (Tcb);
+
+ if (Tcb->TimeWaitTimeout != 0) {
+
+ TcpSetTimer (Tcb, TCP_TIMER_2MSL, Tcb->TimeWaitTimeout);
+ } else {
+
+ TCP4_DEBUG_WARN (("Connection closed immediately "
+ "because app disables TIME_WAIT timer for %x\n", Tcb));
+
+ TcpClose (Tcb);
+ }
+ break;
+ }
+
+ //
+ // Sixth step: Check the URG bit.update the Urg point
+ // if in TCP_CAN_RECV, otherwise, leave the RcvUp intact.
+ //
+StepSix:
+
+ Tcb->Idle = 0;
+ TcpSetKeepaliveTimer (Tcb);
+
+ if (TCP_TIMER_ON (Tcb->EnabledTimer, TCP_TIMER_PROBE)) {
+
+ TcpClearTimer (Tcb, TCP_TIMER_PROBE);
+ }
+
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_URG) &&
+ !TCP_FIN_RCVD (Tcb->State)) {
+
+ TCP4_DEBUG_TRACE (("TcpInput: received urgent data "
+ "from peer for connected TCB %x\n", Tcb));
+
+ Urg = Seg->Seq + Seg->Urg;
+
+ if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RCVD_URG) &&
+ TCP_SEQ_GT (Urg, Tcb->RcvUp)) {
+
+ Tcb->RcvUp = Urg;
+ } else {
+
+ Tcb->RcvUp = Urg;
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_RCVD_URG);
+ }
+ }
+
+ //
+ // Seventh step: Process the segment data
+ //
+ if (Seg->End != Seg->Seq) {
+
+ if (TCP_FIN_RCVD (Tcb->State)) {
+
+ TCP4_DEBUG_WARN (("TcpInput: connection reset because"
+ " data is lost for connected TCB %x\n", Tcb));
+
+ goto RESET_THEN_DROP;
+ }
+
+ if (TCP_LOCAL_CLOSED (Tcb->State) && (Nbuf->TotalSize != 0)) {
+ TCP4_DEBUG_WARN (("TcpInput: connection reset because"
+ " data is lost for connected TCB %x\n", Tcb));
+
+ goto RESET_THEN_DROP;
+ }
+
+ TcpQueueData (Tcb, Nbuf);
+ if (TcpDeliverData (Tcb) == -1) {
+ goto RESET_THEN_DROP;
+ }
+
+ if (!NetListIsEmpty (&Tcb->RcvQue)) {
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW);
+ }
+ }
+
+ //
+ // Eighth step: check the FIN.
+ // This step is moved to TcpDeliverData. FIN will be
+ // processed in sequence there. Check the comments in
+ // the beginning of the file header for information.
+ //
+
+ //
+ // Tcb is a new child of the listening Parent,
+ // commit it.
+ //
+ if (Parent) {
+ Tcb->Parent = Parent;
+ TcpInsertTcb (Tcb);
+ }
+
+ if ((Tcb->State != TCP_CLOSED) &&
+ (!TcpToSendData (Tcb, 0)) &&
+ (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW) || Nbuf->TotalSize)) {
+
+ TcpToSendAck (Tcb);
+ }
+
+ NetbufFree (Nbuf);
+ return 0;
+
+RESET_THEN_DROP:
+ TcpSendReset (Tcb, Head, Len, Dst, Src);
+
+DROP_CONNECTION:
+ ASSERT (Tcb && Tcb->Sk);
+
+ NetbufFree (Nbuf);
+ TcpClose (Tcb);
+
+ return -1;
+
+SEND_RESET:
+
+ TcpSendReset (Tcb, Head, Len, Dst, Src);
+
+DISCARD:
+
+ //
+ // Tcb is a child of Parent, and it doesn't survive
+ //
+ TCP4_DEBUG_WARN (("Tcp4Input: Discard a packet\n"));
+ NetbufFree (Nbuf);
+
+ if (Parent && Tcb) {
+
+ ASSERT (Tcb->Sk);
+ TcpClose (Tcb);
+ }
+
+ return 0;
+}
+
+
+/**
+ Process the received ICMP error messages for TCP.
+
+ @param Nbuf Buffer that contains part of the TCP segment without IP header
+ truncated from the ICMP error packet.
+ @param IcmpErr The ICMP error code interpreted from ICMP error packet.
+ @param Src Source address of the ICMP error message.
+ @param Dst Destination address of the ICMP error message.
+
+ @return None.
+
+**/
+VOID
+TcpIcmpInput (
+ IN NET_BUF *Nbuf,
+ IN ICMP_ERROR IcmpErr,
+ IN UINT32 Src,
+ IN UINT32 Dst
+ )
+{
+ TCP_HEAD *Head;
+ TCP_CB *Tcb;
+ TCP_SEQNO Seq;
+ EFI_STATUS IcmpErrStatus;
+ BOOLEAN IcmpErrIsHard;
+ BOOLEAN IcmpErrNotify;
+
+ Head = (TCP_HEAD *) NetbufGetByte (Nbuf, 0, NULL);
+ Tcb = TcpLocateTcb (
+ Head->DstPort,
+ Dst,
+ Head->SrcPort,
+ Src,
+ FALSE
+ );
+ if (Tcb == NULL || Tcb->State == TCP_CLOSED) {
+
+ goto CLEAN_EXIT;
+ }
+
+ //
+ // Validate the sequence number.
+ //
+ Seq = NTOHL (Head->Seq);
+ if (!(TCP_SEQ_LEQ (Tcb->SndUna, Seq) && TCP_SEQ_LT (Seq, Tcb->SndNxt))) {
+
+ goto CLEAN_EXIT;
+ }
+
+ IcmpErrStatus = IpIoGetIcmpErrStatus (IcmpErr, &IcmpErrIsHard, &IcmpErrNotify);
+
+ if (IcmpErrNotify) {
+
+ SOCK_ERROR (Tcb->Sk, IcmpErrStatus);
+ }
+
+ if (IcmpErrIsHard) {
+
+ TcpClose (Tcb);
+ }
+
+CLEAN_EXIT:
+ NetbufFree (Nbuf);
+}
diff --git a/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Io.c b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Io.c
new file mode 100644
index 0000000000..2f7a49fcb8
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Io.c
@@ -0,0 +1,117 @@
+/** @file
+
+Copyright (c) 2005 - 2006, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+
+ Tcp4Io.c
+
+Abstract:
+
+ I/O interfaces between TCP and IpIo.
+
+
+**/
+
+
+#include "Tcp4Main.h"
+
+
+/**
+ Packet receive callback function provided to IP_IO, used to call
+ the proper function to handle the packet received by IP.
+
+ @param Status Status of the received packet.
+ @param IcmpErr ICMP error number.
+ @param NetSession Pointer to the net session of this packet.
+ @param Pkt Pointer to the recieved packet.
+ @param Context Pointer to the context configured in IpIoOpen(), not used
+ now.
+
+ @return None
+
+**/
+VOID
+Tcp4RxCallback (
+ IN EFI_STATUS Status,
+ IN ICMP_ERROR IcmpErr,
+ IN EFI_NET_SESSION_DATA *NetSession,
+ IN NET_BUF *Pkt,
+ IN VOID *Context OPTIONAL
+ )
+{
+ if (EFI_SUCCESS == Status) {
+ TcpInput (Pkt, NetSession->Source, NetSession->Dest);
+ } else {
+ TcpIcmpInput (Pkt, IcmpErr, NetSession->Source, NetSession->Dest);
+ }
+}
+
+
+/**
+ Send the segment to IP via IpIo function.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+ @param Nbuf Pointer to the TCP segment to be sent.
+ @param Src Source address of the TCP segment.
+ @param Dest Destination address of the TCP segment.
+
+ @retval 0 The segment was sent out successfully.
+ @retval -1 The segment was failed to send.
+
+**/
+INTN
+TcpSendIpPacket (
+ IN TCP_CB *Tcb,
+ IN NET_BUF *Nbuf,
+ IN UINT32 Src,
+ IN UINT32 Dest
+ )
+{
+ EFI_STATUS Status;
+ IP_IO *IpIo;
+ IP_IO_OVERRIDE Override;
+ SOCKET *Sock;
+ VOID *IpSender;
+ TCP4_PROTO_DATA *TcpProto;
+
+ if (NULL == Tcb) {
+
+ IpIo = NULL;
+ IpSender = IpIoFindSender (&IpIo, Src);
+
+ if (IpSender == NULL) {
+ TCP4_DEBUG_WARN (("TcpSendIpPacket: No appropriate IpSender.\n"));
+ return -1;
+ }
+ } else {
+
+ Sock = Tcb->Sk;
+ TcpProto = (TCP4_PROTO_DATA *) Sock->ProtoReserved;
+ IpIo = TcpProto->TcpService->IpIo;
+ IpSender = Tcb->IpInfo;
+ }
+
+ Override.TypeOfService = 0;
+ Override.TimeToLive = 255;
+ Override.DoNotFragment = FALSE;
+ Override.Protocol = EFI_IP_PROTO_TCP;
+ EFI_IP4 (Override.GatewayAddress) = 0;
+ EFI_IP4 (Override.SourceAddress) = Src;
+
+ Status = IpIoSend (IpIo, Nbuf, IpSender, NULL, NULL, Dest, &Override);
+
+ if (EFI_ERROR (Status)) {
+ TCP4_DEBUG_ERROR (("TcpSendIpPacket: return %r error\n", Status));
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Main.c b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Main.c
new file mode 100644
index 0000000000..8a8cd8a9e3
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Main.c
@@ -0,0 +1,564 @@
+/** @file
+
+Copyright (c) 2005 - 2006, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+
+ Tcp4Main.c
+
+Abstract:
+
+ Implementation of TCP4 protocol services.
+
+
+**/
+
+#include "Tcp4Main.h"
+
+
+/**
+ Check the integrity of the data buffer.
+
+ @param DataLen The total length of the data buffer.
+ @param FragmentCount The fragment count of the fragment table.
+ @param FragmentTable Pointer to the fragment table of the data
+ buffer.
+
+ @retval EFI_SUCCESS The integrity check is passed.
+ @retval EFI_INVALID_PARAMETER The integrity check is failed.
+
+**/
+STATIC
+EFI_STATUS
+Tcp4ChkDataBuf (
+ IN UINT32 DataLen,
+ IN UINT32 FragmentCount,
+ IN EFI_TCP4_FRAGMENT_DATA *FragmentTable
+ )
+{
+ UINT32 Index;
+
+ UINT32 Len;
+
+ for (Index = 0, Len = 0; Index < FragmentCount; Index++) {
+ Len = Len + FragmentTable[Index].FragmentLength;
+ }
+
+ if (DataLen != Len) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Get the current operational status.
+
+ @param This Pointer to the EFI_TCP4_PROTOCOL instance.
+ @param Tcp4State Pointer to the buffer to receive the current TCP
+ state.
+ @param Tcp4ConfigData Pointer to the buffer to receive the current TCP
+ configuration.
+ @param Ip4ModeData Pointer to the buffer to receive the current
+ IPv4 configuration.
+ @param MnpConfigData Pointer to the buffer to receive the current MNP
+ configuration data indirectly used by the TCPv4
+ Instance.
+ @param SnpModeData Pointer to the buffer to receive the current SNP
+ configuration data indirectly used by the TCPv4
+ Instance.
+
+ @retval EFI_SUCCESS The mode data was read.
+ @retval EFI_NOT_STARTED No configuration data is available because this
+ instance hasn't been started.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp4GetModeData (
+ IN CONST EFI_TCP4_PROTOCOL * This,
+ OUT EFI_TCP4_CONNECTION_STATE * Tcp4State OPTIONAL,
+ OUT EFI_TCP4_CONFIG_DATA * Tcp4ConfigData OPTIONAL,
+ OUT EFI_IP4_MODE_DATA * Ip4ModeData OPTIONAL,
+ OUT EFI_MANAGED_NETWORK_CONFIG_DATA * MnpConfigData OPTIONAL,
+ OUT EFI_SIMPLE_NETWORK_MODE * SnpModeData OPTIONAL
+ )
+{
+ TCP4_MODE_DATA TcpMode;
+ SOCKET *Sock;
+
+ if (NULL == This) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Sock = SOCK_FROM_THIS (This);
+
+ TcpMode.Tcp4State = Tcp4State;
+ TcpMode.Tcp4ConfigData = Tcp4ConfigData;
+ TcpMode.Ip4ModeData = Ip4ModeData;
+ TcpMode.MnpConfigData = MnpConfigData;
+ TcpMode.SnpModeData = SnpModeData;
+
+ return SockGetMode (Sock, &TcpMode);
+}
+
+
+/**
+ Initialize or brutally reset the operational parameters for
+ this EFI TCPv4 instance.
+
+ @param This Pointer to the EFI_TCP4_PROTOCOL instance.
+ @param TcpConfigData Pointer to the configure data to configure the
+ instance.
+
+ @retval EFI_SUCCESS The operational settings are set, changed, or
+ reset successfully.
+ @retval EFI_NO_MAPPING When using a default address, configuration
+ (through DHCP, BOOTP, RARP, etc.) is not
+ finished.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+ @retval EFI_ACCESS_DENIED Configuring TCP instance when it is already
+ configured.
+ @retval EFI_DEVICE_ERROR An unexpected network or system error occurred.
+ @retval EFI_UNSUPPORTED One or more of the control options are not
+ supported in the implementation.
+ @retval EFI_OUT_OF_RESOURCES Could not allocate enough system resources.
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp4Configure (
+ IN EFI_TCP4_PROTOCOL * This,
+ IN EFI_TCP4_CONFIG_DATA * TcpConfigData OPTIONAL
+ )
+{
+ EFI_TCP4_OPTION *Option;
+ SOCKET *Sock;
+ EFI_STATUS Status;
+
+ if (NULL == This) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Tcp protocol related parameter check will be conducted here
+ //
+ if (NULL != TcpConfigData) {
+ if ((EFI_IP4 (TcpConfigData->AccessPoint.RemoteAddress) != 0) &&
+ !Ip4IsUnicast (EFI_NTOHL (TcpConfigData->AccessPoint.RemoteAddress), 0)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (!TcpConfigData->AccessPoint.UseDefaultAddress) {
+ if (!Ip4IsUnicast (EFI_NTOHL (TcpConfigData->AccessPoint.StationAddress), 0) ||
+ !IP4_IS_VALID_NETMASK (EFI_NTOHL (TcpConfigData->AccessPoint.SubnetMask))
+ ) {
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+
+ if (TcpConfigData->AccessPoint.ActiveFlag &&
+ (0 == TcpConfigData->AccessPoint.RemotePort ||
+ (EFI_IP4 (TcpConfigData->AccessPoint.RemoteAddress) == 0))
+ ) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Option = TcpConfigData->ControlOption;
+ if ((NULL != Option) &&
+ (Option->EnableSelectiveAck || Option->EnablePathMtuDiscovery)) {
+ return EFI_UNSUPPORTED;
+ }
+ }
+
+ Sock = SOCK_FROM_THIS (This);
+
+ if (NULL == TcpConfigData) {
+ return SockFlush (Sock);
+ }
+
+ Status = SockConfigure (Sock, TcpConfigData);
+
+ if (EFI_NO_MAPPING == Status) {
+ Sock->ConfigureState = SO_NO_MAPPING;
+ }
+
+ return Status;
+}
+
+
+/**
+ Add or delete routing entries.
+
+ @param This Pointer to the EFI_TCP4_PROTOCOL instance.
+ @param DeleteRoute If TRUE, delete the specified route from routing
+ table; if FALSE, add the specified route to
+ routing table.
+ @param SubnetAddress The destination network.
+ @param SubnetMask The subnet mask for the destination network.
+ @param GatewayAddress The gateway address for this route.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_NOT_STARTED The EFI_TCP4_PROTOCOL instance has not been
+ configured.
+ @retval EFI_NO_MAPPING When using a default address, configuration
+ (through DHCP, BOOTP, RARP, etc.) is not
+ finished.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+ @retval EFI_OUT_OF_RESOURCES Could not allocate enough resources to add the
+ entry to the routing table.
+ @retval EFI_NOT_FOUND This route is not in the routing table.
+ @retval EFI_ACCESS_DENIED This route is already in the routing table.
+ @retval EFI_UNSUPPORTED The TCP driver does not support this operation.
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp4Routes (
+ IN EFI_TCP4_PROTOCOL *This,
+ IN BOOLEAN DeleteRoute,
+ IN EFI_IPv4_ADDRESS *SubnetAddress,
+ IN EFI_IPv4_ADDRESS *SubnetMask,
+ IN EFI_IPv4_ADDRESS *GatewayAddress
+ )
+{
+ SOCKET *Sock;
+ TCP4_ROUTE_INFO RouteInfo;
+
+ if (NULL == This) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Sock = SOCK_FROM_THIS (This);
+
+ RouteInfo.DeleteRoute = DeleteRoute;
+ RouteInfo.SubnetAddress = SubnetAddress;
+ RouteInfo.SubnetMask = SubnetMask;
+ RouteInfo.GatewayAddress = GatewayAddress;
+
+ return SockRoute (Sock, &RouteInfo);
+}
+
+
+/**
+ Initiate a nonblocking TCP connection request for an active TCP instance.
+
+ @param This Pointer to the EFI_TCP4_PROTOCOL instance
+ @param ConnectionToken Pointer to the connection token to return when
+ the TCP three way handshake finishes.
+
+ @retval EFI_SUCCESS The connection request is successfully
+ initiated.
+ @retval EFI_NOT_STARTED This EFI_TCP4_PROTOCOL instance hasn't been
+ configured.
+ @retval EFI_ACCESS_DENIED The instance is not configured as an active one
+ or it is not in Tcp4StateClosed state.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+ @retval EFI_OUT_OF_RESOURCES The driver can't allocate enough resource to
+ initiate the active open.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp4Connect (
+ IN EFI_TCP4_PROTOCOL *This,
+ IN EFI_TCP4_CONNECTION_TOKEN *ConnectionToken
+ )
+{
+ SOCKET *Sock;
+
+ if (NULL == This ||
+ NULL == ConnectionToken ||
+ NULL == ConnectionToken->CompletionToken.Event) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Sock = SOCK_FROM_THIS (This);
+
+ return SockConnect (Sock, ConnectionToken);
+}
+
+
+/**
+ Listen on the passive instance to accept an incoming connection request.
+
+ @param This Pointer to the EFI_TCP4_PROTOCOL instance
+ @param ListenToken Pointer to the listen token to return when
+ operation finishes.
+
+ @retval EFI_SUCCESS The listen token has been queued successfully.
+ @retval EFI_NOT_STARTED The EFI_TCP4_PROTOCOL instance hasn't been
+ configured.
+ @retval EFI_ACCESS_DENIED The instatnce is not a passive one or it is not
+ in Tcp4StateListen state or a same listen token
+ has already existed in the listen token queue of
+ this TCP instance.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+ @retval EFI_OUT_OF_RESOURCES Could not allocate enough resources to finish
+ the operation.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp4Accept (
+ IN EFI_TCP4_PROTOCOL *This,
+ IN EFI_TCP4_LISTEN_TOKEN *ListenToken
+ )
+{
+ SOCKET *Sock;
+
+ if (NULL == This ||
+ NULL == ListenToken ||
+ NULL == ListenToken->CompletionToken.Event) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Sock = SOCK_FROM_THIS (This);
+
+ return SockAccept (Sock, ListenToken);
+}
+
+
+/**
+ Queues outgoing data into the transmit queue
+
+ @param This Pointer to the EFI_TCP4_PROTOCOL instance
+ @param Token Pointer to the completion token to queue to the
+ transmit queue
+
+ @retval EFI_SUCCESS The data has been queued for transmission
+ @retval EFI_NOT_STARTED The EFI_TCP4_PROTOCOL instance hasn't been
+ configured.
+ @retval EFI_NO_MAPPING When using a default address, configuration
+ (DHCP, BOOTP, RARP, etc.) is not finished yet.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid
+ @retval EFI_ACCESS_DENIED One or more of the following conditions is TRUE:
+ * A transmit completion token with the same
+ Token-> CompletionToken.Event was already in the
+ transmission queue. * The current instance is in
+ Tcp4StateClosed state * The current instance is
+ a passive one and it is in Tcp4StateListen
+ state. * User has called Close() to disconnect
+ this connection.
+ @retval EFI_NOT_READY The completion token could not be queued because
+ the transmit queue is full.
+ @retval EFI_OUT_OF_RESOURCES Could not queue the transmit data because of
+ resource shortage.
+ @retval EFI_NETWORK_UNREACHABLE There is no route to the destination network or
+ address.
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp4Transmit (
+ IN EFI_TCP4_PROTOCOL *This,
+ IN EFI_TCP4_IO_TOKEN *Token
+ )
+{
+ SOCKET *Sock;
+ EFI_STATUS Status;
+
+ if (NULL == This ||
+ NULL == Token ||
+ NULL == Token->CompletionToken.Event ||
+ NULL == Token->Packet.TxData ||
+ 0 == Token->Packet.TxData->FragmentCount ||
+ 0 == Token->Packet.TxData->DataLength
+ ) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = Tcp4ChkDataBuf (
+ Token->Packet.TxData->DataLength,
+ Token->Packet.TxData->FragmentCount,
+ Token->Packet.TxData->FragmentTable
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Sock = SOCK_FROM_THIS (This);
+
+ return SockSend (Sock, Token);
+
+}
+
+
+/**
+ Place an asynchronous receive request into the receiving queue.
+
+ @param This Pointer to the EFI_TCP4_PROTOCOL instance.
+ @param Token Pointer to a token that is associated with the
+ receive data descriptor.
+
+ @retval EFI_SUCCESS The receive completion token was cached
+ @retval EFI_NOT_STARTED The EFI_TCP4_PROTOCOL instance hasn't been
+ configured.
+ @retval EFI_NO_MAPPING When using a default address, configuration
+ (DHCP, BOOTP, RARP, etc.) is not finished yet.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+ @retval EFI_OUT_OF_RESOURCES The receive completion token could not be queued
+ due to a lack of system resources.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+ @retval EFI_ACCESS_DENIED One or more of the following conditions is TRUE:
+ * A receive completion token with the same
+ Token->CompletionToken.Event was already in the
+ receive queue. * The current instance is in
+ Tcp4StateClosed state. * The current instance is
+ a passive one and it is in Tcp4StateListen
+ state. * User has called Close() to disconnect
+ this connection.
+ @retval EFI_CONNECTION_FIN The communication peer has closed the connection
+ and there is no any buffered data in the receive
+ buffer of this instance.
+ @retval EFI_NOT_READY The receive request could not be queued because
+ the receive queue is full.
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp4Receive (
+ IN EFI_TCP4_PROTOCOL *This,
+ IN EFI_TCP4_IO_TOKEN *Token
+ )
+{
+ SOCKET *Sock;
+ EFI_STATUS Status;
+
+ if (NULL == This ||
+ NULL == Token ||
+ NULL == Token->CompletionToken.Event ||
+ NULL == Token->Packet.RxData ||
+ 0 == Token->Packet.RxData->FragmentCount ||
+ 0 == Token->Packet.RxData->DataLength
+ ) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = Tcp4ChkDataBuf (
+ Token->Packet.RxData->DataLength,
+ Token->Packet.RxData->FragmentCount,
+ Token->Packet.RxData->FragmentTable
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Sock = SOCK_FROM_THIS (This);
+
+ return SockRcv (Sock, Token);
+
+}
+
+
+/**
+ Disconnecting a TCP connection gracefully or reset a TCP connection.
+
+ @param This Pointer to the EFI_TCP4_PROTOCOL instance
+ @param CloseToken Pointer to the close token to return when
+ operation finishes.
+
+ @retval EFI_SUCCESS The operation completed successfully
+ @retval EFI_NOT_STARTED The EFI_TCP4_PROTOCOL instance hasn't been
+ configured.
+ @retval EFI_ACCESS_DENIED One or more of the following are TRUE: *
+ Configure() has been called with TcpConfigData
+ set to NULL and this function has not returned.
+ * Previous Close() call on this instance has not
+ finished.
+ @retval EFI_INVALID_PARAMETER One ore more parameters are invalid
+ @retval EFI_OUT_OF_RESOURCES Could not allocate enough resource to finish the
+ operation
+ @retval EFI_DEVICE_ERROR Any unexpected and not belonged to above
+ category error.
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp4Close (
+ IN EFI_TCP4_PROTOCOL *This,
+ IN EFI_TCP4_CLOSE_TOKEN *CloseToken
+ )
+{
+ SOCKET *Sock;
+
+ if (NULL == This ||
+ NULL == CloseToken ||
+ NULL == CloseToken->CompletionToken.Event) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Sock = SOCK_FROM_THIS (This);
+
+ return SockClose (Sock, CloseToken, CloseToken->AbortOnClose);
+}
+
+
+/**
+ Abort an asynchronous connection, listen, transmission or receive request.
+
+ @param This Pointer to the EFI_TCP4_PROTOCOL instance.
+ @param Token Pointer to a token that has been issued by
+ Connect(), Accept(), Transmit() or Receive(). If
+ NULL, all pending tokens issued by above four
+ functions will be aborted.
+
+ @retval EFI_UNSUPPORTED The operation is not supported in current
+ implementation.
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp4Cancel (
+ IN EFI_TCP4_PROTOCOL * This,
+ IN EFI_TCP4_COMPLETION_TOKEN * Token OPTIONAL
+ )
+{
+ return EFI_UNSUPPORTED;
+}
+
+
+/**
+ Poll to receive incoming data and transmit outgoing segments.
+
+ @param This Pointer to the EFI_TCP4_PROTOCOL instance.
+
+ @retval EFI_SUCCESS Incoming or outgoing data was processed.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+ @retval EFI_NOT_READY No incoming or outgoing data was processed.
+ @retval EFI_TIMEOUT Data was dropped out of the transmission or
+ receive queue. Consider increasing the polling
+ rate.
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp4Poll (
+ IN EFI_TCP4_PROTOCOL *This
+ )
+{
+ SOCKET *Sock;
+ EFI_STATUS Status;
+
+ if (NULL == This) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Sock = SOCK_FROM_THIS (This);
+
+ Status = Sock->ProtoHandler (Sock, SOCK_POLL, NULL);
+
+ return Status;
+}
diff --git a/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Main.h b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Main.h
new file mode 100644
index 0000000000..dba46aa80a
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Main.h
@@ -0,0 +1,176 @@
+/** @file
+
+Copyright (c) 2005 - 2006, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+
+ Tcp4Main.h
+
+Abstract:
+
+
+**/
+
+#ifndef _TCP4_MAIN_H_
+#define _TCP4_MAIN_H_
+
+#include "Socket.h"
+
+#include "Tcp4Proto.h"
+#include "Tcp4Driver.h"
+
+
+extern UINT16 mTcp4RandomPort;
+
+//
+// Driver Produced Protocol Prototypes
+//
+//@MT:#include EFI_PROTOCOL_PRODUCER (Tcp4)
+
+#define TCP4_DEBUG_ERROR(PrintArg) NET_DEBUG_ERROR ("Tcp", PrintArg)
+#define TCP4_DEBUG_WARN(PrintArg) NET_DEBUG_WARNING ("Tcp", PrintArg)
+#define TCP4_DEBUG_TRACE(PrintArg) NET_DEBUG_TRACE ("Tcp", PrintArg)
+
+//
+// Function prototype for the Tcp4 socket request handler
+//
+EFI_STATUS
+Tcp4Dispatcher (
+ IN SOCKET *Sock,
+ IN SOCK_REQUEST Request,
+ IN VOID *Data OPTIONAL
+ );
+
+typedef struct _TCP4_MODE_DATA {
+ EFI_TCP4_CONNECTION_STATE *Tcp4State;
+ EFI_TCP4_CONFIG_DATA *Tcp4ConfigData;
+ EFI_IP4_MODE_DATA *Ip4ModeData;
+ EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData;
+ EFI_SIMPLE_NETWORK_MODE *SnpModeData;
+} TCP4_MODE_DATA;
+
+typedef struct _TCP4_ROUTE_INFO {
+ BOOLEAN DeleteRoute;
+ EFI_IPv4_ADDRESS *SubnetAddress;
+ EFI_IPv4_ADDRESS *SubnetMask;
+ EFI_IPv4_ADDRESS *GatewayAddress;
+} TCP4_ROUTE_INFO;
+
+//
+// Get the mode data of a TCP instance
+//
+EFI_STATUS
+EFIAPI
+Tcp4GetModeData (
+ IN CONST EFI_TCP4_PROTOCOL * This,
+ OUT EFI_TCP4_CONNECTION_STATE * Tcp4State OPTIONAL,
+ OUT EFI_TCP4_CONFIG_DATA * Tcp4ConfigData OPTIONAL,
+ OUT EFI_IP4_MODE_DATA * Ip4ModeData OPTIONAL,
+ OUT EFI_MANAGED_NETWORK_CONFIG_DATA * MnpConfigData OPTIONAL,
+ OUT EFI_SIMPLE_NETWORK_MODE * SnpModeData OPTIONAL
+ );
+
+//
+// Initialize or reset a TCP instance
+//
+EFI_STATUS
+EFIAPI
+Tcp4Configure (
+ IN EFI_TCP4_PROTOCOL * This,
+ IN EFI_TCP4_CONFIG_DATA * TcpConfigData OPTIONAL
+ );
+
+//
+// Add a route entry to the route table
+//
+EFI_STATUS
+EFIAPI
+Tcp4Routes (
+ IN EFI_TCP4_PROTOCOL *This,
+ IN BOOLEAN DeleteRoute,
+ IN EFI_IPv4_ADDRESS *SubnetAddress,
+ IN EFI_IPv4_ADDRESS *SubnetMask,
+ IN EFI_IPv4_ADDRESS *GatewayAddress
+ );
+
+//
+// Issue an asynchronous connection establishment
+// request to the peer
+//
+EFI_STATUS
+EFIAPI
+Tcp4Connect (
+ IN EFI_TCP4_PROTOCOL *This,
+ IN EFI_TCP4_CONNECTION_TOKEN *ConnectionToken
+ );
+
+//
+// Issue an asynchronous listent token to accept an
+// incoming connection reques
+//
+EFI_STATUS
+EFIAPI
+Tcp4Accept (
+ IN EFI_TCP4_PROTOCOL *This,
+ IN EFI_TCP4_LISTEN_TOKEN *ListenToken
+ );
+
+//
+// Issue an asynchronous IO token to transmit some data
+// through this TCP instance
+//
+EFI_STATUS
+EFIAPI
+Tcp4Transmit (
+ IN EFI_TCP4_PROTOCOL *This,
+ IN EFI_TCP4_IO_TOKEN *Token
+ );
+
+//
+// Issue an asynchronous IO token to receive some data
+// through this TCP instance
+//
+EFI_STATUS
+Tcp4Receive (
+ IN EFI_TCP4_PROTOCOL *This,
+ IN EFI_TCP4_IO_TOKEN *Token
+ );
+
+//
+// Issue an asynchronous CloseToken to close a TCP
+// connection represented by instance
+//
+EFI_STATUS
+EFIAPI
+Tcp4Close (
+ IN EFI_TCP4_PROTOCOL *This,
+ IN EFI_TCP4_CLOSE_TOKEN *CloseToken
+ );
+
+//
+// cancle an connect, listent or IO token
+//
+EFI_STATUS
+EFIAPI
+Tcp4Cancel (
+ IN EFI_TCP4_PROTOCOL * This,
+ IN EFI_TCP4_COMPLETION_TOKEN * Token OPTIONAL
+ );
+
+//
+// poll data from NIC for receive
+//
+EFI_STATUS
+EFIAPI
+Tcp4Poll (
+ IN EFI_TCP4_PROTOCOL *This
+ );
+
+#endif
diff --git a/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Misc.c b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Misc.c
new file mode 100644
index 0000000000..7f1f141bc8
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Misc.c
@@ -0,0 +1,1091 @@
+/** @file
+
+Copyright (c) 2005 - 2006, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+
+ Tcp4Misc.c
+
+Abstract:
+
+ Misc support routines for tcp.
+
+
+**/
+
+
+#include "Tcp4Main.h"
+
+NET_LIST_ENTRY mTcpRunQue = {
+ &mTcpRunQue,
+ &mTcpRunQue
+};
+
+NET_LIST_ENTRY mTcpListenQue = {
+ &mTcpListenQue,
+ &mTcpListenQue
+};
+
+TCP_SEQNO mTcpGlobalIss = 0x4d7e980b;
+
+STATIC CHAR16 *mTcpStateName[] = {
+ L"TCP_CLOSED",
+ L"TCP_LISTEN",
+ L"TCP_SYN_SENT",
+ L"TCP_SYN_RCVD",
+ L"TCP_ESTABLISHED",
+ L"TCP_FIN_WAIT_1",
+ L"TCP_FIN_WAIT_2",
+ L"TCP_CLOSING",
+ L"TCP_TIME_WAIT",
+ L"TCP_CLOSE_WAIT",
+ L"TCP_LAST_ACK"
+};
+
+
+/**
+ Initialize the Tcb local related members.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+
+ @return None
+
+**/
+VOID
+TcpInitTcbLocal (
+ IN TCP_CB *Tcb
+ )
+{
+ //
+ // Compute the checksum of the fixed parts of pseudo header
+ //
+ Tcb->HeadSum = NetPseudoHeadChecksum (
+ Tcb->LocalEnd.Ip,
+ Tcb->RemoteEnd.Ip,
+ 0x06,
+ 0
+ );
+
+ Tcb->Iss = TcpGetIss ();
+ Tcb->SndUna = Tcb->Iss;
+ Tcb->SndNxt = Tcb->Iss;
+
+ Tcb->SndWl2 = Tcb->Iss;
+ Tcb->SndWnd = 536;
+
+ Tcb->RcvWnd = GET_RCV_BUFFSIZE (Tcb->Sk);
+
+ //
+ // Fisrt window size is never scaled
+ //
+ Tcb->RcvWndScale = 0;
+}
+
+
+/**
+ Initialize the peer related members.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+ @param Seg Pointer to the segment that contains the peer's
+ intial info.
+ @param Opt Pointer to the options announced by the peer.
+
+ @return None
+
+**/
+VOID
+TcpInitTcbPeer (
+ IN TCP_CB *Tcb,
+ IN TCP_SEG *Seg,
+ IN TCP_OPTION *Opt
+ )
+{
+ UINT16 RcvMss;
+
+ ASSERT (Tcb && Seg && Opt);
+ ASSERT (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN));
+
+ Tcb->SndWnd = Seg->Wnd;
+ Tcb->SndWndMax = Tcb->SndWnd;
+ Tcb->SndWl1 = Seg->Seq;
+
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_ACK)) {
+ Tcb->SndWl2 = Seg->Ack;
+ } else {
+ Tcb->SndWl2 = Tcb->Iss + 1;
+ }
+
+ if (TCP_FLG_ON (Opt->Flag, TCP_OPTION_RCVD_MSS)) {
+ Tcb->SndMss = NET_MAX (64, Opt->Mss);
+
+ RcvMss = TcpGetRcvMss (Tcb->Sk);
+ if (Tcb->SndMss > RcvMss) {
+ Tcb->SndMss = RcvMss;
+ }
+
+ } else {
+ //
+ // One end doesn't support MSS option, use default.
+ //
+ Tcb->RcvMss = 536;
+ }
+
+ Tcb->CWnd = Tcb->SndMss;
+
+ Tcb->Irs = Seg->Seq;
+ Tcb->RcvNxt = Tcb->Irs + 1;
+
+ Tcb->RcvWl2 = Tcb->RcvNxt;
+
+ if (TCP_FLG_ON (Opt->Flag, TCP_OPTION_RCVD_WS) &&
+ !TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_WS)) {
+
+ Tcb->SndWndScale = Opt->WndScale;
+
+ Tcb->RcvWndScale = TcpComputeScale (Tcb);
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_RCVD_WS);
+
+ } else {
+ //
+ // One end doesn't support window scale option. use zero.
+ //
+ Tcb->RcvWndScale = 0;
+ }
+
+ if (TCP_FLG_ON (Opt->Flag, TCP_OPTION_RCVD_TS) &&
+ !TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_TS)) {
+
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_SND_TS);
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_RCVD_TS);
+
+ //
+ // Compute the effective SndMss per RFC1122
+ // section 4.2.2.6. If timestamp option is
+ // enabled, it will always occupy 12 bytes.
+ //
+ Tcb->SndMss -= TCP_OPTION_TS_ALIGNED_LEN;
+ }
+}
+
+
+/**
+ Locate a listen TCB that matchs the Local and Remote.
+
+ @param Local Pointer to the local (IP, Port).
+ @param Remote Pointer to the remote (IP, Port).
+
+ @return Pointer to the TCP_CB with the least number of wildcard, if NULL no match is found.
+
+**/
+STATIC
+TCP_CB *
+TcpLocateListenTcb (
+ IN TCP_PEER *Local,
+ IN TCP_PEER *Remote
+ )
+{
+ NET_LIST_ENTRY *Entry;
+ TCP_CB *Node;
+ TCP_CB *Match;
+ INTN Last;
+ INTN Cur;
+
+ Last = 4;
+ Match = NULL;
+
+ NET_LIST_FOR_EACH (Entry, &mTcpListenQue) {
+ Node = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);
+
+ if ((Local->Port != Node->LocalEnd.Port) ||
+ !TCP_PEER_MATCH (Remote, &Node->RemoteEnd) ||
+ !TCP_PEER_MATCH (Local, &Node->LocalEnd)
+ ) {
+
+ continue;
+ }
+
+ //
+ // Compute the number of wildcard
+ //
+ Cur = 0;
+ if (Node->RemoteEnd.Ip == 0) {
+ Cur++;
+ }
+
+ if (Node->RemoteEnd.Port == 0) {
+ Cur++;
+ }
+
+ if (Node->LocalEnd.Ip == 0) {
+ Cur++;
+ }
+
+ if (Cur < Last) {
+ if (Cur == 0) {
+ return Node;
+ }
+
+ Last = Cur;
+ Match = Node;
+ }
+ }
+
+ return Match;
+}
+
+
+/**
+ Try to find one Tcb whose <Ip, Port> equals to <IN Addr, IN Port>.
+
+ @param Addr Pointer to the IP address needs to match.
+ @param Port The port number needs to match.
+
+ @return The Tcb which matches the <Addr Port> paire exists or not.
+
+**/
+BOOLEAN
+TcpFindTcbByPeer (
+ IN EFI_IPv4_ADDRESS *Addr,
+ IN TCP_PORTNO Port
+ )
+{
+ TCP_PORTNO LocalPort;
+ NET_LIST_ENTRY *Entry;
+ TCP_CB *Tcb;
+
+ ASSERT ((Addr != NULL) && (Port != 0));
+
+ LocalPort = HTONS (Port);
+
+ NET_LIST_FOR_EACH (Entry, &mTcpListenQue) {
+ Tcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);
+
+ if ((EFI_IP4 (*Addr) == Tcb->LocalEnd.Ip) &&
+ (LocalPort == Tcb->LocalEnd.Port)) {
+
+ return TRUE;
+ }
+ }
+
+ NET_LIST_FOR_EACH (Entry, &mTcpRunQue) {
+ Tcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);
+
+ if (((EFI_IP4 (*Addr) == Tcb->LocalEnd.Ip)) &&
+ (LocalPort == Tcb->LocalEnd.Port)) {
+
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+
+/**
+ Locate the TCP_CB related to the socket pair.
+
+ @param LocalPort The local port number.
+ @param LocalIp The local IP address.
+ @param RemotePort The remote port number.
+ @param RemoteIp The remote IP address.
+ @param Syn Whether to search the listen sockets, if TRUE, the
+ listen sockets are searched.
+
+ @return Pointer to the related TCP_CB, if NULL no match is found.
+
+**/
+TCP_CB *
+TcpLocateTcb (
+ IN TCP_PORTNO LocalPort,
+ IN UINT32 LocalIp,
+ IN TCP_PORTNO RemotePort,
+ IN UINT32 RemoteIp,
+ IN BOOLEAN Syn
+ )
+{
+ TCP_PEER Local;
+ TCP_PEER Remote;
+ NET_LIST_ENTRY *Entry;
+ TCP_CB *Tcb;
+
+ Local.Port = LocalPort;
+ Local.Ip = LocalIp;
+
+ Remote.Port = RemotePort;
+ Remote.Ip = RemoteIp;
+
+ //
+ // First check for exact match.
+ //
+ NET_LIST_FOR_EACH (Entry, &mTcpRunQue) {
+ Tcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);
+
+ if (TCP_PEER_EQUAL (&Remote, &Tcb->RemoteEnd) &&
+ TCP_PEER_EQUAL (&Local, &Tcb->LocalEnd)) {
+
+ NetListRemoveEntry (&Tcb->List);
+ NetListInsertHead (&mTcpRunQue, &Tcb->List);
+
+ return Tcb;
+ }
+ }
+
+ //
+ // Only check listen queue when SYN flag is on
+ //
+ if (Syn) {
+ return TcpLocateListenTcb (&Local, &Remote);
+ }
+
+ return NULL;
+}
+
+
+/**
+ Insert a Tcb into the proper queue.
+
+ @param Tcb Pointer to the TCP_CB to be inserted.
+
+ @retval 0 The Tcb is inserted successfully.
+ @retval -1 Error condition occurred.
+
+**/
+INTN
+TcpInsertTcb (
+ IN TCP_CB *Tcb
+ )
+{
+ NET_LIST_ENTRY *Entry;
+ NET_LIST_ENTRY *Head;
+ TCP_CB *Node;
+ TCP4_PROTO_DATA *TcpProto;
+
+ ASSERT (
+ Tcb &&
+ (
+ (Tcb->State == TCP_LISTEN) ||
+ (Tcb->State == TCP_SYN_SENT) ||
+ (Tcb->State == TCP_SYN_RCVD) ||
+ (Tcb->State == TCP_CLOSED)
+ )
+ );
+
+ if (Tcb->LocalEnd.Port == 0) {
+ return -1;
+ }
+
+ Head = &mTcpRunQue;
+
+ if (Tcb->State == TCP_LISTEN) {
+ Head = &mTcpListenQue;
+ }
+
+ //
+ // Check that Tcb isn't already on the list.
+ //
+ NET_LIST_FOR_EACH (Entry, Head) {
+ Node = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);
+
+ if (TCP_PEER_EQUAL (&Tcb->LocalEnd, &Node->LocalEnd) &&
+ TCP_PEER_EQUAL (&Tcb->RemoteEnd, &Node->RemoteEnd)) {
+
+ return -1;
+ }
+ }
+
+ NetListInsertHead (Head, &Tcb->List);
+
+ TcpProto = (TCP4_PROTO_DATA *) Tcb->Sk->ProtoReserved;
+ TcpSetVariableData (TcpProto->TcpService);
+
+ return 0;
+}
+
+
+/**
+ Clone a TCP_CB from Tcb.
+
+ @param Tcb Pointer to the TCP_CB to be cloned.
+
+ @return Pointer to the new cloned TCP_CB, if NULL error condition occurred.
+
+**/
+TCP_CB *
+TcpCloneTcb (
+ IN TCP_CB *Tcb
+ )
+{
+ TCP_CB *Clone;
+
+ Clone = NetAllocatePool (sizeof (TCP_CB));
+
+ if (Clone == NULL) {
+ return NULL;
+
+ }
+
+ NetCopyMem (Clone, Tcb, sizeof (TCP_CB));
+
+ //
+ // Increate the reference count of the shared IpInfo.
+ //
+ NET_GET_REF (Tcb->IpInfo);
+
+ NetListInit (&Clone->List);
+ NetListInit (&Clone->SndQue);
+ NetListInit (&Clone->RcvQue);
+
+ Clone->Sk = SockClone (Tcb->Sk);
+ if (Clone->Sk == NULL) {
+ TCP4_DEBUG_ERROR (("TcpCloneTcb: failed to clone a sock\n"));
+ NetFreePool (Clone);
+ return NULL;
+ }
+
+ ((TCP4_PROTO_DATA *) (Clone->Sk->ProtoReserved))->TcpPcb = Clone;
+
+ return Clone;
+}
+
+
+/**
+ Compute an ISS to be used by a new connection.
+
+ None
+
+ @return The result ISS.
+
+**/
+TCP_SEQNO
+TcpGetIss (
+ VOID
+ )
+{
+ mTcpGlobalIss += 2048;
+ return mTcpGlobalIss;
+}
+
+
+/**
+ Get the local mss.
+
+ None
+
+ @return The mss size.
+
+**/
+UINT16
+TcpGetRcvMss (
+ IN SOCKET *Sock
+ )
+{
+ EFI_SIMPLE_NETWORK_MODE SnpMode;
+ TCP4_PROTO_DATA *TcpProto;
+ EFI_IP4_PROTOCOL *Ip;
+
+ ASSERT (Sock);
+
+ TcpProto = (TCP4_PROTO_DATA *) Sock->ProtoReserved;
+ Ip = TcpProto->TcpService->IpIo->Ip;
+ ASSERT (Ip);
+
+ Ip->GetModeData (Ip, NULL, NULL, &SnpMode);
+
+ return (UINT16) (SnpMode.MaxPacketSize - 40);
+}
+
+
+/**
+ Set the Tcb's state.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+ @param State The state to be set.
+
+ @return None
+
+**/
+VOID
+TcpSetState (
+ IN TCP_CB *Tcb,
+ IN UINT8 State
+ )
+{
+ TCP4_DEBUG_TRACE (
+ ("Tcb (%x) state %s --> %s\n",
+ Tcb,
+ mTcpStateName[Tcb->State],
+ mTcpStateName[State])
+ );
+
+ Tcb->State = State;
+
+ switch (State) {
+ case TCP_ESTABLISHED:
+
+ SockConnEstablished (Tcb->Sk);
+ break;
+
+ case TCP_CLOSED:
+
+ SockConnClosed (Tcb->Sk);
+
+ break;
+ }
+}
+
+
+/**
+ Compute the TCP segment's checksum.
+
+ @param Nbuf Pointer to the buffer that contains the TCP
+ segment.
+ @param HeadSum The checksum value of the fixed part of pseudo
+ header.
+
+ @return The checksum value.
+
+**/
+UINT16
+TcpChecksum (
+ IN NET_BUF *Nbuf,
+ IN UINT16 HeadSum
+ )
+{
+ UINT16 Checksum;
+
+ Checksum = NetbufChecksum (Nbuf);
+ Checksum = NetAddChecksum (Checksum, HeadSum);
+
+ Checksum = NetAddChecksum (
+ Checksum,
+ HTONS ((UINT16) Nbuf->TotalSize)
+ );
+
+ return ~Checksum;
+}
+
+
+/**
+ Translate the information from the head of the received TCP
+ segment Nbuf contains and fill it into a TCP_SEG structure.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+ @param Nbuf Pointer to the buffer contains the TCP segment.
+
+ @return Pointer to the TCP_SEG that contains the translated TCP head information.
+
+**/
+TCP_SEG *
+TcpFormatNetbuf (
+ IN TCP_CB *Tcb,
+ IN NET_BUF *Nbuf
+ )
+{
+ TCP_SEG *Seg;
+ TCP_HEAD *Head;
+
+ Seg = TCPSEG_NETBUF (Nbuf);
+ Head = (TCP_HEAD *) NetbufGetByte (Nbuf, 0, NULL);
+ Nbuf->Tcp = Head;
+
+ Seg->Seq = NTOHL (Head->Seq);
+ Seg->Ack = NTOHL (Head->Ack);
+ Seg->End = Seg->Seq + (Nbuf->TotalSize - (Head->HeadLen << 2));
+
+ Seg->Urg = NTOHS (Head->Urg);
+ Seg->Wnd = (NTOHS (Head->Wnd) << Tcb->SndWndScale);
+ Seg->Flag = Head->Flag;
+
+ //
+ // SYN and FIN flag occupy one sequence space each.
+ //
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) {
+ //
+ // RFC requires that initial window not be scaled
+ //
+ Seg->Wnd = NTOHS (Head->Wnd);
+ Seg->End++;
+ }
+
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_FIN)) {
+ Seg->End++;
+ }
+
+ return Seg;
+}
+
+
+/**
+ Reset the connection related with Tcb.
+
+ @param Tcb Pointer to the TCP_CB of the connection to be
+ reset.
+
+ @return None
+
+**/
+VOID
+TcpResetConnection (
+ IN TCP_CB *Tcb
+ )
+{
+ NET_BUF *Nbuf;
+ TCP_HEAD *Nhead;
+
+ Nbuf = NetbufAlloc (TCP_MAX_HEAD);
+
+ if (Nbuf == NULL) {
+ return ;
+ }
+
+ Nhead = (TCP_HEAD *) NetbufAllocSpace (
+ Nbuf,
+ sizeof (TCP_HEAD),
+ NET_BUF_TAIL
+ );
+
+ ASSERT (Nhead != NULL);
+
+ Nbuf->Tcp = Nhead;
+
+ Nhead->Flag = TCP_FLG_RST;
+ Nhead->Seq = HTONL (Tcb->SndNxt);
+ Nhead->Ack = HTONL (Tcb->RcvNxt);
+ Nhead->SrcPort = Tcb->LocalEnd.Port;
+ Nhead->DstPort = Tcb->RemoteEnd.Port;
+ Nhead->HeadLen = (sizeof (TCP_HEAD) >> 2);
+ Nhead->Res = 0;
+ Nhead->Wnd = HTONS (0xFFFF);
+ Nhead->Checksum = 0;
+ Nhead->Urg = 0;
+ Nhead->Checksum = TcpChecksum (Nbuf, Tcb->HeadSum);
+
+ TcpSendIpPacket (Tcb, Nbuf, Tcb->LocalEnd.Ip, Tcb->RemoteEnd.Ip);
+
+ NetbufFree (Nbuf);
+}
+
+
+/**
+ Initialize an active connection,
+
+ @param Tcb Pointer to the TCP_CB that wants to initiate a
+ connection.
+
+ @return None
+
+**/
+VOID
+TcpOnAppConnect (
+ IN TCP_CB *Tcb
+ )
+{
+ TcpInitTcbLocal (Tcb);
+ TcpSetState (Tcb, TCP_SYN_SENT);
+
+ TcpSetTimer (Tcb, TCP_TIMER_CONNECT, Tcb->ConnectTimeout);
+ TcpToSendData (Tcb, 1);
+}
+
+
+/**
+ Initiate the connection close procedure, called when
+ applications want to close the connection.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+
+ @return None.
+
+**/
+VOID
+TcpOnAppClose (
+ IN TCP_CB *Tcb
+ )
+{
+ ASSERT (Tcb);
+
+ if (!NetListIsEmpty (&Tcb->RcvQue) || GET_RCV_DATASIZE (Tcb->Sk)) {
+
+ TCP4_DEBUG_WARN (("TcpOnAppClose: connection reset "
+ "because data is lost for TCB %x\n", Tcb));
+
+ TcpResetConnection (Tcb);
+ TcpClose (Tcb);
+ return;
+ }
+
+ switch (Tcb->State) {
+ case TCP_CLOSED:
+ case TCP_LISTEN:
+ case TCP_SYN_SENT:
+ TcpSetState (Tcb, TCP_CLOSED);
+ break;
+
+ case TCP_SYN_RCVD:
+ case TCP_ESTABLISHED:
+ TcpSetState (Tcb, TCP_FIN_WAIT_1);
+ break;
+
+ case TCP_CLOSE_WAIT:
+ TcpSetState (Tcb, TCP_LAST_ACK);
+ break;
+ }
+
+ TcpToSendData (Tcb, 1);
+}
+
+
+/**
+ Check whether the application's newly delivered data
+ can be sent out.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+
+ @retval 0 Whether the data is sent out or is buffered for
+ further sending.
+ @retval -1 The Tcb is not in a state that data is permitted to
+ be sent out.
+
+**/
+INTN
+TcpOnAppSend (
+ IN TCP_CB *Tcb
+ )
+{
+
+ switch (Tcb->State) {
+ case TCP_CLOSED:
+ return -1;
+ break;
+
+ case TCP_LISTEN:
+ return -1;
+ break;
+
+ case TCP_SYN_SENT:
+ case TCP_SYN_RCVD:
+ return 0;
+ break;
+
+ case TCP_ESTABLISHED:
+ case TCP_CLOSE_WAIT:
+ TcpToSendData (Tcb, 0);
+ return 0;
+ break;
+
+ case TCP_FIN_WAIT_1:
+ case TCP_FIN_WAIT_2:
+ case TCP_CLOSING:
+ case TCP_LAST_ACK:
+ case TCP_TIME_WAIT:
+ return -1;
+ break;
+ }
+
+ return 0;
+}
+
+
+/**
+ Application has consumed some data, check whether
+ to send a window updata ack or a delayed ack.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+
+
+**/
+INTN
+TcpOnAppConsume (
+ IN TCP_CB *Tcb
+ )
+{
+
+ switch (Tcb->State) {
+ case TCP_CLOSED:
+ return -1;
+ break;
+
+ case TCP_LISTEN:
+ return -1;
+ break;
+
+ case TCP_SYN_SENT:
+ case TCP_SYN_RCVD:
+ return 0;
+ break;
+
+ case TCP_ESTABLISHED:
+ if (TcpRcvWinNow (Tcb) > TcpRcvWinOld (Tcb)) {
+
+ if (TcpRcvWinOld (Tcb) < Tcb->RcvMss) {
+
+ TCP4_DEBUG_TRACE (("TcpOnAppConsume: send a window"
+ " update for a window closed Tcb(%x)\n", Tcb));
+
+ TcpSendAck (Tcb);
+ } else if (Tcb->DelayedAck == 0) {
+
+ TCP4_DEBUG_TRACE (("TcpOnAppConsume: scheduled a delayed"
+ " ACK to update window for Tcb(%x)\n", Tcb));
+
+ Tcb->DelayedAck = 1;
+ }
+ }
+
+ break;
+
+ case TCP_CLOSE_WAIT:
+ return 0;
+ break;
+
+ case TCP_FIN_WAIT_1:
+ case TCP_FIN_WAIT_2:
+ case TCP_CLOSING:
+ case TCP_LAST_ACK:
+ case TCP_TIME_WAIT:
+ return -1;
+ break;
+ }
+
+ return -1;
+}
+
+
+/**
+ Abort the connection by sending a reset segment, called
+ when the application wants to abort the connection.
+
+ @param Tcb Pointer to the TCP_CB of the TCP instance.
+
+ @return None.
+
+**/
+VOID
+TcpOnAppAbort (
+ IN TCP_CB *Tcb
+ )
+{
+ TCP4_DEBUG_WARN (("TcpOnAppAbort: connection reset "
+ "issued by application for TCB %x\n", Tcb));
+
+ switch (Tcb->State) {
+ case TCP_SYN_RCVD:
+ case TCP_ESTABLISHED:
+ case TCP_FIN_WAIT_1:
+ case TCP_FIN_WAIT_2:
+ case TCP_CLOSE_WAIT:
+ TcpResetConnection (Tcb);
+ break;
+ }
+
+ TcpSetState (Tcb, TCP_CLOSED);
+}
+
+
+/**
+ Set the Tdp4 variable data.
+
+ @param Tcp4Service Tcp4 service data.
+
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources to set the variable.
+ @retval other Set variable failed.
+
+**/
+EFI_STATUS
+TcpSetVariableData (
+ IN TCP4_SERVICE_DATA *Tcp4Service
+ )
+{
+ UINT32 NumConfiguredInstance;
+ NET_LIST_ENTRY *Entry;
+ TCP_CB *TcpPcb;
+ TCP4_PROTO_DATA *TcpProto;
+ UINTN VariableDataSize;
+ EFI_TCP4_VARIABLE_DATA *Tcp4VariableData;
+ EFI_TCP4_SERVICE_POINT *Tcp4ServicePoint;
+ CHAR16 *NewMacString;
+ EFI_STATUS Status;
+
+ NumConfiguredInstance = 0;
+
+ //
+ // Go through the running queue to count the instances.
+ //
+ NET_LIST_FOR_EACH (Entry, &mTcpRunQue) {
+ TcpPcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);
+
+ TcpProto = (TCP4_PROTO_DATA *) TcpPcb->Sk->ProtoReserved;
+
+ if (TcpProto->TcpService == Tcp4Service) {
+ //
+ // This tcp instance belongs to the Tcp4Service.
+ //
+ NumConfiguredInstance++;
+ }
+ }
+
+ //
+ // Go through the listening queue to count the instances.
+ //
+ NET_LIST_FOR_EACH (Entry, &mTcpListenQue) {
+ TcpPcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);
+
+ TcpProto = (TCP4_PROTO_DATA *) TcpPcb->Sk->ProtoReserved;
+
+ if (TcpProto->TcpService == Tcp4Service) {
+ //
+ // This tcp instance belongs to the Tcp4Service.
+ //
+ NumConfiguredInstance++;
+ }
+ }
+
+ //
+ // Calculate the size of the Tcp4VariableData. As there may be no Tcp4 child,
+ // we should add extra buffer for the service points only if the number of configured
+ // children is more than 1.
+ //
+ VariableDataSize = sizeof (EFI_TCP4_VARIABLE_DATA);
+
+ if (NumConfiguredInstance > 1) {
+ VariableDataSize += sizeof (EFI_TCP4_SERVICE_POINT) * (NumConfiguredInstance - 1);
+ }
+
+ Tcp4VariableData = NetAllocatePool (VariableDataSize);
+ if (Tcp4VariableData == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Tcp4VariableData->DriverHandle = Tcp4Service->DriverBindingHandle;
+ Tcp4VariableData->ServiceCount = NumConfiguredInstance;
+
+ Tcp4ServicePoint = &Tcp4VariableData->Services[0];
+
+ //
+ // Go through the running queue to fill the service points.
+ //
+ NET_LIST_FOR_EACH (Entry, &mTcpRunQue) {
+ TcpPcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);
+
+ TcpProto = (TCP4_PROTO_DATA *) TcpPcb->Sk->ProtoReserved;
+
+ if (TcpProto->TcpService == Tcp4Service) {
+ //
+ // This tcp instance belongs to the Tcp4Service.
+ //
+ Tcp4ServicePoint->InstanceHandle = TcpPcb->Sk->SockHandle;
+ EFI_IP4 (Tcp4ServicePoint->LocalAddress) = TcpPcb->LocalEnd.Ip;
+ Tcp4ServicePoint->LocalPort = NTOHS (TcpPcb->LocalEnd.Port);
+ EFI_IP4 (Tcp4ServicePoint->RemoteAddress) = TcpPcb->RemoteEnd.Ip;
+ Tcp4ServicePoint->RemotePort = NTOHS (TcpPcb->RemoteEnd.Port);
+
+ Tcp4ServicePoint++;
+ }
+ }
+
+ //
+ // Go through the listening queue to fill the service points.
+ //
+ NET_LIST_FOR_EACH (Entry, &mTcpListenQue) {
+ TcpPcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);
+
+ TcpProto = (TCP4_PROTO_DATA *) TcpPcb->Sk->ProtoReserved;
+
+ if (TcpProto->TcpService == Tcp4Service) {
+ //
+ // This tcp instance belongs to the Tcp4Service.
+ //
+ Tcp4ServicePoint->InstanceHandle = TcpPcb->Sk->SockHandle;
+ EFI_IP4 (Tcp4ServicePoint->LocalAddress) = TcpPcb->LocalEnd.Ip;
+ Tcp4ServicePoint->LocalPort = NTOHS (TcpPcb->LocalEnd.Port);
+ EFI_IP4 (Tcp4ServicePoint->RemoteAddress) = TcpPcb->RemoteEnd.Ip;
+ Tcp4ServicePoint->RemotePort = NTOHS (TcpPcb->RemoteEnd.Port);
+
+ Tcp4ServicePoint++;
+ }
+ }
+
+ //
+ // Get the mac string.
+ //
+ Status = NetLibGetMacString (
+ Tcp4Service->ControllerHandle,
+ Tcp4Service->DriverBindingHandle,
+ &NewMacString
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ if (Tcp4Service->MacString != NULL) {
+ //
+ // The variable is set already, we're going to update it.
+ //
+ if (StrCmp (Tcp4Service->MacString, NewMacString) != 0) {
+ //
+ // The mac address is changed, delete the previous variable first.
+ //
+ gRT->SetVariable (
+ Tcp4Service->MacString,
+ &gEfiTcp4ServiceBindingProtocolGuid,
+ EFI_VARIABLE_BOOTSERVICE_ACCESS,
+ 0,
+ NULL
+ );
+ }
+
+ NetFreePool (Tcp4Service->MacString);
+ }
+
+ Tcp4Service->MacString = NewMacString;
+
+ Status = gRT->SetVariable (
+ Tcp4Service->MacString,
+ &gEfiTcp4ServiceBindingProtocolGuid,
+ EFI_VARIABLE_BOOTSERVICE_ACCESS,
+ VariableDataSize,
+ (VOID *) Tcp4VariableData
+ );
+
+ON_ERROR:
+
+ NetFreePool (Tcp4VariableData);
+
+ return Status;
+}
+
+
+/**
+ Clear the variable and free the resource.
+
+ @param Tcp4Service Tcp4 service data.
+
+ @return None.
+
+**/
+VOID
+TcpClearVariableData (
+ IN TCP4_SERVICE_DATA *Tcp4Service
+ )
+{
+ ASSERT (Tcp4Service->MacString != NULL);
+
+ gRT->SetVariable (
+ Tcp4Service->MacString,
+ &gEfiTcp4ServiceBindingProtocolGuid,
+ EFI_VARIABLE_BOOTSERVICE_ACCESS,
+ 0,
+ NULL
+ );
+
+ NetFreePool (Tcp4Service->MacString);
+ Tcp4Service->MacString = NULL;
+}
+
diff --git a/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Option.c b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Option.c
new file mode 100644
index 0000000000..d430a2e10d
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Option.c
@@ -0,0 +1,380 @@
+/** @file
+
+Copyright (c) 2005 - 2006, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+
+ Tcp4Option.c
+
+Abstract:
+
+ Routines to process TCP option.
+
+
+**/
+
+#include "Tcp4Main.h"
+
+STATIC
+UINT16
+TcpGetUint16 (
+ IN UINT8 *Buf
+ )
+{
+ UINT16 Value;
+ NetCopyMem (&Value, Buf, sizeof (UINT16));
+ return NTOHS (Value);
+}
+
+STATIC
+VOID
+TcpPutUint16 (
+ IN UINT8 *Buf,
+ IN UINT16 Data
+ )
+{
+ Data = HTONS (Data);
+ NetCopyMem (Buf, &Data, sizeof (UINT16));
+}
+
+STATIC
+UINT32
+TcpGetUint32 (
+ IN UINT8 *Buf
+ )
+{
+ UINT32 Value;
+ NetCopyMem (&Value, Buf, sizeof (UINT32));
+ return NTOHL (Value);
+}
+
+STATIC
+VOID
+TcpPutUint32 (
+ IN UINT8 *Buf,
+ IN UINT32 Data
+ )
+{
+ Data = HTONL (Data);
+ NetCopyMem (Buf, &Data, sizeof (UINT32));
+}
+
+
+/**
+ Compute the window scale value according to the given
+ buffer size.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+
+ @retval UINT8 The scale value.
+
+**/
+UINT8
+TcpComputeScale (
+ IN TCP_CB *Tcb
+ )
+{
+ UINT8 Scale;
+ UINT32 BufSize;
+
+ ASSERT (Tcb && Tcb->Sk);
+
+ BufSize = GET_RCV_BUFFSIZE (Tcb->Sk);
+
+ Scale = 0;
+ while ((Scale < TCP_OPTION_MAX_WS) &&
+ ((UINT32) (TCP_OPTION_MAX_WIN << Scale) < BufSize)) {
+
+ Scale++;
+ }
+
+ return Scale;
+}
+
+
+/**
+ Build the TCP option in three-way handshake.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+ @param Nbuf Pointer to the buffer to store the options.
+
+ @return The total length of the TCP option field.
+
+**/
+UINT16
+TcpSynBuildOption (
+ IN TCP_CB *Tcb,
+ IN NET_BUF *Nbuf
+ )
+{
+ char *Data;
+ UINT16 Len;
+
+ ASSERT (Tcb && Nbuf && !Nbuf->Tcp);
+
+ Len = 0;
+
+ //
+ // Add timestamp option if not disabled by application
+ // and it is the first SYN segment or the peer has sent
+ // us its timestamp.
+ //
+ if (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_TS) &&
+ (!TCP_FLG_ON (TCPSEG_NETBUF (Nbuf)->Flag, TCP_FLG_ACK) ||
+ TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RCVD_TS))) {
+
+ Data = NetbufAllocSpace (
+ Nbuf,
+ TCP_OPTION_TS_ALIGNED_LEN,
+ NET_BUF_HEAD
+ );
+
+ ASSERT (Data);
+ Len += TCP_OPTION_TS_ALIGNED_LEN;
+
+ TcpPutUint32 (Data, TCP_OPTION_TS_FAST);
+ TcpPutUint32 (Data + 4, mTcpTick);
+ TcpPutUint32 (Data + 8, 0);
+ }
+
+ //
+ // Build window scale option, only when are configured
+ // to send WS option, and either we are doing active
+ // open or we have received WS option from peer.
+ //
+ if (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_WS) &&
+ (!TCP_FLG_ON (TCPSEG_NETBUF (Nbuf)->Flag, TCP_FLG_ACK) ||
+ TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RCVD_WS))) {
+
+ Data = NetbufAllocSpace (
+ Nbuf,
+ TCP_OPTION_WS_ALIGNED_LEN,
+ NET_BUF_HEAD
+ );
+
+ ASSERT (Data);
+
+ Len += TCP_OPTION_WS_ALIGNED_LEN;
+ TcpPutUint32 (Data, TCP_OPTION_WS_FAST | TcpComputeScale (Tcb));
+ }
+
+ //
+ // Build MSS option
+ //
+ Data = NetbufAllocSpace (Nbuf, TCP_OPTION_MSS_LEN, 1);
+ ASSERT (Data);
+
+ Len += TCP_OPTION_MSS_LEN;
+ TcpPutUint32 (Data, TCP_OPTION_MSS_FAST | Tcb->RcvMss);
+
+ return Len;
+}
+
+
+/**
+ Build the TCP option in synchronized states.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+ @param Nbuf Pointer to the buffer to store the options.
+
+ @return The total length of the TCP option field.
+
+**/
+UINT16
+TcpBuildOption (
+ IN TCP_CB *Tcb,
+ IN NET_BUF *Nbuf
+ )
+{
+ char *Data;
+ UINT16 Len;
+
+ ASSERT (Tcb && Nbuf && !Nbuf->Tcp);
+ Len = 0;
+
+ //
+ // Build Timestamp option
+ //
+ if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_SND_TS) &&
+ !TCP_FLG_ON (TCPSEG_NETBUF (Nbuf)->Flag, TCP_FLG_RST)) {
+
+ Data = NetbufAllocSpace (
+ Nbuf,
+ TCP_OPTION_TS_ALIGNED_LEN,
+ NET_BUF_HEAD
+ );
+
+ ASSERT (Data);
+ Len += TCP_OPTION_TS_ALIGNED_LEN;
+
+ TcpPutUint32 (Data, TCP_OPTION_TS_FAST);
+ TcpPutUint32 (Data + 4, mTcpTick);
+ TcpPutUint32 (Data + 8, Tcb->TsRecent);
+ }
+
+ return Len;
+}
+
+
+/**
+ Parse the supported options.
+
+ @param Tcp Pointer to the TCP_CB of this TCP instance.
+ @param Option Pointer to the TCP_OPTION used to store the successfully pasrsed
+ options.
+
+ @retval 0 The options are successfully pasrsed.
+ @retval -1 Ilegal option was found.
+
+**/
+INTN
+TcpParseOption (
+ IN TCP_HEAD *Tcp,
+ IN TCP_OPTION *Option
+ )
+{
+ UINT8 *Head;
+ UINT8 TotalLen;
+ UINT8 Cur;
+ UINT8 Type;
+ UINT8 Len;
+
+ ASSERT (Tcp && Option);
+
+ Option->Flag = 0;
+
+ TotalLen = (Tcp->HeadLen << 2) - sizeof (TCP_HEAD);
+ if (TotalLen <= 0) {
+ return 0;
+ }
+
+ Head = (UINT8 *) (Tcp + 1);
+
+ //
+ // Fast process of timestamp option
+ //
+ if ((TotalLen == TCP_OPTION_TS_ALIGNED_LEN) &&
+ (TcpGetUint32 (Head) == TCP_OPTION_TS_FAST)) {
+
+ Option->TSVal = TcpGetUint32 (Head + 4);
+ Option->TSEcr = TcpGetUint32 (Head + 8);
+ Option->Flag = TCP_OPTION_RCVD_TS;
+
+ return 0;
+ }
+
+ //
+ // Slow path to process the options.
+ //
+ Cur = 0;
+
+ while (Cur < TotalLen) {
+ Type = Head[Cur];
+
+ switch (Type) {
+ case TCP_OPTION_MSS:
+ Len = Head[Cur + 1];
+
+ if ((Len != TCP_OPTION_MSS_LEN) ||
+ (TotalLen - Cur < TCP_OPTION_MSS_LEN)) {
+
+ return -1;
+ }
+
+ Option->Mss = TcpGetUint16 (&Head[Cur + 2]);
+ TCP_SET_FLG (Option->Flag, TCP_OPTION_RCVD_MSS);
+
+ Cur += TCP_OPTION_MSS_LEN;
+ break;
+
+ case TCP_OPTION_WS:
+ Len = Head[Cur + 1];
+
+ if ((Len != TCP_OPTION_WS_LEN) ||
+ (TotalLen - Cur < TCP_OPTION_WS_LEN)) {
+
+ return -1;
+ }
+
+ Option->WndScale = NET_MIN (14, Head[Cur + 2]);
+ TCP_SET_FLG (Option->Flag, TCP_OPTION_RCVD_WS);
+
+ Cur += TCP_OPTION_WS_LEN;
+ break;
+
+ case TCP_OPTION_TS:
+ Len = Head[Cur + 1];
+
+ if ((Len != TCP_OPTION_TS_LEN) ||
+ (TotalLen - Cur < TCP_OPTION_TS_LEN)) {
+
+ return -1;
+ }
+
+ Option->TSVal = TcpGetUint32 (&Head[Cur + 2]);
+ Option->TSEcr = TcpGetUint32 (&Head[Cur + 6]);
+ TCP_SET_FLG (Option->Flag, TCP_OPTION_RCVD_TS);
+
+ Cur += TCP_OPTION_TS_LEN;
+ break;
+
+ case TCP_OPTION_NOP:
+ Cur++;
+ break;
+
+ case TCP_OPTION_EOP:
+ Cur = TotalLen;
+ break;
+
+ default:
+ Len = Head[Cur + 1];
+
+ if (TotalLen - Cur < Len || Len < 2) {
+ return -1;
+ }
+
+ Cur = Cur + Len;
+ break;
+ }
+
+ }
+
+ return 0;
+}
+
+
+/**
+ Check the segment against PAWS.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+ @param TSVal The timestamp value.
+
+ @retval 1 The segment passed the PAWS check.
+ @retval 0 The segment failed to pass the PAWS check.
+
+**/
+UINT32
+TcpPawsOK (
+ IN TCP_CB *Tcb,
+ IN UINT32 TSVal
+ )
+{
+ //
+ // PAWS as defined in RFC1323, buggy...
+ //
+ if (TCP_TIME_LT (TSVal, Tcb->TsRecent) &&
+ TCP_TIME_LT (Tcb->TsRecentAge + TCP_PAWS_24DAY, mTcpTick)) {
+
+ return 0;
+
+ }
+
+ return 1;
+}
diff --git a/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Option.h b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Option.h
new file mode 100644
index 0000000000..8074cb5564
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Option.h
@@ -0,0 +1,107 @@
+/** @file
+
+Copyright (c) 2005 - 2006, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+
+ Tcp4Option.h
+
+Abstract:
+
+
+**/
+
+#ifndef _TCP4_OPTION_H_
+#define _TCP4_OPTION_H_
+
+//
+// The structure to store the parse option value.
+// ParseOption only parse the options, don't process them.
+//
+typedef struct s_TCP_OPTION {
+ UINT8 Flag; // flag such as TCP_OPTION_RCVD_MSS
+ UINT8 WndScale; // the WndScale received
+ UINT16 Mss; // the Mss received
+ UINT32 TSVal; // the TSVal field in a timestamp option
+ UINT32 TSEcr; // the TSEcr field in a timestamp option
+} TCP_OPTION;
+
+enum {
+
+ //
+ // supported TCP option type and their length
+ //
+ TCP_OPTION_EOP = 0, // End Of oPtion
+ TCP_OPTION_NOP = 1, // No-Option.
+ TCP_OPTION_MSS = 2, // Maximum Segment Size
+ TCP_OPTION_WS = 3, // Window scale
+ TCP_OPTION_TS = 8, // Timestamp
+ TCP_OPTION_MSS_LEN = 4, // length of MSS option
+ TCP_OPTION_WS_LEN = 3, // length of window scale option
+ TCP_OPTION_TS_LEN = 10, // length of timestamp option
+ TCP_OPTION_WS_ALIGNED_LEN = 4, // length of window scale option, aligned
+ TCP_OPTION_TS_ALIGNED_LEN = 12, // length of timestamp option, aligned
+
+ //
+ // recommend format of timestamp window scale
+ // option for fast process.
+ //
+ TCP_OPTION_TS_FAST = ((TCP_OPTION_NOP << 24) |
+ (TCP_OPTION_NOP << 16) |
+ (TCP_OPTION_TS << 8) |
+ TCP_OPTION_TS_LEN),
+
+ TCP_OPTION_WS_FAST = ((TCP_OPTION_NOP << 24) |
+ (TCP_OPTION_WS << 16) |
+ (TCP_OPTION_WS_LEN << 8)),
+
+ TCP_OPTION_MSS_FAST = ((TCP_OPTION_MSS << 24) |
+ (TCP_OPTION_MSS_LEN << 16)),
+
+ //
+ // Other misc definations
+ //
+ TCP_OPTION_MAX_WS = 14, // Maxium window scale value
+ TCP_OPTION_MAX_WIN = 0xffff, // max window size in TCP header
+ TCP_OPTION_RCVD_MSS = 0x01,
+ TCP_OPTION_RCVD_WS = 0x02,
+ TCP_OPTION_RCVD_TS = 0x04,
+};
+
+UINT8
+TcpComputeScale (
+ IN TCP_CB *Tcb
+ );
+
+UINT16
+TcpSynBuildOption (
+ IN TCP_CB *Tcb,
+ IN NET_BUF *Buf
+ );
+
+UINT16
+TcpBuildOption (
+ IN TCP_CB *Tcb,
+ IN NET_BUF *Buf
+ );
+
+INTN
+TcpParseOption (
+ IN TCP_HEAD *Tcp,
+ IN TCP_OPTION *Option
+ );
+
+UINT32
+TcpPawsOK (
+ IN TCP_CB *Tcb,
+ IN UINT32 TSVal
+ );
+
+#endif
diff --git a/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Output.c b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Output.c
new file mode 100644
index 0000000000..f2987e769c
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Output.c
@@ -0,0 +1,1218 @@
+/** @file
+
+Copyright (c) 2005 - 2006, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+
+ Tcp4Output.c
+
+Abstract:
+
+ TCP output process routines.
+
+
+**/
+
+#include "Tcp4Main.h"
+
+STATIC UINT8 mTcpOutFlag[] = {
+ 0, // TCP_CLOSED
+ 0, // TCP_LISTEN
+ TCP_FLG_SYN, // TCP_SYN_SENT
+ TCP_FLG_SYN | TCP_FLG_ACK, // TCP_SYN_RCVD
+ TCP_FLG_ACK, // TCP_ESTABLISHED
+ TCP_FLG_FIN | TCP_FLG_ACK, // TCP_FIN_WAIT_1
+ TCP_FLG_ACK, // TCP_FIN_WAIT_2
+ TCP_FLG_ACK | TCP_FLG_FIN, // TCP_CLOSING
+ TCP_FLG_ACK, // TCP_TIME_WAIT
+ TCP_FLG_ACK, // TCP_CLOSE_WAIT
+ TCP_FLG_FIN | TCP_FLG_ACK // TCP_LAST_ACK
+};
+
+
+/**
+ Compute the sequence space left in the old receive window.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+
+ @return The sequence space left in the old receive window.
+
+**/
+UINT32
+TcpRcvWinOld (
+ IN TCP_CB *Tcb
+ )
+{
+ UINT32 OldWin;
+
+ OldWin = 0;
+
+ if (TCP_SEQ_GT (Tcb->RcvWl2 + Tcb->RcvWnd, Tcb->RcvNxt)) {
+
+ OldWin = TCP_SUB_SEQ (
+ Tcb->RcvWl2 + Tcb->RcvWnd,
+ Tcb->RcvNxt
+ );
+ }
+
+ return OldWin;
+}
+
+
+/**
+ Compute the current receive window.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+
+ @return The size of the current receive window, in bytes.
+
+**/
+UINT32
+TcpRcvWinNow (
+ IN TCP_CB *Tcb
+ )
+{
+ SOCKET *Sk;
+ UINT32 Win;
+ UINT32 Increase;
+ UINT32 OldWin;
+
+ Sk = Tcb->Sk;
+ ASSERT (Sk);
+
+ OldWin = TcpRcvWinOld (Tcb);
+
+ Win = SockGetFreeSpace (Sk, SOCK_RCV_BUF);
+
+ Increase = 0;
+ if (Win > OldWin) {
+ Increase = Win - OldWin;
+ }
+
+ //
+ // Receiver's SWS: don't advertise a bigger window
+ // unless it can be increased by at least one Mss or
+ // half of the receive buffer.
+ //
+ if ((Increase > Tcb->SndMss) ||
+ (2 * Increase >= GET_RCV_BUFFSIZE (Sk))) {
+
+ return Win;
+ }
+
+ return OldWin;
+}
+
+
+/**
+ Compute the value to fill in the window size field
+ of the outgoing segment.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+ @param Syn The flag to indicate whether the outgoing segment is a SYN
+ segment.
+
+ @return The value of the local receive window size used to fill the outing segment.
+
+**/
+STATIC
+UINT16
+TcpComputeWnd (
+ IN TCP_CB *Tcb,
+ IN BOOLEAN Syn
+ )
+{
+ UINT32 Wnd;
+
+ //
+ // RFC requires that initial window not be scaled
+ //
+ if (Syn) {
+
+ Wnd = GET_RCV_BUFFSIZE (Tcb->Sk);
+ } else {
+
+ Wnd = TcpRcvWinNow (Tcb);
+
+ Tcb->RcvWnd = Wnd;
+ }
+
+ Wnd = NET_MIN (Wnd >> Tcb->RcvWndScale, 0xffff);
+ return NTOHS ((UINT16) Wnd);
+}
+
+
+/**
+ Get the maximum SndNxt.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+
+ @return The sequence number of the maximum SndNxt.
+
+**/
+TCP_SEQNO
+TcpGetMaxSndNxt (
+ IN TCP_CB *Tcb
+ )
+{
+ NET_LIST_ENTRY *Entry;
+ NET_BUF *Nbuf;
+
+ if (NetListIsEmpty (&Tcb->SndQue)) {
+ return Tcb->SndNxt;
+ }
+
+ Entry = Tcb->SndQue.BackLink;
+ Nbuf = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);
+
+ ASSERT (TCP_SEQ_GEQ (TCPSEG_NETBUF (Nbuf)->End, Tcb->SndNxt));
+ return TCPSEG_NETBUF (Nbuf)->End;
+}
+
+
+/**
+ Compute how much data to send.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+ @param Force Whether to ignore the sender's SWS avoidance algorithm and send
+ out data by force.
+
+ @return The length of the data can be sent, if 0, no data can be sent.
+
+**/
+UINT32
+TcpDataToSend (
+ IN TCP_CB *Tcb,
+ IN INTN Force
+ )
+{
+ SOCKET *Sk;
+ UINT32 Win;
+ UINT32 Len;
+ UINT32 Left;
+ UINT32 Limit;
+
+ Sk = Tcb->Sk;
+ ASSERT (Sk);
+
+ //
+ // TCP should NOT send data beyond the send window
+ // and congestion window. The right edge of send
+ // window is defined as SND.WL2 + SND.WND. The right
+ // edge of congestion window is defined as SND.UNA +
+ // CWND.
+ //
+ Win = 0;
+ Limit = Tcb->SndWl2 + Tcb->SndWnd;
+
+ if (TCP_SEQ_GT (Limit, Tcb->SndUna + Tcb->CWnd)) {
+
+ Limit = Tcb->SndUna + Tcb->CWnd;
+ }
+
+ if (TCP_SEQ_GT (Limit, Tcb->SndNxt)) {
+ Win = TCP_SUB_SEQ (Limit, Tcb->SndNxt);
+ }
+
+ //
+ // The data to send contains two parts: the data on the
+ // socket send queue, and the data on the TCB's send
+ // buffer. The later can be non-zero if the peer shrinks
+ // its advertised window.
+ //
+ Left = GET_SND_DATASIZE (Sk) +
+ TCP_SUB_SEQ (TcpGetMaxSndNxt (Tcb), Tcb->SndNxt);
+
+ Len = NET_MIN (Win, Left);
+
+ if (Len > Tcb->SndMss) {
+ Len = Tcb->SndMss;
+ }
+
+ if (Force || (Len == 0 && Left == 0)) {
+ return Len;
+ }
+
+ if (Len == 0 && Left != 0) {
+ goto SetPersistTimer;
+ }
+
+ //
+ // Sender's SWS avoidance: Don't send a small segment unless
+ // a)A full-sized segment can be sent,
+ // b)at least one-half of the maximum sized windows that
+ // the other end has ever advertised.
+ // c)It can send everything it has and either it isn't
+ // expecting an ACK or the Nagle algorithm is disabled.
+ //
+ if ((Len == Tcb->SndMss) || (2 * Len >= Tcb->SndWndMax)) {
+
+ return Len;
+ }
+
+ if ((Len == Left) &&
+ ((Tcb->SndNxt == Tcb->SndUna) ||
+ TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_NAGLE))) {
+
+ return Len;
+ }
+
+ //
+ // RFC1122 suggests to set a timer when SWSA forbids TCP
+ // sending more data, and combine it with probe timer.
+ //
+SetPersistTimer:
+ if (!TCP_TIMER_ON (Tcb->EnabledTimer, TCP_TIMER_REXMIT)) {
+
+ TCP4_DEBUG_WARN (
+ ("TcpDataToSend: enter persistent state for TCB %x\n",
+ Tcb)
+ );
+
+ TcpSetProbeTimer (Tcb);
+ }
+
+ return 0;
+}
+
+
+/**
+ Build the TCP header of the TCP segment and transmit the
+ segment by IP.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+ @param Nbuf Pointer to the buffer containing the segment to be sent out.
+
+ @retval 0 The segment is sent out successfully.
+ @retval other Error condition occurred.
+
+**/
+STATIC
+INTN
+TcpTransmitSegment (
+ IN TCP_CB *Tcb,
+ IN NET_BUF *Nbuf
+ )
+{
+ UINT16 Len;
+ TCP_HEAD *Head;
+ TCP_SEG *Seg;
+ BOOLEAN Syn;
+ UINT32 DataLen;
+
+ ASSERT (Nbuf && (Nbuf->Tcp == NULL) && TcpVerifySegment (Nbuf));
+
+ DataLen = Nbuf->TotalSize;
+
+ Seg = TCPSEG_NETBUF (Nbuf);
+ Syn = TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN);
+
+ if (Syn) {
+
+ Len = TcpSynBuildOption (Tcb, Nbuf);
+ } else {
+
+ Len = TcpBuildOption (Tcb, Nbuf);
+ }
+
+ ASSERT ((Len % 4 == 0) && (Len <= 40));
+
+ Len += sizeof (TCP_HEAD);
+
+ Head = (TCP_HEAD *) NetbufAllocSpace (
+ Nbuf,
+ sizeof (TCP_HEAD),
+ NET_BUF_HEAD
+ );
+
+ ASSERT (Head != NULL);
+
+ Nbuf->Tcp = Head;
+
+ Head->SrcPort = Tcb->LocalEnd.Port;
+ Head->DstPort = Tcb->RemoteEnd.Port;
+ Head->Seq = NTOHL (Seg->Seq);
+ Head->Ack = NTOHL (Tcb->RcvNxt);
+ Head->HeadLen = (UINT8) (Len >> 2);
+ Head->Res = 0;
+ Head->Wnd = TcpComputeWnd (Tcb, Syn);
+ Head->Checksum = 0;
+
+ //
+ // Check whether to set the PSH flag.
+ //
+ TCP_CLEAR_FLG (Seg->Flag, TCP_FLG_PSH);
+
+ if (DataLen != 0) {
+ if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_SND_PSH) &&
+ TCP_SEQ_BETWEEN (Seg->Seq, Tcb->SndPsh, Seg->End)) {
+
+ TCP_SET_FLG (Seg->Flag, TCP_FLG_PSH);
+ TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_SND_PSH);
+
+ } else if ((Seg->End == Tcb->SndNxt) &&
+ (GET_SND_DATASIZE (Tcb->Sk) == 0)) {
+
+ TCP_SET_FLG (Seg->Flag, TCP_FLG_PSH);
+ }
+ }
+
+ //
+ // Check whether to set the URG flag and the urgent pointer.
+ //
+ TCP_CLEAR_FLG (Seg->Flag, TCP_FLG_URG);
+
+ if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_SND_URG) &&
+ TCP_SEQ_LEQ (Seg->Seq, Tcb->SndUp)) {
+
+ TCP_SET_FLG (Seg->Flag, TCP_FLG_URG);
+
+ if (TCP_SEQ_LT (Tcb->SndUp, Seg->End)) {
+
+ Seg->Urg = (UINT16) TCP_SUB_SEQ (Tcb->SndUp, Seg->Seq);
+ } else {
+
+ Seg->Urg = (UINT16) NET_MIN (
+ TCP_SUB_SEQ (Tcb->SndUp,
+ Seg->Seq),
+ 0xffff
+ );
+ }
+ }
+
+ Head->Flag = Seg->Flag;
+ Head->Urg = NTOHS (Seg->Urg);
+ Head->Checksum = TcpChecksum (Nbuf, Tcb->HeadSum);
+
+ //
+ // update the TCP session's control information
+ //
+ Tcb->RcvWl2 = Tcb->RcvNxt;
+ if (Syn) {
+ Tcb->RcvWnd = NTOHS (Head->Wnd);
+ }
+
+ //
+ // clear delayedack flag
+ //
+ Tcb->DelayedAck = 0;
+
+ return TcpSendIpPacket (Tcb, Nbuf, Tcb->LocalEnd.Ip, Tcb->RemoteEnd.Ip);
+}
+
+
+/**
+ Get a segment from the Tcb's SndQue.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+ @param Seq The sequence number of the segment.
+ @param Len The maximum length of the segment.
+
+ @return Pointer to the segment, if NULL some error occurred.
+
+**/
+NET_BUF *
+TcpGetSegmentSndQue (
+ IN TCP_CB *Tcb,
+ IN TCP_SEQNO Seq,
+ IN UINT32 Len
+ )
+{
+ NET_LIST_ENTRY *Head;
+ NET_LIST_ENTRY *Cur;
+ NET_BUF *Node;
+ TCP_SEG *Seg;
+ NET_BUF *Nbuf;
+ TCP_SEQNO End;
+ UINT8 *Data;
+ UINT8 Flag;
+ INT32 Offset;
+ INT32 CopyLen;
+
+ ASSERT (Tcb && TCP_SEQ_LEQ (Seq, Tcb->SndNxt) && (Len > 0));
+
+ //
+ // Find the segment that contains the Seq.
+ //
+ Head = &Tcb->SndQue;
+
+ Node = NULL;
+ Seg = NULL;
+
+ NET_LIST_FOR_EACH (Cur, Head) {
+ Node = NET_LIST_USER_STRUCT (Cur, NET_BUF, List);
+ Seg = TCPSEG_NETBUF (Node);
+
+ if (TCP_SEQ_LT (Seq, Seg->End) && TCP_SEQ_LEQ (Seg->Seq, Seq)) {
+
+ break;
+ }
+ }
+
+ ASSERT (Cur != Head);
+
+ //
+ // Return the buffer if it can be returned without
+ // adjustment:
+ //
+ if ((Seg->Seq == Seq) &&
+ TCP_SEQ_LEQ (Seg->End, Seg->Seq + Len) &&
+ !NET_BUF_SHARED (Node)) {
+
+ NET_GET_REF (Node);
+ return Node;
+ }
+
+ //
+ // Create a new buffer and copy data there.
+ //
+ Nbuf = NetbufAlloc (Len + TCP_MAX_HEAD);
+
+ if (Nbuf == NULL) {
+ return NULL;
+ }
+
+ NetbufReserve (Nbuf, TCP_MAX_HEAD);
+
+ Flag = Seg->Flag;
+ End = Seg->End;
+
+ if (TCP_SEQ_LT (Seq + Len, Seg->End)) {
+ End = Seq + Len;
+ }
+
+ CopyLen = TCP_SUB_SEQ (End, Seq);
+ Offset = TCP_SUB_SEQ (Seq, Seg->Seq);
+
+ //
+ // If SYN is set and out of the range, clear the flag.
+ // Becuase the sequence of the first byte is SEG.SEQ+1,
+ // adjust Offset by -1. If SYN is in the range, copy
+ // one byte less.
+ //
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) {
+
+ if (TCP_SEQ_LT (Seg->Seq, Seq)) {
+
+ TCP_CLEAR_FLG (Flag, TCP_FLG_SYN);
+ Offset--;
+ } else {
+
+ CopyLen--;
+ }
+ }
+
+ //
+ // If FIN is set and in the range, copy one byte less,
+ // and if it is out of the range, clear the flag.
+ //
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_FIN)) {
+
+ if (Seg->End == End) {
+
+ CopyLen--;
+ } else {
+
+ TCP_CLEAR_FLG (Flag, TCP_FLG_FIN);
+ }
+ }
+
+ ASSERT (CopyLen >= 0);
+
+ //
+ // copy data to the segment
+ //
+ if (CopyLen) {
+ Data = NetbufAllocSpace (Nbuf, CopyLen, NET_BUF_TAIL);
+ ASSERT (Data);
+
+ if ((INT32) NetbufCopy (Node, Offset, CopyLen, Data) != CopyLen) {
+ goto OnError;
+ }
+ }
+
+ NetCopyMem (TCPSEG_NETBUF (Nbuf), Seg, sizeof (TCP_SEG));
+
+ TCPSEG_NETBUF (Nbuf)->Seq = Seq;
+ TCPSEG_NETBUF (Nbuf)->End = End;
+ TCPSEG_NETBUF (Nbuf)->Flag = Flag;
+
+ return Nbuf;
+
+OnError:
+ NetbufFree (Nbuf);
+ return NULL;
+}
+
+
+/**
+ Get a segment from the Tcb's socket buffer.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+ @param Seq The sequence number of the segment.
+ @param Len The maximum length of the segment.
+
+ @return Pointer to the segment, if NULL some error occurred.
+
+**/
+NET_BUF *
+TcpGetSegmentSock (
+ IN TCP_CB *Tcb,
+ IN TCP_SEQNO Seq,
+ IN UINT32 Len
+ )
+{
+ NET_BUF *Nbuf;
+ UINT8 *Data;
+ UINT32 DataGet;
+
+ ASSERT (Tcb && Tcb->Sk);
+
+ Nbuf = NetbufAlloc (Len + TCP_MAX_HEAD);
+
+ if (Nbuf == NULL) {
+ TCP4_DEBUG_ERROR (("TcpGetSegmentSock: failed to allocate "
+ "a netbuf for TCB %x\n",Tcb));
+
+ return NULL;
+ }
+
+ NetbufReserve (Nbuf, TCP_MAX_HEAD);
+
+ DataGet = 0;
+
+ if (Len) {
+ //
+ // copy data to the segment.
+ //
+ Data = NetbufAllocSpace (Nbuf, Len, NET_BUF_TAIL);
+ ASSERT (Data);
+
+ DataGet = SockGetDataToSend (Tcb->Sk, 0, Len, Data);
+ }
+
+ NET_GET_REF (Nbuf);
+
+ TCPSEG_NETBUF (Nbuf)->Seq = Seq;
+ TCPSEG_NETBUF (Nbuf)->End = Seq + Len;
+
+ NetListInsertTail (&(Tcb->SndQue), &(Nbuf->List));
+
+ if (DataGet != 0) {
+
+ SockDataSent (Tcb->Sk, DataGet);
+ }
+
+ return Nbuf;
+}
+
+
+/**
+ Get a segment starting from sequence Seq of a maximum
+ length of Len.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+ @param Seq The sequence number of the segment.
+ @param Len The maximum length of the segment.
+
+ @return Pointer to the segment, if NULL some error occurred.
+
+**/
+NET_BUF *
+TcpGetSegment (
+ IN TCP_CB *Tcb,
+ IN TCP_SEQNO Seq,
+ IN UINT32 Len
+ )
+{
+ NET_BUF *Nbuf;
+
+ ASSERT (Tcb);
+
+ //
+ // Compare the SndNxt with the max sequence number sent.
+ //
+ if ((Len != 0) && TCP_SEQ_LT (Seq, TcpGetMaxSndNxt (Tcb))) {
+
+ Nbuf = TcpGetSegmentSndQue (Tcb, Seq, Len);
+ } else {
+
+ Nbuf = TcpGetSegmentSock (Tcb, Seq, Len);
+ }
+
+ ASSERT (TcpVerifySegment (Nbuf));
+ return Nbuf;
+}
+
+
+/**
+ Retransmit the segment from sequence Seq.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+ @param Seq The sequence number of the segment to be retransmitted.
+
+ @retval 0 Retransmission succeeded.
+ @retval -1 Error condition occurred.
+
+**/
+INTN
+TcpRetransmit (
+ IN TCP_CB *Tcb,
+ IN TCP_SEQNO Seq
+ )
+{
+ NET_BUF *Nbuf;
+ UINT32 Len;
+
+ //
+ // Compute the maxium length of retransmission. It is
+ // limited by three factors:
+ // 1. Less than SndMss
+ // 2. must in the current send window
+ // 3. will not change the boundaries of queued segments.
+ //
+ if (TCP_SEQ_LT (Tcb->SndWl2 + Tcb->SndWnd, Seq)) {
+ TCP4_DEBUG_WARN (("TcpRetransmit: retransmission cancelled "
+ "because send window too small for TCB %x\n", Tcb));
+
+ return 0;
+ }
+
+ Len = TCP_SUB_SEQ (Tcb->SndWl2 + Tcb->SndWnd, Seq);
+ Len = NET_MIN (Len, Tcb->SndMss);
+
+ Nbuf = TcpGetSegmentSndQue (Tcb, Seq, Len);
+ if (Nbuf == NULL) {
+ return -1;
+ }
+
+ ASSERT (TcpVerifySegment (Nbuf));
+
+ if (TcpTransmitSegment (Tcb, Nbuf) != 0) {
+ goto OnError;
+ }
+
+ //
+ // The retransmitted buffer may be on the SndQue,
+ // trim TCP head because all the buffer on SndQue
+ // are headless.
+ //
+ ASSERT (Nbuf->Tcp);
+ NetbufTrim (Nbuf, (Nbuf->Tcp->HeadLen << 2), NET_BUF_HEAD);
+ Nbuf->Tcp = NULL;
+
+ NetbufFree (Nbuf);
+ return 0;
+
+OnError:
+ if (Nbuf != NULL) {
+ NetbufFree (Nbuf);
+ }
+
+ return -1;
+}
+
+
+/**
+ Check whether to send data/SYN/FIN and piggy back an ACK.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+ @param Force Whether to ignore the sender's SWS avoidance algorithm and send
+ out data by force.
+
+ @return The number of bytes sent.
+
+**/
+INTN
+TcpToSendData (
+ IN TCP_CB *Tcb,
+ IN INTN Force
+ )
+{
+ UINT32 Len;
+ INTN Sent;
+ UINT8 Flag;
+ NET_BUF *Nbuf;
+ TCP_SEG *Seg;
+ TCP_SEQNO Seq;
+ TCP_SEQNO End;
+
+ ASSERT (Tcb && Tcb->Sk && (Tcb->State != TCP_LISTEN));
+
+ Sent = 0;
+
+ if ((Tcb->State == TCP_CLOSED) ||
+ TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_FIN_SENT)) {
+
+ return 0;
+ }
+
+SEND_AGAIN:
+ //
+ // compute how much data can be sent
+ //
+ Len = TcpDataToSend (Tcb, Force);
+ Seq = Tcb->SndNxt;
+
+ Flag = mTcpOutFlag[Tcb->State];
+
+ if (Flag & TCP_FLG_SYN) {
+
+ Seq = Tcb->Iss;
+ Len = 0;
+ }
+
+ //
+ // only send a segment without data if SYN or
+ // FIN is set.
+ //
+ if ((Len == 0) && !(Flag & (TCP_FLG_SYN | TCP_FLG_FIN))) {
+ return Sent;
+ }
+
+ Nbuf = TcpGetSegment (Tcb, Seq, Len);
+
+ if (Nbuf == NULL) {
+ TCP4_DEBUG_ERROR (
+ ("TcpToSendData: failed to get a segment for TCB %x\n",
+ Tcb)
+ );
+
+ goto OnError;
+ }
+
+ Seg = TCPSEG_NETBUF (Nbuf);
+
+ //
+ // Set the TcpSeg in Nbuf.
+ //
+ Len = Nbuf->TotalSize;
+ End = Seq + Len;
+ if (TCP_FLG_ON (Flag, TCP_FLG_SYN)) {
+ End++;
+ }
+
+ if (Flag & TCP_FLG_FIN) {
+ //
+ // Send FIN if all data is sent, and FIN is
+ // in the window
+ //
+ if ((TcpGetMaxSndNxt (Tcb) == Tcb->SndNxt) &&
+ (GET_SND_DATASIZE (Tcb->Sk) == 0) &&
+ TCP_SEQ_LT (End + 1, Tcb->SndWnd + Tcb->SndWl2)
+ ) {
+
+ TCP4_DEBUG_TRACE (("TcpToSendData: send FIN "
+ "to peer for TCB %x in state %d\n", Tcb, Tcb->State));
+
+ End++;
+ } else {
+ TCP_CLEAR_FLG (Flag, TCP_FLG_FIN);
+ }
+ }
+
+ Seg->Seq = Seq;
+ Seg->End = End;
+ Seg->Flag = Flag;
+
+ ASSERT (TcpVerifySegment (Nbuf));
+ ASSERT (TcpCheckSndQue (&Tcb->SndQue));
+
+ //
+ // don't send an empty segment here.
+ //
+ if (Seg->End == Seg->Seq) {
+ TCP4_DEBUG_WARN (("TcpToSendData: created a empty"
+ " segment for TCB %x, free it now\n", Tcb));
+
+ NetbufFree (Nbuf);
+ return Sent;
+ }
+
+ if (TcpTransmitSegment (Tcb, Nbuf) != 0) {
+ //
+ // TODO: double check this
+ //
+ NetbufTrim (Nbuf, (Nbuf->Tcp->HeadLen << 2), NET_BUF_HEAD);
+ Nbuf->Tcp = NULL;
+
+ if (Flag & TCP_FLG_FIN) {
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_FIN_SENT);
+ }
+
+ goto OnError;
+ }
+
+ Sent += TCP_SUB_SEQ (End, Seq);
+
+ //
+ // All the buffer in the SndQue is headless
+ //
+ ASSERT (Nbuf->Tcp);
+
+ NetbufTrim (Nbuf, (Nbuf->Tcp->HeadLen << 2), NET_BUF_HEAD);
+ Nbuf->Tcp = NULL;
+
+ NetbufFree (Nbuf);
+
+ //
+ // update status in TCB
+ //
+ Tcb->DelayedAck = 0;
+
+ if (Flag & TCP_FLG_FIN) {
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_FIN_SENT);
+ }
+
+ if (TCP_SEQ_GT (End, Tcb->SndNxt)) {
+ Tcb->SndNxt = End;
+ }
+
+ if (!TCP_TIMER_ON (Tcb->EnabledTimer, TCP_TIMER_REXMIT)) {
+ TcpSetTimer (Tcb, TCP_TIMER_REXMIT, Tcb->Rto);
+ }
+
+ //
+ // Enable RTT measurement only if not in retransmit.
+ // Karn's algorithm reqires not to update RTT when in loss.
+ //
+ if ((Tcb->CongestState == TCP_CONGEST_OPEN) &&
+ !TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RTT_ON)) {
+
+ TCP4_DEBUG_TRACE (("TcpToSendData: set RTT measure "
+ "sequence %d for TCB %x\n", Seq, Tcb));
+
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_RTT_ON);
+ Tcb->RttSeq = Seq;
+ Tcb->RttMeasure = 0;
+ }
+
+ if (Len == Tcb->SndMss) {
+ goto SEND_AGAIN;
+ }
+
+ return Sent;
+
+OnError:
+ if (Nbuf != NULL) {
+ NetbufFree (Nbuf);
+ }
+
+ return Sent;
+}
+
+
+/**
+ Send an ACK immediately.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+
+ @return None.
+
+**/
+VOID
+TcpSendAck (
+ IN TCP_CB *Tcb
+ )
+{
+ NET_BUF *Nbuf;
+ TCP_SEG *Seg;
+
+ Nbuf = NetbufAlloc (TCP_MAX_HEAD);
+
+ if (Nbuf == NULL) {
+ return;
+ }
+
+ NetbufReserve (Nbuf, TCP_MAX_HEAD);
+
+ Seg = TCPSEG_NETBUF (Nbuf);
+ Seg->Seq = Tcb->SndNxt;
+ Seg->End = Tcb->SndNxt;
+ Seg->Flag = TCP_FLG_ACK;
+
+ if (TcpTransmitSegment (Tcb, Nbuf) == 0) {
+ TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW);
+ Tcb->DelayedAck = 0;
+ }
+
+ NetbufFree (Nbuf);
+}
+
+
+/**
+ Send a zero probe segment. It can be used by keepalive
+ and zero window probe.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+
+ @retval 0 The zero probe segment was sent out successfully.
+ @retval other Error condition occurred.
+
+**/
+INTN
+TcpSendZeroProbe (
+ IN TCP_CB *Tcb
+ )
+{
+ NET_BUF *Nbuf;
+ TCP_SEG *Seg;
+ INTN Result;
+
+ Nbuf = NetbufAlloc (TCP_MAX_HEAD);
+
+ if (Nbuf == NULL) {
+ return -1;
+ }
+
+ NetbufReserve (Nbuf, TCP_MAX_HEAD);
+
+ //
+ // SndNxt-1 is out of window. The peer should respond
+ // with an ACK.
+ //
+ Seg = TCPSEG_NETBUF (Nbuf);
+ Seg->Seq = Tcb->SndNxt - 1;
+ Seg->End = Tcb->SndNxt - 1;
+ Seg->Flag = TCP_FLG_ACK;
+
+ Result = TcpTransmitSegment (Tcb, Nbuf);
+ NetbufFree (Nbuf);
+
+ return Result;
+}
+
+
+/**
+ Check whether to send an ACK or delayed ACK.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+
+ @return None.
+
+**/
+VOID
+TcpToSendAck (
+ IN TCP_CB *Tcb
+ )
+{
+ //
+ // Generally, TCP should send a delayed ACK unless:
+ // 1. ACK at least every other FULL sized segment received,
+ // 2. Packets received out of order
+ // 3. Receiving window is open
+ //
+ if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW) ||
+ (Tcb->DelayedAck >= 1) ||
+ (TcpRcvWinNow (Tcb) > TcpRcvWinOld (Tcb))
+ ) {
+ TcpSendAck (Tcb);
+ return;
+ }
+
+ TCP4_DEBUG_TRACE (("TcpToSendAck: scheduled a delayed"
+ " ACK for TCB %x\n", Tcb));
+
+ //
+ // schedule a delayed ACK
+ //
+ Tcb->DelayedAck++;
+}
+
+
+/**
+ Send a RESET segment in response to the segment received.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance, may be NULL.
+ @param Head TCP header of the segment that triggers the reset.
+ @param Len Length of the segment that triggers the reset.
+ @param Local Local IP address.
+ @param Remote Remote peer's IP address.
+
+ @retval 0 A reset is sent or no need to send it.
+ @retval -1 No reset is sent.
+
+**/
+INTN
+TcpSendReset (
+ IN TCP_CB *Tcb,
+ IN TCP_HEAD *Head,
+ IN INT32 Len,
+ IN UINT32 Local,
+ IN UINT32 Remote
+ )
+{
+ NET_BUF *Nbuf;
+ TCP_HEAD *Nhead;
+ UINT16 HeadSum;
+
+ //
+ // Don't respond to a Reset with reset
+ //
+ if (Head->Flag & TCP_FLG_RST) {
+ return 0;
+ }
+
+ Nbuf = NetbufAlloc (TCP_MAX_HEAD);
+
+ if (Nbuf == NULL) {
+ return -1;
+ }
+
+ Nhead = (TCP_HEAD *) NetbufAllocSpace (
+ Nbuf,
+ sizeof (TCP_HEAD),
+ NET_BUF_TAIL
+ );
+
+ ASSERT (Nhead != NULL);
+
+ Nbuf->Tcp = Nhead;
+ Nhead->Flag = TCP_FLG_RST;
+
+ //
+ // Derive Seq/ACK from the segment if no TCB
+ // associated with it, otherwise from the Tcb
+ //
+ if (Tcb == NULL) {
+
+ if (TCP_FLG_ON (Head->Flag, TCP_FLG_ACK)) {
+ Nhead->Seq = Head->Ack;
+ Nhead->Ack = 0;
+ } else {
+ Nhead->Seq = 0;
+ TCP_SET_FLG (Nhead->Flag, TCP_FLG_ACK);
+ Nhead->Ack = HTONL (NTOHL (Head->Seq) + Len);
+ }
+ } else {
+
+ Nhead->Seq = HTONL (Tcb->SndNxt);
+ Nhead->Ack = HTONL (Tcb->RcvNxt);
+ TCP_SET_FLG (Nhead->Flag, TCP_FLG_ACK);
+ }
+
+ Nhead->SrcPort = Head->DstPort;
+ Nhead->DstPort = Head->SrcPort;
+ Nhead->HeadLen = (sizeof (TCP_HEAD) >> 2);
+ Nhead->Res = 0;
+ Nhead->Wnd = HTONS (0xFFFF);
+ Nhead->Checksum = 0;
+ Nhead->Urg = 0;
+
+ HeadSum = NetPseudoHeadChecksum (Local, Remote, 6, 0);
+ Nhead->Checksum = TcpChecksum (Nbuf, HeadSum);
+
+ TcpSendIpPacket (Tcb, Nbuf, Local, Remote);
+
+ NetbufFree (Nbuf);
+ return 0;
+}
+
+
+/**
+ Verify that the segment is in good shape.
+
+ @param Nbuf Buffer that contains the segment to be checked.
+
+ @retval 0 The segment is broken.
+ @retval 1 The segment is in good shape.
+
+**/
+INTN
+TcpVerifySegment (
+ IN NET_BUF *Nbuf
+ )
+{
+ TCP_HEAD *Head;
+ TCP_SEG *Seg;
+ UINT32 Len;
+
+ if (Nbuf == NULL) {
+ return 1;
+ }
+
+ NET_CHECK_SIGNATURE (Nbuf, NET_BUF_SIGNATURE);
+
+ Seg = TCPSEG_NETBUF (Nbuf);
+ Len = Nbuf->TotalSize;
+ Head = Nbuf->Tcp;
+
+ if (Head != NULL) {
+ if (Head->Flag != Seg->Flag) {
+ return 0;
+ }
+
+ Len -= (Head->HeadLen << 2);
+ }
+
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) {
+ Len++;
+ }
+
+ if (TCP_FLG_ON (Seg->Flag, TCP_FLG_FIN)) {
+ Len++;
+ }
+
+ if (Seg->Seq + Len != Seg->End) {
+ return 0;
+ }
+
+ return 1;
+}
+
+
+/**
+ Verify that all the segments in SndQue are in good shape.
+
+ @param Head Pointer to the head node of the SndQue.
+
+ @retval 0 At least one segment is broken.
+ @retval 1 All segments in the specific queue are in good shape.
+
+**/
+INTN
+TcpCheckSndQue (
+ IN NET_LIST_ENTRY *Head
+ )
+{
+ NET_LIST_ENTRY *Entry;
+ NET_BUF *Nbuf;
+ TCP_SEQNO Seq;
+
+ if (NetListIsEmpty (Head)) {
+ return 1;
+ }
+ //
+ // Initialize the Seq
+ //
+ Entry = Head->ForwardLink;
+ Nbuf = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);
+ Seq = TCPSEG_NETBUF (Nbuf)->Seq;
+
+ NET_LIST_FOR_EACH (Entry, Head) {
+ Nbuf = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);
+
+ if (TcpVerifySegment (Nbuf) == 0) {
+ return 0;
+ }
+
+ //
+ // All the node in the SndQue should has:
+ // SEG.SEQ = LAST_SEG.END
+ //
+ if (Seq != TCPSEG_NETBUF (Nbuf)->Seq) {
+ return 0;
+ }
+
+ Seq = TCPSEG_NETBUF (Nbuf)->End;
+ }
+
+ return 1;
+}
diff --git a/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Proto.h b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Proto.h
new file mode 100644
index 0000000000..03daa6f04e
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Proto.h
@@ -0,0 +1,355 @@
+/** @file
+
+Copyright (c) 2005 - 2006, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+
+ Tcp4Proto.h
+
+Abstract:
+
+
+**/
+
+#ifndef _TCP4_PROTO_H_
+#define _TCP4_PROTO_H_
+
+typedef struct _TCP_CB TCP_CB;
+
+#include "Tcp4Driver.h"
+#include "Socket.h"
+#include "Tcp4Option.h"
+
+//
+// tcp states, Don't change their order, it is used as
+// index to mTcpOutFlag and other macros
+//
+enum {
+ TCP_CLOSED = 0,
+ TCP_LISTEN,
+ TCP_SYN_SENT,
+ TCP_SYN_RCVD,
+ TCP_ESTABLISHED,
+ TCP_FIN_WAIT_1,
+ TCP_FIN_WAIT_2,
+ TCP_CLOSING,
+ TCP_TIME_WAIT,
+ TCP_CLOSE_WAIT,
+ TCP_LAST_ACK,
+};
+
+//
+// flags in the TCP header
+//
+enum {
+
+ TCP_FLG_FIN = 0x01,
+ TCP_FLG_SYN = 0x02,
+ TCP_FLG_RST = 0x04,
+ TCP_FLG_PSH = 0x08,
+ TCP_FLG_ACK = 0x10,
+ TCP_FLG_URG = 0x20,
+ TCP_FLG_FLAG = 0x3F, // mask for all the flags
+};
+
+enum {
+
+ //
+ // TCP error status
+ //
+ TCP_CONNECT_REFUSED = -1,
+ TCP_CONNECT_RESET = -2,
+ TCP_CONNECT_CLOSED = -3,
+
+ //
+ // Current congestion status as suggested by RFC3782.
+ //
+ TCP_CONGEST_RECOVER = 1, // during the NewReno fast recovery
+ TCP_CONGEST_LOSS = 2, // retxmit because of retxmit time out
+ TCP_CONGEST_OPEN = 3, // TCP is opening its congestion window
+
+ //
+ // TCP control flags
+ //
+ TCP_CTRL_NO_NAGLE = 0x0001, // disable Nagle algorithm
+ TCP_CTRL_NO_KEEPALIVE = 0x0002, // disable keepalive timer
+ TCP_CTRL_NO_WS = 0x0004, // disable window scale option
+ TCP_CTRL_RCVD_WS = 0x0008, // rcvd a wnd scale option in syn
+ TCP_CTRL_NO_TS = 0x0010, // disable Timestamp option
+ TCP_CTRL_RCVD_TS = 0x0020, // rcvd a Timestamp option in syn
+ TCP_CTRL_SND_TS = 0x0040, // Send Timestamp option to remote
+ TCP_CTRL_SND_URG = 0x0080, // in urgent send mode
+ TCP_CTRL_RCVD_URG = 0x0100, // in urgent receive mode
+ TCP_CTRL_SND_PSH = 0x0200, // in PUSH send mode
+ TCP_CTRL_FIN_SENT = 0x0400, // FIN is sent
+ TCP_CTRL_FIN_ACKED = 0x0800, // FIN is ACKed.
+ TCP_CTRL_TIMER_ON = 0x1000, // At least one of the timer is on
+ TCP_CTRL_RTT_ON = 0x2000, // The RTT measurement is on
+ TCP_CTRL_ACK_NOW = 0x4000, // Send the ACK now, don't delay
+
+ //
+ // Timer related values
+ //
+ TCP_TIMER_CONNECT = 0, // Connection establishment timer
+ TCP_TIMER_REXMIT = 1, // retransmit timer
+ TCP_TIMER_PROBE = 2, // Window probe timer
+ TCP_TIMER_KEEPALIVE = 3, // Keepalive timer
+ TCP_TIMER_FINWAIT2 = 4, // FIN_WAIT_2 timer
+ TCP_TIMER_2MSL = 5, // TIME_WAIT tiemr
+ TCP_TIMER_NUMBER = 6, // the total number of TCP timer.
+ TCP_TICK = 200, // every TCP tick is 200ms
+ TCP_TICK_HZ = 5, // the frequence of TCP tick
+ TCP_RTT_SHIFT = 3, // SRTT & RTTVAR scaled by 8
+ TCP_RTO_MIN = TCP_TICK_HZ, // the minium value of RTO
+ TCP_RTO_MAX = TCP_TICK_HZ *60, // the maxium value of RTO
+ TCP_FOLD_RTT = 4, // timeout threshod to fold RTT
+
+ //
+ // default values for some timers
+ //
+ TCP_MAX_LOSS = 12, // default max times to retxmit
+ TCP_KEEPALIVE_IDLE_MIN = TCP_TICK_HZ *60 *60 *2, // First keep alive
+ TCP_KEEPALIVE_PERIOD = TCP_TICK_HZ *60,
+ TCP_MAX_KEEPALIVE = 8,
+ TCP_FIN_WAIT2_TIME = 2 *TCP_TICK_HZ, // * 60,
+ TCP_TIME_WAIT_TIME = 2 *TCP_TICK_HZ,
+ TCP_PAWS_24DAY = 24 *24 *60 *60 *TCP_TICK_HZ,
+ TCP_CONNECT_TIME = 75 *TCP_TICK_HZ,
+
+ //
+ // The header space to be reserved before TCP data to accomodate :
+ // 60byte IP head + 60byte TCP head + link layer head
+ //
+ TCP_MAX_HEAD = 192,
+
+ //
+ // value ranges for some control option
+ //
+ TCP_RCV_BUF_SIZE = 2 *1024 *1024,
+ TCP_RCV_BUF_SIZE_MIN = 8 *1024,
+ TCP_SND_BUF_SIZE = 2 *1024 *1024,
+ TCP_SND_BUF_SIZE_MIN = 8 *1024,
+ TCP_BACKLOG = 10,
+ TCP_BACKLOG_MIN = 5,
+ TCP_MAX_LOSS_MIN = 6,
+ TCP_CONNECT_TIME_MIN = 60 *TCP_TICK_HZ,
+ TCP_MAX_KEEPALIVE_MIN = 4,
+ TCP_KEEPALIVE_IDLE_MAX = TCP_TICK_HZ *60 *60 *4,
+ TCP_KEEPALIVE_PERIOD_MIN= TCP_TICK_HZ *30,
+ TCP_FIN_WAIT2_TIME_MAX = 4 *TCP_TICK_HZ,
+ TCP_TIME_WAIT_TIME_MAX = 60 *TCP_TICK_HZ,
+};
+
+typedef struct _TCP_SEG {
+ TCP_SEQNO Seq; // Starting sequence number
+ TCP_SEQNO End; // The sequence of the last byte + 1,
+ // include SYN/FIN. End-Seq = SEG.LEN
+ TCP_SEQNO Ack; // ACK fild in the segment
+ UINT8 Flag; // TCP header flags
+ UINT16 Urg; // Valid if URG flag is set.
+ UINT32 Wnd; // TCP window size field
+} TCP_SEG;
+
+typedef struct _TCP_PEER {
+ UINT32 Ip; // Network byte order
+ TCP_PORTNO Port; // Network byte order
+} TCP_PEER;
+
+//
+// tcp control block, it includes various states
+//
+typedef struct _TCP_CB {
+ NET_LIST_ENTRY List;
+ TCP_CB *Parent;
+
+ SOCKET *Sk;
+ TCP_PEER LocalEnd;
+ TCP_PEER RemoteEnd;
+
+ NET_LIST_ENTRY SndQue; // retxmission queue
+ NET_LIST_ENTRY RcvQue; // reassemble queue
+ UINT32 CtrlFlag; // control flags, such as NO_NAGLE
+ INT32 Error; // soft error status,TCP_CONNECT_RESET...
+
+ //
+ // RFC793 and RFC1122 defined variables
+ //
+ UINT8 State; // TCP state, such as SYN_SENT, LISTEN
+ UINT8 DelayedAck; // number of delayed ACKs
+ UINT16 HeadSum; // checksum of the fixed parts of pesudo
+ // header: Src IP, Dst IP, 0, Protocol,
+ // not include the TCP length.
+
+ TCP_SEQNO Iss; // Initial Sending Sequence
+ TCP_SEQNO SndUna; // first unacknowledged data
+ TCP_SEQNO SndNxt; // next data sequence to send.
+ TCP_SEQNO SndPsh; // Send PUSH point
+ TCP_SEQNO SndUp; // Send urgent point
+ UINT32 SndWnd; // Window advertised by the remote peer
+ UINT32 SndWndMax; // max send window advertised by the peer
+ TCP_SEQNO SndWl1; // Seq number used for last window update
+ TCP_SEQNO SndWl2; // ack no of last window update
+ UINT16 SndMss; // Max send segment size
+ TCP_SEQNO RcvNxt; // Next sequence no to receive
+ UINT32 RcvWnd; // Window advertised by the local peer
+ TCP_SEQNO RcvWl2; // The RcvNxt (or ACK) of last window update.
+ // It is necessary because of delayed ACK
+
+ TCP_SEQNO RcvUp; // urgent point;
+ TCP_SEQNO Irs; // Initial Receiving Sequence
+ UINT16 RcvMss; // Max receive segment size
+ UINT16 EnabledTimer; // which timer is currently enabled
+ UINT32 Timer[TCP_TIMER_NUMBER]; // when the timer will expire
+ INT32 NextExpire; // count down offset for the nearest timer
+ UINT32 Idle; // How long the connection is in idle
+ UINT32 ProbeTime; // the time out value for current window prober
+
+ //
+ // RFC1323 defined variables, about window scale,
+ // timestamp and PAWS
+ //
+ UINT8 SndWndScale; // Wndscale received from the peer
+ UINT8 RcvWndScale; // Wndscale used to scale local buffer
+ UINT32 TsRecent; // TsRecent to echo to the remote peer
+ UINT32 TsRecentAge; // When this TsRecent is updated
+
+ // TCP_SEQNO LastAckSent;
+ // It isn't necessary to add LastAckSent here,
+ // since it is the same as RcvWl2
+
+ //
+ // RFC2988 defined variables. about RTT measurement
+ //
+ TCP_SEQNO RttSeq; // the seq of measured segment now
+ UINT32 RttMeasure; // currently measured RTT in heart beats
+ UINT32 SRtt; // Smoothed RTT, scaled by 8
+ UINT32 RttVar; // RTT variance, scaled by 8
+ UINT32 Rto; // Current RTO, not scaled
+
+ //
+ // RFC2581, and 3782 variables.
+ // Congestion control + NewReno fast recovery.
+ //
+ UINT32 CWnd; // Sender's congestion window
+ UINT32 Ssthresh; // Slow start threshold.
+ TCP_SEQNO Recover; // recover point for NewReno
+ UINT16 DupAck; // number of duplicate ACKs
+ UINT8 CongestState; // the current congestion state(RFC3782)
+ UINT8 LossTimes; // number of retxmit timeouts in a row
+ TCP_SEQNO LossRecover; // recover point for retxmit
+
+ //
+ // configuration parameters, for EFI_TCP4_PROTOCOL specification
+ //
+ UINT32 KeepAliveIdle; // idle time before sending first probe
+ UINT32 KeepAlivePeriod; // interval for subsequent keep alive probe
+ UINT8 MaxKeepAlive; // Maxium keep alive probe times.
+ UINT8 KeepAliveProbes; // the number of keep alive probe.
+ UINT16 MaxRexmit; // The maxium number of retxmit before abort
+ UINT32 FinWait2Timeout; // The FIN_WAIT_2 time out
+ UINT32 TimeWaitTimeout; // The TIME_WAIT time out
+ UINT32 ConnectTimeout;
+
+ //
+ // configuration for tcp provided by user
+ //
+ BOOLEAN UseDefaultAddr;
+ UINT8 TOS;
+ UINT8 TTL;
+ EFI_IPv4_ADDRESS SubnetMask;
+
+ //
+ // pointer reference to Ip used to send pkt
+ //
+ IP_IO_IP_INFO *IpInfo;
+} TCP_CB;
+
+extern NET_LIST_ENTRY mTcpRunQue;
+extern NET_LIST_ENTRY mTcpListenQue;
+extern TCP_SEQNO mTcpGlobalIss;
+extern UINT32 mTcpTick;
+
+//
+// TCP_CONNECTED: both ends have synchronized their ISN.
+//
+#define TCP_CONNECTED(state) ((state) > TCP_SYN_RCVD)
+
+#define TCP_FIN_RCVD(State) \
+ (((State) == TCP_CLOSE_WAIT) || \
+ ((State) == TCP_LAST_ACK) || \
+ ((State) == TCP_CLOSING) || \
+ ((State) == TCP_TIME_WAIT))
+
+#define TCP_LOCAL_CLOSED(State) \
+ (((State) == TCP_FIN_WAIT_1) || \
+ ((State) == TCP_FIN_WAIT_2) || \
+ ((State) == TCP_CLOSING) || \
+ ((State) == TCP_TIME_WAIT) || \
+ ((State) == TCP_LAST_ACK))
+
+//
+// Get the TCP_SEG point from a net buffer's ProtoData
+//
+#define TCPSEG_NETBUF(NBuf) ((TCP_SEG *) ((NBuf)->ProtoData))
+
+//
+// macros to compare sequence no
+//
+#define TCP_SEQ_LT(SeqA, SeqB) ((INT32) ((SeqA) - (SeqB)) < 0)
+#define TCP_SEQ_LEQ(SeqA, SeqB) ((INT32) ((SeqA) - (SeqB)) <= 0)
+#define TCP_SEQ_GT(SeqA, SeqB) ((INT32) ((SeqB) - (SeqA)) < 0)
+#define TCP_SEQ_GEQ(SeqA, SeqB) ((INT32) ((SeqB) - (SeqA)) <= 0)
+
+//
+// TCP_SEQ_BETWEEN return whether b <= m <= e
+//
+#define TCP_SEQ_BETWEEN(b, m, e) ((e) - (b) >= (m) - (b))
+
+//
+// TCP_SUB_SEQ returns Seq1 - Seq2. Make sure Seq1 >= Seq2
+//
+#define TCP_SUB_SEQ(Seq1, Seq2) ((UINT32) ((Seq1) - (Seq2)))
+
+#define TCP_FLG_ON(Value, Flag) ((BOOLEAN) (((Value) & (Flag)) != 0))
+#define TCP_SET_FLG(Value, Flag) ((Value) |= (Flag))
+#define TCP_CLEAR_FLG(Value, Flag) ((Value) &= ~(Flag))
+
+//
+// test whether two peers are equal
+//
+#define TCP_PEER_EQUAL(Pa, Pb) \
+ (((Pa)->Ip == (Pb)->Ip) && ((Pa)->Port == (Pb)->Port))
+
+//
+// test whether Pa matches Pb, or Pa is more specific
+// than pb. Zero means wildcard.
+//
+#define TCP_PEER_MATCH(Pa, Pb) \
+ ((((Pb)->Ip == 0) || ((Pb)->Ip == (Pa)->Ip)) && \
+ (((Pb)->Port == 0) || ((Pb)->Port == (Pa)->Port)))
+
+#define TCP_TIMER_ON(Flag, Timer) ((Flag) & (1 << (Timer)))
+#define TCP_SET_TIMER(Flag, Timer) ((Flag) |= (1 << (Timer)))
+#define TCP_CLEAR_TIMER(Flag, Timer) ((Flag) &= ~(1 << (Timer)))
+
+#define TCP_TIME_LT(Ta, Tb) ((INT32) ((Ta) - (Tb)) < 0)
+#define TCP_TIME_LEQ(Ta, Tb) ((INT32) ((Ta) - (Tb)) <= 0)
+#define TCP_SUB_TIME(Ta, Tb) ((UINT32) ((Ta) - (Tb)))
+
+#define TCP_MAX_WIN 0xFFFFU
+
+typedef
+VOID
+(*TCP_TIMER_HANDLER) (
+ IN TCP_CB * Tcb
+ );
+
+#include "Tcp4Func.h"
+#endif
diff --git a/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Timer.c b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Timer.c
new file mode 100644
index 0000000000..20679b4f95
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Timer.c
@@ -0,0 +1,582 @@
+/** @file
+
+Copyright (c) 2005 - 2006, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+
+ Tcp4Timer.c
+
+Abstract:
+
+ TCP timer related functions.
+
+
+**/
+
+#include "Tcp4Main.h"
+
+UINT32 mTcpTick = 1000;
+
+STATIC
+VOID
+TcpConnectTimeout (
+ IN TCP_CB *Tcb
+ );
+
+STATIC
+VOID
+TcpRexmitTimeout (
+ IN TCP_CB *Tcb
+ );
+
+STATIC
+VOID
+TcpProbeTimeout (
+ IN TCP_CB *Tcb
+ );
+
+STATIC
+VOID
+TcpKeepaliveTimeout (
+ IN TCP_CB *Tcb
+ );
+
+STATIC
+VOID
+TcpFinwait2Timeout (
+ IN TCP_CB *Tcb
+ );
+
+STATIC
+VOID
+Tcp2MSLTimeout (
+ IN TCP_CB *Tcb
+ );
+
+TCP_TIMER_HANDLER mTcpTimerHandler[TCP_TIMER_NUMBER] = {
+ TcpConnectTimeout,
+ TcpRexmitTimeout,
+ TcpProbeTimeout,
+ TcpKeepaliveTimeout,
+ TcpFinwait2Timeout,
+ Tcp2MSLTimeout,
+};
+
+
+/**
+ Close the TCP connection.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+
+ @return None.
+
+**/
+VOID
+TcpClose (
+ IN TCP_CB *Tcb
+ )
+{
+ NetbufFreeList (&Tcb->SndQue);
+ NetbufFreeList (&Tcb->RcvQue);
+
+ TcpSetState (Tcb, TCP_CLOSED);
+}
+
+
+/**
+ Connect timeout handler.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+
+ @return None.
+
+**/
+STATIC
+VOID
+TcpConnectTimeout (
+ IN TCP_CB *Tcb
+ )
+{
+ if (!TCP_CONNECTED (Tcb->State)) {
+ TCP4_DEBUG_ERROR (("TcpConnectTimeout: connection closed "
+ "because conenction timer timeout for TCB %x\n", Tcb));
+
+ if (EFI_ABORTED == Tcb->Sk->SockError) {
+ SOCK_ERROR (Tcb->Sk, EFI_TIMEOUT);
+ }
+
+ if (TCP_SYN_RCVD == Tcb->State) {
+ TCP4_DEBUG_WARN (("TcpConnectTimeout: send reset because "
+ "connection timer timeout for TCB %x\n", Tcb));
+
+ TcpResetConnection (Tcb);
+
+ }
+
+ TcpClose (Tcb);
+ }
+}
+
+
+/**
+ Timeout handler for TCP retransmission timer.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+
+ @return None.
+
+**/
+STATIC
+VOID
+TcpRexmitTimeout (
+ IN TCP_CB *Tcb
+ )
+{
+ UINT32 FlightSize;
+
+ TCP4_DEBUG_WARN (("TcpRexmitTimeout: transmission "
+ "timeout for TCB %x\n", Tcb));
+
+ //
+ // Set the congestion window. FlightSize is the
+ // amount of data that has been sent but not
+ // yet ACKed.
+ //
+ FlightSize = TCP_SUB_SEQ (Tcb->SndNxt, Tcb->SndUna);
+ Tcb->Ssthresh = NET_MAX ((UINT32) (2 * Tcb->SndMss), FlightSize / 2);
+
+ Tcb->CWnd = Tcb->SndMss;
+ Tcb->LossRecover = Tcb->SndNxt;
+
+ Tcb->LossTimes++;
+ if (Tcb->LossTimes > Tcb->MaxRexmit &&
+ !TCP_TIMER_ON (Tcb->EnabledTimer, TCP_TIMER_CONNECT)) {
+
+ TCP4_DEBUG_ERROR (("TcpRexmitTimeout: connection closed "
+ "because too many timeouts for TCB %x\n", Tcb));
+
+ if (EFI_ABORTED == Tcb->Sk->SockError) {
+ SOCK_ERROR (Tcb->Sk, EFI_TIMEOUT);
+ }
+
+ TcpClose (Tcb);
+ return ;
+ }
+
+ TcpBackoffRto (Tcb);
+ TcpRetransmit (Tcb, Tcb->SndUna);
+ TcpSetTimer (Tcb, TCP_TIMER_REXMIT, Tcb->Rto);
+
+ Tcb->CongestState = TCP_CONGEST_LOSS;
+
+ TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_RTT_ON);
+}
+
+
+/**
+ Timeout handler for window probe timer.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+
+ @return None.
+
+**/
+STATIC
+VOID
+TcpProbeTimeout (
+ IN TCP_CB *Tcb
+ )
+{
+ //
+ // This is the timer for sender's SWSA. RFC1122 requires
+ // a timer set for sender's SWSA, and suggest combine it
+ // with window probe timer. If data is sent, don't set
+ // the probe timer, since retransmit timer is on.
+ //
+ if ((TcpDataToSend (Tcb, 1) != 0) && (TcpToSendData (Tcb, 1) > 0)) {
+
+ ASSERT (TCP_TIMER_ON (Tcb->EnabledTimer, TCP_TIMER_REXMIT));
+ return ;
+ }
+
+ TcpSendZeroProbe (Tcb);
+ TcpSetProbeTimer (Tcb);
+}
+
+
+/**
+ Timeout handler for keepalive timer.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+
+ @return None.
+
+**/
+STATIC
+VOID
+TcpKeepaliveTimeout (
+ IN TCP_CB *Tcb
+ )
+{
+ Tcb->KeepAliveProbes++;
+
+ //
+ // Too many Keep-alive probes, drop the connection
+ //
+ if (Tcb->KeepAliveProbes > Tcb->MaxKeepAlive) {
+
+ if (EFI_ABORTED == Tcb->Sk->SockError) {
+ SOCK_ERROR (Tcb->Sk, EFI_TIMEOUT);
+ }
+
+ TcpClose (Tcb);
+ return ;
+ }
+
+ TcpSendZeroProbe (Tcb);
+ TcpSetKeepaliveTimer (Tcb);
+}
+
+
+/**
+ Timeout handler for FIN_WAIT_2 timer.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+
+ @return None.
+
+**/
+STATIC
+VOID
+TcpFinwait2Timeout (
+ IN TCP_CB *Tcb
+ )
+{
+ TCP4_DEBUG_WARN (("TcpFinwait2Timeout: connection closed "
+ "because FIN_WAIT2 timer timeouts for TCB %x\n", Tcb));
+
+ TcpClose (Tcb);
+}
+
+
+/**
+ Timeout handler for 2MSL timer.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+
+ @return None.
+
+**/
+STATIC
+VOID
+Tcp2MSLTimeout (
+ IN TCP_CB *Tcb
+ )
+{
+ TCP4_DEBUG_WARN (("Tcp2MSLTimeout: connection closed "
+ "because TIME_WAIT timer timeouts for TCB %x\n", Tcb));
+
+ TcpClose (Tcb);
+}
+
+
+/**
+ Update the timer status and the next expire time
+ according to the timers to expire in a specific
+ future time slot.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+
+ @return None.
+
+**/
+STATIC
+VOID
+TcpUpdateTimer (
+ IN TCP_CB *Tcb
+ )
+{
+ UINT16 Index;
+
+ //
+ // Don't use a too large value to init NextExpire
+ // since mTcpTick wraps around as sequence no does.
+ //
+ Tcb->NextExpire = 65535;
+ TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_TIMER_ON);
+
+ for (Index = 0; Index < TCP_TIMER_NUMBER; Index++) {
+
+ if (TCP_TIMER_ON (Tcb->EnabledTimer, Index) &&
+ TCP_TIME_LT (Tcb->Timer[Index], mTcpTick + Tcb->NextExpire)) {
+
+ Tcb->NextExpire = TCP_SUB_TIME (Tcb->Timer[Index], mTcpTick);
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_TIMER_ON);
+ }
+ }
+}
+
+
+/**
+ Enable a TCP timer.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+ @param Timer The index of the timer to be enabled.
+ @param TimeOut The timeout value of this timer.
+
+ @return None.
+
+**/
+VOID
+TcpSetTimer (
+ IN TCP_CB *Tcb,
+ IN UINT16 Timer,
+ IN UINT32 TimeOut
+ )
+{
+ TCP_SET_TIMER (Tcb->EnabledTimer, Timer);
+ Tcb->Timer[Timer] = mTcpTick + TimeOut;
+
+ TcpUpdateTimer (Tcb);
+}
+
+
+/**
+ Clear one TCP timer.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+ @param Timer The index of the timer to be cleared.
+
+ @return None.
+
+**/
+VOID
+TcpClearTimer (
+ IN TCP_CB *Tcb,
+ IN UINT16 Timer
+ )
+{
+ TCP_CLEAR_TIMER (Tcb->EnabledTimer, Timer);
+ TcpUpdateTimer (Tcb);
+}
+
+
+/**
+ Clear all TCP timers.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+
+ @return None.
+
+**/
+VOID
+TcpClearAllTimer (
+ IN TCP_CB *Tcb
+ )
+{
+ Tcb->EnabledTimer = 0;
+ TcpUpdateTimer (Tcb);
+}
+
+
+/**
+ Enable the window prober timer and set the timeout value.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+
+ @return None.
+
+**/
+VOID
+TcpSetProbeTimer (
+ IN TCP_CB *Tcb
+ )
+{
+ if (!TCP_TIMER_ON (Tcb->EnabledTimer, TCP_TIMER_PROBE)) {
+ Tcb->ProbeTime = Tcb->Rto;
+
+ } else {
+ Tcb->ProbeTime <<= 1;
+ }
+
+ if (Tcb->ProbeTime < TCP_RTO_MIN) {
+
+ Tcb->ProbeTime = TCP_RTO_MIN;
+ } else if (Tcb->ProbeTime > TCP_RTO_MAX) {
+
+ Tcb->ProbeTime = TCP_RTO_MAX;
+ }
+
+ TcpSetTimer (Tcb, TCP_TIMER_PROBE, Tcb->ProbeTime);
+}
+
+
+/**
+ Enable the keepalive timer and set the timeout value.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+
+ @return None.
+
+**/
+VOID
+TcpSetKeepaliveTimer (
+ IN TCP_CB *Tcb
+ )
+{
+ if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_KEEPALIVE)) {
+ return ;
+
+ }
+
+ //
+ // Set the timer to KeepAliveIdle if either
+ // 1. the keepalive timer is off
+ // 2. The keepalive timer is on, but the idle
+ // is less than KeepAliveIdle, that means the
+ // connection is alive since our last probe.
+ //
+ if (!TCP_TIMER_ON (Tcb->EnabledTimer, TCP_TIMER_KEEPALIVE) ||
+ (Tcb->Idle < Tcb->KeepAliveIdle)) {
+
+ TcpSetTimer (Tcb, TCP_TIMER_KEEPALIVE, Tcb->KeepAliveIdle);
+ Tcb->KeepAliveProbes = 0;
+
+ } else {
+
+ TcpSetTimer (Tcb, TCP_TIMER_KEEPALIVE, Tcb->KeepAlivePeriod);
+ }
+}
+
+
+/**
+ Backoff the RTO.
+
+ @param Tcb Pointer to the TCP_CB of this TCP instance.
+
+ @return None.
+
+**/
+VOID
+TcpBackoffRto (
+ IN TCP_CB *Tcb
+ )
+{
+ //
+ // Fold the RTT estimate if too many times, the estimate
+ // may be wrong, fold it. So the next time a valid
+ // measurement is sampled, we can start fresh.
+ //
+ if ((Tcb->LossTimes >= TCP_FOLD_RTT) && Tcb->SRtt) {
+ Tcb->RttVar += Tcb->SRtt >> 2;
+ Tcb->SRtt = 0;
+ }
+
+ Tcb->Rto <<= 1;
+
+ if (Tcb->Rto < TCP_RTO_MIN) {
+
+ Tcb->Rto = TCP_RTO_MIN;
+ } else if (Tcb->Rto > TCP_RTO_MAX) {
+
+ Tcb->Rto = TCP_RTO_MAX;
+ }
+}
+
+
+/**
+ Heart beat timer handler.
+
+ @param Event Timer event signaled, ignored.
+ @param Context Context of the timer event, ignored.
+
+ @return None.
+
+**/
+VOID
+EFIAPI
+TcpTicking (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ NET_LIST_ENTRY *Entry;
+ NET_LIST_ENTRY *Next;
+ TCP_CB *Tcb;
+ INT16 Index;
+
+ mTcpTick++;
+ mTcpGlobalIss += 100;
+
+ //
+ // Don't use LIST_FOR_EACH, which isn't delete safe.
+ //
+ for (Entry = mTcpRunQue.ForwardLink; Entry != &mTcpRunQue; Entry = Next) {
+
+ Next = Entry->ForwardLink;
+
+ Tcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);
+
+ if (Tcb->State == TCP_CLOSED) {
+ continue;
+ }
+ //
+ // The connection is doing RTT measurement.
+ //
+ if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RTT_ON)) {
+ Tcb->RttMeasure++;
+ }
+
+ Tcb->Idle++;
+
+ if (Tcb->DelayedAck) {
+ TcpSendAck (Tcb);
+ }
+
+ //
+ // No timer is active or no timer expired
+ //
+ if (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_TIMER_ON) ||
+ ((--Tcb->NextExpire) > 0)) {
+
+ continue;
+ }
+
+ //
+ // Call the timeout handler for each expired timer.
+ //
+ for (Index = 0; Index < TCP_TIMER_NUMBER; Index++) {
+
+ if (TCP_TIMER_ON (Tcb->EnabledTimer, Index) &&
+ TCP_TIME_LEQ (Tcb->Timer[Index], mTcpTick)) {
+ //
+ // disable the timer before calling the handler
+ // in case the handler enables it again.
+ //
+ TCP_CLEAR_TIMER (Tcb->EnabledTimer, Index);
+ mTcpTimerHandler[Index](Tcb);
+
+ //
+ // The Tcb may have been deleted by the timer, or
+ // no other timer is set.
+ //
+ if ((Next->BackLink != Entry) ||
+ (Tcb->EnabledTimer == 0)) {
+
+ goto NextConnection;
+ }
+ }
+ }
+
+ TcpUpdateTimer (Tcb);
+NextConnection:
+ ;
+ }
+}
diff --git a/MdeModulePkg/Universal/Network/Udp4Dxe/ComponentName.c b/MdeModulePkg/Universal/Network/Udp4Dxe/ComponentName.c
new file mode 100644
index 0000000000..b934765207
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/Udp4Dxe/ComponentName.c
@@ -0,0 +1,169 @@
+/** @file
+
+Copyright (c) 2006 - 2007, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+
+ ComponentName.c
+
+Abstract:
+
+
+**/
+
+
+#include "Udp4Impl.h"
+
+//
+// EFI Component Name Functions
+//
+EFI_STATUS
+EFIAPI
+UdpComponentNameGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ );
+
+EFI_STATUS
+EFIAPI
+UdpComponentNameGetControllerName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle OPTIONAL,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ );
+
+//
+// EFI Component Name Protocol
+//
+EFI_COMPONENT_NAME_PROTOCOL gUdp4ComponentName = {
+ UdpComponentNameGetDriverName,
+ UdpComponentNameGetControllerName,
+ "eng"
+};
+
+static EFI_UNICODE_STRING_TABLE mUdpDriverNameTable[] = {
+ {
+ "eng",
+ L"UDP Network Service Driver"
+ },
+ {
+ NULL,
+ NULL
+ }
+};
+
+EFI_STATUS
+EFIAPI
+UdpComponentNameGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ )
+/*++
+
+Routine Description:
+
+ Retrieves a Unicode string that is the user readable name of the EFI Driver.
+
+Arguments:
+
+ This - A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance.
+ Language - A pointer to a three character ISO 639-2 language identifier.
+ This is the language of the driver name that that the caller
+ is requesting, and it must match one of the languages specified
+ in SupportedLanguages. The number of languages supported by a
+ driver is up to the driver writer.
+ DriverName - A pointer to the Unicode string to return. This Unicode string
+ is the name of the driver specified by This in the language
+ specified by Language.
+
+Returns:
+
+ EFI_SUCCES - The Unicode string for the Driver specified by This
+ and the language specified by Language was returned
+ in DriverName.
+ EFI_INVALID_PARAMETER - Language is NULL.
+ EFI_INVALID_PARAMETER - DriverName is NULL.
+ EFI_UNSUPPORTED - The driver specified by This does not support the
+ language specified by Language.
+
+--*/
+{
+ return LookupUnicodeString (
+ Language,
+ gUdp4ComponentName.SupportedLanguages,
+ mUdpDriverNameTable,
+ DriverName
+ );
+}
+
+EFI_STATUS
+EFIAPI
+UdpComponentNameGetControllerName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle OPTIONAL,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ )
+/*++
+
+Routine Description:
+
+ Retrieves a Unicode string that is the user readable name of the controller
+ that is being managed by an EFI Driver.
+
+Arguments:
+
+ This - A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance.
+ ControllerHandle - The handle of a controller that the driver specified by
+ This is managing. This handle specifies the controller
+ whose name is to be returned.
+ ChildHandle - The handle of the child controller to retrieve the name
+ of. This is an optional parameter that may be NULL. It
+ will be NULL for device drivers. It will also be NULL
+ for a bus drivers that wish to retrieve the name of the
+ bus controller. It will not be NULL for a bus driver
+ that wishes to retrieve the name of a child controller.
+ Language - A pointer to a three character ISO 639-2 language
+ identifier. This is the language of the controller name
+ that that the caller is requesting, and it must match one
+ of the languages specified in SupportedLanguages. The
+ number of languages supported by a driver is up to the
+ driver writer.
+ ControllerName - A pointer to the Unicode string to return. This Unicode
+ string is the name of the controller specified by
+ ControllerHandle and ChildHandle in the language specified
+ by Language from the point of view of the driver specified
+ by This.
+
+Returns:
+
+ EFI_SUCCESS - The Unicode string for the user readable name in the
+ language specified by Language for the driver
+ specified by This was returned in DriverName.
+ EFI_INVALID_PARAMETER - ControllerHandle is not a valid EFI_HANDLE.
+ EFI_INVALID_PARAMETER - ChildHandle is not NULL and it is not a valid EFI_HANDLE.
+ EFI_INVALID_PARAMETER - Language is NULL.
+ EFI_INVALID_PARAMETER - ControllerName is NULL.
+ EFI_UNSUPPORTED - The driver specified by This is not currently managing
+ the controller specified by ControllerHandle and
+ ChildHandle.
+ EFI_UNSUPPORTED - The driver specified by This does not support the
+ language specified by Language.
+
+--*/
+{
+ return EFI_UNSUPPORTED;
+}
+
diff --git a/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Driver.c b/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Driver.c
new file mode 100644
index 0000000000..7b47ef44a9
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Driver.c
@@ -0,0 +1,526 @@
+/** @file
+
+Copyright (c) 2006, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+
+ Udp4Driver.c
+
+Abstract:
+
+
+**/
+
+
+#include "Udp4Impl.h"
+
+EFI_DRIVER_BINDING_PROTOCOL gUdp4DriverBinding = {
+ Udp4DriverBindingSupported,
+ Udp4DriverBindingStart,
+ Udp4DriverBindingStop,
+ 0xa,
+ NULL,
+ NULL
+};
+
+EFI_SERVICE_BINDING_PROTOCOL mUdp4ServiceBinding = {
+ Udp4ServiceBindingCreateChild,
+ Udp4ServiceBindingDestroyChild
+};
+
+
+/**
+ Test to see if this driver supports ControllerHandle.
+
+ @param This Protocol instance pointer.
+ @param ControllerHandle Handle of device to test.
+ @param RemainingDevicePath Optional parameter use to pick a specific child
+ device to start.
+
+ @retval EFI_SUCCES This driver supports this device.
+ @retval EFI_ALREADY_STARTED This driver is already running on this device.
+
+**/
+EFI_STATUS
+EFIAPI
+Udp4DriverBindingSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+
+ //
+ // Test for the Udp4ServiceBinding Protocol
+ //
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiUdp4ServiceBindingProtocolGuid,
+ NULL,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+ if (!EFI_ERROR (Status)) {
+ return EFI_ALREADY_STARTED;
+ }
+
+ //
+ // Test for the Ip4 Protocol
+ //
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiIp4ServiceBindingProtocolGuid,
+ NULL,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+
+ return Status;
+}
+
+
+/**
+ Start this driver on ControllerHandle.
+
+ @param This Protocol instance pointer.
+ @param ControllerHandle Handle of device to bind driver to
+ @param RemainingDevicePath Optional parameter use to pick a specific child
+ device to start.
+
+ @retval EFI_SUCCES This driver is added to ControllerHandle
+ @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle
+ @retval other This driver does not support this device
+
+**/
+EFI_STATUS
+EFIAPI
+Udp4DriverBindingStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ UDP4_SERVICE_DATA *Udp4Service;
+
+ //
+ // Allocate Private Context Data Structure.
+ //
+ Udp4Service = NetAllocatePool (sizeof (UDP4_SERVICE_DATA));
+ if (Udp4Service == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Status = Udp4CreateService (Udp4Service, This->DriverBindingHandle, ControllerHandle);
+ if (EFI_ERROR (Status)) {
+ goto FREE_SERVICE;
+ }
+
+ //
+ // Install the Udp4ServiceBindingProtocol on the ControllerHandle.
+ //
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ &ControllerHandle,
+ &gEfiUdp4ServiceBindingProtocolGuid,
+ &Udp4Service->ServiceBinding,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ goto CLEAN_SERVICE;
+ }
+
+ Udp4SetVariableData (Udp4Service);
+
+ return Status;
+
+CLEAN_SERVICE:
+
+ Udp4CleanService (Udp4Service);
+
+FREE_SERVICE:
+
+ NetFreePool (Udp4Service);
+
+ return Status;
+}
+
+
+/**
+ Stop this driver on ControllerHandle.
+
+ @param This Protocol instance pointer.
+ @param ControllerHandle Handle of device to stop driver on
+ @param NumberOfChildren Number of Handles in ChildHandleBuffer. If number
+ of children is zero stop the entire bus driver.
+ @param ChildHandleBuffer List of Child Handles to Stop.
+
+ @retval EFI_SUCCES This driver is removed ControllerHandle.
+ @retval other This driver was not removed from this device.
+
+**/
+EFI_STATUS
+EFIAPI
+Udp4DriverBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer
+ )
+{
+ EFI_STATUS Status;
+ EFI_HANDLE NicHandle;
+ EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding;
+ UDP4_SERVICE_DATA *Udp4Service;
+ UDP4_INSTANCE_DATA *Instance;
+
+ //
+ // Find the NicHandle where UDP4 ServiceBinding Protocol is installed.
+ //
+ NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiIp4ProtocolGuid);
+ if (NicHandle == NULL) {
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Retrieve the UDP4 ServiceBinding Protocol.
+ //
+ Status = gBS->OpenProtocol (
+ NicHandle,
+ &gEfiUdp4ServiceBindingProtocolGuid,
+ (VOID **) &ServiceBinding,
+ This->DriverBindingHandle,
+ NicHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ Udp4Service = UDP4_SERVICE_DATA_FROM_THIS (ServiceBinding);
+
+ //
+ // Uninstall the UDP4 ServiceBinding Protocol.
+ //
+ Status = gBS->UninstallMultipleProtocolInterfaces (
+ NicHandle,
+ &gEfiUdp4ServiceBindingProtocolGuid,
+ &Udp4Service->ServiceBinding,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ while (!NetListIsEmpty (&Udp4Service->ChildrenList)) {
+ //
+ // Destroy all instances.
+ //
+ Instance = NET_LIST_HEAD (&Udp4Service->ChildrenList, UDP4_INSTANCE_DATA, Link);
+
+ ServiceBinding->DestroyChild (ServiceBinding, Instance->ChildHandle);
+ }
+
+ Udp4ClearVariableData (Udp4Service);
+
+ Udp4CleanService (Udp4Service);
+
+ NetFreePool (Udp4Service);
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Creates a child handle with a set of I/O services.
+
+ @param This Protocol instance pointer.
+ @param ChildHandle Pointer to the handle of the child to create. If
+ it is NULL, then a new handle is created. If it
+ is not NULL, then the I/O services are added to
+ the existing child handle.
+
+ @retval EFI_SUCCES The child handle was created with the I/O services
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources availabe to create
+ the child
+ @retval other The child handle was not created
+
+**/
+EFI_STATUS
+EFIAPI
+Udp4ServiceBindingCreateChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE *ChildHandle
+ )
+{
+ EFI_STATUS Status;
+ UDP4_SERVICE_DATA *Udp4Service;
+ UDP4_INSTANCE_DATA *Instance;
+ EFI_TPL OldTpl;
+ VOID *Ip4;
+
+ if ((This == NULL) || (ChildHandle == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Udp4Service = UDP4_SERVICE_DATA_FROM_THIS (This);
+
+ //
+ // Allocate the instance private data structure.
+ //
+ Instance = NetAllocatePool (sizeof (UDP4_INSTANCE_DATA));
+ if (Instance == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Udp4InitInstance (Udp4Service, Instance);
+
+ //
+ // Add an IpInfo for this instance.
+ //
+ Instance->IpInfo = IpIoAddIp (Udp4Service->IpIo);
+ if (Instance->IpInfo == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto FREE_INSTANCE;
+ }
+
+ //
+ // Install the Udp4Protocol for this instance.
+ //
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ ChildHandle,
+ &gEfiUdp4ProtocolGuid,
+ &Instance->Udp4Proto,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ goto REMOVE_IPINFO;
+ }
+
+ Instance->ChildHandle = *ChildHandle;
+
+ //
+ // Open the default Ip4 protocol in the IP_IO BY_CHILD.
+ //
+ Status = gBS->OpenProtocol (
+ Udp4Service->IpIo->ChildHandle,
+ &gEfiIp4ProtocolGuid,
+ (VOID **) &Ip4,
+ gUdp4DriverBinding.DriverBindingHandle,
+ Instance->ChildHandle,
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
+ );
+ if (EFI_ERROR (Status)) {
+ goto UNINSTALL_PROTOCOL;
+ }
+
+ OldTpl = NET_RAISE_TPL (NET_TPL_LOCK);
+
+ //
+ // Link this instance into the service context data and increase the ChildrenNumber.
+ //
+ NetListInsertTail (&Udp4Service->ChildrenList, &Instance->Link);
+ Udp4Service->ChildrenNumber++;
+
+ NET_RESTORE_TPL (OldTpl);
+
+ return Status;
+
+UNINSTALL_PROTOCOL:
+
+ gBS->UninstallMultipleProtocolInterfaces (
+ Instance->ChildHandle,
+ &gEfiUdp4ProtocolGuid,
+ &Instance->Udp4Proto,
+ NULL
+ );
+
+REMOVE_IPINFO:
+
+ IpIoRemoveIp (Udp4Service->IpIo, Instance->IpInfo);
+
+FREE_INSTANCE:
+
+ Udp4CleanInstance (Instance);
+
+ NetFreePool (Instance);
+
+ return Status;
+}
+
+
+/**
+ Destroys a child handle with a set of I/O services.
+
+ @param This Protocol instance pointer.
+ @param ChildHandle Handle of the child to destroy
+
+ @retval EFI_SUCCES The I/O services were removed from the child
+ handle
+ @retval EFI_UNSUPPORTED The child handle does not support the I/O services
+ that are being removed
+ @retval EFI_INVALID_PARAMETER Child handle is not a valid EFI Handle.
+ @retval EFI_ACCESS_DENIED The child handle could not be destroyed because
+ its I/O services are being used.
+ @retval other The child handle was not destroyed
+
+**/
+EFI_STATUS
+EFIAPI
+Udp4ServiceBindingDestroyChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ChildHandle
+ )
+{
+ EFI_STATUS Status;
+ UDP4_SERVICE_DATA *Udp4Service;
+ EFI_UDP4_PROTOCOL *Udp4Proto;
+ UDP4_INSTANCE_DATA *Instance;
+ EFI_TPL OldTpl;
+
+ if ((This == NULL) || (ChildHandle == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Udp4Service = UDP4_SERVICE_DATA_FROM_THIS (This);
+
+ //
+ // Try to get the Udp4 protocol from the ChildHandle.
+ //
+ Status = gBS->OpenProtocol (
+ ChildHandle,
+ &gEfiUdp4ProtocolGuid,
+ (VOID **) &Udp4Proto,
+ gUdp4DriverBinding.DriverBindingHandle,
+ ChildHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ return EFI_UNSUPPORTED;
+ }
+
+ Instance = UDP4_INSTANCE_DATA_FROM_THIS (Udp4Proto);
+
+ if (Instance->Destroyed) {
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Use the Destroyed flag to avoid the re-entering of the following code.
+ //
+ Instance->Destroyed = TRUE;
+
+ //
+ // Close the Ip4 protocol.
+ //
+ gBS->CloseProtocol (
+ Udp4Service->IpIo->ChildHandle,
+ &gEfiIp4ProtocolGuid,
+ gUdp4DriverBinding.DriverBindingHandle,
+ Instance->ChildHandle
+ );
+
+ //
+ // Uninstall the Udp4Protocol previously installed on the ChildHandle.
+ //
+ Status = gBS->UninstallMultipleProtocolInterfaces (
+ ChildHandle,
+ &gEfiUdp4ProtocolGuid,
+ (VOID *) &Instance->Udp4Proto,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ Instance->Destroyed = FALSE;
+ return Status;
+ }
+
+ //
+ // Reset the configuration in case the instance's consumer forgets to do this.
+ //
+ Udp4Proto->Configure (Udp4Proto, NULL);
+
+ //
+ // Remove the IpInfo this instance consumes.
+ //
+ IpIoRemoveIp (Udp4Service->IpIo, Instance->IpInfo);
+
+ OldTpl = NET_RAISE_TPL (NET_TPL_LOCK);
+
+ //
+ // Remove this instance from the service context data's ChildrenList.
+ //
+ NetListRemoveEntry (&Instance->Link);
+ Udp4Service->ChildrenNumber--;
+
+ //
+ // Clean the instance.
+ //
+ Udp4CleanInstance (Instance);
+
+ NET_RESTORE_TPL (OldTpl);
+
+ NetFreePool (Instance);
+
+ return EFI_SUCCESS;
+}
+
+//@MT: EFI_DRIVER_ENTRY_POINT (Udp4DriverEntryPoint)
+
+EFI_STATUS
+EFIAPI
+Udp4DriverEntryPoint (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+/*++
+
+Routine Description:
+
+ The entry point for Udp4 driver which installs the driver binding
+ and component name protocol on its ImageHandle.
+
+Arguments:
+
+ ImageHandle - The image handle of the driver.
+ SystemTable - The system table.
+
+Returns:
+
+ EFI_SUCCESS - if the driver binding and component name protocols are
+ successfully installed, otherwise if failed.
+
+--*/
+{
+ EFI_STATUS Status;
+
+ //
+ // Install the Udp4DriverBinding and Udp4ComponentName protocols.
+ //
+ Status = NetLibInstallAllDriverProtocols (
+ ImageHandle,
+ SystemTable,
+ &gUdp4DriverBinding,
+ ImageHandle,
+ &gUdp4ComponentName,
+ NULL,
+ NULL
+ );
+ if (!EFI_ERROR (Status)) {
+ //
+ // Initialize the UDP random port.
+ //
+ mUdp4RandomPort = ((UINT16) NetRandomInitSeed ()) % UDP4_PORT_KNOWN + UDP4_PORT_KNOWN;
+ }
+
+ return Status;
+}
+
diff --git a/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Driver.h b/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Driver.h
new file mode 100644
index 0000000000..59acb44452
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Driver.h
@@ -0,0 +1,70 @@
+/** @file
+
+Copyright (c) 2006, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+
+ Udp4Driver.h
+
+Abstract:
+
+
+**/
+
+#ifndef _UDP4_DRIVER_H_
+#define _UDP4_DRIVER_H_
+
+#include <PiDxe.h>
+#include <Library/BaseLib.h>
+#include <Library/NetLib.h>
+#include <Protocol/DriverBinding.h>
+#include <Protocol/ServiceBinding.h>
+
+EFI_STATUS
+EFIAPI
+Udp4DriverBindingSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ );
+
+EFI_STATUS
+EFIAPI
+Udp4DriverBindingStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ );
+
+EFI_STATUS
+EFIAPI
+Udp4DriverBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer
+ );
+
+EFI_STATUS
+EFIAPI
+Udp4ServiceBindingCreateChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE *ChildHandle
+ );
+
+EFI_STATUS
+EFIAPI
+Udp4ServiceBindingDestroyChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ChildHandle
+ );
+
+#endif
+
diff --git a/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Dxe.inf b/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Dxe.inf
new file mode 100644
index 0000000000..3f97393dc3
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Dxe.inf
@@ -0,0 +1,66 @@
+#/** @file
+# Component name for module Udp4
+#
+# FIX ME!
+# Copyright (c) 2006, Intel Corporation. All right reserved.
+#
+# All rights reserved. This program and the accompanying materials
+# are licensed and made available under the terms and conditions of the BSD License
+# which accompanies this distribution. The full text of the license may be found at
+# http://opensource.org/licenses/bsd-license.php
+#
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+#
+#
+#**/
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = Udp4
+ FILE_GUID = 6d6963ab-906d-4a65-a7ca-bd40e5d6af2b
+ MODULE_TYPE = DXE_DRIVER
+ VERSION_STRING = 1.0
+ EDK_RELEASE_VERSION = 0x00020000
+ EFI_SPECIFICATION_VERSION = 0x00020000
+
+ ENTRY_POINT = Udp4DriverEntryPoint
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 IPF EBC
+#
+
+[Sources.common]
+ Udp4Impl.h
+ Udp4Main.c
+ ComponentName.c
+ Udp4Impl.c
+ Udp4Driver.h
+ Udp4Driver.c
+ CommonHeader.h
+
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+
+
+[LibraryClasses]
+ UefiLib
+ BaseLib
+ UefiBootServicesTableLib
+ UefiDriverEntryPoint
+ UefiRuntimeServicesTableLib
+ DebugLib
+ IpIoLib
+ NetLib
+
+
+[Protocols]
+ gEfiIp4ProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+ gEfiUdp4ServiceBindingProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+ gEfiIp4ServiceBindingProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+ gEfiUdp4ProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+
diff --git a/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Dxe.msa b/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Dxe.msa
new file mode 100644
index 0000000000..1415458270
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Dxe.msa
@@ -0,0 +1,77 @@
+<ModuleSurfaceArea xmlns="http://www.TianoCore.org/2006/Edk2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <MsaHeader>
+ <ModuleName>Udp4</ModuleName>
+ <ModuleType>DXE_DRIVER</ModuleType>
+ <GuidValue>6d6963ab-906d-4a65-a7ca-bd40e5d6af2b</GuidValue>
+ <Version>1.0</Version>
+ <Abstract>Component name for module Udp4</Abstract>
+ <Description>FIX ME!</Description>
+ <Copyright>Copyright (c) 2006, Intel Corporation. All right reserved.</Copyright>
+ <License>All rights reserved. This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.</License>
+ <Specification>FRAMEWORK_BUILD_PACKAGING_SPECIFICATION 0x00000052</Specification>
+ </MsaHeader>
+ <ModuleDefinitions>
+ <SupportedArchitectures>IA32 X64 IPF EBC</SupportedArchitectures>
+ <BinaryModule>false</BinaryModule>
+ <OutputFileBasename>Udp4</OutputFileBasename>
+ </ModuleDefinitions>
+ <LibraryClassDefinitions>
+ <LibraryClass Usage="ALWAYS_CONSUMED">
+ <Keyword>DebugLib</Keyword>
+ </LibraryClass>
+ <LibraryClass Usage="ALWAYS_CONSUMED">
+ <Keyword>UefiRuntimeServicesTableLib</Keyword>
+ </LibraryClass>
+ <LibraryClass Usage="ALWAYS_CONSUMED">
+ <Keyword>UefiDriverEntryPoint</Keyword>
+ </LibraryClass>
+ <LibraryClass Usage="ALWAYS_CONSUMED">
+ <Keyword>UefiBootServicesTableLib</Keyword>
+ </LibraryClass>
+ <LibraryClass Usage="ALWAYS_CONSUMED">
+ <Keyword>BaseLib</Keyword>
+ </LibraryClass>
+ <LibraryClass Usage="ALWAYS_CONSUMED">
+ <Keyword>UefiLib</Keyword>
+ </LibraryClass>
+ </LibraryClassDefinitions>
+ <SourceFiles>
+ <Filename>Udp4Driver.c</Filename>
+ <Filename>Udp4Driver.h</Filename>
+ <Filename>Udp4Impl.c</Filename>
+ <Filename>ComponentName.c</Filename>
+ <Filename>Udp4Main.c</Filename>
+ <Filename>Udp4Impl.h</Filename>
+ </SourceFiles>
+ <PackageDependencies>
+ <Package PackageGuid="5e0e9358-46b6-4ae2-8218-4ab8b9bbdcec"/>
+ <Package PackageGuid="68169ab0-d41b-4009-9060-292c253ac43d"/>
+ </PackageDependencies>
+ <Protocols>
+ <Protocol Usage="ALWAYS_CONSUMED">
+ <ProtocolCName>gEfiUdp4ProtocolGuid</ProtocolCName>
+ </Protocol>
+ <Protocol Usage="ALWAYS_CONSUMED">
+ <ProtocolCName>gEfiIp4ServiceBindingProtocolGuid</ProtocolCName>
+ </Protocol>
+ <Protocol Usage="ALWAYS_CONSUMED">
+ <ProtocolCName>gEfiUdp4ServiceBindingProtocolGuid</ProtocolCName>
+ </Protocol>
+ <Protocol Usage="ALWAYS_CONSUMED">
+ <ProtocolCName>gEfiIp4ProtocolGuid</ProtocolCName>
+ </Protocol>
+ </Protocols>
+ <Externs>
+ <Specification>EFI_SPECIFICATION_VERSION 0x00020000</Specification>
+ <Specification>EDK_RELEASE_VERSION 0x00020000</Specification>
+ <Extern>
+ <ModuleEntryPoint>Udp4DriverEntryPoint</ModuleEntryPoint>
+ </Extern>
+ </Externs>
+</ModuleSurfaceArea> \ No newline at end of file
diff --git a/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Impl.c b/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Impl.c
new file mode 100644
index 0000000000..dd7522e91f
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Impl.c
@@ -0,0 +1,1951 @@
+/** @file
+
+Copyright (c) 2006 - 2007, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+
+ Udp4Impl.c
+
+Abstract:
+
+ The implementation of the Udp4 protocol.
+
+
+**/
+
+
+#include "Udp4Impl.h"
+
+UINT16 mUdp4RandomPort;
+
+STATIC
+VOID
+EFIAPI
+Udp4CheckTimeout (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ );
+
+STATIC
+BOOLEAN
+Udp4FindInstanceByPort (
+ IN NET_LIST_ENTRY *InstanceList,
+ IN EFI_IPv4_ADDRESS *Address,
+ IN UINT16 Port
+ );
+
+STATIC
+VOID
+Udp4DgramSent (
+ IN EFI_STATUS Status,
+ IN VOID *Context,
+ IN VOID *Sender,
+ IN VOID *NotifyData
+ );
+
+STATIC
+VOID
+Udp4DgramRcvd (
+ IN EFI_STATUS Status,
+ IN ICMP_ERROR IcmpError,
+ IN EFI_NET_SESSION_DATA *NetSession,
+ IN NET_BUF *Packet,
+ IN VOID *Context
+ );
+
+STATIC
+EFI_STATUS
+Udp4CancelTokens (
+ IN NET_MAP *Map,
+ IN NET_MAP_ITEM *Item,
+ IN VOID *Arg OPTIONAL
+ );
+
+STATIC
+BOOLEAN
+Udp4MatchDgram (
+ IN UDP4_INSTANCE_DATA *Instance,
+ IN EFI_UDP4_SESSION_DATA *Udp4Session
+ );
+
+STATIC
+VOID
+EFIAPI
+Udp4RecycleRxDataWrap (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ );
+
+STATIC
+UDP4_RXDATA_WRAP *
+Udp4WrapRxData (
+ IN UDP4_INSTANCE_DATA *Instance,
+ IN NET_BUF *Packet,
+ IN EFI_UDP4_RECEIVE_DATA *RxData
+ );
+
+STATIC
+UINTN
+Udp4EnqueueDgram (
+ IN UDP4_SERVICE_DATA *Udp4Service,
+ IN NET_BUF *Packet,
+ IN EFI_UDP4_RECEIVE_DATA *RxData
+ );
+
+STATIC
+VOID
+Udp4DeliverDgram (
+ IN UDP4_SERVICE_DATA *Udp4Service
+ );
+
+STATIC
+VOID
+Udp4Demultiplex (
+ IN UDP4_SERVICE_DATA *Udp4Service,
+ IN EFI_NET_SESSION_DATA *NetSession,
+ IN NET_BUF *Packet
+ );
+
+STATIC
+VOID
+Udp4IcmpHandler (
+ IN UDP4_SERVICE_DATA *Udp4Service,
+ IN ICMP_ERROR IcmpError,
+ IN EFI_NET_SESSION_DATA *NetSession,
+ IN NET_BUF *Packet
+ );
+
+STATIC
+VOID
+Udp4SendPortUnreach (
+ IN IP_IO *IpIo,
+ IN EFI_NET_SESSION_DATA *NetSession,
+ IN VOID *Udp4Header
+ );
+
+
+/**
+ Create the Udp service context data.
+
+ @param Udp4Service Pointer to the UDP4_SERVICE_DATA.
+ @param ImageHandle The image handle of this udp4 driver.
+ @param ControllerHandle The controller handle this udp4 driver binds on.
+
+ @retval EFI_SUCCESS The udp4 service context data is created and
+ initialized.
+ @retval EFI_OUT_OF_RESOURCES Cannot allocate memory.
+
+**/
+EFI_STATUS
+Udp4CreateService (
+ IN UDP4_SERVICE_DATA *Udp4Service,
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_HANDLE ControllerHandle
+ )
+{
+ EFI_STATUS Status;
+ IP_IO_OPEN_DATA OpenData;
+
+ Udp4Service->Signature = UDP4_SERVICE_DATA_SIGNATURE;
+ Udp4Service->ServiceBinding = mUdp4ServiceBinding;
+ Udp4Service->ImageHandle = ImageHandle;
+ Udp4Service->ControllerHandle = ControllerHandle;
+ Udp4Service->ChildrenNumber = 0;
+
+ NetListInit (&Udp4Service->ChildrenList);
+
+ //
+ // Create the IpIo for this service context.
+ //
+ Udp4Service->IpIo = IpIoCreate (ImageHandle, ControllerHandle);
+ if (Udp4Service->IpIo == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Set the OpenData used to open the IpIo.
+ //
+ OpenData.IpConfigData = mIpIoDefaultIpConfigData;
+ OpenData.IpConfigData.AcceptBroadcast = TRUE;
+ OpenData.RcvdContext = (VOID *) Udp4Service;
+ OpenData.SndContext = NULL;
+ OpenData.PktRcvdNotify = Udp4DgramRcvd;
+ OpenData.PktSentNotify = Udp4DgramSent;
+
+ //
+ // Configure and start the IpIo.
+ //
+ Status = IpIoOpen (Udp4Service->IpIo, &OpenData);
+ if (EFI_ERROR (Status)) {
+ goto RELEASE_IPIO;
+ }
+
+ //
+ // Create the event for Udp timeout checking.
+ //
+ Status = gBS->CreateEvent (
+ EVT_TIMER | EVT_NOTIFY_SIGNAL,
+ NET_TPL_FAST_TIMER,
+ Udp4CheckTimeout,
+ Udp4Service,
+ &Udp4Service->TimeoutEvent
+ );
+ if (EFI_ERROR (Status)) {
+ goto RELEASE_IPIO;
+ }
+
+ //
+ // Start the timeout timer event.
+ //
+ Status = gBS->SetTimer (
+ Udp4Service->TimeoutEvent,
+ TimerPeriodic,
+ UDP4_TIMEOUT_INTERVAL
+ );
+ if (EFI_ERROR (Status)) {
+ goto RELEASE_ALL;
+ }
+
+ Udp4Service->MacString = NULL;
+
+ return EFI_SUCCESS;
+
+RELEASE_ALL:
+
+ gBS->CloseEvent (Udp4Service->TimeoutEvent);
+
+RELEASE_IPIO:
+
+ IpIoDestroy (Udp4Service->IpIo);
+
+ return Status;
+}
+
+
+/**
+ Clean the Udp service context data.
+
+ @param Udp4Service Pointer to the UDP4_SERVICE_DATA.
+
+ @return None.
+
+**/
+VOID
+Udp4CleanService (
+ IN UDP4_SERVICE_DATA *Udp4Service
+ )
+{
+ //
+ // Cancel the TimeoutEvent timer.
+ //
+ gBS->SetTimer (Udp4Service->TimeoutEvent, TimerCancel, 0);
+
+ //
+ // Close the TimeoutEvent timer.
+ //
+ gBS->CloseEvent (Udp4Service->TimeoutEvent);
+
+ //
+ // Destroy the IpIo.
+ //
+ IpIoDestroy (Udp4Service->IpIo);
+}
+
+
+/**
+ This function checks and timeouts the I/O datagrams holding by the corresponding
+ service context.
+
+ @param Event The event this function registered to.
+ @param Conext The context data registered during the creation of
+ the Event.
+
+ @return None.
+
+**/
+STATIC
+VOID
+EFIAPI
+Udp4CheckTimeout (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ UDP4_SERVICE_DATA *Udp4Service;
+ NET_LIST_ENTRY *Entry;
+ UDP4_INSTANCE_DATA *Instance;
+ NET_LIST_ENTRY *WrapEntry;
+ NET_LIST_ENTRY *NextEntry;
+ UDP4_RXDATA_WRAP *Wrap;
+
+ Udp4Service = (UDP4_SERVICE_DATA *) Context;
+ NET_CHECK_SIGNATURE (Udp4Service, UDP4_SERVICE_DATA_SIGNATURE);
+
+ NET_LIST_FOR_EACH (Entry, &Udp4Service->ChildrenList) {
+ //
+ // Iterate all the instances belonging to this service context.
+ //
+ Instance = NET_LIST_USER_STRUCT (Entry, UDP4_INSTANCE_DATA, Link);
+ NET_CHECK_SIGNATURE (Instance, UDP4_INSTANCE_DATA_SIGNATURE);
+
+ if (!Instance->Configured || (Instance->ConfigData.ReceiveTimeout == 0)) {
+ //
+ // Skip this instance if it's not configured or no receive timeout.
+ //
+ continue;
+ }
+
+ NET_LIST_FOR_EACH_SAFE (WrapEntry, NextEntry, &Instance->RcvdDgramQue) {
+ //
+ // Iterate all the rxdatas belonging to this udp instance.
+ //
+ Wrap = NET_LIST_USER_STRUCT (Entry, UDP4_RXDATA_WRAP, Link);
+
+ if (Wrap->TimeoutTick <= UDP4_TIMEOUT_INTERVAL / 1000) {
+ //
+ // Remove this RxData if it timeouts.
+ //
+ Udp4RecycleRxDataWrap (NULL, (VOID *) Wrap);
+ } else {
+ Wrap->TimeoutTick -= UDP4_TIMEOUT_INTERVAL / 1000;
+ }
+ }
+ }
+}
+
+
+/**
+ This function intializes the new created udp instance.
+
+ @param Udp4Service Pointer to the UDP4_SERVICE_DATA.
+ @param Instance Pointer to the un-initialized UDP4_INSTANCE_DATA.
+
+ @return None.
+
+**/
+VOID
+Udp4InitInstance (
+ IN UDP4_SERVICE_DATA *Udp4Service,
+ IN UDP4_INSTANCE_DATA *Instance
+ )
+{
+ //
+ // Set the signature.
+ //
+ Instance->Signature = UDP4_INSTANCE_DATA_SIGNATURE;
+
+ //
+ // Init the lists.
+ //
+ NetListInit (&Instance->Link);
+ NetListInit (&Instance->RcvdDgramQue);
+ NetListInit (&Instance->DeliveredDgramQue);
+
+ //
+ // Init the NET_MAPs.
+ //
+ NetMapInit (&Instance->TxTokens);
+ NetMapInit (&Instance->RxTokens);
+ NetMapInit (&Instance->McastIps);
+
+ //
+ // Save the pointer to the UDP4_SERVICE_DATA, and initialize other members.
+ //
+ Instance->Udp4Service = Udp4Service;
+ Instance->Udp4Proto = mUdp4Protocol;
+ Instance->IcmpError = EFI_SUCCESS;
+ Instance->Configured = FALSE;
+ Instance->IsNoMapping = FALSE;
+ Instance->Destroyed = FALSE;
+}
+
+
+/**
+ This function cleans the udp instance.
+
+ @param Instance Pointer to the UDP4_INSTANCE_DATA to clean.
+
+ @return None.
+
+**/
+VOID
+Udp4CleanInstance (
+ IN UDP4_INSTANCE_DATA *Instance
+ )
+{
+ NetMapClean (&Instance->McastIps);
+ NetMapClean (&Instance->RxTokens);
+ NetMapClean (&Instance->TxTokens);
+}
+
+
+/**
+ This function finds the udp instance by the specified <Address, Port> pair.
+
+ @param InstanceList Pointer to the head of the list linking the udp
+ instances.
+ @param Address Pointer to the specified IPv4 address.
+ @param Port The udp port number.
+
+ @return Is the specified <Address, Port> pair found or not.
+
+**/
+STATIC
+BOOLEAN
+Udp4FindInstanceByPort (
+ IN NET_LIST_ENTRY *InstanceList,
+ IN EFI_IPv4_ADDRESS *Address,
+ IN UINT16 Port
+ )
+{
+ NET_LIST_ENTRY *Entry;
+ UDP4_INSTANCE_DATA *Instance;
+ EFI_UDP4_CONFIG_DATA *ConfigData;
+
+ NET_LIST_FOR_EACH (Entry, InstanceList) {
+ //
+ // Iterate all the udp instances.
+ //
+ Instance = NET_LIST_USER_STRUCT (Entry, UDP4_INSTANCE_DATA, Link);
+ ConfigData = &Instance->ConfigData;
+
+ if (!Instance->Configured || ConfigData->AcceptAnyPort) {
+ //
+ // If the instance is not configured or the configdata of the instance indicates
+ // this instance accepts any port, skip it.
+ //
+ continue;
+ }
+
+ if (EFI_IP_EQUAL (ConfigData->StationAddress, *Address) &&
+ (ConfigData->StationPort == Port)) {
+ //
+ // if both the address and the port are the same, return TRUE.
+ //
+ return TRUE;
+ }
+ }
+
+ //
+ // return FALSE when matching fails.
+ //
+ return FALSE;
+}
+
+
+/**
+ This function tries to bind the udp instance according to the configured port
+ allocation stragety.
+
+ @param InstanceList Pointer to the head of the list linking the udp
+ instances.
+ @param ConfigData Pointer to the ConfigData of the instance to be
+ bound.
+
+ @retval EFI_SUCCESS The bound operation is completed successfully.
+ @retval EFI_ACCESS_DENIED The <Address, Port> specified by the ConfigData is
+ already used by other instance.
+ @retval EFI_OUT_OF_RESOURCES No available port resources.
+
+**/
+EFI_STATUS
+Udp4Bind (
+ IN NET_LIST_ENTRY *InstanceList,
+ IN EFI_UDP4_CONFIG_DATA *ConfigData
+ )
+{
+ EFI_IPv4_ADDRESS *StationAddress;
+ UINT16 StartPort;
+
+ if (ConfigData->AcceptAnyPort) {
+ return EFI_SUCCESS;
+ }
+
+ StationAddress = &ConfigData->StationAddress;
+
+ if (ConfigData->StationPort != 0) {
+
+ if (!ConfigData->AllowDuplicatePort &&
+ Udp4FindInstanceByPort (InstanceList, StationAddress, ConfigData->StationPort)) {
+ //
+ // Do not allow duplicate port and the port is already used by other instance.
+ //
+ return EFI_ACCESS_DENIED;
+ }
+ } else {
+ //
+ // select a random port for this instance;
+ //
+
+ if (ConfigData->AllowDuplicatePort) {
+ //
+ // Just pick up the random port if the instance allows duplicate port.
+ //
+ ConfigData->StationPort = mUdp4RandomPort;
+ } else {
+
+ StartPort = mUdp4RandomPort;
+
+ while (Udp4FindInstanceByPort(InstanceList, StationAddress, mUdp4RandomPort)) {
+
+ mUdp4RandomPort++;
+ if (mUdp4RandomPort == 0) {
+ mUdp4RandomPort = UDP4_PORT_KNOWN;
+ }
+
+ if (mUdp4RandomPort == StartPort) {
+ //
+ // No available port.
+ //
+ return EFI_OUT_OF_RESOURCES;
+ }
+ }
+
+ ConfigData->StationPort = mUdp4RandomPort;
+ }
+
+ mUdp4RandomPort++;
+ if (mUdp4RandomPort == 0) {
+ mUdp4RandomPort = UDP4_PORT_KNOWN;
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ This function is used to check whether the NewConfigData has any un-reconfigurable
+ parameters changed compared to the OldConfigData.
+
+ @param OldConfigData Pointer to the current ConfigData the udp instance
+ uses.
+ @param NewConfigData Pointer to the new ConfigData.
+
+ @return The instance is reconfigurable or not according to the NewConfigData.
+
+**/
+BOOLEAN
+Udp4IsReconfigurable (
+ IN EFI_UDP4_CONFIG_DATA *OldConfigData,
+ IN EFI_UDP4_CONFIG_DATA *NewConfigData
+ )
+{
+ if ((NewConfigData->AcceptAnyPort != OldConfigData->AcceptAnyPort) ||
+ (NewConfigData->AcceptBroadcast != OldConfigData->AcceptBroadcast) ||
+ (NewConfigData->AcceptPromiscuous != OldConfigData->AcceptPromiscuous) ||
+ (NewConfigData->AllowDuplicatePort != OldConfigData->AllowDuplicatePort)) {
+ //
+ // The receiving filter parameters cannot be changed.
+ //
+ return FALSE;
+ }
+
+ if ((!NewConfigData->AcceptAnyPort) &&
+ (NewConfigData->StationPort != OldConfigData->StationPort)) {
+ //
+ // The port is not changeable.
+ //
+ return FALSE;
+ }
+
+ if (!NewConfigData->AcceptPromiscuous) {
+
+ if (NewConfigData->UseDefaultAddress != OldConfigData->UseDefaultAddress) {
+ //
+ // The NewConfigData differs to the old one on the UseDefaultAddress.
+ //
+ return FALSE;
+ }
+
+ if (!NewConfigData->UseDefaultAddress &&
+ (!EFI_IP_EQUAL (NewConfigData->StationAddress, OldConfigData->StationAddress) ||
+ !EFI_IP_EQUAL (NewConfigData->SubnetMask, OldConfigData->SubnetMask))) {
+ //
+ // If the instance doesn't use the default address, and the new address or
+ // new subnet mask is different from the old values.
+ //
+ return FALSE;
+ }
+ }
+
+ if (!EFI_IP_EQUAL (NewConfigData->RemoteAddress, OldConfigData->RemoteAddress)) {
+ //
+ // The remoteaddress is not the same.
+ //
+ return FALSE;
+ }
+
+ if ((EFI_IP4 (NewConfigData->RemoteAddress) != 0) &&
+ (NewConfigData->RemotePort != OldConfigData->RemotePort)) {
+ //
+ // The RemotePort differs if it's designated in the configdata.
+ //
+ return FALSE;
+ }
+
+ //
+ // All checks pass, return TRUE.
+ //
+ return TRUE;
+}
+
+
+/**
+ This function builds the Ip4 configdata from the Udp4ConfigData.
+
+ @param Udp4ConfigData Pointer to the EFI_UDP4_CONFIG_DATA.
+ @param Ip4ConfigData Pointer to the EFI_IP4_CONFIG_DATA.
+
+ @return None.
+
+**/
+VOID
+Udp4BuildIp4ConfigData (
+ IN EFI_UDP4_CONFIG_DATA *Udp4ConfigData,
+ IN EFI_IP4_CONFIG_DATA *Ip4ConfigData
+ )
+{
+ *Ip4ConfigData = mIpIoDefaultIpConfigData;
+ Ip4ConfigData->DefaultProtocol = EFI_IP_PROTO_UDP;
+ Ip4ConfigData->AcceptBroadcast = Udp4ConfigData->AcceptBroadcast;
+ Ip4ConfigData->AcceptPromiscuous = Udp4ConfigData->AcceptPromiscuous;
+ Ip4ConfigData->UseDefaultAddress = Udp4ConfigData->UseDefaultAddress;
+ Ip4ConfigData->StationAddress = Udp4ConfigData->StationAddress;
+ Ip4ConfigData->SubnetMask = Udp4ConfigData->SubnetMask;
+
+ //
+ // use the -1 magic number to disable the receiving process of the ip instance.
+ //
+ Ip4ConfigData->ReceiveTimeout = (UINT32) (-1);
+}
+
+
+/**
+ This function validates the TxToken, it returns the error code according to the spec.
+
+ @param Instance Pointer to the udp instance context data.
+ @param TxToken Pointer to the token to be checked.
+
+ @retval EFI_SUCCESS The TxToken is valid.
+ @retval EFI_INVALID_PARAMETER One or more of the following are TRUE: This is
+ NULL. Token is NULL. Token.Event is NULL.
+ Token.Packet.TxData is NULL.
+ Token.Packet.TxData.FragmentCount is zero.
+ Token.Packet.TxData.DataLength is not equal to the
+ sum of fragment lengths. One or more of the
+ Token.Packet.TxData.FragmentTable[].
+ FragmentLength fields is zero. One or more of the
+ Token.Packet.TxData.FragmentTable[].
+ FragmentBuffer fields is NULL.
+ Token.Packet.TxData. GatewayAddress is not a
+ unicast IPv4 address if it is not NULL. One or
+ more IPv4 addresses in Token.Packet.TxData.
+ UdpSessionData are not valid unicast IPv4
+ addresses if the UdpSessionData is not NULL.
+ @retval EFI_BAD_BUFFER_SIZE The data length is greater than the maximum UDP
+ packet size.
+
+**/
+EFI_STATUS
+Udp4ValidateTxToken (
+ IN UDP4_INSTANCE_DATA *Instance,
+ IN EFI_UDP4_COMPLETION_TOKEN *TxToken
+ )
+{
+ EFI_UDP4_TRANSMIT_DATA *TxData;
+ UINT32 Index;
+ UINT32 TotalLen;
+ EFI_UDP4_CONFIG_DATA *ConfigData;
+ EFI_UDP4_SESSION_DATA *UdpSessionData;
+ IP4_ADDR SourceAddress;
+
+ if (TxToken->Event == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ TxData = TxToken->Packet.TxData;
+
+ if ((TxData == NULL) || (TxData->FragmentCount == 0)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ TotalLen = 0;
+ for (Index = 0; Index < TxData->FragmentCount; Index++) {
+
+ if ((TxData->FragmentTable[Index].FragmentBuffer == NULL) ||
+ (TxData->FragmentTable[Index].FragmentLength == 0)) {
+ //
+ // if the FragmentBuffer is NULL or the FragmentLeng is zero.
+ //
+ return EFI_INVALID_PARAMETER;
+ }
+
+ TotalLen += TxData->FragmentTable[Index].FragmentLength;
+ }
+
+ if (TotalLen != TxData->DataLength) {
+ //
+ // The TotalLen calculated by adding all the FragmentLeng doesn't equal to the
+ // DataLength.
+ //
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((TxData->GatewayAddress != NULL) &&
+ !Ip4IsUnicast(EFI_NTOHL (*(TxData->GatewayAddress)), 0)) {
+ //
+ // The specified GatewayAddress is not a unicast IPv4 address while it's not 0.
+ //
+ return EFI_INVALID_PARAMETER;
+ }
+
+ ConfigData = &Instance->ConfigData;
+ UdpSessionData = TxData->UdpSessionData;
+
+ if (UdpSessionData != NULL) {
+
+ SourceAddress = EFI_NTOHL (UdpSessionData->SourceAddress);
+
+ if ((SourceAddress != 0) && !Ip4IsUnicast (SourceAddress, 0)) {
+ //
+ // Check whether SourceAddress is a valid IPv4 address in case it's not zero.
+ // The configured station address is used if SourceAddress is zero.
+ //
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((UdpSessionData->DestinationPort == 0) && (ConfigData->RemotePort == 0)) {
+ //
+ // Ambiguous, no avalaible DestinationPort for this token.
+ //
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (EFI_IP4 (UdpSessionData->DestinationAddress) == 0) {
+ //
+ // The DestinationAddress specified in the UdpSessionData is 0.
+ //
+ return EFI_INVALID_PARAMETER;
+ }
+ } else if (EFI_IP4 (ConfigData->RemoteAddress) == 0) {
+ //
+ // the configured RemoteAddress is all zero, and the user doens't override the
+ // destination address.
+ //
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (TxData->DataLength > UDP4_MAX_DATA_SIZE) {
+ return EFI_BAD_BUFFER_SIZE;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ This function checks whether the specified Token duplicates with the one in the Map.
+
+ @param Map Pointer to the NET_MAP.
+ @param Item Pointer to the NET_MAP_ITEM contain the pointer to
+ the Token.
+ @param Context Pointer to the Token to be checked.
+
+ @retval EFI_SUCCESS The Token specified by Context differs from the
+ one in the Item.
+ @retval EFI_ACCESS_DENIED The Token duplicates with the one in the Item.
+
+**/
+EFI_STATUS
+Udp4TokenExist (
+ IN NET_MAP *Map,
+ IN NET_MAP_ITEM *Item,
+ IN VOID *Context
+ )
+{
+ EFI_UDP4_COMPLETION_TOKEN *Token;
+ EFI_UDP4_COMPLETION_TOKEN *TokenInItem;
+
+ Token = (EFI_UDP4_COMPLETION_TOKEN*) Context;
+ TokenInItem = (EFI_UDP4_COMPLETION_TOKEN*) Item->Key;
+
+ if ((Token == TokenInItem) || (Token->Event == TokenInItem->Event)) {
+ //
+ // The Token duplicates with the TokenInItem in case either the two pointers are the
+ // same or the Events of these two tokens are the same.
+ //
+ return EFI_ACCESS_DENIED;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ This function calculates the checksum for the Packet, utilizing the pre-calculated
+ pseudo HeadSum to reduce some overhead.
+
+ @param Packet Pointer to the NET_BUF contains the udp datagram.
+ @param HeadSum Checksum of the pseudo header execpt the length
+ field.
+
+ @return The 16-bit checksum of this udp datagram.
+
+**/
+UINT16
+Udp4Checksum (
+ IN NET_BUF *Packet,
+ IN UINT16 HeadSum
+ )
+{
+ UINT16 Checksum;
+
+ Checksum = NetbufChecksum (Packet);
+ Checksum = NetAddChecksum (Checksum, HeadSum);
+
+ Checksum = NetAddChecksum (Checksum, HTONS ((UINT16) Packet->TotalSize));
+
+ return ~Checksum;
+}
+
+
+/**
+ This function removes the specified Token from the TokenMap.
+
+ @param TokenMap Pointer to the NET_MAP containing the tokens.
+ @param Token Pointer to the Token to be removed.
+
+ @retval EFI_SUCCESS The specified Token is removed from the TokenMap.
+ @retval EFI_NOT_FOUND The specified Token is not found in the TokenMap.
+
+**/
+EFI_STATUS
+Udp4RemoveToken (
+ IN NET_MAP *TokenMap,
+ IN EFI_UDP4_COMPLETION_TOKEN *Token
+ )
+{
+ NET_MAP_ITEM *Item;
+
+ //
+ // Find the Token first.
+ //
+ Item = NetMapFindKey (TokenMap, (VOID *) Token);
+
+ if (Item != NULL) {
+ //
+ // Remove the token if it's found in the map.
+ //
+ NetMapRemoveItem (TokenMap, Item, NULL);
+
+ return EFI_SUCCESS;
+ }
+
+ return EFI_NOT_FOUND;
+}
+
+
+/**
+ This function is the packet transmitting notify function registered to the IpIo
+ interface. It's called to signal the udp TxToken when IpIo layer completes the
+ transmitting of the udp datagram.
+
+ @param Status The completion status of the output udp datagram.
+ @param Context Pointer to the context data.
+ @param Sender Pointer to the Ip sender of the udp datagram.
+ @param NotifyData Pointer to the notify data.
+
+ @return None.
+
+**/
+STATIC
+VOID
+Udp4DgramSent (
+ IN EFI_STATUS Status,
+ IN VOID *Context,
+ IN VOID *Sender,
+ IN VOID *NotifyData
+ )
+{
+ UDP4_INSTANCE_DATA *Instance;
+ EFI_UDP4_COMPLETION_TOKEN *Token;
+
+ Instance = (UDP4_INSTANCE_DATA *) Context;
+ Token = (EFI_UDP4_COMPLETION_TOKEN *) NotifyData;
+
+ if (Udp4RemoveToken (&Instance->TxTokens, Token) == EFI_SUCCESS) {
+ //
+ // The token may be cancelled. Only signal it if the remove operation succeeds.
+ //
+ Token->Status = Status;
+ gBS->SignalEvent (Token->Event);
+ }
+}
+
+
+/**
+ This function processes the received datagram passed up by the IpIo layer.
+
+ @param Status The status of this udp datagram.
+ @param IcmpError The IcmpError code, only available when Status is
+ EFI_ICMP_ERROR.
+ @param NetSession Pointer to the EFI_NET_SESSION_DATA.
+ @param Packet Pointer to the NET_BUF containing the received udp
+ datagram.
+ @param Context Pointer to the context data.
+
+ @return None.
+
+**/
+STATIC
+VOID
+Udp4DgramRcvd (
+ IN EFI_STATUS Status,
+ IN ICMP_ERROR IcmpError,
+ IN EFI_NET_SESSION_DATA *NetSession,
+ IN NET_BUF *Packet,
+ IN VOID *Context
+ )
+{
+ NET_CHECK_SIGNATURE (Packet, NET_BUF_SIGNATURE);
+
+ //
+ // IpIo only passes received packets with Status EFI_SUCCESS or EFI_ICMP_ERROR.
+ //
+ if (Status == EFI_SUCCESS) {
+ //
+ // Demultiplex the received datagram.
+ //
+ Udp4Demultiplex ((UDP4_SERVICE_DATA *) Context, NetSession, Packet);
+ } else {
+ //
+ // Handle the ICMP_ERROR packet.
+ //
+ Udp4IcmpHandler ((UDP4_SERVICE_DATA *) Context, IcmpError, NetSession, Packet);
+ }
+}
+
+
+/**
+ This function removes the multicast group specified by Arg from the Map.
+
+ @param Map Pointer to the NET_MAP.
+ @param Item Pointer to the NET_MAP_ITEM.
+ @param Arg Pointer to the Arg, it's the pointer to a
+ multicast IPv4 Address.
+
+ @retval EFI_SUCCESS The multicast address is removed.
+ @retval EFI_ABORTED The specified multicast address is removed and the
+ Arg is not NULL.
+
+**/
+EFI_STATUS
+Udp4LeaveGroup (
+ IN NET_MAP *Map,
+ IN NET_MAP_ITEM *Item,
+ IN VOID *Arg OPTIONAL
+ )
+{
+ EFI_IPv4_ADDRESS *McastIp;
+
+ McastIp = Arg;
+
+ if ((McastIp != NULL) && ((UINTN) EFI_IP4 (*McastIp) != (UINTN) (Item->Key))) {
+ //
+ // McastIp is not NULL and the multicast address contained in the Item
+ // is not the same as McastIp.
+ //
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Remove this Item.
+ //
+ NetMapRemoveItem (Map, Item, NULL);
+
+ if (McastIp != NULL) {
+ //
+ // Return EFI_ABORTED in case McastIp is not NULL to terminate the iteration.
+ //
+ return EFI_ABORTED;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ This function cancle the token specified by Arg in the Map.
+
+ @param Map Pointer to the NET_MAP.
+ @param Item Pointer to the NET_MAP_ITEM.
+ @param Arg Pointer to the token to be cancelled, if NULL, all
+ the tokens in this Map will be cancelled.
+
+ @retval EFI_SUCCESS The token is cancelled if Arg is NULL or the token
+ is not the same as that in the Item if Arg is not
+ NULL.
+ @retval EFI_ABORTED Arg is not NULL, and the token specified by Arg is
+ cancelled.
+
+**/
+STATIC
+EFI_STATUS
+Udp4CancelTokens (
+ IN NET_MAP *Map,
+ IN NET_MAP_ITEM *Item,
+ IN VOID *Arg OPTIONAL
+ )
+{
+ EFI_UDP4_COMPLETION_TOKEN *TokenToCancel;
+ NET_BUF *Packet;
+ IP_IO *IpIo;
+
+ if ((Arg != NULL) && (Item->Key != Arg)) {
+ return EFI_SUCCESS;
+ }
+
+ if (Item->Value != NULL) {
+ //
+ // If the token is a transmit token, the corresponding Packet is recorded in
+ // Item->Value, invoke IpIo to cancel this packet first. The IpIoCancelTxToken
+ // will invoke Udp4DgramSent, the token will be signaled and this Item will
+ // be removed from the Map there.
+ //
+ Packet = (NET_BUF *) (Item->Value);
+ IpIo = (IP_IO *) (*((UINTN *) &Packet->ProtoData[0]));
+
+ IpIoCancelTxToken (IpIo, Packet);
+ } else {
+ //
+ // The token is a receive token. Abort it and remove it from the Map.
+ //
+ TokenToCancel = (EFI_UDP4_COMPLETION_TOKEN *) Item->Key;
+
+ TokenToCancel->Status = EFI_ABORTED;
+ gBS->SignalEvent (TokenToCancel->Event);
+
+ NetMapRemoveItem (Map, Item, NULL);
+ }
+
+ if (Arg != NULL) {
+ return EFI_ABORTED;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ This function removes all the Wrap datas in the RcvdDgramQue.
+
+ @param RcvdDgramQue Pointer to the list containing all the Wrap datas.
+
+ @return None.
+
+**/
+VOID
+Udp4FlushRxData (
+ IN NET_LIST_ENTRY *RcvdDgramQue
+ )
+{
+ UDP4_RXDATA_WRAP *Wrap;
+ EFI_TPL OldTpl;
+
+ OldTpl = NET_RAISE_TPL (NET_TPL_RECYCLE);
+
+ while (!NetListIsEmpty (RcvdDgramQue)) {
+ //
+ // Iterate all the Wraps in the RcvdDgramQue.
+ //
+ Wrap = NET_LIST_HEAD (RcvdDgramQue, UDP4_RXDATA_WRAP, Link);
+
+ //
+ // The Wrap will be removed from the RcvdDgramQue by this function call.
+ //
+ Udp4RecycleRxDataWrap (NULL, (VOID *) Wrap);
+ }
+
+ NET_RESTORE_TPL (OldTpl);
+}
+
+
+
+/**
+
+ @param Instance Pointer to the udp instance context data.
+ @param Token Pointer to the token to be canceled, if NULL, all
+ tokens in this instance will be cancelled.
+
+ @retval EFI_SUCCESS The Token is cancelled.
+ @retval EFI_NOT_FOUND The Token is not found.
+
+**/
+EFI_STATUS
+Udp4InstanceCancelToken (
+ IN UDP4_INSTANCE_DATA *Instance,
+ IN EFI_UDP4_COMPLETION_TOKEN *Token OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+
+ //
+ // Cancle this token from the TxTokens map.
+ //
+ Status = NetMapIterate (&Instance->TxTokens, Udp4CancelTokens, Token);
+
+ if ((Token != NULL) && (Status == EFI_ABORTED)) {
+ //
+ // If Token isn't NULL and Status is EFI_ABORTED, the token is cancelled from
+ // the TxTokens, just return success.
+ //
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Try to cancel this token from the RxTokens map in condition either the Token
+ // is NULL or the specified Token is not in TxTokens.
+ //
+ Status = NetMapIterate (&Instance->RxTokens, Udp4CancelTokens, Token);
+
+ if ((Token != NULL) && (Status == EFI_SUCCESS)) {
+ //
+ // If Token isn't NULL and Status is EFI_SUCCESS, the token is neither in the
+ // TxTokens nor the RxTokens, or say, it's not found.
+ //
+ return EFI_NOT_FOUND;
+ }
+
+ ASSERT ((Token != NULL) || ((0 == NetMapGetCount (&Instance->TxTokens))
+ && (0 == NetMapGetCount (&Instance->RxTokens))));
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ This function matches the received udp datagram with the Instance.
+
+ @param Instance Pointer to the udp instance context data.
+ @param Udp4Session Pointer to the EFI_UDP4_SESSION_DATA abstracted
+ from the received udp datagram.
+
+ @return The udp datagram matches the receiving requirments of the Instance or not.
+
+**/
+STATIC
+BOOLEAN
+Udp4MatchDgram (
+ IN UDP4_INSTANCE_DATA *Instance,
+ IN EFI_UDP4_SESSION_DATA *Udp4Session
+ )
+{
+ EFI_UDP4_CONFIG_DATA *ConfigData;
+ IP4_ADDR Destination;
+
+ ConfigData = &Instance->ConfigData;
+
+ if (ConfigData->AcceptPromiscuous) {
+ //
+ // Always matches if this instance is in the promiscuous state.
+ //
+ return TRUE;
+ }
+
+ if ((!ConfigData->AcceptAnyPort && (Udp4Session->DestinationPort != ConfigData->StationPort)) ||
+ ((ConfigData->RemotePort != 0) && (Udp4Session->SourcePort != ConfigData->RemotePort))) {
+ //
+ // The local port or the remote port doesn't match.
+ //
+ return FALSE;
+ }
+
+ if ((EFI_IP4 (ConfigData->RemoteAddress) != 0) &&
+ !EFI_IP_EQUAL (ConfigData->RemoteAddress, Udp4Session->SourceAddress)) {
+ //
+ // This datagram doesn't come from the instance's specified sender.
+ //
+ return FALSE;
+ }
+
+ if ((EFI_IP4 (ConfigData->StationAddress) == 0) ||
+ EFI_IP_EQUAL (Udp4Session->DestinationAddress, ConfigData->StationAddress)) {
+ //
+ // The instance is configured to receive datagrams destinated to any station IP or
+ // the destination address of this datagram matches the configured station IP.
+ //
+ return TRUE;
+ }
+
+ Destination = EFI_IP4 (Udp4Session->DestinationAddress);
+
+ if (IP4_IS_LOCAL_BROADCAST (Destination) && ConfigData->AcceptBroadcast) {
+ //
+ // The instance is configured to receive broadcast and this is a broadcast packet.
+ //
+ return TRUE;
+ }
+
+ if (IP4_IS_MULTICAST (NTOHL (Destination)) &&
+ (NULL != NetMapFindKey (&Instance->McastIps, (VOID *) (UINTN) Destination))) {
+ //
+ // It's a multicast packet and the multicast address is accepted by this instance.
+ //
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+/**
+ This function removes the Wrap specified by Context and release relevant resources.
+
+ @param Event The Event this notify function registered to.
+ @param Context Pointer to the context data.
+
+ @return None.
+
+**/
+STATIC
+VOID
+EFIAPI
+Udp4RecycleRxDataWrap (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ UDP4_RXDATA_WRAP *Wrap;
+
+ Wrap = (UDP4_RXDATA_WRAP *) Context;
+
+ //
+ // Remove the Wrap from the list it belongs to.
+ //
+ NetListRemoveEntry (&Wrap->Link);
+
+ //
+ // Free the Packet associated with this Wrap.
+ //
+ NetbufFree (Wrap->Packet);
+
+ //
+ // Close the event.
+ //
+ gBS->CloseEvent (Wrap->RxData.RecycleSignal);
+
+ NetFreePool (Wrap);
+}
+
+
+/**
+ This function wraps the Packet and the RxData.
+
+ @param Instance Pointer to the instance context data.
+ @param Packet Pointer to the buffer containing the received
+ datagram.
+ @param RxData Pointer to the EFI_UDP4_RECEIVE_DATA of this
+ datagram.
+
+ @return Pointer to the structure wrapping the RxData and the Packet.
+
+**/
+STATIC
+UDP4_RXDATA_WRAP *
+Udp4WrapRxData (
+ IN UDP4_INSTANCE_DATA *Instance,
+ IN NET_BUF *Packet,
+ IN EFI_UDP4_RECEIVE_DATA *RxData
+ )
+{
+ EFI_STATUS Status;
+ UDP4_RXDATA_WRAP *Wrap;
+
+ //
+ // Allocate buffer for the Wrap.
+ //
+ Wrap = NetAllocatePool (sizeof (UDP4_RXDATA_WRAP) +
+ (Packet->BlockOpNum - 1) * sizeof (EFI_UDP4_FRAGMENT_DATA));
+ if (Wrap == NULL) {
+ return NULL;
+ }
+
+ NetListInit (&Wrap->Link);
+
+ Wrap->RxData = *RxData;
+
+ //
+ // Create the Recycle event.
+ //
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ NET_TPL_RECYCLE,
+ Udp4RecycleRxDataWrap,
+ Wrap,
+ &Wrap->RxData.RecycleSignal
+ );
+ if (EFI_ERROR (Status)) {
+ NetFreePool (Wrap);
+ return NULL;
+ }
+
+ Wrap->Packet = Packet;
+ Wrap->TimeoutTick = Instance->ConfigData.ReceiveTimeout;
+
+ return Wrap;
+}
+
+
+/**
+ This function enqueues the received datagram into the instances' receiving queues.
+
+ @param Udp4Service Pointer to the udp service context data.
+ @param Packet Pointer to the buffer containing the received
+ datagram.
+ @param RxData Pointer to the EFI_UDP4_RECEIVE_DATA of this
+ datagram.
+
+ @return The times this datagram is enqueued.
+
+**/
+STATIC
+UINTN
+Udp4EnqueueDgram (
+ IN UDP4_SERVICE_DATA *Udp4Service,
+ IN NET_BUF *Packet,
+ IN EFI_UDP4_RECEIVE_DATA *RxData
+ )
+{
+ NET_LIST_ENTRY *Entry;
+ UDP4_INSTANCE_DATA *Instance;
+ UDP4_RXDATA_WRAP *Wrap;
+ UINTN Enqueued;
+
+ Enqueued = 0;
+
+ NET_LIST_FOR_EACH (Entry, &Udp4Service->ChildrenList) {
+ //
+ // Iterate the instances.
+ //
+ Instance = NET_LIST_USER_STRUCT (Entry, UDP4_INSTANCE_DATA, Link);
+
+ if (!Instance->Configured) {
+ continue;
+ }
+
+ if (Udp4MatchDgram (Instance, &RxData->UdpSession)) {
+ //
+ // Wrap the RxData and put this Wrap into the instances RcvdDgramQue.
+ //
+ Wrap = Udp4WrapRxData (Instance, Packet, RxData);
+ if (Wrap == NULL) {
+ continue;
+ }
+
+ NET_GET_REF (Packet);
+
+ NetListInsertTail (&Instance->RcvdDgramQue, &Wrap->Link);
+
+ Enqueued++;
+ }
+ }
+
+ return Enqueued;
+}
+
+
+/**
+ This function delivers the received datagrams for the specified instance.
+
+ @param Instance Pointer to the instance context data.
+
+ @return None.
+
+**/
+VOID
+Udp4InstanceDeliverDgram (
+ IN UDP4_INSTANCE_DATA *Instance
+ )
+{
+ UDP4_RXDATA_WRAP *Wrap;
+ EFI_UDP4_COMPLETION_TOKEN *Token;
+ NET_BUF *Dup;
+ EFI_UDP4_RECEIVE_DATA *RxData;
+
+ if (!NetListIsEmpty (&Instance->RcvdDgramQue) &&
+ !NetMapIsEmpty (&Instance->RxTokens)) {
+
+ Wrap = NET_LIST_HEAD (&Instance->RcvdDgramQue, UDP4_RXDATA_WRAP, Link);
+
+ if (NET_BUF_SHARED (Wrap->Packet)) {
+ //
+ // Duplicate the Packet if it is shared between instances.
+ //
+ Dup = NetbufDuplicate (Wrap->Packet, NULL, 0);
+ if (Dup == NULL) {
+ return;
+ }
+
+ NetbufFree (Wrap->Packet);
+
+ Wrap->Packet = Dup;
+ }
+
+ NetListRemoveHead (&Instance->RcvdDgramQue);
+
+ Token = (EFI_UDP4_COMPLETION_TOKEN *) NetMapRemoveHead (&Instance->RxTokens, NULL);
+
+ //
+ // Build the FragmentTable and set the FragmentCount in RxData.
+ //
+ RxData = &Wrap->RxData;
+ RxData->FragmentCount = Wrap->Packet->BlockOpNum;
+
+ NetbufBuildExt (
+ Wrap->Packet,
+ (NET_FRAGMENT *) RxData->FragmentTable,
+ &RxData->FragmentCount
+ );
+
+ Token->Status = EFI_SUCCESS;
+ Token->Packet.RxData = &Wrap->RxData;
+
+ gBS->SignalEvent (Token->Event);
+
+ NetListInsertTail (&Instance->DeliveredDgramQue, &Wrap->Link);
+ }
+}
+
+
+/**
+ This function delivers the datagrams enqueued in the instances.
+
+ @param Udp4Service Pointer to the udp service context data.
+
+ @return None.
+
+**/
+STATIC
+VOID
+Udp4DeliverDgram (
+ IN UDP4_SERVICE_DATA *Udp4Service
+ )
+{
+ NET_LIST_ENTRY *Entry;
+ UDP4_INSTANCE_DATA *Instance;
+
+ NET_LIST_FOR_EACH (Entry, &Udp4Service->ChildrenList) {
+ //
+ // Iterate the instances.
+ //
+ Instance = NET_LIST_USER_STRUCT (Entry, UDP4_INSTANCE_DATA, Link);
+
+ if (!Instance->Configured) {
+ continue;
+ }
+
+ //
+ // Deliver the datagrams of this instance.
+ //
+ Udp4InstanceDeliverDgram (Instance);
+ }
+}
+
+
+/**
+ This function demultiplexes the received udp datagram to the apropriate instances.
+
+ @param Udp4Service Pointer to the udp service context data.
+ @param NetSession Pointer to the EFI_NET_SESSION_DATA abstrated from
+ the received datagram.
+ @param Packet Pointer to the buffer containing the received udp
+ datagram.
+
+ @return None.
+
+**/
+STATIC
+VOID
+Udp4Demultiplex (
+ IN UDP4_SERVICE_DATA *Udp4Service,
+ IN EFI_NET_SESSION_DATA *NetSession,
+ IN NET_BUF *Packet
+ )
+{
+ EFI_UDP4_HEADER *Udp4Header;
+ UINT16 HeadSum;
+ EFI_UDP4_RECEIVE_DATA RxData;
+ EFI_UDP4_SESSION_DATA *Udp4Session;
+ UINTN Enqueued;
+
+ //
+ // Get the datagram header from the packet buffer.
+ //
+ Udp4Header = (EFI_UDP4_HEADER *) NetbufGetByte (Packet, 0, NULL);
+
+ if (Udp4Header->Checksum != 0) {
+ //
+ // check the checksum.
+ //
+ HeadSum = NetPseudoHeadChecksum (
+ NetSession->Source,
+ NetSession->Dest,
+ EFI_IP_PROTO_UDP,
+ 0
+ );
+
+ if (Udp4Checksum (Packet, HeadSum) != 0) {
+ //
+ // Wrong checksum.
+ //
+ return;
+ }
+ }
+
+ gRT->GetTime (&RxData.TimeStamp, NULL);
+
+ Udp4Session = &RxData.UdpSession;
+ EFI_IP4 (Udp4Session->SourceAddress) = NetSession->Source;
+ EFI_IP4 (Udp4Session->DestinationAddress) = NetSession->Dest;
+ Udp4Session->SourcePort = NTOHS (Udp4Header->SrcPort);
+ Udp4Session->DestinationPort = NTOHS (Udp4Header->DstPort);
+
+ //
+ // Trim the UDP header.
+ //
+ NetbufTrim (Packet, UDP4_HEADER_SIZE, TRUE);
+
+ RxData.DataLength = (UINT32) Packet->TotalSize;
+
+ //
+ // Try to enqueue this datagram into the instances.
+ //
+ Enqueued = Udp4EnqueueDgram (Udp4Service, Packet, &RxData);
+
+ if (Enqueued == 0) {
+ //
+ // Send the port unreachable ICMP packet before we free this NET_BUF
+ //
+ Udp4SendPortUnreach (Udp4Service->IpIo, NetSession, Udp4Header);
+ }
+
+ //
+ // Try to free the packet before deliver it.
+ //
+ NetbufFree (Packet);
+
+ if (Enqueued > 0) {
+ //
+ // Deliver the datagram.
+ //
+ Udp4DeliverDgram (Udp4Service);
+ }
+}
+
+
+/**
+ This function builds and sends out a icmp port unreachable message.
+
+ @param IpIo Pointer to the IP_IO instance.
+ @param NetSession Pointer to the EFI_NET_SESSION_DATA of the packet
+ causes this icmp error message.
+ @param Udp4Header Pointer to the udp header of the datagram causes
+ this icmp error message.
+
+ @return None.
+
+**/
+STATIC
+VOID
+Udp4SendPortUnreach (
+ IN IP_IO *IpIo,
+ IN EFI_NET_SESSION_DATA *NetSession,
+ IN VOID *Udp4Header
+ )
+{
+ NET_BUF *Packet;
+ UINT32 Len;
+ IP4_ICMP_ERROR_HEAD *IcmpErrHdr;
+ EFI_IP4_HEADER *IpHdr;
+ UINT8 *Ptr;
+ IP_IO_OVERRIDE Override;
+ IP_IO_IP_INFO *IpSender;
+
+ IpSender = IpIoFindSender (&IpIo, NetSession->Dest);
+ if (IpSender == NULL) {
+ //
+ // No apropriate sender, since we cannot send out the ICMP message through
+ // the default zero station address IP instance, abort.
+ //
+ return;
+ }
+
+ IpHdr = NetSession->IpHdr;
+
+ //
+ // Calculate the requried length of the icmp error message.
+ //
+ Len = sizeof (IP4_ICMP_ERROR_HEAD) + (EFI_IP4_HEADER_LEN (IpHdr) -
+ sizeof (IP4_HEAD)) + ICMP_ERROR_PACKET_LENGTH;
+
+ //
+ // Allocate buffer for the icmp error message.
+ //
+ Packet = NetbufAlloc (Len);
+ if (Packet == NULL) {
+ return;
+ }
+
+ //
+ // Allocate space for the IP4_ICMP_ERROR_HEAD.
+ //
+ IcmpErrHdr = (IP4_ICMP_ERROR_HEAD *) NetbufAllocSpace (Packet, Len, FALSE);
+
+ //
+ // Set the required fields for the icmp port unreachable message.
+ //
+ IcmpErrHdr->Head.Type = ICMP_TYPE_UNREACH;
+ IcmpErrHdr->Head.Code = ICMP_CODE_UNREACH_PORT;
+ IcmpErrHdr->Head.Checksum = 0;
+ IcmpErrHdr->Fourth = 0;
+
+ //
+ // Copy the IP header of the datagram tragged the error.
+ //
+ NetCopyMem (&IcmpErrHdr->IpHead, IpHdr, EFI_IP4_HEADER_LEN (IpHdr));
+
+ //
+ // Copy the UDP header.
+ //
+ Ptr = (UINT8 *) &IcmpErrHdr->IpHead + EFI_IP4_HEADER_LEN (IpHdr);
+ NetCopyMem (Ptr, Udp4Header, ICMP_ERROR_PACKET_LENGTH);
+
+ //
+ // Calculate the checksum.
+ //
+ IcmpErrHdr->Head.Checksum = ~(NetbufChecksum (Packet));
+
+ //
+ // Fill the override data.
+ //
+ Override.DoNotFragment = FALSE;
+ Override.TypeOfService = 0;
+ Override.TimeToLive = 255;
+ Override.Protocol = EFI_IP_PROTO_ICMP;
+ EFI_IP4 (Override.SourceAddress) = NetSession->Dest;
+ EFI_IP4 (Override.GatewayAddress) = 0;
+
+ //
+ // Send out this icmp packet.
+ //
+ IpIoSend (IpIo, Packet, IpSender, NULL, NULL, NetSession->Source, &Override);
+
+ NetbufFree (Packet);
+}
+
+
+/**
+ This function handles the received Icmp Error message and demultiplexes it to the
+ instance.
+
+ @param Udp4Service Pointer to the udp service context data.
+ @param IcmpError The icmp error code.
+ @param NetSession Pointer to the EFI_NET_SESSION_DATA abstracted
+ from the received Icmp Error packet.
+ @param Packet Pointer to the Icmp Error packet.
+
+ @return None.
+
+**/
+STATIC
+VOID
+Udp4IcmpHandler (
+ IN UDP4_SERVICE_DATA *Udp4Service,
+ IN ICMP_ERROR IcmpError,
+ IN EFI_NET_SESSION_DATA *NetSession,
+ IN NET_BUF *Packet
+ )
+{
+ EFI_UDP4_HEADER *Udp4Header;
+ EFI_UDP4_SESSION_DATA Udp4Session;
+ NET_LIST_ENTRY *Entry;
+ UDP4_INSTANCE_DATA *Instance;
+
+ Udp4Header = (EFI_UDP4_HEADER *) NetbufGetByte (Packet, 0, NULL);
+
+ EFI_IP4 (Udp4Session.SourceAddress) = NetSession->Source;
+ EFI_IP4 (Udp4Session.DestinationAddress) = NetSession->Dest;
+ Udp4Session.SourcePort = NTOHS (Udp4Header->DstPort);
+ Udp4Session.DestinationPort = NTOHS (Udp4Header->SrcPort);
+
+ NET_LIST_FOR_EACH (Entry, &Udp4Service->ChildrenList) {
+ //
+ // Iterate all the instances.
+ //
+ Instance = NET_LIST_USER_STRUCT (Entry, UDP4_INSTANCE_DATA, Link);
+
+ if (!Instance->Configured ||
+ Instance->ConfigData.AcceptPromiscuous ||
+ Instance->ConfigData.AcceptAnyPort ||
+ (EFI_IP4 (Instance->ConfigData.StationAddress) == 0)) {
+ //
+ // Don't try to deliver the ICMP error to this instance if it is not configured,
+ // or it's configured to be promiscuous or accept any port or accept all the
+ // datagrams.
+ //
+ continue;
+ }
+
+ if (Udp4MatchDgram (Instance, &Udp4Session)) {
+ //
+ // Translate the Icmp Error code according to the udp spec.
+ //
+ Instance->IcmpError = IpIoGetIcmpErrStatus (IcmpError, NULL, NULL);
+
+ if (IcmpError > ICMP_ERR_UNREACH_PORT) {
+ Instance->IcmpError = EFI_ICMP_ERROR;
+ }
+
+ //
+ // Notify the instance with the received Icmp Error.
+ //
+ Udp4ReportIcmpError (Instance);
+
+ break;
+ }
+ }
+
+ NetbufFree (Packet);
+}
+
+
+/**
+ This function reports the received ICMP error.
+
+ @param Instance Pointer to the udp instance context data.
+
+ @return None.
+
+**/
+VOID
+Udp4ReportIcmpError (
+ IN UDP4_INSTANCE_DATA *Instance
+ )
+{
+ EFI_UDP4_COMPLETION_TOKEN *Token;
+
+ if (NetMapIsEmpty (&Instance->RxTokens)) {
+ //
+ // There are no receive tokens to deliver the ICMP error.
+ //
+ return;
+ }
+
+ if (EFI_ERROR (Instance->IcmpError)) {
+ //
+ // Try to get a RxToken from the RxTokens map.
+ //
+ Token = (EFI_UDP4_COMPLETION_TOKEN *) NetMapRemoveHead (&Instance->RxTokens, NULL);
+
+ if (Token != NULL) {
+ //
+ // Report the error through the Token.
+ //
+ Token->Status = Instance->IcmpError;
+ gBS->SignalEvent (Token->Event);
+
+ //
+ // Clear the IcmpError.
+ //
+ Instance->IcmpError = EFI_SUCCESS;
+ }
+ }
+}
+
+
+/**
+ This function is a dummy ext-free function for the NET_BUF created for the output
+ udp datagram.
+
+ @param Context Pointer to the context data.
+
+ @return None.
+
+**/
+VOID
+Udp4NetVectorExtFree (
+ VOID *Context
+ )
+{
+}
+
+
+/**
+ Set the Udp4 variable data.
+
+ @param Udp4Service Udp4 service data.
+
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources to set the
+ variable.
+ @retval other Set variable failed.
+
+**/
+EFI_STATUS
+Udp4SetVariableData (
+ IN UDP4_SERVICE_DATA *Udp4Service
+ )
+{
+ UINT32 NumConfiguredInstance;
+ NET_LIST_ENTRY *Entry;
+ UINTN VariableDataSize;
+ EFI_UDP4_VARIABLE_DATA *Udp4VariableData;
+ EFI_UDP4_SERVICE_POINT *Udp4ServicePoint;
+ UDP4_INSTANCE_DATA *Udp4Instance;
+ CHAR16 *NewMacString;
+ EFI_STATUS Status;
+
+ NumConfiguredInstance = 0;
+
+ //
+ // Go through the children list to count the configured children.
+ //
+ NET_LIST_FOR_EACH (Entry, &Udp4Service->ChildrenList) {
+ Udp4Instance = NET_LIST_USER_STRUCT_S (
+ Entry,
+ UDP4_INSTANCE_DATA,
+ Link,
+ UDP4_INSTANCE_DATA_SIGNATURE
+ );
+
+ if (Udp4Instance->Configured) {
+ NumConfiguredInstance++;
+ }
+ }
+
+ //
+ // Calculate the size of the Udp4VariableData. As there may be no Udp4 child,
+ // we should add extra buffer for the service points only if the number of configured
+ // children is more than 1.
+ //
+ VariableDataSize = sizeof (EFI_UDP4_VARIABLE_DATA);
+
+ if (NumConfiguredInstance > 1) {
+ VariableDataSize += sizeof (EFI_UDP4_SERVICE_POINT) * (NumConfiguredInstance - 1);
+ }
+
+ Udp4VariableData = NetAllocatePool (VariableDataSize);
+ if (Udp4VariableData == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Udp4VariableData->DriverHandle = Udp4Service->ImageHandle;
+ Udp4VariableData->ServiceCount = NumConfiguredInstance;
+
+ Udp4ServicePoint = &Udp4VariableData->Services[0];
+
+ //
+ // Go through the children list to fill the configured children's address pairs.
+ //
+ NET_LIST_FOR_EACH (Entry, &Udp4Service->ChildrenList) {
+ Udp4Instance = NET_LIST_USER_STRUCT_S (
+ Entry,
+ UDP4_INSTANCE_DATA,
+ Link,
+ UDP4_INSTANCE_DATA_SIGNATURE
+ );
+
+ if (Udp4Instance->Configured) {
+ Udp4ServicePoint->InstanceHandle = Udp4Instance->ChildHandle;
+ Udp4ServicePoint->LocalAddress = Udp4Instance->ConfigData.StationAddress;
+ Udp4ServicePoint->LocalPort = Udp4Instance->ConfigData.StationPort;
+ Udp4ServicePoint->RemoteAddress = Udp4Instance->ConfigData.RemoteAddress;
+ Udp4ServicePoint->RemotePort = Udp4Instance->ConfigData.RemotePort;
+
+ Udp4ServicePoint++;
+ }
+ }
+
+ //
+ // Get the mac string.
+ //
+ Status = NetLibGetMacString (
+ Udp4Service->ControllerHandle,
+ Udp4Service->ImageHandle,
+ &NewMacString
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ if (Udp4Service->MacString != NULL) {
+ //
+ // The variable is set already, we're going to update it.
+ //
+ if (StrCmp (Udp4Service->MacString, NewMacString) != 0) {
+ //
+ // The mac address is changed, delete the previous variable first.
+ //
+ gRT->SetVariable (
+ Udp4Service->MacString,
+ &gEfiUdp4ServiceBindingProtocolGuid,
+ EFI_VARIABLE_BOOTSERVICE_ACCESS,
+ 0,
+ NULL
+ );
+ }
+
+ NetFreePool (Udp4Service->MacString);
+ }
+
+ Udp4Service->MacString = NewMacString;
+
+ Status = gRT->SetVariable (
+ Udp4Service->MacString,
+ &gEfiUdp4ServiceBindingProtocolGuid,
+ EFI_VARIABLE_BOOTSERVICE_ACCESS,
+ VariableDataSize,
+ (VOID *) Udp4VariableData
+ );
+
+ON_ERROR:
+
+ NetFreePool (Udp4VariableData);
+
+ return Status;
+}
+
+
+/**
+ Clear the variable and free the resource.
+
+ @param Udp4Service Udp4 service data.
+
+ @return None.
+
+**/
+VOID
+Udp4ClearVariableData (
+ IN UDP4_SERVICE_DATA *Udp4Service
+ )
+{
+ ASSERT (Udp4Service->MacString != NULL);
+
+ gRT->SetVariable (
+ Udp4Service->MacString,
+ &gEfiUdp4ServiceBindingProtocolGuid,
+ EFI_VARIABLE_BOOTSERVICE_ACCESS,
+ 0,
+ NULL
+ );
+
+ NetFreePool (Udp4Service->MacString);
+ Udp4Service->MacString = NULL;
+}
diff --git a/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Impl.h b/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Impl.h
new file mode 100644
index 0000000000..7825624090
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Impl.h
@@ -0,0 +1,299 @@
+/** @file
+
+Copyright (c) 2006 - 2007, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+
+ Udp4Impl.h
+
+Abstract:
+
+ EFI UDPv4 protocol implementation
+
+
+**/
+
+#ifndef _UDP4_IMPL_H_
+#define _UDP4_IMPL_H_
+
+#include <PiDxe.h>
+
+#include <Protocol/IP4.h>
+#include <Protocol/Udp4.h>
+
+#include <Library/IpIoLib.h>
+#include <Library/DebugLib.h>
+#include <Library/UefiRuntimeServicesTableLib.h>
+#include <Library/UefiDriverEntryPoint.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/BaseLib.h>
+#include <Library/UefiLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+
+#include "Udp4Driver.h"
+
+
+extern EFI_COMPONENT_NAME_PROTOCOL gUdp4ComponentName;
+extern EFI_SERVICE_BINDING_PROTOCOL mUdp4ServiceBinding;
+extern EFI_UDP4_PROTOCOL mUdp4Protocol;
+extern UINT16 mUdp4RandomPort;
+
+#define ICMP_ERROR_PACKET_LENGTH 8
+
+#define UDP4_TIMEOUT_INTERVAL (50 * TICKS_PER_MS) // 50 milliseconds
+
+#define UDP4_HEADER_SIZE sizeof (EFI_UDP4_HEADER)
+#define UDP4_MAX_DATA_SIZE 65507
+
+#define UDP4_PORT_KNOWN 1024
+
+#define UDP4_SERVICE_DATA_SIGNATURE EFI_SIGNATURE_32('U', 'd', 'p', '4')
+
+#define UDP4_SERVICE_DATA_FROM_THIS(a) \
+ CR ( \
+ (a), \
+ UDP4_SERVICE_DATA, \
+ ServiceBinding, \
+ UDP4_SERVICE_DATA_SIGNATURE \
+ )
+
+typedef struct _UDP4_SERVICE_DATA_ {
+ UINT32 Signature;
+ EFI_SERVICE_BINDING_PROTOCOL ServiceBinding;
+ EFI_HANDLE ImageHandle;
+ EFI_HANDLE ControllerHandle;
+ NET_LIST_ENTRY ChildrenList;
+ UINTN ChildrenNumber;
+ IP_IO *IpIo;
+
+ EFI_EVENT TimeoutEvent;
+
+ CHAR16 *MacString;
+} UDP4_SERVICE_DATA;
+
+#define UDP4_INSTANCE_DATA_SIGNATURE EFI_SIGNATURE_32('U', 'd', 'p', 'I')
+
+#define UDP4_INSTANCE_DATA_FROM_THIS(a) \
+ CR ( \
+ (a), \
+ UDP4_INSTANCE_DATA, \
+ Udp4Proto, \
+ UDP4_INSTANCE_DATA_SIGNATURE \
+ )
+
+typedef struct _UDP4_INSTANCE_DATA_ {
+ UINT32 Signature;
+ NET_LIST_ENTRY Link;
+
+ UDP4_SERVICE_DATA *Udp4Service;
+ EFI_UDP4_PROTOCOL Udp4Proto;
+ EFI_UDP4_CONFIG_DATA ConfigData;
+ EFI_HANDLE ChildHandle;
+ BOOLEAN Configured;
+ BOOLEAN IsNoMapping;
+
+ NET_MAP TxTokens;
+ NET_MAP RxTokens;
+
+ NET_MAP McastIps;
+
+ NET_LIST_ENTRY RcvdDgramQue;
+ NET_LIST_ENTRY DeliveredDgramQue;
+
+ UINT16 HeadSum;
+
+ EFI_STATUS IcmpError;
+
+ IP_IO_IP_INFO *IpInfo;
+
+ BOOLEAN Destroyed;
+} UDP4_INSTANCE_DATA;
+
+typedef struct _UDP4_RXDATA_WRAP_ {
+ NET_LIST_ENTRY Link;
+ NET_BUF *Packet;
+ UINT32 TimeoutTick;
+ EFI_UDP4_RECEIVE_DATA RxData;
+} UDP4_RXDATA_WRAP;
+
+EFI_STATUS
+EFIAPI
+Udp4GetModeData (
+ IN EFI_UDP4_PROTOCOL *This,
+ OUT EFI_UDP4_CONFIG_DATA *Udp4ConfigData OPTIONAL,
+ OUT EFI_IP4_MODE_DATA *Ip4ModeData OPTIONAL,
+ OUT EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData OPTIONAL,
+ OUT EFI_SIMPLE_NETWORK_MODE *SnpModeData OPTIONAL
+ );
+
+EFI_STATUS
+EFIAPI
+Udp4Configure (
+ IN EFI_UDP4_PROTOCOL *This,
+ IN EFI_UDP4_CONFIG_DATA *UdpConfigData OPTIONAL
+ );
+
+EFI_STATUS
+EFIAPI
+Udp4Groups (
+ IN EFI_UDP4_PROTOCOL *This,
+ IN BOOLEAN JoinFlag,
+ IN EFI_IPv4_ADDRESS *MulticastAddress OPTIONAL
+ );
+
+EFI_STATUS
+EFIAPI
+Udp4Routes (
+ IN EFI_UDP4_PROTOCOL *This,
+ IN BOOLEAN DeleteRoute,
+ IN EFI_IPv4_ADDRESS *SubnetAddress,
+ IN EFI_IPv4_ADDRESS *SubnetMask,
+ IN EFI_IPv4_ADDRESS *GatewayAddress
+ );
+
+EFI_STATUS
+EFIAPI
+Udp4Transmit (
+ IN EFI_UDP4_PROTOCOL *This,
+ IN EFI_UDP4_COMPLETION_TOKEN *Token
+ );
+
+EFI_STATUS
+EFIAPI
+Udp4Receive (
+ IN EFI_UDP4_PROTOCOL *This,
+ IN EFI_UDP4_COMPLETION_TOKEN *Token
+ );
+
+EFI_STATUS
+EFIAPI
+Udp4Cancel (
+ IN EFI_UDP4_PROTOCOL *This,
+ IN EFI_UDP4_COMPLETION_TOKEN *Token OPTIONAL
+ );
+
+EFI_STATUS
+EFIAPI
+Udp4Poll (
+ IN EFI_UDP4_PROTOCOL *This
+ );
+
+EFI_STATUS
+Udp4CreateService (
+ IN UDP4_SERVICE_DATA *Udp4Service,
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_HANDLE ControllerHandle
+ );
+
+VOID
+Udp4CleanService (
+ IN UDP4_SERVICE_DATA *Udp4Service
+ );
+
+VOID
+Udp4InitInstance (
+ IN UDP4_SERVICE_DATA *Udp4Service,
+ IN UDP4_INSTANCE_DATA *Instance
+ );
+
+VOID
+Udp4CleanInstance (
+ IN UDP4_INSTANCE_DATA *Instance
+ );
+
+EFI_STATUS
+Udp4Bind (
+ IN NET_LIST_ENTRY *InstanceList,
+ IN EFI_UDP4_CONFIG_DATA *ConfigData
+ );
+
+BOOLEAN
+Udp4IsReconfigurable (
+ IN EFI_UDP4_CONFIG_DATA *OldConfigData,
+ IN EFI_UDP4_CONFIG_DATA *NewConfigData
+ );
+
+VOID
+Udp4BuildIp4ConfigData (
+ IN EFI_UDP4_CONFIG_DATA *Udp4ConfigData,
+ IN EFI_IP4_CONFIG_DATA *Ip4ConfigData
+ );
+
+EFI_STATUS
+Udp4ValidateTxToken (
+ IN UDP4_INSTANCE_DATA *Instance,
+ IN EFI_UDP4_COMPLETION_TOKEN *TxToken
+ );
+
+EFI_STATUS
+Udp4TokenExist (
+ IN NET_MAP *Map,
+ IN NET_MAP_ITEM *Item,
+ IN VOID *Context
+ );
+
+UINT16
+Udp4Checksum (
+ IN NET_BUF *Packet,
+ IN UINT16 HeadSum
+ );
+
+EFI_STATUS
+Udp4RemoveToken (
+ IN NET_MAP *TokenMap,
+ IN EFI_UDP4_COMPLETION_TOKEN *Token
+ );
+
+EFI_STATUS
+Udp4LeaveGroup (
+ IN NET_MAP *Map,
+ IN NET_MAP_ITEM *Item,
+ IN VOID *Arg OPTIONAL
+ );
+
+VOID
+Udp4FlushRxData (
+ IN NET_LIST_ENTRY *RcvdDgramQue
+ );
+
+EFI_STATUS
+Udp4InstanceCancelToken (
+ IN UDP4_INSTANCE_DATA *Instance,
+ IN EFI_UDP4_COMPLETION_TOKEN *Token OPTIONAL
+ );
+
+VOID
+Udp4InstanceDeliverDgram (
+ IN UDP4_INSTANCE_DATA *Instance
+ );
+
+VOID
+Udp4ReportIcmpError (
+ IN UDP4_INSTANCE_DATA *Instance
+ );
+
+VOID
+Udp4NetVectorExtFree (
+ VOID *Context
+ );
+
+EFI_STATUS
+Udp4SetVariableData (
+ IN UDP4_SERVICE_DATA *Udp4Service
+ );
+
+VOID
+Udp4ClearVariableData (
+ IN UDP4_SERVICE_DATA *Udp4Service
+ );
+
+#endif
+
diff --git a/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Main.c b/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Main.c
new file mode 100644
index 0000000000..4f897cb54b
--- /dev/null
+++ b/MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Main.c
@@ -0,0 +1,869 @@
+/** @file
+
+Copyright (c) 2006 - 2007, Intel Corporation
+All rights reserved. This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+Module Name:
+
+ Udp4Main.c
+
+Abstract:
+
+
+**/
+
+#include "Udp4Impl.h"
+
+#include <Protocol/Ip4.h>
+
+EFI_UDP4_PROTOCOL mUdp4Protocol = {
+ Udp4GetModeData,
+ Udp4Configure,
+ Udp4Groups,
+ Udp4Routes,
+ Udp4Transmit,
+ Udp4Receive,
+ Udp4Cancel,
+ Udp4Poll
+};
+
+
+/**
+ This function copies the current operational settings of this EFI UDPv4 Protocol
+ instance into user-supplied buffers. This function is used optionally to retrieve
+ the operational mode data of underlying networks or drivers.
+
+ @param This Pointer to the EFI_UDP4_PROTOCOL instance.
+ @param Udp4ConfigData Pointer to the buffer to receive the current
+ configuration data.
+ @param Ip4ModeData Pointer to the EFI IPv4 Protocol mode data
+ structure.
+ @param MnpConfigData Pointer to the managed network configuration data
+ structure.
+ @param SnpModeData Pointer to the simple network mode data structure.
+
+ @retval EFI_SUCCESS The mode data was read.
+ @retval EFI_NOT_STARTED When Udp4ConfigData is queried, no configuration
+ data is available because this instance has not
+ been started.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+Udp4GetModeData (
+ IN EFI_UDP4_PROTOCOL *This,
+ OUT EFI_UDP4_CONFIG_DATA *Udp4ConfigData OPTIONAL,
+ OUT EFI_IP4_MODE_DATA *Ip4ModeData OPTIONAL,
+ OUT EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData OPTIONAL,
+ OUT EFI_SIMPLE_NETWORK_MODE *SnpModeData OPTIONAL
+ )
+{
+ UDP4_INSTANCE_DATA *Instance;
+ EFI_IP4_PROTOCOL *Ip;
+ EFI_TPL OldTpl;
+ EFI_STATUS Status;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = UDP4_INSTANCE_DATA_FROM_THIS (This);
+
+ if (!Instance->Configured && (Udp4ConfigData != NULL)) {
+ return EFI_NOT_STARTED;
+ }
+
+ OldTpl = NET_RAISE_TPL (NET_TPL_LOCK);
+
+ if (Udp4ConfigData != NULL) {
+ //
+ // Set the Udp4ConfigData.
+ //
+ *Udp4ConfigData = Instance->ConfigData;
+ }
+
+ Ip = Instance->IpInfo->Ip;
+
+ //
+ // Get the underlying Ip4ModeData, MnpConfigData and SnpModeData.
+ //
+ Status = Ip->GetModeData (Ip, Ip4ModeData, MnpConfigData, SnpModeData);
+
+ NET_RESTORE_TPL (OldTpl);
+
+ return Status;
+}
+
+
+/**
+ This function is used to do the following:
+ Initialize and start this instance of the EFI UDPv4 Protocol.
+ Change the filtering rules and operational parameters.
+ Reset this instance of the EFI UDPv4 Protocol.
+
+ @param This Pointer to the EFI_UDP4_PROTOCOL instance.
+ @param UdpConfigData Pointer to the buffer to receive the current mode
+ data.
+
+ @retval EFI_SUCCESS The configuration settings were set, changed, or
+ reset successfully.
+ @retval EFI_NO_MAPPING When using a default address, configuration (DHCP,
+ BOOTP, RARP, etc.) is not finished yet.
+ @retval EFI_INVALID_PARAMETER One or more following conditions are TRUE: This is
+ NULL. UdpConfigData.StationAddress is not a valid
+ unicast IPv4 address. UdpConfigData.SubnetMask is
+ not a valid IPv4 address mask.
+ UdpConfigData.RemoteAddress is not a valid unicast
+ IPv4 address if it is not zero.
+ @retval EFI_ALREADY_STARTED The EFI UDPv4 Protocol instance is already
+ started/configured and must be stopped/reset
+ before it can be reconfigured. Only TypeOfService,
+ TimeToLive, DoNotFragment, ReceiveTimeout, and
+ TransmitTimeout can be reconfigured without
+ stopping the current instance of the EFI UDPv4
+ Protocol.
+ @retval EFI_ACCESS_DENIED UdpConfigData.AllowDuplicatePort is FALSE and
+ UdpConfigData.StationPort is already used by other
+ instance.
+ @retval EFI_OUT_OF_RESOURCES The EFI UDPv4 Protocol driver cannot allocate
+ memory for this EFI UDPv4 Protocol instance.
+ @retval EFI_DEVICE_ERROR An unexpected network or system error occurred and
+ this instance was not opened.
+
+**/
+EFI_STATUS
+EFIAPI
+Udp4Configure (
+ IN EFI_UDP4_PROTOCOL *This,
+ IN EFI_UDP4_CONFIG_DATA *UdpConfigData OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ UDP4_INSTANCE_DATA *Instance;
+ UDP4_SERVICE_DATA *Udp4Service;
+ EFI_TPL OldTpl;
+ IP4_ADDR StationAddress;
+ IP4_ADDR SubnetMask;
+ IP4_ADDR RemoteAddress;
+ EFI_IP4_CONFIG_DATA Ip4ConfigData;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = UDP4_INSTANCE_DATA_FROM_THIS (This);
+
+ if (!Instance->Configured && (UdpConfigData == NULL)) {
+ return EFI_SUCCESS;
+ }
+
+ Udp4Service = Instance->Udp4Service;
+ Status = EFI_SUCCESS;
+
+ OldTpl = NET_RAISE_TPL (NET_TPL_LOCK);
+
+ if (UdpConfigData != NULL) {
+
+ StationAddress = EFI_NTOHL (UdpConfigData->StationAddress);
+ SubnetMask = EFI_NTOHL (UdpConfigData->SubnetMask);
+ RemoteAddress = EFI_NTOHL (UdpConfigData->RemoteAddress);
+
+ if (!UdpConfigData->UseDefaultAddress &&
+ (!IP4_IS_VALID_NETMASK (SubnetMask) ||
+ !((StationAddress == 0) || Ip4IsUnicast (StationAddress, SubnetMask)) ||
+ !((RemoteAddress == 0) || Ip4IsUnicast (RemoteAddress, 0)))) {
+ //
+ // Don't use default address, and subnet mask is invalid or StationAddress is not
+ // a valid unicast IPv4 address or RemoteAddress is not a valid unicast IPv4 address
+ // if it is not 0.
+ //
+ Status = EFI_INVALID_PARAMETER;
+ goto ON_EXIT;
+ }
+
+ if (Instance->Configured) {
+ //
+ // The instance is already configured, try to do the re-configuration.
+ //
+ if (!Udp4IsReconfigurable (&Instance->ConfigData, UdpConfigData)) {
+ //
+ // If the new configuration data wants to change some unreconfigurable
+ // settings, return EFI_ALREADY_STARTED.
+ //
+ Status = EFI_ALREADY_STARTED;
+ goto ON_EXIT;
+ }
+
+ //
+ // Save the reconfigurable parameters.
+ //
+ Instance->ConfigData.TypeOfService = UdpConfigData->TypeOfService;
+ Instance->ConfigData.TimeToLive = UdpConfigData->TimeToLive;
+ Instance->ConfigData.DoNotFragment = UdpConfigData->DoNotFragment;
+ Instance->ConfigData.ReceiveTimeout = UdpConfigData->ReceiveTimeout;
+ Instance->ConfigData.TransmitTimeout = UdpConfigData->TransmitTimeout;
+ } else {
+ //
+ // Construct the Ip configuration data from the UdpConfigData.
+ //
+ Udp4BuildIp4ConfigData (UdpConfigData, &Ip4ConfigData);
+
+ //
+ // Configure the Ip instance wrapped in the IpInfo.
+ //
+ Status = IpIoConfigIp (Instance->IpInfo, &Ip4ConfigData);
+ if (EFI_ERROR (Status)) {
+ if (Status == EFI_NO_MAPPING) {
+ Instance->IsNoMapping = TRUE;
+ }
+
+ goto ON_EXIT;
+ }
+
+ Instance->IsNoMapping = FALSE;
+
+ //
+ // Save the configuration data.
+ //
+ Instance->ConfigData = *UdpConfigData;
+ Instance->ConfigData.StationAddress = Ip4ConfigData.StationAddress;
+ Instance->ConfigData.SubnetMask = Ip4ConfigData.SubnetMask;
+
+ //
+ // Try to allocate the required port resource.
+ //
+ Status = Udp4Bind (&Udp4Service->ChildrenList, &Instance->ConfigData);
+ if (EFI_ERROR (Status)) {
+ //
+ // Reset the ip instance if bind fails.
+ //
+ IpIoConfigIp (Instance->IpInfo, NULL);
+ goto ON_EXIT;
+ }
+
+ //
+ // Pre calculate the checksum for the pseudo head, ignore the UDP length first.
+ //
+ Instance->HeadSum = NetPseudoHeadChecksum (
+ EFI_IP4 (Instance->ConfigData.StationAddress),
+ EFI_IP4 (Instance->ConfigData.RemoteAddress),
+ EFI_IP_PROTO_UDP,
+ 0
+ );
+
+ Instance->Configured = TRUE;
+ }
+ } else {
+ //
+ // UdpConfigData is NULL, reset the instance.
+ //
+ Instance->Configured = FALSE;
+ Instance->IsNoMapping = FALSE;
+
+ //
+ // Reset the Ip instance wrapped in the IpInfo.
+ //
+ IpIoConfigIp (Instance->IpInfo, NULL);
+
+ //
+ // Cancel all the user tokens.
+ //
+ Udp4InstanceCancelToken (Instance, NULL);
+
+ //
+ // Remove the buffered RxData for this instance.
+ //
+ Udp4FlushRxData (&Instance->RcvdDgramQue);
+ }
+
+ Udp4SetVariableData (Instance->Udp4Service);
+
+ON_EXIT:
+
+ NET_RESTORE_TPL (OldTpl);
+
+ return Status;
+}
+
+
+/**
+ This function is used to enable and disable the multicast group filtering.
+
+ @param This Pointer to the EFI_UDP4_PROTOCOL instance.
+ @param JoinFlag Set to TRUE to join a multicast group. Set to
+ FALSE to leave one or all multicast groups.
+ @param MulticastAddress Pointer to multicast group address to join or
+ leave.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_NOT_STARTED The EFI UDPv4 Protocol instance has not been
+ started.
+ @retval EFI_NO_MAPPING When using a default address, configuration (DHCP,
+ BOOTP, RARP, etc.) is not finished yet.
+ @retval EFI_OUT_OF_RESOURCES Could not allocate resources to join the group.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ This is NULL. JoinFlag is TRUE and
+ MulticastAddress is NULL. JoinFlag is TRUE and
+ *MulticastAddress is not a valid multicast
+ address.
+ @retval EFI_ALREADY_STARTED The group address is already in the group table
+ (when JoinFlag is TRUE).
+ @retval EFI_NOT_FOUND The group address is not in the group table (when
+ JoinFlag is FALSE).
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+Udp4Groups (
+ IN EFI_UDP4_PROTOCOL *This,
+ IN BOOLEAN JoinFlag,
+ IN EFI_IPv4_ADDRESS *MulticastAddress OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ UDP4_INSTANCE_DATA *Instance;
+ EFI_IP4_PROTOCOL *Ip;
+ EFI_TPL OldTpl;
+
+ if ((This == NULL) ||
+ (JoinFlag && (MulticastAddress == NULL)) ||
+ (JoinFlag && !IP4_IS_MULTICAST (EFI_NTOHL (*MulticastAddress)))) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = UDP4_INSTANCE_DATA_FROM_THIS (This);
+
+ if (Instance->IsNoMapping) {
+ return EFI_NO_MAPPING;
+ }
+
+ if (!Instance->Configured) {
+ return EFI_NOT_STARTED;
+ }
+
+ Ip = Instance->IpInfo->Ip;
+
+ OldTpl = NET_RAISE_TPL (NET_TPL_LOCK);
+
+ //
+ // Invoke the Ip instance the Udp4 instance consumes to do the group operation.
+ //
+ Status = Ip->Groups (Ip, JoinFlag, MulticastAddress);
+
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ //
+ // Keep a local copy of the configured multicast IPs because IpIo receives
+ // datagrams from the 0 station address IP instance and then UDP delivers to
+ // the matched instance. This copy of multicast IPs is used to avoid receive
+ // the mutlicast datagrams destinated to multicast IPs the other instances configured.
+ //
+ if (JoinFlag) {
+
+ NetMapInsertTail (
+ &Instance->McastIps,
+ (VOID *) (UINTN) EFI_IP4 (*MulticastAddress),
+ NULL
+ );
+ } else {
+
+ NetMapIterate (&Instance->McastIps, Udp4LeaveGroup, MulticastAddress);
+ }
+
+ON_EXIT:
+
+ NET_RESTORE_TPL (OldTpl);
+
+ return Status;
+}
+
+
+/**
+ This function adds a route to or deletes a route from the routing table.
+
+ @param This Pointer to the EFI_UDP4_PROTOCOL instance.
+ @param DeleteRoute Set to TRUE to delete this route from the routing
+ table. Set to FALSE to add this route to the
+ routing table.
+ @param SubnetAddress The destination network address that needs to be
+ routed.
+ @param SubnetMask The subnet mask of SubnetAddress.
+ @param GatewayAddress The gateway IP address for this route.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_NOT_STARTED The EFI UDPv4 Protocol instance has not been
+ started.
+ @retval EFI_NO_MAPPING When using a default address, configuration (DHCP,
+ BOOTP, RARP, etc.) is not finished yet.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ This is NULL. SubnetAddress is NULL. SubnetMask is
+ NULL. GatewayAddress is NULL. SubnetAddress is not
+ a valid subnet address. SubnetMask is not a valid
+ subnet mask. GatewayAddress is not a valid unicast
+ IP address.
+ @retval EFI_OUT_OF_RESOURCES Could not add the entry to the routing table.
+ @retval EFI_NOT_FOUND This route is not in the routing table.
+ @retval EFI_ACCESS_DENIED The route is already defined in the routing table.
+
+**/
+EFI_STATUS
+EFIAPI
+Udp4Routes (
+ IN EFI_UDP4_PROTOCOL *This,
+ IN BOOLEAN DeleteRoute,
+ IN EFI_IPv4_ADDRESS *SubnetAddress,
+ IN EFI_IPv4_ADDRESS *SubnetMask,
+ IN EFI_IPv4_ADDRESS *GatewayAddress
+ )
+{
+ UDP4_INSTANCE_DATA *Instance;
+ EFI_IP4_PROTOCOL *Ip;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = UDP4_INSTANCE_DATA_FROM_THIS (This);
+
+ if (Instance->IsNoMapping) {
+ return EFI_NO_MAPPING;
+ }
+
+ if (!Instance->Configured) {
+ return EFI_NOT_STARTED;
+ }
+
+ Ip = Instance->IpInfo->Ip;
+
+ //
+ // Invoke the Ip instance the Udp4 instance consumes to do the actual operation.
+ //
+ return Ip->Routes (Ip, DeleteRoute, SubnetAddress, SubnetMask, GatewayAddress);
+}
+
+
+/**
+ This function places a sending request to this instance of the EFI UDPv4 Protocol,
+ alongside the transmit data that was filled by the user.
+
+ @param This Pointer to the EFI_UDP4_PROTOCOL instance.
+ @param Token Pointer to the completion token that will be
+ placed into the transmit queue.
+
+ @retval EFI_SUCCESS The data has been queued for transmission.
+ @retval EFI_NOT_STARTED This EFI UDPv4 Protocol instance has not been
+ started.
+ @retval EFI_NO_MAPPING When using a default address, configuration (DHCP,
+ BOOTP, RARP, etc.) is not finished yet.
+ @retval EFI_INVALID_PARAMETER One or more of the following are TRUE: This is
+ NULL. Token is NULL. Token.Event is NULL.
+ Token.Packet.TxData is NULL.
+ Token.Packet.TxData.FragmentCount is zero.
+ Token.Packet.TxData.DataLength is not equal to the
+ sum of fragment lengths. One or more of the
+ Token.Packet.TxData.FragmentTable[].
+ FragmentLength fields is zero. One or more of the
+ Token.Packet.TxData.FragmentTable[].
+ FragmentBuffer fields is NULL.
+ Token.Packet.TxData. GatewayAddress is not a
+ unicast IPv4 address if it is not NULL. One or
+ more IPv4 addresses in Token.Packet.TxData.
+ UdpSessionData are not valid unicast IPv4
+ addresses if the UdpSessionData is not NULL.
+ @retval EFI_ACCESS_DENIED The transmit completion token with the same
+ Token.Event is already in the transmit queue.
+ @retval EFI_NOT_READY The completion token could not be queued because
+ the transmit queue is full.
+ @retval EFI_OUT_OF_RESOURCES Could not queue the transmit data.
+ @retval EFI_NOT_FOUND There is no route to the destination network or
+ address.
+ @retval EFI_BAD_BUFFER_SIZE The data length is greater than the maximum UDP
+ packet size. Or the length of the IP header + UDP
+ header + data length is greater than MTU if
+ DoNotFragment is TRUE.
+
+**/
+EFI_STATUS
+EFIAPI
+Udp4Transmit (
+ IN EFI_UDP4_PROTOCOL *This,
+ IN EFI_UDP4_COMPLETION_TOKEN *Token
+ )
+{
+ EFI_STATUS Status;
+ UDP4_INSTANCE_DATA *Instance;
+ EFI_TPL OldTpl;
+ NET_BUF *Packet;
+ EFI_UDP4_HEADER *Udp4Header;
+ EFI_UDP4_CONFIG_DATA *ConfigData;
+ IP4_ADDR Destination;
+ EFI_UDP4_TRANSMIT_DATA *TxData;
+ EFI_UDP4_SESSION_DATA *UdpSessionData;
+ UDP4_SERVICE_DATA *Udp4Service;
+ IP_IO_OVERRIDE Override;
+ UINT16 HeadSum;
+
+ if ((This == NULL) || (Token == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = UDP4_INSTANCE_DATA_FROM_THIS (This);
+
+ if (Instance->IsNoMapping) {
+ return EFI_NO_MAPPING;
+ }
+
+ if (!Instance->Configured) {
+ return EFI_NOT_STARTED;
+ }
+
+ OldTpl = NET_RAISE_TPL (NET_TPL_LOCK);
+
+ //
+ // Validate the Token, if the token is invalid return the error code.
+ //
+ Status = Udp4ValidateTxToken (Instance, Token);
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ if (EFI_ERROR (NetMapIterate (&Instance->TxTokens, Udp4TokenExist, Token)) ||
+ EFI_ERROR (NetMapIterate (&Instance->RxTokens, Udp4TokenExist, Token))) {
+ //
+ // Try to find a duplicate token in the two token maps, if found, return
+ // EFI_ACCESS_DENIED.
+ //
+ Status = EFI_ACCESS_DENIED;
+ goto ON_EXIT;
+ }
+
+ TxData = Token->Packet.TxData;
+
+ //
+ // Create a net buffer to hold the user buffer and the udp header.
+ //
+ Packet = NetbufFromExt (
+ (NET_FRAGMENT *)TxData->FragmentTable,
+ TxData->FragmentCount,
+ UDP4_HEADER_SIZE,
+ 0,
+ Udp4NetVectorExtFree,
+ NULL
+ );
+ if (Packet == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+
+ //
+ // Store the IpIo in ProtoData.
+ //
+ Udp4Service = Instance->Udp4Service;
+ *((UINTN *) &Packet->ProtoData[0]) = (UINTN) (Udp4Service->IpIo);
+
+ Udp4Header = (EFI_UDP4_HEADER *) NetbufAllocSpace (Packet, UDP4_HEADER_SIZE, TRUE);
+ ConfigData = &Instance->ConfigData;
+
+ //
+ // Fill the udp header.
+ //
+ Udp4Header->SrcPort = HTONS (ConfigData->StationPort);
+ Udp4Header->DstPort = HTONS (ConfigData->RemotePort);
+ Udp4Header->Length = HTONS (Packet->TotalSize);
+ Udp4Header->Checksum = 0;
+
+ UdpSessionData = TxData->UdpSessionData;
+ Override.SourceAddress = ConfigData->StationAddress;
+
+ if (UdpSessionData != NULL) {
+ //
+ // Set the SourceAddress, SrcPort and Destination according to the specified
+ // UdpSessionData.
+ //
+ if (EFI_IP4 (UdpSessionData->SourceAddress) != 0) {
+ Override.SourceAddress = UdpSessionData->SourceAddress;
+ }
+
+ if (UdpSessionData->SourcePort != 0) {
+ Udp4Header->SrcPort = HTONS (UdpSessionData->SourcePort);
+ }
+
+ Destination = EFI_IP4 (UdpSessionData->DestinationAddress);
+
+ if (UdpSessionData->DestinationPort != 0) {
+ Udp4Header->DstPort = HTONS (UdpSessionData->DestinationPort);
+ }
+
+ //
+ // calculate the pseudo head checksum using the overridden parameters.
+ //
+ HeadSum = NetPseudoHeadChecksum (
+ EFI_IP4 (Override.SourceAddress),
+ Destination,
+ EFI_IP_PROTO_UDP,
+ 0
+ );
+ } else {
+ //
+ // UdpSessionData is NULL, use the address and port information previously configured.
+ //
+ Destination = EFI_IP4 (ConfigData->RemoteAddress);
+ HeadSum = Instance->HeadSum;
+ }
+
+ //
+ // calculate the checksum.
+ //
+ Udp4Header->Checksum = Udp4Checksum (Packet, HeadSum);
+ if (Udp4Header->Checksum == 0) {
+ //
+ // If the calculated checksum is 0, fill the Checksum field with all ones.
+ //
+ Udp4Header->Checksum = 0xffff;
+ }
+
+ //
+ // Fill the IpIo Override data.
+ //
+ EFI_IP4 (Override.GatewayAddress) = (TxData->GatewayAddress != NULL) ?
+ EFI_IP4 (*(TxData->GatewayAddress)) : 0;
+ Override.Protocol = EFI_IP_PROTO_UDP;
+ Override.TypeOfService = ConfigData->TypeOfService;
+ Override.TimeToLive = ConfigData->TimeToLive;
+ Override.DoNotFragment = ConfigData->DoNotFragment;
+
+ //
+ // Save the token into the TxToken map.
+ //
+ Status = NetMapInsertTail (&Instance->TxTokens, Token, Packet);
+ if (EFI_ERROR (Status)) {
+ goto FREE_PACKET;
+ }
+
+ //
+ // Send out this datagram through IpIo.
+ //
+ Status = IpIoSend (
+ Udp4Service->IpIo,
+ Packet,
+ Instance->IpInfo,
+ Instance,
+ Token,
+ Destination,
+ &Override
+ );
+ if (EFI_ERROR (Status)) {
+ //
+ // Remove this token from the TxTokens.
+ //
+ Udp4RemoveToken (&Instance->TxTokens, Token);
+ }
+
+FREE_PACKET:
+
+ NetbufFree (Packet);
+
+ON_EXIT:
+
+ NET_RESTORE_TPL (OldTpl);
+
+ return Status;
+}
+
+
+/**
+ This function places a completion token into the receive packet queue. This function
+ is always asynchronous.
+
+ @param This Pointer to the EFI_UDP4_PROTOCOL instance.
+ @param Token Pointer to a token that is associated with the
+ receive data descriptor.
+
+ @retval EFI_SUCCESS The receive completion token is cached.
+ @retval EFI_NOT_STARTED This EFI UDPv4 Protocol instance has not been
+ started.
+ @retval EFI_NO_MAPPING When using a default address, configuration (DHCP,
+ BOOTP, RARP, etc.) is not finished yet.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ This is NULL. Token is NULL. Token.Event is NULL.
+ @retval EFI_OUT_OF_RESOURCES The receive completion token could not be queued
+ due to a lack of system resources (usually
+ memory).
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+ The EFI UDPv4 Protocol instance has been reset to
+ startup defaults.
+ @retval EFI_ACCESS_DENIED A receive completion token with the same
+ Token.Event is already in the receive queue.
+ @retval EFI_NOT_READY The receive request could not be queued because
+ the receive queue is full.
+
+**/
+EFI_STATUS
+EFIAPI
+Udp4Receive (
+ IN EFI_UDP4_PROTOCOL *This,
+ IN EFI_UDP4_COMPLETION_TOKEN *Token
+ )
+{
+ EFI_STATUS Status;
+ UDP4_INSTANCE_DATA *Instance;
+ EFI_TPL OldTpl;
+
+ if ((This == NULL) || (Token == NULL) || (Token->Event == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = UDP4_INSTANCE_DATA_FROM_THIS (This);
+
+ if (Instance->IsNoMapping) {
+ return EFI_NO_MAPPING;
+ }
+
+ if (!Instance->Configured) {
+ return EFI_NOT_STARTED;
+ }
+
+ OldTpl = NET_RAISE_TPL (NET_TPL_LOCK);
+
+ if (EFI_ERROR (NetMapIterate (&Instance->RxTokens, Udp4TokenExist, Token))||
+ EFI_ERROR (NetMapIterate (&Instance->TxTokens, Udp4TokenExist, Token))) {
+ //
+ // Return EFI_ACCESS_DENIED if the specified token is already in the TxTokens or
+ // RxTokens map.
+ //
+ Status = EFI_ACCESS_DENIED;
+ goto ON_EXIT;
+ }
+
+ Token->Packet.RxData = NULL;
+
+ //
+ // Save the token into the RxTokens map.
+ //
+ Status = NetMapInsertTail (&Instance->RxTokens, Token, NULL);
+ if (EFI_ERROR (Status)) {
+ return EFI_NOT_READY;
+ }
+
+ //
+ // If there is an icmp error, report it.
+ //
+ Udp4ReportIcmpError (Instance);
+
+ //
+ // Try to delivered the received datagrams.
+ //
+ Udp4InstanceDeliverDgram (Instance);
+
+ON_EXIT:
+
+ NET_RESTORE_TPL (OldTpl);
+
+ return Status;
+}
+
+
+/**
+ This function is used to abort a pending transmit or receive request.
+
+ @param This Pointer to the EFI_UDP4_PROTOCOL instance.
+ @param Token Pointer to a token that has been issued by
+ EFI_UDP4_PROTOCOL.Transmit() or
+ EFI_UDP4_PROTOCOL.Receive().
+
+ @retval EFI_SUCCESS The asynchronous I/O request is aborted and
+ Token.Event is signaled. When Token is NULL, all
+ pending requests are aborted and their events are
+ signaled.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+ @retval EFI_NOT_STARTED This instance has not been started.
+ @retval EFI_NO_MAPPING When using the default address, configuration
+ (DHCP, BOOTP, RARP, etc.) is not finished yet.
+ @retval EFI_NOT_FOUND When Token is not NULL, the asynchronous I/O
+ request is not found in the transmit or receive
+ queue. It is either completed or not issued by
+ Transmit() or Receive().
+
+**/
+EFI_STATUS
+EFIAPI
+Udp4Cancel (
+ IN EFI_UDP4_PROTOCOL *This,
+ IN EFI_UDP4_COMPLETION_TOKEN *Token OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ UDP4_INSTANCE_DATA *Instance;
+ EFI_TPL OldTpl;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = UDP4_INSTANCE_DATA_FROM_THIS (This);
+
+ if (Instance->IsNoMapping) {
+ return EFI_NO_MAPPING;
+ }
+
+ if (!Instance->Configured) {
+ return EFI_NOT_STARTED;
+ }
+
+ OldTpl = NET_RAISE_TPL (NET_TPL_LOCK);
+
+ //
+ // Cancle the tokens specified by Token for this instance.
+ //
+ Status = Udp4InstanceCancelToken (Instance, Token);
+
+ NET_RESTORE_TPL (OldTpl);
+
+ return Status;
+}
+
+
+/**
+ This function can be used by network drivers and applications to increase the rate that
+ data packets are moved between the communications device and the transmit/receive queues.
+ Argumens:
+ This - Pointer to the EFI_UDP4_PROTOCOL instance.
+
+ @retval EFI_SUCCESS Incoming or outgoing data was processed.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+ @retval EFI_TIMEOUT Data was dropped out of the transmit and/or
+ receive queue.
+
+**/
+EFI_STATUS
+EFIAPI
+Udp4Poll (
+ IN EFI_UDP4_PROTOCOL *This
+ )
+{
+ UDP4_INSTANCE_DATA *Instance;
+ EFI_IP4_PROTOCOL *Ip;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = UDP4_INSTANCE_DATA_FROM_THIS (This);
+ Ip = Instance->IpInfo->Ip;
+
+ //
+ // Invode the Ip instance consumed by the udp instance to do the poll operation.
+ //
+ return Ip->Poll (Ip);
+}