| /** @file | |
| Implement the recvfrom API. | |
| 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 <SocketInternals.h> | |
| /** | |
| Receive data from a network connection and return the remote system's address. | |
| The recvfrom routine waits for receive data from a remote network | |
| connection. This routine is typically called for SOCK_DGRAM sockets | |
| when the socket is being shared by multiple remote systems and it is | |
| important to get the remote system address for a response. | |
| The | |
| <a href="http://pubs.opengroup.org/onlinepubs/9699919799/functions/recv.html">POSIX</a> | |
| documentation is available online. | |
| @param [in] s Socket file descriptor returned from ::socket. | |
| @param [in] buffer Address of a buffer to receive the data. | |
| @param [in] length Length of the buffer in bytes. | |
| @param [in] flags Message control flags | |
| @param [out] address Network address to receive the remote system address | |
| @param [in] address_len Length of the remote network address structure | |
| @return This routine returns the number of valid bytes in the buffer, | |
| zero if no data was received, and -1 when an error occurs. | |
| In the case of an error, ::errno contains more details. | |
| **/ | |
| ssize_t | |
| recvfrom ( | |
| int s, | |
| void * buffer, | |
| size_t length, | |
| int flags, | |
| struct sockaddr * address, | |
| socklen_t * address_len | |
| ) | |
| { | |
| socklen_t ByteCount; | |
| ssize_t LengthInBytes; | |
| UINT8 * pData; | |
| EFI_SOCKET_PROTOCOL * pSocketProtocol; | |
| EFI_STATUS Status; | |
| struct timeval TimeVal; | |
| EFI_EVENT pTimer; | |
| UINT64 Timeout; | |
| ssize_t TotalBytes; | |
| // | |
| // Assume failure | |
| // | |
| LengthInBytes = -1; | |
| // | |
| // Locate the context for this socket | |
| // | |
| pSocketProtocol = BslFdToSocketProtocol ( s, NULL, &errno ); | |
| if ( NULL != pSocketProtocol ) { | |
| // | |
| // Receive the data from the socket | |
| // | |
| Status = pSocketProtocol->pfnReceive ( pSocketProtocol, | |
| flags, | |
| length, | |
| buffer, | |
| (size_t *)&LengthInBytes, | |
| address, | |
| address_len, | |
| &errno ); | |
| if ( EFI_ERROR ( Status )) { | |
| LengthInBytes = -1; | |
| if ( EAGAIN == errno ) { | |
| // | |
| // Get the timeout | |
| // | |
| ByteCount = sizeof ( TimeVal ); | |
| LengthInBytes = getsockopt ( s, | |
| SOL_SOCKET, | |
| SO_RCVTIMEO, | |
| &TimeVal, | |
| &ByteCount ); | |
| if ( 0 == LengthInBytes ) { | |
| // | |
| // Compute the timeout | |
| // | |
| Timeout = TimeVal.tv_sec; | |
| Timeout *= 1000 * 1000; | |
| Timeout += TimeVal.tv_usec; | |
| Timeout *= 10; | |
| // | |
| // The timer is only necessary if a timeout is running | |
| // | |
| LengthInBytes = -1; | |
| Status = EFI_SUCCESS; | |
| pTimer = NULL; | |
| if ( 0 != Timeout ) { | |
| Status = gBS->CreateEvent ( EVT_TIMER, | |
| TPL_NOTIFY, | |
| NULL, | |
| NULL, | |
| &pTimer ); | |
| } | |
| if ( !EFI_ERROR ( Status )) { | |
| // | |
| // Start the timer | |
| // | |
| if ( NULL != pTimer ) { | |
| Status = gBS->SetTimer ( pTimer, | |
| TimerRelative, | |
| Timeout ); | |
| } | |
| if ( !EFI_ERROR ( Status )) { | |
| // | |
| // Loop until data is received or the timeout | |
| // expires | |
| // | |
| TotalBytes = 0; | |
| pData = (UINT8 *)buffer; | |
| do { | |
| // | |
| // Determine if the timeout expired | |
| // | |
| if ( NULL != pTimer ) { | |
| Status = gBS->CheckEvent ( pTimer ); | |
| if ( EFI_SUCCESS == Status ) { | |
| errno = ETIMEDOUT; | |
| if ( 0 == TotalBytes ) { | |
| TotalBytes = -1; | |
| } | |
| break; | |
| } | |
| } | |
| // | |
| // Attempt to receive some data | |
| // | |
| Status = pSocketProtocol->pfnReceive ( pSocketProtocol, | |
| flags, | |
| length, | |
| pData, | |
| (size_t *)&LengthInBytes, | |
| address, | |
| address_len, | |
| &errno ); | |
| if ( !EFI_ERROR ( Status )) { | |
| // | |
| // Account for the data received | |
| // | |
| TotalBytes += LengthInBytes; | |
| pData += LengthInBytes; | |
| length -= LengthInBytes; | |
| } | |
| } while ( EFI_NOT_READY == Status ); | |
| LengthInBytes = TotalBytes; | |
| // | |
| // Stop the timer | |
| // | |
| if ( NULL != pTimer ) { | |
| gBS->SetTimer ( pTimer, | |
| TimerCancel, | |
| 0 ); | |
| } | |
| } | |
| // | |
| // Release the timer | |
| // | |
| if ( NULL != pTimer ) { | |
| gBS->CloseEvent ( pTimer ); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| // | |
| // Return the receive data length, -1 for errors | |
| // | |
| return LengthInBytes; | |
| } |