summaryrefslogtreecommitdiff
path: root/MdeModulePkg/Universal/Network/Tcp4Dxe
diff options
context:
space:
mode:
authorvanjeff <vanjeff@6f19259b-4bc3-4df7-8a09-765794883524>2007-07-24 08:06:37 +0000
committervanjeff <vanjeff@6f19259b-4bc3-4df7-8a09-765794883524>2007-07-24 08:06:37 +0000
commit8a67d61da4d5a8f08a656cbeea2d902d0ad9042a (patch)
tree6618049196a9f4a206b8d6e42fb8b67a71558503 /MdeModulePkg/Universal/Network/Tcp4Dxe
parentf9bef4b3ac2bf3bd5f79313f772519800761f104 (diff)
downloadedk2-platforms-8a67d61da4d5a8f08a656cbeea2d902d0ad9042a.tar.xz
Import SnpDxe, Tcp4Dxe, Udp4Dxe and MnpDxe.
git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@3416 6f19259b-4bc3-4df7-8a09-765794883524
Diffstat (limited to 'MdeModulePkg/Universal/Network/Tcp4Dxe')
-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
21 files changed, 11106 insertions, 0 deletions
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:
+ ;
+ }
+}