/** @file | |
Implementation of the Socket. | |
Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR> | |
SPDX-License-Identifier: BSD-2-Clause-Patent | |
**/ | |
#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] Arg The Arg parameter forwarded by NetbufFromExt(). Ignored. | |
**/ | |
VOID | |
EFIAPI | |
SockFreeFoo ( | |
IN VOID *Arg | |
) | |
{ | |
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 retrieved. | |
**/ | |
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) | |
); | |
// | |
// Process 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 ( | |
(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[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 ( | |
(DEBUG_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 ( | |
(DEBUG_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 ((DEBUG_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 ( | |
(DEBUG_ERROR, | |
"SockCreate: No resource to allocate SndBuffer for new socket\n") | |
); | |
goto OnError; | |
} | |
Sock->RcvBuffer.DataQueue = NetbufQueAlloc (); | |
if (NULL == Sock->RcvBuffer.DataQueue) { | |
DEBUG ( | |
(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->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 ( | |
(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++; | |
DEBUG ( | |
(DEBUG_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 ( | |
(DEBUG_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 ((DEBUG_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); | |
} | |
} |