[syslinux] [PATCH 0/1] UEFI UDP/TFTP

Patrick Masotta
Sun, 28 Feb 2016 16:26:07 -0000

Hi guys,

I have re-implemented /efi/udp.c


The new code fixes:

1) The low and decreasing throughput on TFTP transfers.

2) The added delay between consecutive TFTP transfers.

3) The TFTP errors induced by broadcast traffic like ARP.


Initial tests on a 50MB transfer showed times going from 3 minutes

to ~12 seconds, also tested OK with nested TFTP transfers
(include command).


This new code affects/fixes the problems reported at:


a) "Problem with 90MB Initrd"
http://www.syslinux.org/archives/2015-September/024236.html

b) "UEFI: Failed to load ldlinux.e64/ldlinux.e32"
http://www.syslinux.org/archives/2015-October/024341.html
This report has led to Gene's patch:

https://github.com/geneC/syslinux/commit/9e0926bb338deb5c634ccb4ee29eb4577158cfdc
it was reported working but I think "it cannot work" if it has these
lines

+	memcpy(&udata.StationAddress, &IPInfo.myip, sizeof(IPInfo.myip));
+	memcpy(&udata.StationAddress, &IPInfo.netmask,
sizeof(IPInfo.netmask));

the StationAddress gets overwritten by a netmask...



There's a picture attached comparing performance between the old and new
udp.c code.

Testing reports are welcome.


Best,
Patrick


Signed-off-by: Patrick Masotta <masottaus at yahoo.com>

diff -uprN a/core/fs/pxe/pxe.h b/core/fs/pxe/pxe.h
--- a/core/fs/pxe/pxe.h	2014-10-06 10:27:44.000000000 -0600
+++ b/core/fs/pxe/pxe.h	2016-02-28 08:44:47.127996962 -0700
@@ -130,6 +130,7 @@ union net_private {
struct net_private_efi {
struct efi_binding *binding; /* EFI binding for protocol */
uint16_t localport;          /* Local port number (0=not in use) */
+	void*	 context;            /* Holds the UDP_IO Instance (used in the new efi
UDP codebase)  */
} efi;
};

diff -uprN a/efi/udp.c b/efi/udp.c
--- a/efi/udp.c	2014-10-06 10:27:44.000000000 -0600
+++ b/efi/udp.c	2016-02-28 08:41:29.867226806 -0700
@@ -1,5 +1,6 @@
/*
- * Copyright 2013-2014 Intel Corporation - All Rights Reserved
+ *   Serva (c) 2010-2016 Patrick Masotta - All Rights Reserved
+ *
*/

#include <string.h>
@@ -8,50 +9,189 @@
#include "net.h"
#include "fs/pxe/pxe.h"

+
+#define TICKS_PER_SECOND        10000000
+#define TICKS_PER_MS            10000
+
+#define UDP_TIME_TO_GETMAP		5 * TICKS_PER_SECOND
+#define UDP_TIME_TX_TIMEOUT		1 * TICKS_PER_SECOND + 500 * TICKS_PER_MS
+#define UDP_TIME_RX_TIMEOUT		1 * TICKS_PER_SECOND + 500 * TICKS_PER_MS
+
+
+///
+/// Type defined as RX.
+/// This data structure wraps receive udp parameters.
+///
+typedef struct  {
+	void*		buf;
+	uint16_t*	buf_len;
+	uint32_t*	src_ip;
+	uint16_t*	src_port;
+}RX;
+
+///
+/// Type defined as UDP_IO.
+/// This data structure wraps the UDP instance and configuration.
+///
+typedef struct  {
+	uint16_t			known_TID;
+	struct efi_binding*		b;
+	EFI_UDP4* 			Protocol_Udp4;
+	EFI_UDP4_CONFIG_DATA		Config_Udp4;
+	EFI_UDP4_COMPLETION_TOKEN*	Token;
+	EFI_EVENT			Timeout_Timer;
+	EFI_EVENT			CB_OK_Event;
+	RX				rx;
+}UDP_IO;
+
+
+#define EFI_IP4_EQUAL(Ip1, Ip2)  (CompareMem ((Ip1), (Ip2), sizeof
(EFI_IPv4_ADDRESS)) == 0)
+EFI_IPv4_ADDRESS  mZeroIp4Addr = {{0, 0, 0, 0}};
+static BOOLEAN volatile UseDefaultSetting = TRUE;
+
extern EFI_GUID Udp4ServiceBindingProtocol, Udp4Protocol;

-/*
- * This UDP binding is configured to operate in promiscuous mode. It is
- * only used for reading packets. It has no associated state unlike
- * socket->net.efi.binding, which has a remote IP address and port
- * number.
- */
-static struct efi_binding *udp_reader;
+
+
+
+/**
+  Poll the UDP to get the IP4 default address, which may be retrieved
+  by DHCP.
+
+  The default time out value is 5 seconds. If IP has retrieved the default
address,
+  the UDP is reconfigured.
+
+
+  @param  udp_io                The UDP_IO instance
+  @param  udata             	The UDP configure data to reconfigure the UDP_IO
+
+  @retval TRUE                  The default address is retrieved and UDP is
reconfigured.
+  @retval FALSE                 Some error occured.
+
+**/
+BOOLEAN
+efi_udp_get_mapping (UDP_IO* udp_io, EFI_UDP4_CONFIG_DATA* udata)
+{
+  EFI_STATUS			status;
+  EFI_UDP4*			udp;
+  EFI_IP4_MODE_DATA		Ip4Mode;
+
+//  ASSERT (UseDefaultSetting);
+
+  udp     = udp_io->Protocol_Udp4;
+
+  status = uefi_call_wrapper(	BS->SetTimer, 3,
+				udp_io->Timeout_Timer,
+				TimerRelative,
+				UDP_TIME_TO_GETMAP
+				);
+
+  if (EFI_ERROR (status))
+	return FALSE;
+
+  while (EFI_ERROR ( uefi_call_wrapper(BS->CheckEvent, 1,
udp_io->Timeout_Timer) ))
+	{
+		udp->Poll(udp);
+
+		if (!EFI_ERROR(udp->GetModeData(udp, NULL, &Ip4Mode, NULL, NULL))
&&
+			Ip4Mode.IsConfigured)
+			{
+				udp->Configure(udp, NULL);
+				return (BOOLEAN) (udp->Configure(udp, udata) == EFI_SUCCESS);
+			}
+	}
+
+  return FALSE;
+}
+
+

/**
- * Try to configure this UDP socket
+ * Try to configure this UDP_IO
*
- * @param:udp, the EFI_UDP4 socket to configure
- * @param:udata, the EFI_UDP4_CONFIG_DATA to use
- * @param:f, the name of the function as a wide string.
+ * @param:udp_io, 	The UDP_IO containing the EFI_UDP4 socket to configure
+ * 					and the EFI_UDP4_CONFIG_DATA to use.
+ * @param:f, 		The name of the invoking function as a wide string.
*
* @out: status as EFI_STATUS
*/
-
-EFI_STATUS core_udp_configure(EFI_UDP4 *udp, EFI_UDP4_CONFIG_DATA *udata,
-	short unsigned int *f)
+EFI_STATUS efi_udp_configure(UDP_IO *udp_io, short unsigned int *f)
{
-    EFI_STATUS status;
-    int unmapped = 1;
-    jiffies_t start, last, cur;
-
-    last = start = jiffies();
-    while (unmapped){
-	status = uefi_call_wrapper(udp->Configure, 2, udp, udata);
-	if (status != EFI_NO_MAPPING)
-		unmapped = 0;
-	else {
-	    cur = jiffies();
-	    if ( (cur - last) >= EFI_NOMAP_PRINT_DELAY ) {
-		last = cur;
-		Print(L"%s: stalling on configure with no mapping\n", f);
-	    } else if ( (cur - start) > EFI_NOMAP_PRINT_DELAY *
EFI_NOMAP_PRINT_COUNT) {
-		Print(L"%s: aborting on no mapping\n", f);
-		unmapped = 0;
-	    }
-	}
-    }
-    return status;
+
+    EFI_STATUS 			status;
+    EFI_UDP4* 			udp	= udp_io->Protocol_Udp4;
+    EFI_UDP4_CONFIG_DATA*	udata 	= &udp_io->Config_Udp4;
+
+
+	status = uefi_call_wrapper(udp->Configure, 2, udp, NULL);
+	if (EFI_ERROR (status))
+		{
+			return status;
+		}
+
+
+	status= uefi_call_wrapper(udp->Configure, 2, udp, udata);
+	if( status == EFI_SUCCESS )
+		{
+			EFI_STATUS status;
+
+			status = uefi_call_wrapper(udp->GetModeData, 5, udp,
+								udata, NULL, NULL, NULL);
+
+			if(status == EFI_SUCCESS )
+				return EFI_SUCCESS;
+			else
+				return -1;
+		}
+
+    else
+    if (	status == EFI_NO_MAPPING 							&&
+			UseDefaultSetting							&&
+			efi_udp_get_mapping (udp_io, udata)					&&
+			EFI_SUCCESS == uefi_call_wrapper(udp->GetModeData, 5, udp,
+								udata, NULL, NULL, NULL)
+		)
+		return EFI_SUCCESS;
+	else
+	if(
+			UseDefaultSetting							&&
+			!EFI_IP4_EQUAL (&mZeroIp4Addr, &IPInfo.gateway)				&&
+			!EFI_IP4_EQUAL (&mZeroIp4Addr, &IPInfo.myip)				&&
+			!EFI_IP4_EQUAL (&mZeroIp4Addr, &IPInfo.netmask)
+		)
+		{	// udp->Configure() and efi_udp_get_mapping() failed lets try a manual
config
+			EFI_STATUS status;
+
+			UseDefaultSetting=FALSE;
+			udata->UseDefaultAddress = FALSE;
+			memcpy(&udata->StationAddress , &IPInfo.myip,
sizeof(IPInfo.myip));
+			memcpy(&udata->SubnetMask, 		&IPInfo.netmask,
sizeof(IPInfo.netmask));
+
+			status = uefi_call_wrapper(udp->Routes , 5,
+										 udp,
+										 FALSE,
+										 &mZeroIp4Addr,
+										 &mZeroIp4Addr,
+										 (EFI_IPv4_ADDRESS *)&IPInfo.gateway
+										 );
+			if (EFI_ERROR (status))
+			  {
+				uefi_call_wrapper(udp->Configure, 2, udp, NULL);
+				return -1;
+			  }
+
+			if( EFI_ERROR (uefi_call_wrapper(udp->Configure, 2, udp, NULL))	||
+				EFI_ERROR (uefi_call_wrapper(udp->Configure, 2, udp, udata))
+				)
+				return -1;
+			else
+				return EFI_SUCCESS;
+
+		}
+
+	else
+		return -1;
+
}

/**
@@ -63,55 +203,76 @@ EFI_STATUS core_udp_configure(EFI_UDP4 *
*/
int core_udp_open(struct pxe_pvt_inode *socket)
{
-    EFI_UDP4_CONFIG_DATA udata;
-    struct efi_binding *b;
-    EFI_STATUS status;
-    EFI_UDP4 *udp;
-
+    EFI_STATUS 			status;
+    struct efi_binding*		b;
+    EFI_UDP4*			udp;
+    UDP_IO*			udp_io;
+
(void)socket;

-    udp_reader = efi_create_binding(&Udp4ServiceBindingProtocol,
&Udp4Protocol);
-    if (!udp_reader)
-	return -1;
-
-    b = efi_create_binding(&Udp4ServiceBindingProtocol, &Udp4Protocol);
-    if (!b)
-	goto bail;
-
-    udp = (EFI_UDP4 *)udp_reader->this;
-
-    memset(&udata, 0, sizeof(udata));
-
-    status = core_udp_configure(udp, &udata, L"core_udp_open");
-    if (status != EFI_SUCCESS)
-	goto bail;
-
-    socket->net.efi.binding = b;
-
-    /*
-     * Save the random local port number that the UDPv4 Protocol
-     * Driver picked for us. The TFTP protocol uses the local port
-     * number as the TID.
-     */
-    status = uefi_call_wrapper(udp->GetModeData, 5, udp,
-			       &udata, NULL, NULL, NULL);
-    if (status != EFI_SUCCESS)
-	Print(L"Failed to get UDP mode data: %d\n", status);
-    else
-	socket->net.efi.localport = udata.StationPort;
+
+	b 	= efi_create_binding(&Udp4ServiceBindingProtocol, &Udp4Protocol);
+	if(!b)
+		return -1;
+
+	udp_io=zalloc(sizeof(UDP_IO));
+	if(!udp_io)
+		goto bail;
+
+	udp_io->b = b;
+
+	udp = (EFI_UDP4 *)b->this;
+	udp_io->Protocol_Udp4 = udp;
+
+    socket->net.efi.context = udp_io;
+
+	//
+	// Create the timer used to time out transmit, receive, and get map events
+	//
+	status = uefi_call_wrapper(BS->CreateEvent, 5,
+							  EVT_TIMER,
+							  TPL_CALLBACK,
+							  NULL,
+							  NULL,
+							  &udp_io->Timeout_Timer
+							  );
+	if (EFI_ERROR (status))
+		goto bail;
+
+	//
+	// Create the event to signal when the send/receive callback function ends
+	// correctly.
+	//
+	status = uefi_call_wrapper(BS->CreateEvent, 5,
+							  EVT_TIMER,
+							  TPL_CALLBACK,
+							  NULL,
+							  NULL,
+							  &udp_io->CB_OK_Event
+							  );
+	if (EFI_ERROR (status))
+		goto bail;

return 0;

bail:
if (b)
-	efi_destroy_binding(b, &Udp4ServiceBindingProtocol);
+		efi_destroy_binding(b, &Udp4ServiceBindingProtocol);

-    efi_destroy_binding(udp_reader, &Udp4ServiceBindingProtocol);
-    udp_reader = NULL;
+    if(udp_io && udp_io->Timeout_Timer)
+		uefi_call_wrapper(BS->CloseEvent, 1, udp_io->Timeout_Timer);
+
+     if(udp_io && udp_io->CB_OK_Event)
+		uefi_call_wrapper(BS->CloseEvent, 1, udp_io->CB_OK_Event);
+
+
+    if(udp_io)
+		free(udp_io);

return -1;
}

+
/**
* Close a socket
*
@@ -119,299 +280,394 @@ bail:
*/
void core_udp_close(struct pxe_pvt_inode *socket)
{
-    efi_destroy_binding(udp_reader, &Udp4ServiceBindingProtocol);
-    udp_reader = NULL;

-    if (!socket->net.efi.binding)
-	return;
-
-    efi_destroy_binding(socket->net.efi.binding,
&Udp4ServiceBindingProtocol);
-    socket->net.efi.binding = NULL;
+	UDP_IO* udp_io;
+	udp_io = (UDP_IO*)socket->net.efi.context;
+
+
+    if (!udp_io->b)
+		return;
+
+    efi_destroy_binding(udp_io->b, &Udp4ServiceBindingProtocol);
+    udp_io->b = NULL;
+
+	if(udp_io->Timeout_Timer)
+		uefi_call_wrapper(BS->CloseEvent, 1, udp_io->Timeout_Timer);
+
+	if(udp_io->CB_OK_Event)
+		uefi_call_wrapper(BS->CloseEvent, 1, udp_io->CB_OK_Event);
+
+	if(socket->net.efi.context)
+		free(socket->net.efi.context);
+	socket->net.efi.context=NULL;
+
+
}

/**
* Establish a connection on an open socket
*
- * @param:socket, the open socket
- * @param:ip, the ip address
- * @param:port, the port number, host-byte order
+ * @param:socket, 	the open socket
+ * @param:ip, 		the source ip address
+ * @param:port, 	the source port number, host-byte order
*/
-void core_udp_connect(struct pxe_pvt_inode *socket, uint32_t ip,
-		      uint16_t port)
+void core_udp_connect(struct pxe_pvt_inode *socket, uint32_t src_ip,
+		      uint16_t src_port)
{
-    EFI_UDP4_CONFIG_DATA udata;
-    EFI_STATUS status;
-    EFI_UDP4 *udp;
-
-    udp = (EFI_UDP4 *)socket->net.efi.binding->this;

-    memset(&udata, 0, sizeof(udata));
-
-    /* Re-use the existing local port number */
-    udata.StationPort = socket->net.efi.localport;
-
-    udata.UseDefaultAddress = TRUE;
-    memcpy(&udata.RemoteAddress, &ip, sizeof(ip));
-    udata.RemotePort = port;
-    udata.AcceptPromiscuous = TRUE;
-    udata.TimeToLive = 64;
-
-    status = core_udp_configure(udp, &udata,
L"core_udp_connect");
-    if (status != EFI_SUCCESS) {
-	Print(L"Failed to configure UDP: %d\n", status);
-	return;
-    }
+    EFI_STATUS status;
+    UDP_IO*	udp_io;
+
+    udp_io = (UDP_IO*)socket->net.efi.context;
+
+	if ( src_port != udp_io->Config_Udp4.RemotePort )
+		{
+			udp_io->Config_Udp4.RemotePort		= src_port;
+
+			status = efi_udp_configure(udp_io, L"core_udp_connect");
+			if (EFI_ERROR (status))
+				{
+					Print(L"Failed to configure UDP: %d\n", status);
+					return;
+				}
+		}
+
}

/**
* Tear down a connection on an open socket
*
- * @param:socket, the open socket
+ * @param:socket,	the open socket
*/
void core_udp_disconnect(struct pxe_pvt_inode *socket)
{
-    EFI_STATUS status;
-    EFI_UDP4 *udp;
-
-    udp = (EFI_UDP4 *)socket->net.efi.binding->this;
-
-    /* Reset */
-    status = uefi_call_wrapper(udp->Configure, 2, udp, NULL);
-    if (status != EFI_SUCCESS)
-	Print(L"Failed to reset UDP: %d\n", status);

+
+ return;
+
}

-static int volatile cb_status = -1;
-static EFIAPI void udp4_cb(EFI_EVENT event, void *context)
-{
-    (void)event;
-
-    EFI_UDP4_COMPLETION_TOKEN *token = context;

-    if (token->Status == EFI_SUCCESS)
-	cb_status = 0;
-    else
-	cb_status = 1;
+/**
+ *
+ * Send callback function
+ *
+ */
+VOID
+EFIAPI
+UdpIoOnDgramSent (
+  IN EFI_EVENT	event,
+  IN VOID*		context
+  )
+{
+    UDP_IO*			udp_io;
+    EFI_UDP4_COMPLETION_TOKEN*	token;
+
+    udp_io = (UDP_IO*) context;
+    if(	udp_io					&&
+		(token = udp_io->Token)		&&
+		token->Status == EFI_SUCCESS
+		)
+		uefi_call_wrapper(BS->SignalEvent, 1, udp_io->CB_OK_Event);
}

+
/**
- * Read data from the network stack
+ * Send a UDP packet (blocking call).
*
- * @param:socket, the open socket
- * @param:buf, location of buffer to store data
- * @param:buf_len, size of buffer
-
- * @out: src_ip, ip address of the data source
- * @out: src_port, port number of the data source, host-byte order
+ * @param:socket, 	the open socket
+ * @param:data, 	data buffer to send
+ * @param:len, 		size of data bufer
*/
-int core_udp_recv(struct pxe_pvt_inode *socket, void *buf, uint16_t *buf_len,
-		  uint32_t *src_ip, uint16_t *src_port)
+
+void core_udp_send(struct pxe_pvt_inode *socket, const void *data, size_t len)
{
-    EFI_UDP4_COMPLETION_TOKEN token;
-    EFI_UDP4_FRAGMENT_DATA *frag;
-    EFI_UDP4_RECEIVE_DATA *rxdata;
-    struct efi_binding *b;
EFI_STATUS status;
-    EFI_UDP4 *udp;
-    size_t size;
-    int rv = -1;
-    jiffies_t start;
-
-    (void)socket;
-
-    b = udp_reader;
-    udp = (EFI_UDP4 *)b->this;
-    memset(&token, 0, sizeof(token));
-
-    status = efi_setup_event(&token.Event, (EFI_EVENT_NOTIFY)udp4_cb,
-			     &token);
-    if (status != EFI_SUCCESS)
-	return -1;
-
-    status = uefi_call_wrapper(udp->Receive, 2, udp, &token);
-    if (status != EFI_SUCCESS)
-	goto bail;
-
-    start = jiffies();
-    while (cb_status == -1) {
-	/* 15ms receive timeout... */
-	if (jiffies() - start >= 15) {
-	    if (jiffies() - start >= 30)
-		dprintf("Failed to cancel UDP\n");
-
-	    uefi_call_wrapper(udp->Cancel, 2, udp, &token);
-	    dprintf("core_udp_recv: timed out\n");
-	}
+    UDP_IO*			udp_io;
+    EFI_UDP4*			udp;
+    EFI_UDP4_COMPLETION_TOKEN*	token=NULL;
+    EFI_UDP4_TRANSMIT_DATA*	txdata=NULL;
+    EFI_UDP4_FRAGMENT_DATA*	frag;

-	uefi_call_wrapper(udp->Poll, 1, udp);
-    }
+    udp_io 	= (UDP_IO*)socket->net.efi.context;
+	udp		= udp_io->Protocol_Udp4;

-    if (cb_status == 0)
-	rv = 0;

-    /* Reset */
-    cb_status = -1;
-
-    if (rv)
-	goto bail;
-
-    rxdata = token.Packet.RxData;
-    frag = &rxdata->FragmentTable[0];
+    token = zalloc(sizeof(EFI_UDP4_COMPLETION_TOKEN));
+    if (!token)
+		goto bail;

-    size = min(frag->FragmentLength, *buf_len);
-    memcpy(buf, frag->FragmentBuffer, size);
-    *buf_len = size;
+    txdata = zalloc(sizeof(EFI_UDP4_TRANSMIT_DATA));
+    if (!txdata)
+		goto bail;

-    memcpy(src_port, &rxdata->UdpSession.SourcePort, sizeof(*src_port));
-    memcpy(src_ip, &rxdata->UdpSession.SourceAddress, sizeof(*src_ip));
+    status = efi_setup_event(&token->Event,
(EFI_EVENT_NOTIFY)UdpIoOnDgramSent,
+			     udp_io);
+    if (EFI_ERROR (status))
+		goto bail;

-    uefi_call_wrapper(BS->SignalEvent, 1, rxdata->RecycleSignal);

-bail:
-    uefi_call_wrapper(BS->CloseEvent, 1, token.Event);
-    return rv;
-}
+	udp_io->Token = token;

-/**
- * Send a UDP packet.
- *
- * @param:socket, the open socket
- * @param:data, data buffer to send
- * @param:len, size of data bufer
- */
-void core_udp_send(struct pxe_pvt_inode *socket, const void *data, size_t len)
-{
-    EFI_UDP4_COMPLETION_TOKEN *token;
-    EFI_UDP4_TRANSMIT_DATA *txdata;
-    EFI_UDP4_FRAGMENT_DATA *frag;
-    struct efi_binding *b = socket->net.efi.binding;
-    EFI_STATUS status;
-    EFI_UDP4 *udp = (EFI_UDP4 *)b->this;
-
-    token = zalloc(sizeof(*token));
-    if (!token)
-	return;
-
-    txdata = zalloc(sizeof(*txdata));
-    if (!txdata) {
-	free(token);
-	return;
-    }
-
-    status = efi_setup_event(&token->Event, (EFI_EVENT_NOTIFY)udp4_cb,
-			     token);
-    if (status != EFI_SUCCESS)
-	goto bail;
-
-    txdata->DataLength = len;
-    txdata->FragmentCount = 1;
+    txdata->DataLength 		= len;
+    txdata->FragmentCount 	= 1;
+
frag = &txdata->FragmentTable[0];
-
frag->FragmentLength = len;
frag->FragmentBuffer = (void *)data;

token->Packet.TxData = txdata;
-
+
+    status = uefi_call_wrapper(BS->SetTimer, 3,
+                  udp_io->Timeout_Timer,
+                  TimerRelative,
+                  UDP_TIME_TX_TIMEOUT
+                  );
+	if (EFI_ERROR (status))
+		goto bail;
+
+	uefi_call_wrapper(BS->CheckEvent, 1, udp_io->CB_OK_Event);
status = uefi_call_wrapper(udp->Transmit, 2, udp, token);
-    if (status != EFI_SUCCESS)
-	goto close;
+    if (EFI_ERROR (status))
+    {
+		goto close;
+	}

-    while (cb_status == -1)
-	uefi_call_wrapper(udp->Poll, 1, udp);
+
+	while (1)
+		{
+			uefi_call_wrapper(udp->Poll, 1, udp);
+
+			if(!EFI_ERROR ( uefi_call_wrapper(BS->CheckEvent, 1,
udp_io->CB_OK_Event) ))
+				{
+					goto close;	//TX OK
+				}
+
+			if(!EFI_ERROR ( uefi_call_wrapper(BS->CheckEvent, 1,
udp_io->Timeout_Timer) ))
+				{
+					dprintf("core_udp_send: timed out\n");
+					goto close;	//TX timeout
+				}
+		}

-    /* Reset */
-    cb_status = -1;

close:
uefi_call_wrapper(BS->CloseEvent, 1, token->Event);

bail:
-    free(txdata);
-    free(token);
+    if(txdata)
+		free(txdata);
+
+	if(token)
+		free(token);
+
+    udp_io->Token = NULL;
}

+
+
/**
* Send a UDP packet to a destination
*
- * @param:socket, the open socket
- * @param:data, data buffer to send
- * @param:len, size of data bufer
- * @param:ip, the ip address
- * @param:port, the port number, host-byte order
+ * @param:socket, 	the open socket
+ * @param:data, 	data buffer to send
+ * @param:len, 		size of data bufer
+ * @param:ip, 		the remote ip address
+ * @param:port, 	the remote port number, host-byte order
*/
void core_udp_sendto(struct pxe_pvt_inode *socket, const void *data,
-		     size_t len, uint32_t ip, uint16_t port)
+		     size_t len, uint32_t remote_ip, uint16_t remote_port)
{
-    EFI_UDP4_COMPLETION_TOKEN *token;
-    EFI_UDP4_TRANSMIT_DATA *txdata;
-    EFI_UDP4_FRAGMENT_DATA *frag;
-    EFI_UDP4_CONFIG_DATA udata;
-    EFI_STATUS status;
-    struct efi_binding *b;
-    EFI_UDP4 *udp;
+    EFI_STATUS 		status;
+    UDP_IO*		udp_io;

-    (void)socket;

-    b = efi_create_binding(&Udp4ServiceBindingProtocol, &Udp4Protocol);
-    if (!b)
-	return;
+    udp_io 	= (UDP_IO*)socket->net.efi.context;
+
+ 	if(!udp_io->known_TID)
+		udp_io->known_TID=remote_port;
+
+	udp_io->Config_Udp4.AcceptBroadcast    = FALSE;
+	udp_io->Config_Udp4.AcceptPromiscuous  = FALSE;
+	udp_io->Config_Udp4.AcceptAnyPort      = FALSE;
+	udp_io->Config_Udp4.AllowDuplicatePort = FALSE;
+	udp_io->Config_Udp4.TypeOfService      = 0;
+	udp_io->Config_Udp4.TimeToLive         = 64;
+	udp_io->Config_Udp4.DoNotFragment      = FALSE;
+	udp_io->Config_Udp4.ReceiveTimeout     = 0;
+	udp_io->Config_Udp4.TransmitTimeout    = 0;
+
+
+    if (UseDefaultSetting)
+		{
+			udp_io->Config_Udp4.UseDefaultAddress = TRUE;
+	    }
+	else
+		{
+			udp_io->Config_Udp4.UseDefaultAddress = FALSE;
+			memcpy(&udp_io->Config_Udp4.StationAddress , &IPInfo.myip,
sizeof(IPInfo.myip));
+			memcpy(&udp_io->Config_Udp4.SubnetMask, &IPInfo.netmask,
sizeof(IPInfo.netmask));
+	    }
+
+	//udp_io->Config_Udp4.StationPort        = 0;   //Reuse or initially
assigned by FW
+
+    memcpy(&udp_io->Config_Udp4.RemoteAddress, &remote_ip,
sizeof(remote_ip));
+    udp_io->Config_Udp4.RemotePort = remote_port;
+
+
+    status = efi_udp_configure(udp_io, L"core_udp_sendto");
+    if (EFI_ERROR (status))
+		return;
+
+	core_udp_send(socket, data,  len);

-    udp = (EFI_UDP4 *)b->this;
+}

-    token = zalloc(sizeof(*token));
-    if (!token)
-	goto out;
+
+
+/**
+ *
+ * Receive callback function
+ *
+ */
+VOID
+EFIAPI
+UdpIoOnDgramRecvd (
+  IN EFI_EVENT	event,
+  IN VOID*		context
+  )
+{
+    UDP_IO*			udp_io;
+    EFI_UDP4_COMPLETION_TOKEN*	token;
+    EFI_UDP4_FRAGMENT_DATA*	frag;
+    EFI_UDP4_RECEIVE_DATA*	rxdata;
+
+    size_t 			size;
+
+    udp_io = (UDP_IO*) context;
+
+    if(	udp_io					&&
+		(token = udp_io->Token)		&&
+		token->Status == EFI_SUCCESS
+		)
+		{
+			rxdata = token->Packet.RxData;
+			frag = &rxdata->FragmentTable[0];
+
+			size = min(frag->FragmentLength, *(udp_io->rx.buf_len));
+			memcpy(udp_io->rx.buf, frag->FragmentBuffer, size);
+			*(udp_io->rx.buf_len) = size;
+
+			memcpy(udp_io->rx.src_port,	&rxdata->UdpSession.SourcePort,
sizeof(*udp_io->rx.src_port));
+			memcpy(udp_io->rx.src_ip,	&rxdata->UdpSession.SourceAddress,
sizeof(*udp_io->rx.src_ip));
+
+			uefi_call_wrapper(BS->SignalEvent, 1, rxdata->RecycleSignal);
+
+			uefi_call_wrapper(BS->SignalEvent, 1, udp_io->CB_OK_Event);
+
+		}
+
+}

-    txdata = zalloc(sizeof(*txdata));
-    if (!txdata)
-	goto bail;
-
-    memset(&udata, 0, sizeof(udata));
-
-    /* Re-use the existing local port number */
-    udata.StationPort = socket->net.efi.localport;
-
-    udata.UseDefaultAddress = TRUE;
-    memcpy(&udata.RemoteAddress, &ip, sizeof(ip));
-    udata.RemotePort = port;
-    udata.AcceptPromiscuous = TRUE;
-    udata.TimeToLive = 64;
-
-    status = core_udp_configure(udp, &udata, L"core_udp_sendto");
-    if (status != EFI_SUCCESS)
-	goto bail;
-
-    status = efi_setup_event(&token->Event, (EFI_EVENT_NOTIFY)udp4_cb,
-			     token);
-    if (status != EFI_SUCCESS)
-	goto bail;

-    txdata->DataLength = len;
-    txdata->FragmentCount = 1;
-    frag = &txdata->FragmentTable[0];
+/**
+ * Read data from the network stack (blocking call)
+ *
+ * @param:socket,	the open socket
+ * @param:buf, 		location of buffer to store data
+ * @param:buf_len, 	size of buffer

-    frag->FragmentLength = len;
-    frag->FragmentBuffer = (void *)data;
+ * @out:src_ip, 	ip address of the data source
+ * @out:src_port, 	port number of the data source, host-byte order
+ */
+
+int core_udp_recv(struct pxe_pvt_inode *socket, void *buf, uint16_t *buf_len,
+		  uint32_t *src_ip, uint16_t *src_port)
+{
+    EFI_STATUS 			status;
+    UDP_IO*			udp_io;
+    EFI_UDP4*			udp;
+    EFI_UDP4_COMPLETION_TOKEN* 	token;
+    int 			rv = -1;

-    token->Packet.TxData = txdata;

-    status = uefi_call_wrapper(udp->Transmit, 2, udp, token);
-    if (status != EFI_SUCCESS)
-	goto close;
+    udp_io 	= (UDP_IO*)socket->net.efi.context;
+    udp		= udp_io->Protocol_Udp4;
+
+    token = zalloc(sizeof(EFI_UDP4_COMPLETION_TOKEN));
+
+    if (!token)
+		return -1;

-    while (cb_status == -1)
-	uefi_call_wrapper(udp->Poll, 1, udp);

-    /* Reset */
-    cb_status = -1;
+    status = efi_setup_event(&token->Event,
(EFI_EVENT_NOTIFY)UdpIoOnDgramRecvd,
+			     udp_io);
+    if (EFI_ERROR (status))
+		goto bail;
+
+
+	udp_io->Token 		= token;
+
+	udp_io->rx.buf		= buf;
+	udp_io->rx.buf_len	= buf_len;
+	udp_io->rx.src_ip	= src_ip;
+	udp_io->rx.src_port	= src_port;
+
+    status = uefi_call_wrapper(	BS->SetTimer, 3,
+								udp_io->Timeout_Timer,
+								TimerRelative,
+								UDP_TIME_RX_TIMEOUT
+								);
+	if (EFI_ERROR (status))
+		goto bail;
+
+	if(	udp_io->Config_Udp4.RemotePort == udp_io->known_TID )
+		{
+			udp_io->Config_Udp4.RemotePort=0;
+			status = efi_udp_configure(udp_io, L"core_udp_recv");
+			if (EFI_ERROR (status))
+				goto bail;
+		}
+
+	uefi_call_wrapper(BS->CheckEvent, 1, udp_io->CB_OK_Event);
+
+    status = uefi_call_wrapper(udp->Receive, 2, udp, token);
+    if (EFI_ERROR (status))
+		goto bail;
+
+	while (1)
+		{
+			uefi_call_wrapper(udp->Poll, 1, udp);
+
+
+			if(!EFI_ERROR ( uefi_call_wrapper(BS->CheckEvent, 1,
udp_io->CB_OK_Event) ))
+				{
+					rv = 0;
+					goto close;	//RX OK
+				}
+
+			if( ( EFI_SUCCESS == uefi_call_wrapper(BS->CheckEvent, 1,
udp_io->Timeout_Timer)) )
+				{
+					uefi_call_wrapper(udp->Cancel, 2, udp, token);
+					dprintf("core_udp_recv: timed out\n");
+					rv = -1;
+					goto bail;	//RX timeout
+				}
+
+		}

-close:
-    uefi_call_wrapper(BS->CloseEvent, 1, token->Event);

+close:
+
bail:
-    free(txdata);
-    free(token);
-out:
-    efi_destroy_binding(b, &Udp4ServiceBindingProtocol);
+    uefi_call_wrapper(BS->CloseEvent, 1, token->Event);
+
+	if(token)
+		free(token);
+
+	udp_io->Token=NULL;
+
+    return rv;
}
+
+
-------------- next part --------------
A non-text attachment was scrubbed...
Name: UEFI UDP compare-.png
Type: image/png
Size: 70861 bytes
Desc: not available
URL:
<http://www.zytor.com/pipermail/syslinux/attachments/20160228/a7d90d8d/attachment-0001.png>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: efi_udp.patch
Type: application/octet-stream
Size: 24996 bytes
Desc: not available
URL:
<http://www.zytor.com/pipermail/syslinux/attachments/20160228/a7d90d8d/attachment-0001.obj>