From 3bdf9aae5f7f4c5f47fc8f807ae4409dfe3bdd58 Mon Sep 17 00:00:00 2001 From: lpleahy Date: Thu, 9 Feb 2012 19:16:44 +0000 Subject: Merged socket development branch: * Fixed bug report (Duane Voth: Python sockets test application not working) by starting the receive operations when a connection is established! * Increased performance by extending the idle loop into the network stack with the Poll call. * Added support for TCPv6 (SOCK_STREAM) and UDPv6 (SOCK_DGRAM). * Added support for getaddrinfo and getnameinfo calls. * Moved application PCD values into AppPkg Signed-off-by: lpleahy git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@13002 6f19259b-4bc3-4df7-8a09-765794883524 --- StdLib/EfiSocketLib/Udp6.c | 1094 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1094 insertions(+) create mode 100644 StdLib/EfiSocketLib/Udp6.c (limited to 'StdLib/EfiSocketLib/Udp6.c') diff --git a/StdLib/EfiSocketLib/Udp6.c b/StdLib/EfiSocketLib/Udp6.c new file mode 100644 index 0000000000..187f0ad641 --- /dev/null +++ b/StdLib/EfiSocketLib/Udp6.c @@ -0,0 +1,1094 @@ +/** @file + Implement the UDP4 driver support for the socket layer. + + Copyright (c) 2011, 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. + +**/ + +#include "Socket.h" + + +/** + Get the local socket address + + This routine returns the IPv6 address and UDP port number associated + with the local socket. + + This routine is called by ::EslSocketGetLocalAddress to determine the + network address for the SOCK_DGRAM socket. + + @param [in] pPort Address of an ::ESL_PORT structure. + + @param [out] pSockAddr Network address to receive the local system address + +**/ +VOID +EslUdp6LocalAddressGet ( + IN ESL_PORT * pPort, + OUT struct sockaddr * pSockAddr + ) +{ + struct sockaddr_in6 * pLocalAddress; + ESL_UDP6_CONTEXT * pUdp6; + + DBG_ENTER ( ); + + // + // Return the local address + // + pUdp6 = &pPort->Context.Udp6; + pLocalAddress = (struct sockaddr_in6 *)pSockAddr; + pLocalAddress->sin6_family = AF_INET6; + pLocalAddress->sin6_port = SwapBytes16 ( pUdp6->ConfigData.StationPort ); + CopyMem ( &pLocalAddress->sin6_addr, + &pUdp6->ConfigData.StationAddress.Addr[0], + sizeof ( pLocalAddress->sin6_addr )); + + DBG_EXIT ( ); +} + + +/** + Set the local port address. + + This routine sets the local port address. + + This support routine is called by ::EslSocketPortAllocate. + + @param [in] pPort Address of an ESL_PORT structure + @param [in] pSockAddr Address of a sockaddr structure that contains the + connection point on the local machine. An IPv6 address + of INADDR_ANY specifies that the connection is made to + all of the network stacks on the platform. Specifying a + specific IPv6 address restricts the connection to the + network stack supporting that address. Specifying zero + for the port causes the network layer to assign a port + number from the dynamic range. Specifying a specific + port number causes the network layer to use that port. + + @param [in] bBindTest TRUE = run bind testing + + @retval EFI_SUCCESS The operation was successful + + **/ +EFI_STATUS +EslUdp6LocalAddressSet ( + IN ESL_PORT * pPort, + IN CONST struct sockaddr * pSockAddr, + IN BOOLEAN bBindTest + ) +{ + EFI_UDP6_CONFIG_DATA * pConfig; + CONST struct sockaddr_in6 * pIpAddress; + CONST UINT8 * pIPv6Address; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Set the local address + // + pIpAddress = (struct sockaddr_in6 *)pSockAddr; + pIPv6Address = (UINT8 *)&pIpAddress->sin6_addr; + pConfig = &pPort->Context.Udp6.ConfigData; + CopyMem ( &pConfig->StationAddress, + pIPv6Address, + sizeof ( pConfig->StationAddress )); + + // + // Validate the IP address + // + pConfig->StationPort = 0; + Status = bBindTest ? EslSocketBindTest ( pPort, EADDRNOTAVAIL ) + : EFI_SUCCESS; + if ( !EFI_ERROR ( Status )) { + // + // Set the port number + // + pConfig->StationPort = SwapBytes16 ( pIpAddress->sin6_port ); + + // + // Display the local address + // + DEBUG (( DEBUG_BIND, + "0x%08x: Port, Local UDP6 Address: [%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:%d\r\n", + pPort, + pConfig->StationAddress.Addr[0], + pConfig->StationAddress.Addr[1], + pConfig->StationAddress.Addr[2], + pConfig->StationAddress.Addr[3], + pConfig->StationAddress.Addr[4], + pConfig->StationAddress.Addr[5], + pConfig->StationAddress.Addr[6], + pConfig->StationAddress.Addr[7], + pConfig->StationAddress.Addr[8], + pConfig->StationAddress.Addr[9], + pConfig->StationAddress.Addr[10], + pConfig->StationAddress.Addr[11], + pConfig->StationAddress.Addr[12], + pConfig->StationAddress.Addr[13], + pConfig->StationAddress.Addr[14], + pConfig->StationAddress.Addr[15], + pConfig->StationPort )); + } + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Free a receive packet + + This routine performs the network specific operations necessary + to free a receive packet. + + This routine is called by ::EslSocketPortCloseTxDone to free a + receive packet. + + @param [in] pPacket Address of an ::ESL_PACKET structure. + @param [in, out] pRxBytes Address of the count of RX bytes + +**/ +VOID +EslUdp6PacketFree ( + IN ESL_PACKET * pPacket, + IN OUT size_t * pRxBytes + ) +{ + EFI_UDP6_RECEIVE_DATA * pRxData; + + DBG_ENTER ( ); + + // + // Account for the receive bytes + // + pRxData = pPacket->Op.Udp6Rx.pRxData; + *pRxBytes -= pRxData->DataLength; + + // + // Disconnect the buffer from the packet + // + pPacket->Op.Udp6Rx.pRxData = NULL; + + // + // Return the buffer to the UDP6 driver + // + gBS->SignalEvent ( pRxData->RecycleSignal ); + DBG_EXIT ( ); +} + + +/** + Initialize the network specific portions of an ::ESL_PORT structure. + + This routine initializes the network specific portions of an + ::ESL_PORT structure for use by the socket. + + This support routine is called by ::EslSocketPortAllocate + to connect the socket with the underlying network adapter + running the UDPv4 protocol. + + @param [in] pPort Address of an ESL_PORT structure + @param [in] DebugFlags Flags for debug messages + + @retval EFI_SUCCESS - Socket successfully created + + **/ +EFI_STATUS +EslUdp6PortAllocate ( + IN ESL_PORT * pPort, + IN UINTN DebugFlags + ) +{ + EFI_UDP6_CONFIG_DATA * pConfig; + ESL_SOCKET * pSocket; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Initialize the port + // + pSocket = pPort->pSocket; + pSocket->TxPacketOffset = OFFSET_OF ( ESL_PACKET, Op.Udp6Tx.TxData ); + pSocket->TxTokenEventOffset = OFFSET_OF ( ESL_IO_MGMT, Token.Udp6Tx.Event ); + pSocket->TxTokenOffset = OFFSET_OF ( EFI_UDP6_COMPLETION_TOKEN, Packet.TxData ); + + // + // Save the cancel, receive and transmit addresses + // + pPort->pfnConfigure = (PFN_NET_CONFIGURE)pPort->pProtocol.UDPv6->Configure; + pPort->pfnRxCancel = (PFN_NET_IO_START)pPort->pProtocol.UDPv6->Cancel; + pPort->pfnRxPoll = (PFN_NET_POLL)pPort->pProtocol.UDPv6->Poll; + pPort->pfnRxStart = (PFN_NET_IO_START)pPort->pProtocol.UDPv6->Receive; + pPort->pfnTxStart = (PFN_NET_IO_START)pPort->pProtocol.UDPv6->Transmit; + + // + // Do not drop packets + // + pConfig = &pPort->Context.Udp6.ConfigData; + pConfig->ReceiveTimeout = 0; + pConfig->ReceiveTimeout = pConfig->ReceiveTimeout; + + // + // Set the configuration flags + // + pConfig->AllowDuplicatePort = TRUE; + pConfig->AcceptAnyPort = FALSE; + pConfig->AcceptPromiscuous = FALSE; + pConfig->HopLimit = 255; + pConfig->TrafficClass = 0; + + Status = EFI_SUCCESS; + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Receive data from a network connection. + + This routine attempts to return buffered data to the caller. The + data is removed from the urgent queue if the message flag MSG_OOB + is specified, otherwise data is removed from the normal queue. + See the \ref ReceiveEngine section. + + This routine is called by ::EslSocketReceive to handle the network + specific receive operation to support SOCK_DGRAM sockets. + + @param [in] pPort Address of an ::ESL_PORT structure. + + @param [in] pPacket Address of an ::ESL_PACKET structure. + + @param [in] pbConsumePacket Address of a BOOLEAN indicating if the packet is to be consumed + + @param [in] BufferLength Length of the the buffer + + @param [in] pBuffer Address of a buffer to receive the data. + + @param [in] pDataLength Number of received data bytes in the buffer. + + @param [out] pAddress Network address to receive the remote system address + + @param [out] pSkipBytes Address to receive the number of bytes skipped + + @return Returns the address of the next free byte in the buffer. + + **/ +UINT8 * +EslUdp6Receive ( + IN ESL_PORT * pPort, + IN ESL_PACKET * pPacket, + IN BOOLEAN * pbConsumePacket, + IN size_t BufferLength, + IN UINT8 * pBuffer, + OUT size_t * pDataLength, + OUT struct sockaddr * pAddress, + OUT size_t * pSkipBytes + ) +{ + size_t DataBytes; + struct sockaddr_in6 * pRemoteAddress; + EFI_UDP6_RECEIVE_DATA * pRxData; + + DBG_ENTER ( ); + + pRxData = pPacket->Op.Udp6Rx.pRxData; + // + // Return the remote system address if requested + // + if ( NULL != pAddress ) { + // + // Build the remote address + // + DEBUG (( DEBUG_RX, + "Getting packet remote address: [%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:%d\r\n", + pRxData->UdpSession.SourceAddress.Addr[0], + pRxData->UdpSession.SourceAddress.Addr[1], + pRxData->UdpSession.SourceAddress.Addr[2], + pRxData->UdpSession.SourceAddress.Addr[3], + pRxData->UdpSession.SourceAddress.Addr[4], + pRxData->UdpSession.SourceAddress.Addr[5], + pRxData->UdpSession.SourceAddress.Addr[6], + pRxData->UdpSession.SourceAddress.Addr[7], + pRxData->UdpSession.SourceAddress.Addr[8], + pRxData->UdpSession.SourceAddress.Addr[9], + pRxData->UdpSession.SourceAddress.Addr[10], + pRxData->UdpSession.SourceAddress.Addr[11], + pRxData->UdpSession.SourceAddress.Addr[12], + pRxData->UdpSession.SourceAddress.Addr[13], + pRxData->UdpSession.SourceAddress.Addr[14], + pRxData->UdpSession.SourceAddress.Addr[15], + pRxData->UdpSession.SourcePort )); + pRemoteAddress = (struct sockaddr_in6 *)pAddress; + CopyMem ( &pRemoteAddress->sin6_addr, + &pRxData->UdpSession.SourceAddress.Addr[0], + sizeof ( pRemoteAddress->sin6_addr )); + pRemoteAddress->sin6_port = SwapBytes16 ( pRxData->UdpSession.SourcePort ); + } + + // + // Copy the received data + // + pBuffer = EslSocketCopyFragmentedBuffer ( pRxData->FragmentCount, + (EFI_IP4_FRAGMENT_DATA *)&pRxData->FragmentTable[0], + BufferLength, + pBuffer, + &DataBytes ); + + // + // Determine if the data is being read + // + if ( *pbConsumePacket ) { + // + // Display for the bytes consumed + // + DEBUG (( DEBUG_RX, + "0x%08x: Port account for 0x%08x bytes\r\n", + pPort, + DataBytes )); + + // + // Account for any discarded data + // + *pSkipBytes = pRxData->DataLength - DataBytes; + } + + // + // Return the data length and the buffer address + // + *pDataLength = DataBytes; + DBG_EXIT_HEX ( pBuffer ); + return pBuffer; +} + + +/** + Get the remote socket address + + This routine returns the address of the remote connection point + associated with the SOCK_DGRAM socket. + + This routine is called by ::EslSocketGetPeerAddress to detemine + the UDPv4 address and port number associated with the network adapter. + + @param [in] pPort Address of an ::ESL_PORT structure. + + @param [out] pAddress Network address to receive the remote system address + +**/ +VOID +EslUdp6RemoteAddressGet ( + IN ESL_PORT * pPort, + OUT struct sockaddr * pAddress + ) +{ + struct sockaddr_in6 * pRemoteAddress; + ESL_UDP6_CONTEXT * pUdp6; + + DBG_ENTER ( ); + + // + // Return the remote address + // + pUdp6 = &pPort->Context.Udp6; + pRemoteAddress = (struct sockaddr_in6 *)pAddress; + pRemoteAddress->sin6_family = AF_INET6; + pRemoteAddress->sin6_port = SwapBytes16 ( pUdp6->ConfigData.RemotePort ); + CopyMem ( &pRemoteAddress->sin6_addr, + &pUdp6->ConfigData.RemoteAddress.Addr[0], + sizeof ( pRemoteAddress->sin6_addr )); + + DBG_EXIT ( ); +} + + +/** + Set the remote address + + This routine sets the remote address in the port. + + This routine is called by ::EslSocketConnect to specify the + remote network address. + + @param [in] pPort Address of an ::ESL_PORT structure. + + @param [in] pSockAddr Network address of the remote system. + + @param [in] SockAddrLength Length in bytes of the network address. + + @retval EFI_SUCCESS The operation was successful + + **/ +EFI_STATUS +EslUdp6RemoteAddressSet ( + IN ESL_PORT * pPort, + IN CONST struct sockaddr * pSockAddr, + IN socklen_t SockAddrLength + ) +{ + CONST struct sockaddr_in6 * pRemoteAddress; + ESL_UDP6_CONTEXT * pUdp6; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Set the remote address + // + pUdp6 = &pPort->Context.Udp6; + pRemoteAddress = (struct sockaddr_in6 *)pSockAddr; + CopyMem ( &pUdp6->ConfigData.RemoteAddress, + &pRemoteAddress->sin6_addr, + sizeof ( pUdp6->ConfigData.RemoteAddress )); + pUdp6->ConfigData.RemotePort = SwapBytes16 ( pRemoteAddress->sin6_port ); + Status = EFI_SUCCESS; + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Process the receive completion + + This routine keeps the UDPv4 driver's buffer and queues it in + in FIFO order to the data queue. The UDP6 driver's buffer will + be returned by either ::EslUdp6Receive or ::EslSocketPortCloseTxDone. + See the \ref ReceiveEngine section. + + This routine is called by the UDPv4 driver when data is + received. + + @param [in] Event The receive completion event + + @param [in] pIo Address of an ::ESL_IO_MGMT structure + +**/ +VOID +EslUdp6RxComplete ( + IN EFI_EVENT Event, + IN ESL_IO_MGMT * pIo + ) +{ + size_t LengthInBytes; + ESL_PACKET * pPacket; + EFI_UDP6_RECEIVE_DATA * pRxData; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Get the operation status. + // + Status = pIo->Token.Udp6Rx.Status; + + // + // Get the packet length + // + pRxData = pIo->Token.Udp6Rx.Packet.RxData; + LengthInBytes = pRxData->DataLength; + + // + // +--------------------+ +-----------------------+ + // | ESL_IO_MGMT | | Data Buffer | + // | | | (Driver owned) | + // | +---------------+ +-----------------------+ + // | | Token | ^ + // | | Rx Event | | + // | | | +-----------------------+ + // | | RxData --> | EFI_UDP6_RECEIVE_DATA | + // +----+---------------+ | (Driver owned) | + // +-----------------------+ + // +--------------------+ ^ + // | ESL_PACKET | . + // | | . + // | +---------------+ . + // | | pRxData --> NULL ....... + // +----+---------------+ + // + // + // Save the data in the packet + // + pPacket = pIo->pPacket; + pPacket->Op.Udp6Rx.pRxData = pRxData; + + // + // Complete this request + // + EslSocketRxComplete ( pIo, Status, LengthInBytes, FALSE ); + DBG_EXIT ( ); +} + + +/** + Determine if the socket is configured. + + This routine uses the flag ESL_SOCKET::bConfigured to determine + if the network layer's configuration routine has been called. + This routine calls the bind and configuration routines if they + were not already called. After the port is configured, the + \ref ReceiveEngine is started. + + This routine is called by EslSocketIsConfigured to verify + that the socket is configured. + + @param [in] pSocket Address of an ::ESL_SOCKET structure + + @retval EFI_SUCCESS - The port is connected + @retval EFI_NOT_STARTED - The port is not connected + + **/ + EFI_STATUS + EslUdp6SocketIsConfigured ( + IN ESL_SOCKET * pSocket + ) +{ + EFI_UDP6_CONFIG_DATA * pConfigData; + ESL_PORT * pPort; + ESL_PORT * pNextPort; + ESL_UDP6_CONTEXT * pUdp6; + EFI_UDP6_PROTOCOL * pUdp6Protocol; + EFI_STATUS Status; + struct sockaddr_in6 LocalAddress; + + DBG_ENTER ( ); + + // + // Assume success + // + Status = EFI_SUCCESS; + + // + // Configure the port if necessary + // + if ( !pSocket->bConfigured ) { + // + // Fill in the port list if necessary + // + if ( NULL == pSocket->pPortList ) { + ZeroMem ( &LocalAddress, sizeof ( LocalAddress )); + LocalAddress.sin6_len = sizeof ( LocalAddress ); + LocalAddress.sin6_family = AF_INET6; + Status = EslSocketBind ( &pSocket->SocketProtocol, + (struct sockaddr *)&LocalAddress, + LocalAddress.sin6_len, + &pSocket->errno ); + } + + // + // Walk the port list + // + pPort = pSocket->pPortList; + while ( NULL != pPort ) { + // + // Attempt to configure the port + // + pNextPort = pPort->pLinkSocket; + pUdp6 = &pPort->Context.Udp6; + pUdp6Protocol = pPort->pProtocol.UDPv6; + pConfigData = &pUdp6->ConfigData; + DEBUG (( DEBUG_TX, + "0x%08x: pPort Configuring for [%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:%d --> [%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:%d\r\n", + pPort, + pConfigData->StationAddress.Addr[0], + pConfigData->StationAddress.Addr[1], + pConfigData->StationAddress.Addr[2], + pConfigData->StationAddress.Addr[3], + pConfigData->StationAddress.Addr[4], + pConfigData->StationAddress.Addr[5], + pConfigData->StationAddress.Addr[6], + pConfigData->StationAddress.Addr[7], + pConfigData->StationAddress.Addr[8], + pConfigData->StationAddress.Addr[9], + pConfigData->StationAddress.Addr[10], + pConfigData->StationAddress.Addr[11], + pConfigData->StationAddress.Addr[12], + pConfigData->StationAddress.Addr[13], + pConfigData->StationAddress.Addr[14], + pConfigData->StationAddress.Addr[15], + pConfigData->StationPort, + pConfigData->RemoteAddress.Addr[0], + pConfigData->RemoteAddress.Addr[1], + pConfigData->RemoteAddress.Addr[2], + pConfigData->RemoteAddress.Addr[3], + pConfigData->RemoteAddress.Addr[4], + pConfigData->RemoteAddress.Addr[5], + pConfigData->RemoteAddress.Addr[6], + pConfigData->RemoteAddress.Addr[7], + pConfigData->RemoteAddress.Addr[8], + pConfigData->RemoteAddress.Addr[9], + pConfigData->RemoteAddress.Addr[10], + pConfigData->RemoteAddress.Addr[11], + pConfigData->RemoteAddress.Addr[12], + pConfigData->RemoteAddress.Addr[13], + pConfigData->RemoteAddress.Addr[14], + pConfigData->RemoteAddress.Addr[15], + pConfigData->RemotePort )); + Status = pUdp6Protocol->Configure ( pUdp6Protocol, + pConfigData ); + if ( !EFI_ERROR ( Status )) { + // + // Update the configuration data + // + Status = pUdp6Protocol->GetModeData ( pUdp6Protocol, + pConfigData, + NULL, + NULL, + NULL ); + } + if ( EFI_ERROR ( Status )) { + DEBUG (( DEBUG_LISTEN, + "ERROR - Failed to configure the Udp6 port, Status: %r\r\n", + Status )); + switch ( Status ) { + case EFI_ACCESS_DENIED: + pSocket->errno = EACCES; + break; + + default: + case EFI_DEVICE_ERROR: + pSocket->errno = EIO; + break; + + case EFI_INVALID_PARAMETER: + pSocket->errno = EADDRNOTAVAIL; + break; + + case EFI_NO_MAPPING: + pSocket->errno = EAFNOSUPPORT; + break; + + case EFI_OUT_OF_RESOURCES: + pSocket->errno = ENOBUFS; + break; + + case EFI_UNSUPPORTED: + pSocket->errno = EOPNOTSUPP; + break; + } + } + else { + DEBUG (( DEBUG_TX, + "0x%08x: pPort Configured for [%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:%d --> [%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:%d\r\n", + pPort, + pConfigData->StationAddress.Addr[0], + pConfigData->StationAddress.Addr[1], + pConfigData->StationAddress.Addr[2], + pConfigData->StationAddress.Addr[3], + pConfigData->StationAddress.Addr[4], + pConfigData->StationAddress.Addr[5], + pConfigData->StationAddress.Addr[6], + pConfigData->StationAddress.Addr[7], + pConfigData->StationAddress.Addr[8], + pConfigData->StationAddress.Addr[9], + pConfigData->StationAddress.Addr[10], + pConfigData->StationAddress.Addr[11], + pConfigData->StationAddress.Addr[12], + pConfigData->StationAddress.Addr[13], + pConfigData->StationAddress.Addr[14], + pConfigData->StationAddress.Addr[15], + pConfigData->StationPort, + pConfigData->RemoteAddress.Addr[0], + pConfigData->RemoteAddress.Addr[1], + pConfigData->RemoteAddress.Addr[2], + pConfigData->RemoteAddress.Addr[3], + pConfigData->RemoteAddress.Addr[4], + pConfigData->RemoteAddress.Addr[5], + pConfigData->RemoteAddress.Addr[6], + pConfigData->RemoteAddress.Addr[7], + pConfigData->RemoteAddress.Addr[8], + pConfigData->RemoteAddress.Addr[9], + pConfigData->RemoteAddress.Addr[10], + pConfigData->RemoteAddress.Addr[11], + pConfigData->RemoteAddress.Addr[12], + pConfigData->RemoteAddress.Addr[13], + pConfigData->RemoteAddress.Addr[14], + pConfigData->RemoteAddress.Addr[15], + pConfigData->RemotePort )); + pPort->bConfigured = TRUE; + + // + // Start the first read on the port + // + EslSocketRxStart ( pPort ); + + // + // The socket is connected + // + pSocket->State = SOCKET_STATE_CONNECTED; + } + + // + // Set the next port + // + pPort = pNextPort; + } + + // + // Determine the configuration status + // + if ( NULL != pSocket->pPortList ) { + pSocket->bConfigured = TRUE; + } + } + + // + // Determine the socket configuration status + // + if ( !EFI_ERROR ( Status )) { + Status = pSocket->bConfigured ? EFI_SUCCESS : EFI_NOT_STARTED; + } + + // + // Return the port connected state. + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Buffer data for transmission over a network connection. + + This routine buffers data for the transmit engine in the normal + data queue. When the \ref TransmitEngine has resources, this + routine will start the transmission of the next buffer on the + network connection. + + This routine is called by ::EslSocketTransmit to buffer + data for transmission. The data is copied into a local buffer + freeing the application buffer for reuse upon return. When + necessary, this routine starts the transmit engine that + performs the data transmission on the network connection. The + transmit engine transmits the data a packet at a time over the + network connection. + + Transmission errors are returned during the next transmission or + during the close operation. Only buffering errors are returned + during the current transmission attempt. + + @param [in] pSocket Address of an ::ESL_SOCKET structure + + @param [in] Flags Message control flags + + @param [in] BufferLength Length of the the buffer + + @param [in] pBuffer Address of a buffer to receive the data. + + @param [in] pDataLength Number of received data bytes in the buffer. + + @param [in] pAddress Network address of the remote system address + + @param [in] AddressLength Length of the remote network address structure + + @retval EFI_SUCCESS - Socket data successfully buffered + +**/ +EFI_STATUS +EslUdp6TxBuffer ( + IN ESL_SOCKET * pSocket, + IN int Flags, + IN size_t BufferLength, + IN CONST UINT8 * pBuffer, + OUT size_t * pDataLength, + IN const struct sockaddr * pAddress, + IN socklen_t AddressLength + ) +{ + ESL_PACKET * pPacket; + ESL_PACKET * pPreviousPacket; + ESL_PORT * pPort; + const struct sockaddr_in6 * pRemoteAddress; + ESL_UDP6_CONTEXT * pUdp6; + size_t * pTxBytes; + ESL_UDP6_TX_DATA * pTxData; + EFI_STATUS Status; + EFI_TPL TplPrevious; + + DBG_ENTER ( ); + + // + // Assume failure + // + Status = EFI_UNSUPPORTED; + pSocket->errno = ENOTCONN; + *pDataLength = 0; + + // + // Verify that the socket is connected + // + if ( SOCKET_STATE_CONNECTED == pSocket->State ) { + // + // Locate the port + // + pPort = pSocket->pPortList; + if ( NULL != pPort ) { + // + // Determine the queue head + // + pUdp6 = &pPort->Context.Udp6; + pTxBytes = &pSocket->TxBytes; + + // + // Verify that there is enough room to buffer another + // transmit operation + // + if ( pSocket->MaxTxBuf > *pTxBytes ) { + // + // Attempt to allocate the packet + // + Status = EslSocketPacketAllocate ( &pPacket, + sizeof ( pPacket->Op.Udp6Tx ) + - sizeof ( pPacket->Op.Udp6Tx.Buffer ) + + BufferLength, + 0, + DEBUG_TX ); + if ( !EFI_ERROR ( Status )) { + // + // Initialize the transmit operation + // + pTxData = &pPacket->Op.Udp6Tx; + pTxData->TxData.UdpSessionData = NULL; + pTxData->TxData.DataLength = (UINT32) BufferLength; + pTxData->TxData.FragmentCount = 1; + pTxData->TxData.FragmentTable[0].FragmentLength = (UINT32) BufferLength; + pTxData->TxData.FragmentTable[0].FragmentBuffer = &pPacket->Op.Udp6Tx.Buffer[0]; + + // + // Set the remote system address if necessary + // + pTxData->TxData.UdpSessionData = NULL; + if ( NULL != pAddress ) { + pRemoteAddress = (const struct sockaddr_in6 *)pAddress; + CopyMem ( &pTxData->Session.SourceAddress, + &pUdp6->ConfigData.StationAddress, + sizeof ( pTxData->Session.SourceAddress )); + pTxData->Session.SourcePort = 0; + CopyMem ( &pTxData->Session.DestinationAddress, + &pRemoteAddress->sin6_addr, + sizeof ( pTxData->Session.DestinationAddress )); + pTxData->Session.DestinationPort = SwapBytes16 ( pRemoteAddress->sin6_port ); + + // + // Use the remote system address when sending this packet + // + pTxData->TxData.UdpSessionData = &pTxData->Session; + } + + // + // Copy the data into the buffer + // + CopyMem ( &pPacket->Op.Udp6Tx.Buffer[0], + pBuffer, + BufferLength ); + + // + // Synchronize with the socket layer + // + RAISE_TPL ( TplPrevious, TPL_SOCKETS ); + + // + // Stop transmission after an error + // + if ( !EFI_ERROR ( pSocket->TxError )) { + // + // Display the request + // + DEBUG (( DEBUG_TX, + "Send %d %s bytes from 0x%08x\r\n", + BufferLength, + pBuffer )); + + // + // Queue the data for transmission + // + pPacket->pNext = NULL; + pPreviousPacket = pSocket->pTxPacketListTail; + if ( NULL == pPreviousPacket ) { + pSocket->pTxPacketListHead = pPacket; + } + else { + pPreviousPacket->pNext = pPacket; + } + pSocket->pTxPacketListTail = pPacket; + DEBUG (( DEBUG_TX, + "0x%08x: Packet on transmit list\r\n", + pPacket )); + + // + // Account for the buffered data + // + *pTxBytes += BufferLength; + *pDataLength = BufferLength; + + // + // Start the transmit engine if it is idle + // + if ( NULL != pPort->pTxFree ) { + EslSocketTxStart ( pPort, + &pSocket->pTxPacketListHead, + &pSocket->pTxPacketListTail, + &pPort->pTxActive, + &pPort->pTxFree ); + } + } + else { + // + // Previous transmit error + // Stop transmission + // + Status = pSocket->TxError; + pSocket->errno = EIO; + + // + // Free the packet + // + EslSocketPacketFree ( pPacket, DEBUG_TX ); + } + + // + // Release the socket layer synchronization + // + RESTORE_TPL ( TplPrevious ); + } + else { + // + // Packet allocation failed + // + pSocket->errno = ENOMEM; + } + } + else { + // + // Not enough buffer space available + // + pSocket->errno = EAGAIN; + Status = EFI_NOT_READY; + } + } + } + + // + // Return the operation status + // + DBG_EXIT_STATUS ( Status ); + return Status; +} + + +/** + Process the transmit completion + + This routine use ::EslSocketTxComplete to perform the transmit + completion processing for data packets. + + This routine is called by the UDPv4 network layer when a data + transmit request completes. + + @param [in] Event The normal transmit completion event + + @param [in] pIo Address of an ::ESL_IO_MGMT structure + +**/ +VOID +EslUdp6TxComplete ( + IN EFI_EVENT Event, + IN ESL_IO_MGMT * pIo + ) +{ + UINT32 LengthInBytes; + ESL_PORT * pPort; + ESL_PACKET * pPacket; + ESL_SOCKET * pSocket; + EFI_STATUS Status; + + DBG_ENTER ( ); + + // + // Locate the active transmit packet + // + pPacket = pIo->pPacket; + pPort = pIo->pPort; + pSocket = pPort->pSocket; + + // + // Get the transmit length and status + // + LengthInBytes = pPacket->Op.Udp6Tx.TxData.DataLength; + pSocket->TxBytes -= LengthInBytes; + Status = pIo->Token.Udp6Tx.Status; + + // + // Complete the transmit operation + // + EslSocketTxComplete ( pIo, + LengthInBytes, + Status, + "UDP ", + &pSocket->pTxPacketListHead, + &pSocket->pTxPacketListTail, + &pPort->pTxActive, + &pPort->pTxFree ); + DBG_EXIT ( ); +} + + +/** + Interface between the socket layer and the network specific + code that supports SOCK_DGRAM sockets over UDPv4. +**/ +CONST ESL_PROTOCOL_API cEslUdp6Api = { + "UDPv6", + IPPROTO_UDP, + OFFSET_OF ( ESL_PORT, Context.Udp6.ConfigData ), + OFFSET_OF ( ESL_LAYER, pUdp6List ), + sizeof ( struct sockaddr_in6 ), + sizeof ( struct sockaddr_in6 ), + AF_INET6, + sizeof (((ESL_PACKET *)0 )->Op.Udp6Rx ), + sizeof (((ESL_PACKET *)0 )->Op.Udp6Rx ), + OFFSET_OF ( ESL_IO_MGMT, Token.Udp6Rx.Packet.RxData ), + FALSE, + EADDRINUSE, + NULL, // Accept + NULL, // ConnectPoll + NULL, // ConnectStart + EslUdp6SocketIsConfigured, + EslUdp6LocalAddressGet, + EslUdp6LocalAddressSet, + NULL, // Listen + NULL, // OptionGet + NULL, // OptionSet + EslUdp6PacketFree, + EslUdp6PortAllocate, + NULL, // PortClose, + NULL, // PortCloseOp + TRUE, + EslUdp6Receive, + EslUdp6RemoteAddressGet, + EslUdp6RemoteAddressSet, + EslUdp6RxComplete, + NULL, // RxStart + EslUdp6TxBuffer, + EslUdp6TxComplete, + NULL // TxOobComplete +}; -- cgit v1.2.3