| /** @file | |
| Implement the UDP4 driver support for the socket layer. | |
| Copyright (c) 2011 - 2015, 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 IPv4 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 | |
| EslUdp4LocalAddressGet ( | |
| IN ESL_PORT * pPort, | |
| OUT struct sockaddr * pSockAddr | |
| ) | |
| { | |
| struct sockaddr_in * pLocalAddress; | |
| ESL_UDP4_CONTEXT * pUdp4; | |
| DBG_ENTER ( ); | |
| // | |
| // Return the local address | |
| // | |
| pUdp4 = &pPort->Context.Udp4; | |
| pLocalAddress = (struct sockaddr_in *)pSockAddr; | |
| pLocalAddress->sin_family = AF_INET; | |
| pLocalAddress->sin_port = SwapBytes16 ( pUdp4->ConfigData.StationPort ); | |
| CopyMem ( &pLocalAddress->sin_addr, | |
| &pUdp4->ConfigData.StationAddress.Addr[0], | |
| sizeof ( pLocalAddress->sin_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 IPv4 address | |
| of INADDR_ANY specifies that the connection is made to | |
| all of the network stacks on the platform. Specifying a | |
| specific IPv4 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 | |
| EslUdp4LocalAddressSet ( | |
| IN ESL_PORT * pPort, | |
| IN CONST struct sockaddr * pSockAddr, | |
| IN BOOLEAN bBindTest | |
| ) | |
| { | |
| EFI_UDP4_CONFIG_DATA * pConfig; | |
| CONST struct sockaddr_in * pIpAddress; | |
| CONST UINT8 * pIpv4Address; | |
| EFI_STATUS Status; | |
| DBG_ENTER ( ); | |
| // | |
| // Validate the address | |
| // | |
| pIpAddress = (struct sockaddr_in *)pSockAddr; | |
| if ( INADDR_BROADCAST == pIpAddress->sin_addr.s_addr ) { | |
| // | |
| // The local address must not be the broadcast address | |
| // | |
| Status = EFI_INVALID_PARAMETER; | |
| pPort->pSocket->errno = EADDRNOTAVAIL; | |
| } | |
| else { | |
| // | |
| // Set the local address | |
| // | |
| pIpAddress = (struct sockaddr_in *)pSockAddr; | |
| pIpv4Address = (UINT8 *)&pIpAddress->sin_addr.s_addr; | |
| pConfig = &pPort->Context.Udp4.ConfigData; | |
| pConfig->StationAddress.Addr[0] = pIpv4Address[0]; | |
| pConfig->StationAddress.Addr[1] = pIpv4Address[1]; | |
| pConfig->StationAddress.Addr[2] = pIpv4Address[2]; | |
| pConfig->StationAddress.Addr[3] = pIpv4Address[3]; | |
| // | |
| // Determine if the default address is used | |
| // | |
| pConfig->UseDefaultAddress = (BOOLEAN)( 0 == pIpAddress->sin_addr.s_addr ); | |
| // | |
| // Set the subnet mask | |
| // | |
| if ( pConfig->UseDefaultAddress ) { | |
| pConfig->SubnetMask.Addr[0] = 0; | |
| pConfig->SubnetMask.Addr[1] = 0; | |
| pConfig->SubnetMask.Addr[2] = 0; | |
| pConfig->SubnetMask.Addr[3] = 0; | |
| } | |
| else { | |
| pConfig->SubnetMask.Addr[0] = 0xff; | |
| pConfig->SubnetMask.Addr[1] = ( 128 <= pConfig->StationAddress.Addr[0]) ? 0xff : 0; | |
| pConfig->SubnetMask.Addr[2] = ( 192 <= pConfig->StationAddress.Addr[0]) ? 0xff : 0; | |
| pConfig->SubnetMask.Addr[3] = ( 224 <= pConfig->StationAddress.Addr[0]) ? 0xff : 0; | |
| } | |
| // | |
| // 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->sin_port ); | |
| // | |
| // Display the local address | |
| // | |
| DEBUG (( DEBUG_BIND, | |
| "0x%08x: Port, Local UDP4 Address: %d.%d.%d.%d:%d\r\n", | |
| pPort, | |
| pConfig->StationAddress.Addr[0], | |
| pConfig->StationAddress.Addr[1], | |
| pConfig->StationAddress.Addr[2], | |
| pConfig->StationAddress.Addr[3], | |
| 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 | |
| EslUdp4PacketFree ( | |
| IN ESL_PACKET * pPacket, | |
| IN OUT size_t * pRxBytes | |
| ) | |
| { | |
| EFI_UDP4_RECEIVE_DATA * pRxData; | |
| DBG_ENTER ( ); | |
| // | |
| // Account for the receive bytes | |
| // | |
| pRxData = pPacket->Op.Udp4Rx.pRxData; | |
| *pRxBytes -= pRxData->DataLength; | |
| // | |
| // Disconnect the buffer from the packet | |
| // | |
| pPacket->Op.Udp4Rx.pRxData = NULL; | |
| // | |
| // Return the buffer to the UDP4 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 | |
| EslUdp4PortAllocate ( | |
| IN ESL_PORT * pPort, | |
| IN UINTN DebugFlags | |
| ) | |
| { | |
| EFI_UDP4_CONFIG_DATA * pConfig; | |
| ESL_SOCKET * pSocket; | |
| EFI_STATUS Status; | |
| DBG_ENTER ( ); | |
| // | |
| // Initialize the port | |
| // | |
| pSocket = pPort->pSocket; | |
| pSocket->TxPacketOffset = OFFSET_OF ( ESL_PACKET, Op.Udp4Tx.TxData ); | |
| pSocket->TxTokenEventOffset = OFFSET_OF ( ESL_IO_MGMT, Token.Udp4Tx.Event ); | |
| pSocket->TxTokenOffset = OFFSET_OF ( EFI_UDP4_COMPLETION_TOKEN, Packet.TxData ); | |
| // | |
| // Save the cancel, receive and transmit addresses | |
| // | |
| pPort->pfnConfigure = (PFN_NET_CONFIGURE)pPort->pProtocol.UDPv4->Configure; | |
| pPort->pfnRxCancel = (PFN_NET_IO_START)pPort->pProtocol.UDPv4->Cancel; | |
| pPort->pfnRxPoll = (PFN_NET_POLL)pPort->pProtocol.UDPv4->Poll; | |
| pPort->pfnRxStart = (PFN_NET_IO_START)pPort->pProtocol.UDPv4->Receive; | |
| pPort->pfnTxStart = (PFN_NET_IO_START)pPort->pProtocol.UDPv4->Transmit; | |
| // | |
| // Set the configuration flags | |
| // | |
| pConfig = &pPort->Context.Udp4.ConfigData; | |
| pConfig->TimeToLive = 255; | |
| pConfig->AcceptAnyPort = FALSE; | |
| pConfig->AcceptBroadcast = FALSE; | |
| pConfig->AcceptPromiscuous = FALSE; | |
| pConfig->AllowDuplicatePort = TRUE; | |
| pConfig->DoNotFragment = FALSE; | |
| 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 * | |
| EslUdp4Receive ( | |
| 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_in * pRemoteAddress; | |
| EFI_UDP4_RECEIVE_DATA * pRxData; | |
| DBG_ENTER ( ); | |
| pRxData = pPacket->Op.Udp4Rx.pRxData; | |
| // | |
| // Return the remote system address if requested | |
| // | |
| if ( NULL != pAddress ) { | |
| // | |
| // Build the remote address | |
| // | |
| DEBUG (( DEBUG_RX, | |
| "Getting packet remote address: %d.%d.%d.%d:%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.SourcePort )); | |
| pRemoteAddress = (struct sockaddr_in *)pAddress; | |
| CopyMem ( &pRemoteAddress->sin_addr, | |
| &pRxData->UdpSession.SourceAddress.Addr[0], | |
| sizeof ( pRemoteAddress->sin_addr )); | |
| pRemoteAddress->sin_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 | |
| EslUdp4RemoteAddressGet ( | |
| IN ESL_PORT * pPort, | |
| OUT struct sockaddr * pAddress | |
| ) | |
| { | |
| struct sockaddr_in * pRemoteAddress; | |
| ESL_UDP4_CONTEXT * pUdp4; | |
| DBG_ENTER ( ); | |
| // | |
| // Return the remote address | |
| // | |
| pUdp4 = &pPort->Context.Udp4; | |
| pRemoteAddress = (struct sockaddr_in *)pAddress; | |
| pRemoteAddress->sin_family = AF_INET; | |
| pRemoteAddress->sin_port = SwapBytes16 ( pUdp4->ConfigData.RemotePort ); | |
| CopyMem ( &pRemoteAddress->sin_addr, | |
| &pUdp4->ConfigData.RemoteAddress.Addr[0], | |
| sizeof ( pRemoteAddress->sin_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 | |
| EslUdp4RemoteAddressSet ( | |
| IN ESL_PORT * pPort, | |
| IN CONST struct sockaddr * pSockAddr, | |
| IN socklen_t SockAddrLength | |
| ) | |
| { | |
| CONST struct sockaddr_in * pRemoteAddress; | |
| ESL_UDP4_CONTEXT * pUdp4; | |
| EFI_STATUS Status; | |
| DBG_ENTER ( ); | |
| // | |
| // Set the remote address | |
| // | |
| pUdp4 = &pPort->Context.Udp4; | |
| pRemoteAddress = (struct sockaddr_in *)pSockAddr; | |
| pUdp4->ConfigData.RemoteAddress.Addr[0] = (UINT8)( pRemoteAddress->sin_addr.s_addr ); | |
| pUdp4->ConfigData.RemoteAddress.Addr[1] = (UINT8)( pRemoteAddress->sin_addr.s_addr >> 8 ); | |
| pUdp4->ConfigData.RemoteAddress.Addr[2] = (UINT8)( pRemoteAddress->sin_addr.s_addr >> 16 ); | |
| pUdp4->ConfigData.RemoteAddress.Addr[3] = (UINT8)( pRemoteAddress->sin_addr.s_addr >> 24 ); | |
| pUdp4->ConfigData.RemotePort = SwapBytes16 ( pRemoteAddress->sin_port ); | |
| pPort->pSocket->bAddressSet = TRUE; | |
| 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 UDP4 driver's buffer will | |
| be returned by either ::EslUdp4Receive 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 | |
| EslUdp4RxComplete ( | |
| IN EFI_EVENT Event, | |
| IN ESL_IO_MGMT * pIo | |
| ) | |
| { | |
| size_t LengthInBytes; | |
| ESL_PACKET * pPacket; | |
| EFI_UDP4_RECEIVE_DATA * pRxData; | |
| EFI_STATUS Status; | |
| DBG_ENTER ( ); | |
| // | |
| // Get the operation status. | |
| // | |
| Status = pIo->Token.Udp4Rx.Status; | |
| // | |
| // Get the packet length | |
| // | |
| pRxData = pIo->Token.Udp4Rx.Packet.RxData; | |
| LengthInBytes = pRxData->DataLength; | |
| // | |
| // +--------------------+ +-----------------------+ | |
| // | ESL_IO_MGMT | | Data Buffer | | |
| // | | | (Driver owned) | | |
| // | +---------------+ +-----------------------+ | |
| // | | Token | ^ | |
| // | | Rx Event | | | |
| // | | | +-----------------------+ | |
| // | | RxData --> | EFI_UDP4_RECEIVE_DATA | | |
| // +----+---------------+ | (Driver owned) | | |
| // +-----------------------+ | |
| // +--------------------+ ^ | |
| // | ESL_PACKET | . | |
| // | | . | |
| // | +---------------+ . | |
| // | | pRxData --> NULL ....... | |
| // +----+---------------+ | |
| // | |
| // | |
| // Save the data in the packet | |
| // | |
| pPacket = pIo->pPacket; | |
| pPacket->Op.Udp4Rx.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 | |
| EslUdp4SocketIsConfigured ( | |
| IN ESL_SOCKET * pSocket | |
| ) | |
| { | |
| EFI_UDP4_CONFIG_DATA * pConfigData; | |
| ESL_PORT * pPort; | |
| ESL_PORT * pNextPort; | |
| ESL_UDP4_CONTEXT * pUdp4; | |
| EFI_UDP4_PROTOCOL * pUdp4Protocol; | |
| EFI_STATUS Status; | |
| struct sockaddr_in LocalAddress; | |
| DBG_ENTER ( ); | |
| // | |
| // Assume success | |
| // | |
| Status = EFI_SUCCESS; | |
| // | |
| // Configure the port if necessary | |
| // | |
| if ( !pSocket->bConfigured ) { | |
| // | |
| // Fill in the port list if necessary | |
| // | |
| pSocket->errno = ENETDOWN; | |
| if ( NULL == pSocket->pPortList ) { | |
| LocalAddress.sin_len = sizeof ( LocalAddress ); | |
| LocalAddress.sin_family = AF_INET; | |
| LocalAddress.sin_addr.s_addr = 0; | |
| LocalAddress.sin_port = 0; | |
| Status = EslSocketBind ( &pSocket->SocketProtocol, | |
| (struct sockaddr *)&LocalAddress, | |
| LocalAddress.sin_len, | |
| &pSocket->errno ); | |
| } | |
| // | |
| // Walk the port list | |
| // | |
| pPort = pSocket->pPortList; | |
| while ( NULL != pPort ) { | |
| // | |
| // Attempt to configure the port | |
| // | |
| pNextPort = pPort->pLinkSocket; | |
| pUdp4 = &pPort->Context.Udp4; | |
| pUdp4Protocol = pPort->pProtocol.UDPv4; | |
| pConfigData = &pUdp4->ConfigData; | |
| DEBUG (( DEBUG_TX, | |
| "0x%08x: pPort Configuring for %d.%d.%d.%d:%d --> %d.%d.%d.%d:%d\r\n", | |
| pPort, | |
| pConfigData->StationAddress.Addr[0], | |
| pConfigData->StationAddress.Addr[1], | |
| pConfigData->StationAddress.Addr[2], | |
| pConfigData->StationAddress.Addr[3], | |
| pConfigData->StationPort, | |
| pConfigData->RemoteAddress.Addr[0], | |
| pConfigData->RemoteAddress.Addr[1], | |
| pConfigData->RemoteAddress.Addr[2], | |
| pConfigData->RemoteAddress.Addr[3], | |
| pConfigData->RemotePort )); | |
| Status = pUdp4Protocol->Configure ( pUdp4Protocol, | |
| pConfigData ); | |
| if ( !EFI_ERROR ( Status )) { | |
| // | |
| // Update the configuration data | |
| // | |
| Status = pUdp4Protocol->GetModeData ( pUdp4Protocol, | |
| pConfigData, | |
| NULL, | |
| NULL, | |
| NULL ); | |
| } | |
| if ( EFI_ERROR ( Status )) { | |
| if ( !pSocket->bConfigured ) { | |
| DEBUG (( DEBUG_LISTEN, | |
| "ERROR - Failed to configure the Udp4 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 %d.%d.%d.%d:%d --> %d.%d.%d.%d:%d\r\n", | |
| pPort, | |
| pConfigData->StationAddress.Addr[0], | |
| pConfigData->StationAddress.Addr[1], | |
| pConfigData->StationAddress.Addr[2], | |
| pConfigData->StationAddress.Addr[3], | |
| pConfigData->StationPort, | |
| pConfigData->RemoteAddress.Addr[0], | |
| pConfigData->RemoteAddress.Addr[1], | |
| pConfigData->RemoteAddress.Addr[2], | |
| pConfigData->RemoteAddress.Addr[3], | |
| pConfigData->RemotePort )); | |
| pPort->bConfigured = TRUE; | |
| pSocket->bConfigured = TRUE; | |
| // | |
| // Start the first read on the port | |
| // | |
| EslSocketRxStart ( pPort ); | |
| // | |
| // The socket is connected | |
| // | |
| pSocket->State = SOCKET_STATE_CONNECTED; | |
| pSocket->errno = 0; | |
| } | |
| // | |
| // Set the next port | |
| // | |
| pPort = pNextPort; | |
| } | |
| } | |
| // | |
| // Determine the socket configuration 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 | |
| EslUdp4TxBuffer ( | |
| 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_in * pRemoteAddress; | |
| ESL_UDP4_CONTEXT * pUdp4; | |
| size_t * pTxBytes; | |
| ESL_UDP4_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 ) { | |
| // | |
| // Verify that there is enough room to buffer another | |
| // transmit operation | |
| // | |
| pTxBytes = &pSocket->TxBytes; | |
| if ( pSocket->MaxTxBuf > *pTxBytes ) { | |
| // | |
| // Locate the port | |
| // | |
| pPort = pSocket->pPortList; | |
| while ( NULL != pPort ) { | |
| // | |
| // Determine the queue head | |
| // | |
| pUdp4 = &pPort->Context.Udp4; | |
| // | |
| // Attempt to allocate the packet | |
| // | |
| Status = EslSocketPacketAllocate ( &pPacket, | |
| sizeof ( pPacket->Op.Udp4Tx ) | |
| - sizeof ( pPacket->Op.Udp4Tx.Buffer ) | |
| + BufferLength, | |
| 0, | |
| DEBUG_TX ); | |
| if ( !EFI_ERROR ( Status )) { | |
| // | |
| // Initialize the transmit operation | |
| // | |
| pTxData = &pPacket->Op.Udp4Tx; | |
| pTxData->TxData.GatewayAddress = NULL; | |
| 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.Udp4Tx.Buffer[0]; | |
| pTxData->RetransmitCount = 0; | |
| // | |
| // Set the remote system address if necessary | |
| // | |
| pTxData->TxData.UdpSessionData = NULL; | |
| if ( NULL != pAddress ) { | |
| pRemoteAddress = (const struct sockaddr_in *)pAddress; | |
| pTxData->Session.SourceAddress.Addr[0] = pUdp4->ConfigData.StationAddress.Addr[0]; | |
| pTxData->Session.SourceAddress.Addr[1] = pUdp4->ConfigData.StationAddress.Addr[1]; | |
| pTxData->Session.SourceAddress.Addr[2] = pUdp4->ConfigData.StationAddress.Addr[2]; | |
| pTxData->Session.SourceAddress.Addr[3] = pUdp4->ConfigData.StationAddress.Addr[3]; | |
| pTxData->Session.SourcePort = 0; | |
| pTxData->Session.DestinationAddress.Addr[0] = (UINT8)pRemoteAddress->sin_addr.s_addr; | |
| pTxData->Session.DestinationAddress.Addr[1] = (UINT8)( pRemoteAddress->sin_addr.s_addr >> 8 ); | |
| pTxData->Session.DestinationAddress.Addr[2] = (UINT8)( pRemoteAddress->sin_addr.s_addr >> 16 ); | |
| pTxData->Session.DestinationAddress.Addr[3] = (UINT8)( pRemoteAddress->sin_addr.s_addr >> 24 ); | |
| pTxData->Session.DestinationPort = SwapBytes16 ( pRemoteAddress->sin_port ); | |
| // | |
| // Use the remote system address when sending this packet | |
| // | |
| pTxData->TxData.UdpSessionData = &pTxData->Session; | |
| } | |
| // | |
| // Copy the data into the buffer | |
| // | |
| CopyMem ( &pPacket->Op.Udp4Tx.Buffer[0], | |
| pBuffer, | |
| BufferLength ); | |
| // | |
| // Synchronize with the socket layer | |
| // | |
| RAISE_TPL ( TplPrevious, TPL_SOCKETS ); | |
| // | |
| // Display the request | |
| // | |
| DEBUG (( DEBUG_TX, | |
| "Send %d bytes from 0x%08x to %d.%d.%d.%d:%d\r\n", | |
| BufferLength, | |
| pBuffer, | |
| pTxData->Session.DestinationAddress.Addr[0], | |
| pTxData->Session.DestinationAddress.Addr[1], | |
| pTxData->Session.DestinationAddress.Addr[2], | |
| pTxData->Session.DestinationAddress.Addr[3], | |
| pTxData->Session.DestinationPort )); | |
| // | |
| // 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 ) { | |
| pPacket = pSocket->pTxPacketListHead; | |
| EslSocketTxStart ( pPort, | |
| &pSocket->pTxPacketListHead, | |
| &pSocket->pTxPacketListTail, | |
| &pPort->pTxActive, | |
| &pPort->pTxFree ); | |
| // | |
| // Ignore any transmit error | |
| // | |
| if ( EFI_ERROR ( pSocket->TxError )) { | |
| DEBUG (( DEBUG_TX, | |
| "0x%08x: Transmit error, Packet: 0x%08x, Status: %r\r\n", | |
| pPort, | |
| pPacket, | |
| pSocket->TxError )); | |
| } | |
| pSocket->TxError = EFI_SUCCESS; | |
| } | |
| // | |
| // Release the socket layer synchronization | |
| // | |
| RESTORE_TPL ( TplPrevious ); | |
| } | |
| else { | |
| // | |
| // Packet allocation failed | |
| // | |
| pSocket->errno = ENOMEM; | |
| break; | |
| } | |
| // | |
| // Set the next port | |
| // | |
| pPort = pPort->pLinkSocket; | |
| } | |
| } | |
| 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 | |
| EslUdp4TxComplete ( | |
| 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.Udp4Tx.TxData.DataLength; | |
| pSocket->TxBytes -= LengthInBytes; | |
| Status = pIo->Token.Udp4Tx.Status; | |
| // | |
| // Ignore the transmit error | |
| // | |
| if ( EFI_ERROR ( Status )) { | |
| DEBUG (( DEBUG_TX, | |
| "0x%08x: Transmit completion error, Packet: 0x%08x, Status: %r\r\n", | |
| pPort, | |
| pPacket, | |
| Status )); | |
| Status = EFI_SUCCESS; | |
| } | |
| // | |
| // Complete the transmit operation | |
| // | |
| EslSocketTxComplete ( pIo, | |
| LengthInBytes, | |
| Status, | |
| "UDP ", | |
| &pSocket->pTxPacketListHead, | |
| &pSocket->pTxPacketListTail, | |
| &pPort->pTxActive, | |
| &pPort->pTxFree ); | |
| DBG_EXIT ( ); | |
| } | |
| /** | |
| Verify the adapter's IP address | |
| This support routine is called by EslSocketBindTest. | |
| @param [in] pPort Address of an ::ESL_PORT structure. | |
| @param [in] pConfigData Address of the configuration data | |
| @retval EFI_SUCCESS - The IP address is valid | |
| @retval EFI_NOT_STARTED - The IP address is invalid | |
| **/ | |
| EFI_STATUS | |
| EslUdp4VerifyLocalIpAddress ( | |
| IN ESL_PORT * pPort, | |
| IN EFI_UDP4_CONFIG_DATA * pConfigData | |
| ) | |
| { | |
| UINTN DataSize; | |
| EFI_IP4_CONFIG2_INTERFACE_INFO * pIfInfo; | |
| EFI_IP4_CONFIG2_PROTOCOL * pIpConfig2Protocol; | |
| ESL_SERVICE * pService; | |
| EFI_STATUS Status; | |
| DBG_ENTER ( ); | |
| // | |
| // Use break instead of goto | |
| // | |
| pIfInfo = NULL; | |
| for ( ; ; ) { | |
| // | |
| // Determine if the IP address is specified | |
| // | |
| DEBUG (( DEBUG_BIND, | |
| "UseDefaultAddress: %s\r\n", | |
| pConfigData->UseDefaultAddress ? L"TRUE" : L"FALSE" )); | |
| DEBUG (( DEBUG_BIND, | |
| "Requested IP address: %d.%d.%d.%d\r\n", | |
| pConfigData->StationAddress.Addr [ 0 ], | |
| pConfigData->StationAddress.Addr [ 1 ], | |
| pConfigData->StationAddress.Addr [ 2 ], | |
| pConfigData->StationAddress.Addr [ 3 ])); | |
| if ( pConfigData->UseDefaultAddress | |
| || (( 0 == pConfigData->StationAddress.Addr [ 0 ]) | |
| && ( 0 == pConfigData->StationAddress.Addr [ 1 ]) | |
| && ( 0 == pConfigData->StationAddress.Addr [ 2 ]) | |
| && ( 0 == pConfigData->StationAddress.Addr [ 3 ]))) | |
| { | |
| Status = EFI_SUCCESS; | |
| break; | |
| } | |
| // | |
| // Open the configuration protocol | |
| // | |
| pService = pPort->pService; | |
| Status = gBS->OpenProtocol ( | |
| pService->Controller, | |
| &gEfiIp4Config2ProtocolGuid, | |
| (VOID **)&pIpConfig2Protocol, | |
| NULL, | |
| NULL, | |
| EFI_OPEN_PROTOCOL_GET_PROTOCOL | |
| ); | |
| if ( EFI_ERROR ( Status )) { | |
| DEBUG (( DEBUG_ERROR, | |
| "ERROR - IP Configuration Protocol not available, Status: %r\r\n", | |
| Status )); | |
| break; | |
| } | |
| // | |
| // Get the interface information size | |
| // | |
| DataSize = 0; | |
| Status = pIpConfig2Protocol->GetData ( | |
| pIpConfig2Protocol, | |
| Ip4Config2DataTypeInterfaceInfo, | |
| &DataSize, | |
| NULL | |
| ); | |
| if ( EFI_BUFFER_TOO_SMALL != Status ) { | |
| DEBUG (( DEBUG_ERROR, | |
| "ERROR - Failed to get the interface information size, Status: %r\r\n", | |
| Status )); | |
| break; | |
| } | |
| // | |
| // Allocate the interface information buffer | |
| // | |
| pIfInfo = AllocatePool ( DataSize ); | |
| if ( NULL == pIfInfo ) { | |
| DEBUG (( DEBUG_ERROR, | |
| "ERROR - Not enough memory to allocate the interface information buffer!\r\n" )); | |
| Status = EFI_OUT_OF_RESOURCES; | |
| break; | |
| } | |
| // | |
| // Get the interface info. | |
| // | |
| Status = pIpConfig2Protocol->GetData ( | |
| pIpConfig2Protocol, | |
| Ip4Config2DataTypeInterfaceInfo, | |
| &DataSize, | |
| pIfInfo | |
| ); | |
| if ( EFI_ERROR ( Status )) { | |
| DEBUG (( DEBUG_ERROR, | |
| "ERROR - Failed to return the interface info, Status: %r\r\n", | |
| Status )); | |
| break; | |
| } | |
| // | |
| // Display the current configuration | |
| // | |
| DEBUG (( DEBUG_BIND, | |
| "Actual adapter IP address: %d.%d.%d.%d\r\n", | |
| pIfInfo->StationAddress.Addr [ 0 ], | |
| pIfInfo->StationAddress.Addr [ 1 ], | |
| pIfInfo->StationAddress.Addr [ 2 ], | |
| pIfInfo->StationAddress.Addr [ 3 ])); | |
| // | |
| // Assume the port is not configured | |
| // | |
| Status = EFI_SUCCESS; | |
| if (( pConfigData->StationAddress.Addr [ 0 ] == pIfInfo->StationAddress.Addr [ 0 ]) | |
| && ( pConfigData->StationAddress.Addr [ 1 ] == pIfInfo->StationAddress.Addr [ 1 ]) | |
| && ( pConfigData->StationAddress.Addr [ 2 ] == pIfInfo->StationAddress.Addr [ 2 ]) | |
| && ( pConfigData->StationAddress.Addr [ 3 ] == pIfInfo->StationAddress.Addr [ 3 ])) { | |
| break; | |
| } | |
| // | |
| // The IP address did not match | |
| // | |
| Status = EFI_NOT_STARTED; | |
| break; | |
| } | |
| // | |
| // Free the buffer if necessary | |
| // | |
| if ( NULL != pIfInfo ) { | |
| FreePool ( pIfInfo ); | |
| } | |
| // | |
| // Return the IP address status | |
| // | |
| DBG_EXIT_STATUS ( Status ); | |
| return Status; | |
| } | |
| /** | |
| Interface between the socket layer and the network specific | |
| code that supports SOCK_DGRAM sockets over UDPv4. | |
| **/ | |
| CONST ESL_PROTOCOL_API cEslUdp4Api = { | |
| "UDPv4", | |
| IPPROTO_UDP, | |
| OFFSET_OF ( ESL_PORT, Context.Udp4.ConfigData ), | |
| OFFSET_OF ( ESL_LAYER, pUdp4List ), | |
| OFFSET_OF ( struct sockaddr_in, sin_zero ), | |
| sizeof ( struct sockaddr_in ), | |
| AF_INET, | |
| sizeof (((ESL_PACKET *)0 )->Op.Udp4Rx ), | |
| sizeof (((ESL_PACKET *)0 )->Op.Udp4Rx ), | |
| OFFSET_OF ( ESL_IO_MGMT, Token.Udp4Rx.Packet.RxData ), | |
| FALSE, | |
| EADDRINUSE, | |
| NULL, // Accept | |
| NULL, // ConnectPoll | |
| NULL, // ConnectStart | |
| EslUdp4SocketIsConfigured, | |
| EslUdp4LocalAddressGet, | |
| EslUdp4LocalAddressSet, | |
| NULL, // Listen | |
| NULL, // OptionGet | |
| NULL, // OptionSet | |
| EslUdp4PacketFree, | |
| EslUdp4PortAllocate, | |
| NULL, // PortClose, | |
| NULL, // PortCloseOp | |
| TRUE, | |
| EslUdp4Receive, | |
| EslUdp4RemoteAddressGet, | |
| EslUdp4RemoteAddressSet, | |
| EslUdp4RxComplete, | |
| NULL, // RxStart | |
| EslUdp4TxBuffer, | |
| EslUdp4TxComplete, | |
| NULL, // TxOobComplete | |
| (PFN_API_VERIFY_LOCAL_IP_ADDRESS)EslUdp4VerifyLocalIpAddress | |
| }; |