summaryrefslogtreecommitdiff
path: root/Core/NetworkPkg/TcpDxe/SockImpl.c
diff options
context:
space:
mode:
Diffstat (limited to 'Core/NetworkPkg/TcpDxe/SockImpl.c')
-rw-r--r--Core/NetworkPkg/TcpDxe/SockImpl.c1239
1 files changed, 1239 insertions, 0 deletions
diff --git a/Core/NetworkPkg/TcpDxe/SockImpl.c b/Core/NetworkPkg/TcpDxe/SockImpl.c
new file mode 100644
index 0000000000..c5fb176255
--- /dev/null
+++ b/Core/NetworkPkg/TcpDxe/SockImpl.c
@@ -0,0 +1,1239 @@
+/** @file
+ Implementation of the Socket.
+
+ Copyright (c) 2009 - 2017, Intel Corporation. All rights reserved.<BR>
+
+ 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.
+
+**/
+
+#include "SockImpl.h"
+
+/**
+ Get the first buffer block in the specific socket buffer.
+
+ @param[in] 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
+ )
+{
+ LIST_ENTRY *NetbufList;
+
+ NetbufList = &(Sockbuf->DataQueue->BufList);
+
+ if (IsListEmpty (NetbufList)) {
+ return NULL;
+ }
+
+ return NET_LIST_HEAD (NetbufList, NET_BUF, List);
+}
+
+/**
+ Get the next buffer block in the specific socket buffer.
+
+ @param[in] Sockbuf Pointer to the socket buffer.
+ @param[in] 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
+ )
+{
+ 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);
+}
+
+/**
+ User provided callback function for NetbufFromExt.
+
+ @param[in] Event The Event this notify function registered to, ignored.
+
+**/
+VOID
+EFIAPI
+SockFreeFoo (
+ IN EFI_EVENT Event
+ )
+{
+ return;
+}
+
+/**
+ Get the length of the data that can be retrieved from the socket
+ receive buffer.
+
+ @param[in] SockBuffer Pointer to the socket receive buffer.
+ @param[out] IsUrg Pointer to a BOOLEAN variable.
+ If TRUE the data is OOB.
+ @param[in] BufLen The maximum length of the data buffer to
+ store the received data in the socket layer.
+
+ @return The length of the data can be retreived.
+
+**/
+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 != NULL) && (IsUrg != NULL) && (BufLen > 0));
+
+ //
+ // Get the first socket receive buffer
+ //
+ RcvBufEntry = SockBufFirst (SockBuffer);
+ ASSERT (RcvBufEntry != NULL);
+
+ TcpRsvData = (TCP_RSV_DATA *) RcvBufEntry->ProtoData;
+
+ //
+ // Check whether the receive data is out of bound. If yes, calculate the maximum
+ // allowed length of the urgent data and output it.
+ //
+ *IsUrg = (BOOLEAN) ((TcpRsvData->UrgLen > 0) ? TRUE : FALSE);
+
+ if (*IsUrg && (TcpRsvData->UrgLen < RcvBufEntry->TotalSize)) {
+
+ DataLen = MIN (TcpRsvData->UrgLen, BufLen);
+
+ if (DataLen < TcpRsvData->UrgLen) {
+ TcpRsvData->UrgLen = TcpRsvData->UrgLen - DataLen;
+ } else {
+ TcpRsvData->UrgLen = 0;
+ }
+
+ return DataLen;
+
+ }
+
+ //
+ // Process the next socket receive buffer to get the maximum allowed length
+ // of the received data.
+ //
+ DataLen = RcvBufEntry->TotalSize;
+
+ RcvBufEntry = SockBufNext (SockBuffer, RcvBufEntry);
+
+ while ((BufLen > DataLen) && (RcvBufEntry != NULL)) {
+
+ TcpRsvData = (TCP_RSV_DATA *) RcvBufEntry->ProtoData;
+
+ Urg = (BOOLEAN) ((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 MIN (TcpRsvData->UrgLen + DataLen, BufLen);
+
+ }
+
+ DataLen += RcvBufEntry->TotalSize;
+
+ RcvBufEntry = SockBufNext (SockBuffer, RcvBufEntry);
+ }
+
+ DataLen = MIN (BufLen, DataLen);
+ return DataLen;
+}
+
+/**
+ Copy data from socket buffer to an application provided receive buffer.
+
+ @param[in] Sock Pointer to the socket.
+ @param[in] TcpRxData Pointer to the application provided receive buffer.
+ @param[in] RcvdBytes The maximum length of the data can be copied.
+ @param[in] IsUrg If TRUE the data is Out of Bound, FALSE the data is normal.
+
+**/
+VOID
+SockSetTcpRxData (
+ IN SOCKET *Sock,
+ IN VOID *TcpRxData,
+ IN UINT32 RcvdBytes,
+ IN BOOLEAN IsUrg
+ )
+{
+ 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 = IsUrg;
+
+ //
+ // Copy the CopyBytes data from socket receive buffer to RxData.
+ //
+ for (Index = 0; (Index < RxData->FragmentCount) && (RcvdBytes > 0); Index++) {
+
+ Fragment = &RxData->FragmentTable[Index];
+ CopyBytes = MIN ((UINT32) (Fragment->FragmentLength), RcvdBytes);
+
+ NetbufQueCopy (
+ Sock->RcvBuffer.DataQueue,
+ OffSet,
+ CopyBytes,
+ Fragment->FragmentBuffer
+ );
+
+ Fragment->FragmentLength = CopyBytes;
+ RcvdBytes -= CopyBytes;
+ OffSet += CopyBytes;
+ }
+}
+
+/**
+ Process the send token.
+
+ @param[in, out] Sock Pointer to the socket.
+
+**/
+VOID
+SockProcessSndToken (
+ IN OUT SOCKET *Sock
+ )
+{
+ UINT32 FreeSpace;
+ SOCK_TOKEN *SockToken;
+ UINT32 DataLen;
+ SOCK_IO_TOKEN *SndToken;
+ EFI_TCP4_TRANSMIT_DATA *TxData;
+ EFI_STATUS Status;
+
+ ASSERT ((Sock != NULL) && (SockStream == 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) && !IsListEmpty (&Sock->SndTokenList)) {
+
+ SockToken = NET_LIST_HEAD (
+ &(Sock->SndTokenList),
+ SOCK_TOKEN,
+ TokenList
+ );
+
+ //
+ // process this token
+ //
+ RemoveEntryList (&(SockToken->TokenList));
+ InsertTailList (
+ &(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:
+
+ RemoveEntryList (&SockToken->TokenList);
+ SIGNAL_TOKEN (SockToken->Token, Status);
+ FreePool (SockToken);
+}
+
+/**
+ Get received data from the socket layer to the receive token.
+
+ @param[in, out] Sock Pointer to the socket.
+ @param[in, out] RcvToken Pointer to the application provided receive token.
+
+ @return The length of data received in this token.
+
+**/
+UINT32
+SockProcessRcvToken (
+ IN OUT SOCKET *Sock,
+ IN OUT SOCK_IO_TOKEN *RcvToken
+ )
+{
+ UINT32 TokenRcvdBytes;
+ EFI_TCP4_RECEIVE_DATA *RxData;
+ BOOLEAN IsUrg;
+
+ ASSERT (Sock != NULL);
+
+ ASSERT (SockStream == 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);
+
+ NetbufQueTrim (Sock->RcvBuffer.DataQueue, 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[in] Sock Pointer to the socket.
+ @param[in] TcpTxData Pointer to the application provided send buffer.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_OUT_OF_RESOURCES Failed due to resource limits.
+
+**/
+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) {
+ DEBUG (
+ (EFI_D_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[in] Sock Pointer to the socket.
+ @param[in, out] PendingTokenList Pointer to the token list to be flushed.
+
+**/
+VOID
+SockFlushPendingToken (
+ IN SOCKET *Sock,
+ IN OUT LIST_ENTRY *PendingTokenList
+ )
+{
+ SOCK_TOKEN *SockToken;
+ SOCK_COMPLETION_TOKEN *Token;
+
+ ASSERT ((Sock != NULL) && (PendingTokenList != NULL));
+
+ while (!IsListEmpty (PendingTokenList)) {
+ SockToken = NET_LIST_HEAD (
+ PendingTokenList,
+ SOCK_TOKEN,
+ TokenList
+ );
+
+ Token = SockToken->Token;
+ SIGNAL_TOKEN (Token, Sock->SockError);
+
+ RemoveEntryList (&(SockToken->TokenList));
+ FreePool (SockToken);
+ }
+}
+
+/**
+ Wake up the connection token while the connection is successfully established,
+ then try to process any pending send token.
+
+ @param[in, out] Sock Pointer to the socket.
+
+**/
+VOID
+SockWakeConnToken (
+ IN OUT 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);
+}
+
+/**
+ Wake up the listen token while the connection is established successfully.
+
+ @param[in, out] Sock Pointer to the socket.
+
+**/
+VOID
+SockWakeListenToken (
+ IN OUT SOCKET *Sock
+ )
+{
+ SOCKET *Parent;
+ SOCK_TOKEN *SockToken;
+ EFI_TCP4_LISTEN_TOKEN *ListenToken;
+
+ Parent = Sock->Parent;
+
+ ASSERT ((Parent != NULL) && SOCK_IS_LISTENING (Parent) && SOCK_IS_CONNECTED (Sock));
+
+ if (!IsListEmpty (&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);
+
+ RemoveEntryList (&SockToken->TokenList);
+ FreePool (SockToken);
+
+ RemoveEntryList (&Sock->ConnectionList);
+
+ Parent->ConnCnt--;
+ DEBUG (
+ (EFI_D_NET,
+ "SockWakeListenToken: accept a socket, now conncnt is %d",
+ Parent->ConnCnt)
+ );
+
+ Sock->Parent = NULL;
+ }
+}
+
+/**
+ Wake up the receive token while some data is received.
+
+ @param[in, out] Sock Pointer to the socket.
+
+**/
+VOID
+SockWakeRcvToken (
+ IN OUT SOCKET *Sock
+ )
+{
+ UINT32 RcvdBytes;
+ UINT32 TokenRcvdBytes;
+ SOCK_TOKEN *SockToken;
+ SOCK_IO_TOKEN *RcvToken;
+
+ ASSERT (Sock->RcvBuffer.DataQueue != NULL);
+
+ RcvdBytes = (Sock->RcvBuffer.DataQueue)->BufSize;
+
+ ASSERT (RcvdBytes > 0);
+
+ while (RcvdBytes > 0 && !IsListEmpty (&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 ;
+ }
+
+ RemoveEntryList (&(SockToken->TokenList));
+ FreePool (SockToken);
+ RcvdBytes -= TokenRcvdBytes;
+ }
+}
+
+/**
+ Cancel the tokens in the specific token list.
+
+ @param[in] Token Pointer to the Token. If NULL, all tokens
+ in SpecifiedTokenList will be canceled.
+ @param[in, out] SpecifiedTokenList Pointer to the token list to be checked.
+
+ @retval EFI_SUCCESS Cancel the tokens in the specific token listsuccessfully.
+ @retval EFI_NOT_FOUND The Token is not found in SpecifiedTokenList.
+
+**/
+EFI_STATUS
+SockCancelToken (
+ IN SOCK_COMPLETION_TOKEN *Token,
+ IN OUT LIST_ENTRY *SpecifiedTokenList
+ )
+{
+ EFI_STATUS Status;
+ LIST_ENTRY *Entry;
+ SOCK_TOKEN *SockToken;
+
+ Status = EFI_SUCCESS;
+ Entry = NULL;
+ SockToken = NULL;
+
+ if (IsListEmpty (SpecifiedTokenList) && Token != NULL) {
+ return EFI_NOT_FOUND;
+ }
+
+ //
+ // Iterate through the SpecifiedTokenList.
+ //
+ Entry = SpecifiedTokenList->ForwardLink;
+ while (Entry != SpecifiedTokenList) {
+ SockToken = NET_LIST_USER_STRUCT (Entry, SOCK_TOKEN, TokenList);
+
+ if (Token == NULL) {
+ SIGNAL_TOKEN (SockToken->Token, EFI_ABORTED);
+ RemoveEntryList (&SockToken->TokenList);
+ FreePool (SockToken);
+
+ Entry = SpecifiedTokenList->ForwardLink;
+ Status = EFI_SUCCESS;
+ } else {
+ if (Token == (VOID *) SockToken->Token) {
+ SIGNAL_TOKEN (Token, EFI_ABORTED);
+ RemoveEntryList (&(SockToken->TokenList));
+ FreePool (SockToken);
+
+ return EFI_SUCCESS;
+ }
+
+ Status = EFI_NOT_FOUND;
+
+ Entry = Entry->ForwardLink;
+ }
+ }
+
+ ASSERT (IsListEmpty (SpecifiedTokenList) || Token != NULL);
+
+ return Status;
+}
+
+/**
+ Create a socket with initial data SockInitData.
+
+ @param[in] SockInitData Pointer to the initial data of the socket.
+
+ @return Pointer to the newly created socket, return NULL when an exception occurs.
+
+**/
+SOCKET *
+SockCreate (
+ IN SOCK_INIT_DATA *SockInitData
+ )
+{
+ SOCKET *Sock;
+ SOCKET *Parent;
+ EFI_STATUS Status;
+ EFI_GUID *TcpProtocolGuid;
+ UINTN ProtocolLength;
+
+ ASSERT ((SockInitData != NULL) && (SockInitData->ProtoHandler != NULL));
+ ASSERT (SockInitData->Type == SockStream);
+ ASSERT ((SockInitData->ProtoData != NULL) && (SockInitData->DataSize <= PROTO_RESERVED_LEN));
+
+ if (SockInitData->IpVersion == IP_VERSION_4) {
+ TcpProtocolGuid = &gEfiTcp4ProtocolGuid;
+ ProtocolLength = sizeof (EFI_TCP4_PROTOCOL);
+ } else {
+ TcpProtocolGuid = &gEfiTcp6ProtocolGuid;
+ ProtocolLength = sizeof (EFI_TCP6_PROTOCOL);
+ }
+
+
+ Parent = SockInitData->Parent;
+
+ if ((Parent != NULL) && (Parent->ConnCnt == Parent->BackLog)) {
+ DEBUG (
+ (EFI_D_ERROR,
+ "SockCreate: Socket parent has reached its connection limit with %d ConnCnt and %d BackLog\n",
+ Parent->ConnCnt,
+ Parent->BackLog)
+ );
+
+ return NULL;
+ }
+
+ Sock = AllocateZeroPool (sizeof (SOCKET));
+ if (NULL == Sock) {
+
+ DEBUG ((EFI_D_ERROR, "SockCreate: No resource to create a new socket\n"));
+ return NULL;
+ }
+
+ InitializeListHead (&Sock->Link);
+ InitializeListHead (&Sock->ConnectionList);
+ InitializeListHead (&Sock->ListenTokenList);
+ InitializeListHead (&Sock->RcvTokenList);
+ InitializeListHead (&Sock->SndTokenList);
+ InitializeListHead (&Sock->ProcessingSndTokenList);
+
+ EfiInitializeLock (&(Sock->Lock), TPL_CALLBACK);
+
+ Sock->SndBuffer.DataQueue = NetbufQueAlloc ();
+ if (NULL == Sock->SndBuffer.DataQueue) {
+ DEBUG (
+ (EFI_D_ERROR,
+ "SockCreate: No resource to allocate SndBuffer for new socket\n")
+ );
+
+ goto OnError;
+ }
+
+ Sock->RcvBuffer.DataQueue = NetbufQueAlloc ();
+ if (NULL == Sock->RcvBuffer.DataQueue) {
+ DEBUG (
+ (EFI_D_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->CreateCallback = SockInitData->CreateCallback;
+ Sock->DestroyCallback = SockInitData->DestroyCallback;
+ Sock->Context = SockInitData->Context;
+
+ Sock->SockError = EFI_ABORTED;
+ Sock->SndBuffer.LowWater = SOCK_BUFF_LOW_WATER;
+ Sock->RcvBuffer.LowWater = SOCK_BUFF_LOW_WATER;
+
+ Sock->IpVersion = SockInitData->IpVersion;
+
+ //
+ // Install protocol on Sock->SockHandle
+ //
+ CopyMem (&Sock->NetProtocol, SockInitData->Protocol, ProtocolLength);
+
+ //
+ // copy the protodata into socket
+ //
+ CopyMem (Sock->ProtoReserved, SockInitData->ProtoData, SockInitData->DataSize);
+
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ &Sock->SockHandle,
+ TcpProtocolGuid,
+ &Sock->NetProtocol,
+ NULL
+ );
+
+ if (EFI_ERROR (Status)) {
+ DEBUG (
+ (EFI_D_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++;
+
+ DEBUG (
+ (EFI_D_NET,
+ "SockCreate: Create a new socket and add to parent, now conncnt is %d\n",
+ Parent->ConnCnt)
+ );
+
+ InsertTailList (&Parent->ConnectionList, &Sock->ConnectionList);
+ }
+
+ if (Sock->CreateCallback != NULL) {
+ Status = Sock->CreateCallback (Sock, Sock->Context);
+ if (EFI_ERROR (Status)) {
+ goto OnError;
+ }
+ }
+
+ return Sock;
+
+OnError:
+
+ if (Sock->SockHandle != NULL) {
+ gBS->UninstallMultipleProtocolInterfaces (
+ Sock->SockHandle,
+ TcpProtocolGuid,
+ &Sock->NetProtocol,
+ NULL
+ );
+ }
+
+ if (NULL != Sock->SndBuffer.DataQueue) {
+ NetbufQueFree (Sock->SndBuffer.DataQueue);
+ }
+
+ if (NULL != Sock->RcvBuffer.DataQueue) {
+ NetbufQueFree (Sock->RcvBuffer.DataQueue);
+ }
+
+ FreePool (Sock);
+
+ return NULL;
+}
+
+/**
+ Destroy a socket.
+
+ @param[in, out] Sock Pointer to the socket.
+
+**/
+VOID
+SockDestroy (
+ IN OUT SOCKET *Sock
+ )
+{
+ ASSERT (SockStream == 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;
+
+ }
+ //
+ // Destroy 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 != NULL) {
+
+ RemoveEntryList (&(Sock->ConnectionList));
+ (Sock->Parent->ConnCnt)--;
+
+ DEBUG (
+ (EFI_D_WARN,
+ "SockDestroy: Delete a unaccepted socket from parent now conncnt is %d\n",
+ Sock->Parent->ConnCnt)
+ );
+
+ Sock->Parent = NULL;
+ }
+
+ FreePool (Sock);
+}
+
+/**
+ Flush the sndBuffer and rcvBuffer of socket.
+
+ @param[in, out] Sock Pointer to the socket.
+
+**/
+VOID
+SockConnFlush (
+ IN OUT SOCKET *Sock
+ )
+{
+ SOCKET *Child;
+
+ ASSERT (Sock != NULL);
+
+ //
+ // 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 (!IsListEmpty (&Sock->ConnectionList)) {
+ Child = NET_LIST_HEAD (
+ &Sock->ConnectionList,
+ SOCKET,
+ ConnectionList
+ );
+
+ SockDestroyChild (Child);
+ }
+
+ Sock->ConnCnt = 0;
+ }
+
+}
+
+/**
+ Set the state of the socket.
+
+ @param[in, out] Sock Pointer to the socket.
+ @param[in] State The new socket state to be set.
+
+**/
+VOID
+SockSetState (
+ IN OUT SOCKET *Sock,
+ IN UINT8 State
+ )
+{
+ Sock->State = State;
+}
+
+/**
+ Clone a new socket, including its associated protocol control block.
+
+ @param[in] Sock Pointer to the socket to be cloned.
+
+ @return Pointer to the newly cloned socket. If NULL, an 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.IpVersion = Sock->IpVersion;
+ InitData.Protocol = &(Sock->NetProtocol);
+ InitData.CreateCallback = Sock->CreateCallback;
+ InitData.DestroyCallback = Sock->DestroyCallback;
+ InitData.Context = Sock->Context;
+ InitData.ProtoData = Sock->ProtoReserved;
+ InitData.DataSize = sizeof (Sock->ProtoReserved);
+
+ ClonedSock = SockCreate (&InitData);
+
+ if (NULL == ClonedSock) {
+ DEBUG ((EFI_D_ERROR, "SockClone: no resource to create a cloned sock\n"));
+ return NULL;
+ }
+
+ 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[in, out] Sock Pointer to the socket associated with the
+ established connection.
+
+**/
+VOID
+SockConnEstablished (
+ IN OUT SOCKET *Sock
+ )
+{
+
+ ASSERT (SO_CONNECTING == Sock->State);
+
+ SockSetState (Sock, SO_CONNECTED);
+
+ if (NULL == Sock->Parent) {
+ SockWakeConnToken (Sock);
+ } else {
+ SockWakeListenToken (Sock);
+ }
+
+}
+
+/**
+ 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[in, out] Sock Pointer to the socket associated with the closed
+ connection.
+
+**/
+VOID
+SockConnClosed (
+ IN OUT SOCKET *Sock
+ )
+{
+ if (Sock->CloseToken != NULL) {
+ 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 was sent or processed.
+
+ This function trims the sent data in the socket send buffer, and signals the data
+ token if proper.
+
+ @param[in, out] Sock Pointer to the socket.
+ @param[in] Count The length of the data processed or sent, in bytes.
+
+**/
+VOID
+SockDataSent (
+ IN OUT SOCKET *Sock,
+ IN UINT32 Count
+ )
+{
+ SOCK_TOKEN *SockToken;
+ SOCK_COMPLETION_TOKEN *SndToken;
+
+ ASSERT (!IsListEmpty (&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) {
+
+ RemoveEntryList (&(SockToken->TokenList));
+ SIGNAL_TOKEN (SndToken, EFI_SUCCESS);
+ Count -= SockToken->RemainDataLen;
+ FreePool (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);
+}
+
+/**
+ Called by the low layer protocol to copy some data in the socket send
+ buffer starting from the specific offset to a buffer provided by
+ the caller.
+
+ @param[in] Sock Pointer to the socket.
+ @param[in] Offset The start point of the data to be copied.
+ @param[in] Len The length of the data to be copied.
+ @param[out] 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,
+ OUT UINT8 *Dest
+ )
+{
+ ASSERT ((Sock != NULL) && SockStream == 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 the
+ urgent data length, and then check if any receive token can be signaled.
+
+ @param[in, out] Sock Pointer to the socket.
+ @param[in, out] NetBuffer Pointer to the buffer that contains the received data.
+ @param[in] UrgLen The length of the urgent data in the received data.
+
+**/
+VOID
+SockDataRcvd (
+ IN OUT SOCKET *Sock,
+ IN OUT NET_BUF *NetBuffer,
+ IN UINT32 UrgLen
+ )
+{
+ ASSERT ((Sock != NULL) && (Sock->RcvBuffer.DataQueue != NULL) &&
+ UrgLen <= NetBuffer->TotalSize);
+
+ NET_GET_REF (NetBuffer);
+
+ ((TCP_RSV_DATA *) (NetBuffer->ProtoData))->UrgLen = UrgLen;
+
+ NetbufQueAppend (Sock->RcvBuffer.DataQueue, NetBuffer);
+
+ SockWakeRcvToken (Sock);
+}
+
+/**
+ Get the length of the free space of the specific socket buffer.
+
+ @param[in] Sock Pointer to the socket.
+ @param[in] 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 != NULL && ((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;
+}
+
+/**
+ Called by the low layer protocol to indicate that there will be no more data
+ from the communication peer.
+
+ This function sets the socket's state to SO_NO_MORE_DATA and signals all queued
+ IO tokens with the error status EFI_CONNECTION_FIN.
+
+ @param[in, out] Sock Pointer to the socket.
+
+**/
+VOID
+SockNoMoreData (
+ IN OUT SOCKET *Sock
+ )
+{
+ EFI_STATUS Err;
+
+ SOCK_NO_MORE_DATA (Sock);
+
+ if (!IsListEmpty (&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);
+
+ }
+}
+