summaryrefslogtreecommitdiff
path: root/StdLib/EfiSocketLib/Udp6.c
diff options
context:
space:
mode:
Diffstat (limited to 'StdLib/EfiSocketLib/Udp6.c')
-rw-r--r--StdLib/EfiSocketLib/Udp6.c1094
1 files changed, 1094 insertions, 0 deletions
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
+};