//LWIP_LIB R1.1, Added a ms_timer that lwip_lib maintains.

//LWIP_LIB R1.0, updated to work with Xilinx LWIP 1.0.a (i.e., LWIP 1.3.0)
//   Updates are marked by "!!!jwh" -- if there is a problem, look for
//   that first!
//   lwip lib will now interact with the hardware using the "CANONICAL"
//   definitions in xparameters.h:
//	- XPAR_INTC_0_DEVICE_ID
//	  o XPAR_INTC_0_EMACLITE_0_VEC_ID
//	  o XPAR_INTC_0_TMRCTR_0_VEC_ID
//	- XPAR_TMRCTR_0_DEVICE_ID
//	- XPAR_EMACLITE_0_DEVICE_ID

#include "lwip_lib.h"

//-----------------------------------------------------------------------------
//  Default values for MAC, Subnet

// Upper 6 bytes of MAC - Xilinx Ethernet OUI = 00-00-00
uint8 DEFAULT_MAC[] = {0x00, 0x00, 0x00, 0x00, 0x22, 0x38};
uint8 DEFAULT_SUBNET[] = {0xFF, 0xFF, 0xFF, 0x00};


//-----------------------------------------------------------------------------
//  Connection state struct

/*
 *  This connection_state struct is not necessary -- it is meant as a place 
 *  holder for a tcp_arg() structure, in case additional functionality is 
 *  needed later.
 */
struct connection_state {
//  struct pbuf *p;
//  u8_t failed;
//#define FAILED_MAX 8
	Xboolean connected;
};


//-----------------------------------------------------------------------------
// External Global Variables

/*
 * tcp_ticks is defined in lwip/src/core/tcp.c
 * We use this for determinint how often to call etharp_tmr()
 * in the my_tmr() function
 */
extern u32_t tcp_ticks; 

//!!!jwh commented out -- not needed with lwip 1.3.0
/*
 * XEmacIf_ConfigTable[] is defined in EDK-generated xemacif_g.c
 * It only has one entry, which we will use when we call netif_add()
 * to point to our emac interface.
 */
//extern XEmacIf_Config XEmacIf_ConfigTable[]; 



//-----------------------------------------------------------------------------
// Internal Global Variables

/*
 * Handles to the hardware we are using
 */

//!!!jwh added handles for the hardware, these are configured
//using the canonical hardware definitions from xparameters.h.
XTmrCtr TimerHandle;
XIntc InterruptHandle;
XEmacLite EthernetHandle;

/*
 * This is our network interface, which we need to setup using 
 * set_global_netif().
 */
struct netif *global_netif = NULL;

/*
 * This is our tcp_pcb (TCP Protocol Control Block) that represents the current
 * open connection.  Note that since we only have a single global tcp_pcb, we
 * can only handle one connection at a time.  This is probably fine for most
 * applications that could use this library, and handling multiple connections
 * would certainly complicate our handling algorithm for the send_buffers
 */
struct tcp_pcb *global_pcb = NULL;


/*
 * These make up our global receive buffer.
 * 
 * MAX_RECV_BUFFER_SIZE will be set when the application calls 
 * lwip_lib_configure_buffers(), which will also allocate the 
 * global_recv_buffer.
 * 
 * global_recv_buffer_len will be increased whenever our tcp_recv callback
 * is invoked with new received packets.  It will be reset to 0 when the
 * application calls lwip_lib_receive_data().
 */
uint16 MAX_RECV_BUFFER_SIZE = 0;
uint8 *global_recv_buffer = NULL;
uint8 global_recv_buffer_len = 0;


/*
 * These make up our global send buffers.
 * 
 * SEND_BUFFER_SIZE and NUM_SEND_BUFFERS will be set when the application
 * calls lwip_lib_configure_buffes(), which will also allocate the
 * send_buffers array of structs.
 * 
 * firstSentBufferIndex will always be set to the index of the longest
 * outstanding buffer that has been sent but not ACK'd.   It will be updated
 * whenever the application sends data via lwip_lib_send_data() or when data
 * has been ACK'd via lwip_lib_send_handler().  If there are no buffers that
 * are outstanding, firstSentBufferIndex will be set to -1.
 * 
 * currentWorkingBufferIndex indicates which buffer the application is writing
 * to.  It is updated in lwip_lib_send_data().
 */
uint16 SEND_BUFFER_SIZE = 0;
uint8  NUM_SEND_BUFFERS = 0;

struct send_buffer {
	uint8 *data;
	uint16 sentLength;
	uint8  wasSent;
};

struct send_buffer *send_buffers;


int8 firstSentBufferIndex = -1;  // int8 allows for -1 to signify no buffers waiting for ACK
uint8 currentWorkingBufferIndex = 0;


//-----------------------------------------------------------------------------
//  Private function prototypes
//  (The public function prototypes are defined in lwip_lib.h)

static uint8*  get_next_buffer( );
static XStatus set_global_netif( uint8 ip[4], uint8 subnet[4], uint8 gateway[4] );
static err_t   lwip_lib_accept_handler(void *arg, struct tcp_pcb *pcb, err_t err);

// These are all set when we accept a new connection (in lwip_lib_accept_handler())
static err_t lwip_lib_poll_handler(void *arg, struct tcp_pcb *tpcb);
static void  lwip_lib_err_handler( void *arg, err_t err);
static err_t lwip_lib_sent_handler(void *arg, struct tcp_pcb *tpcb, u16_t len);
static err_t lwip_lib_recv_data_handler(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err);

static void close_connection( void *arg );

// Used for starting, handling the timer interrupts
static void start_interrupt_timer();
static void lwip_lib_interrupt_handler (void* baseaddr_p);
static void my_tmr(void);

static void print_mac( uint8 mac[6] );
static void print_ip4_addr( struct ip_addr ip_addr );





//-----------------------------------------------------------------------------
// Function definitions


/*===========================================================================*/
/*                                                                           */
/*   External Functions                                                      */
/*                                                                           */
/*===========================================================================*/


/******************************************************************************
 * Completes the many initialization/startup steps to be ready for new 
 * connections.  As such, it takes many arguments; the mac, subnet, and 
 * gateway can be NULL.
 * 
 * If there is an error with any of these steps, NULL will be returned.
 * Otherwise, the first data buffer will be returned and the interface is
 * ready to accept TCP connections.
 *****************************************************************************/
uint8 *lwip_lib_full_startup( uint8 mac[6], 
							  uint8 ip[4], uint8 subnet[4], uint8 gateway[4], 
							  uint16 port, 
							  uint8 numSendBufs, uint16 sendBufSize, 
							  uint16 recvBufSize)
{
	uint8 *firstBuf;
	XEmacLite_Config *ConfigPtr;
	XStatus Status;

	//init hardware
	
	//!!!jwh updated to work with the canonical definitions of the hardware.
	// Canonical definitions for hardware
	//	XPAR_INTC_0_DEVICE_ID
	//	XPAR_INTC_0_EMACLITE_0_VEC_ID
	//	XPAR_INTC_0_TMRCTR_0_VEC_ID
	//
	//	XPAR_TMRCTR_0_DEVICE_ID
	//	XPAR_EMACLITE_0_DEVICE_ID
	
	//Interrupt Controller
	if ( XIntc_Initialize(&InterruptHandle, XPAR_INTC_0_DEVICE_ID) != XST_SUCCESS ) {
		xil_printf("XIntc_Initialize() failed.\r\n");
	}
	//Timer
	if ( XTmrCtr_Initialize(&TimerHandle, XPAR_TMRCTR_0_DEVICE_ID) != XST_SUCCESS ) {
		xil_printf("XTmrCtr_Initialize() failed.\r\n");
	}
	// Initialize the EmacLite device.
	ConfigPtr = XEmacLite_LookupConfig(XPAR_EMACLITE_0_DEVICE_ID);
	if (ConfigPtr == NULL) {
		xil_printf("XEmacLite_LookupConfig() failed.\r\n");
		return NULL;
	}
	Status = XEmacLite_CfgInitialize(&EthernetHandle,
					ConfigPtr,
					ConfigPtr->BaseAddress);
	if (Status != XST_SUCCESS) {
		xil_printf("XEmacLite_CfgInitialize() failed.\r\n");
		return NULL;
	}
	
	//init lwip
	
	if ( lwip_lib_init( mac, ip, subnet, gateway ) != XST_SUCCESS ) {
		xil_printf("lwip_lib_init() failed. Skipping lwip_lib_start().\r\n");
		return NULL;
	}

	firstBuf = lwip_lib_configure_buffers( numSendBufs, sendBufSize, recvBufSize );
	if ( firstBuf == NULL ) {
		xil_printf("lwip_lib_configure_buffers() failed.\r\n");
		return NULL;
	}

	if ( lwip_lib_start( port ) != XST_SUCCESS ) {
		xil_printf("lwip_lib_start() failed.\r\n");
		return NULL;
	}
	
	return firstBuf;
}


/******************************************************************************
 * Performs all the lwip initialization steps in the correct order.
 * 
 * In addition, this sets up the network interface.
 * 
 * Only the ip must be specified.  Reasonable choices will be made for the
 * other arguments if they are passed in as NULL.
 *****************************************************************************/
XStatus lwip_lib_init( uint8 mac[6], uint8 ip[4], uint8 subnet[4], uint8 gateway[4])

{
//!!!jwh Replaced with lwip_init in newer LWIP libraries
//	// Do all the LWIP System Inits
//#ifdef STATS
//	stats_init();
//#endif /* STATS */
//	xil_printf("Initializing Memory Structures.");
//	sys_init();
//	xil_printf(".");
//	mem_init();
//	xil_printf(".");
//	memp_init();
//	xil_printf(".");
//	pbuf_init();
//	xil_printf(" done.\r\n");
//
//	// Sets mac for XEmacIf_ConfigTable[0]
//	// (Must be called before xemacif_init(), which is called by netif_add())
//	if ( mac != NULL ) {
//		xemacif_setmac(0, (uint8 *) mac);
//	} else {
//		xemacif_setmac(0, DEFAULT_MAC);
//	}
//
//	// Initialize the network interface
//	netif_init( );
//	
//	// Initialize TCP stack
//	tcp_init();

//!!!jwh
	lwip_init();
	
	// Setup a network interface with our specified IP, etc.
	if ( set_global_netif( ip, subnet, gateway ) != XST_SUCCESS ) {
		return XST_FAILURE;
	}

	// Don't think we need this -- probably won't even work, anyway.
	// if ( force10Mbps() != XST_SUCCESS ) { return XST_FAILURE; }

	xil_printf("\r\nNetwork Interface started...\r\n");
	xil_printf("  MAC Address: "); print_mac(mac==NULL?DEFAULT_MAC:mac);   xil_printf("\r\n");
	xil_printf("  IP Address:  "); print_ip4_addr(global_netif->ip_addr);  xil_printf("\r\n");
	xil_printf("  Subnet Mask: "); print_ip4_addr(global_netif->netmask);  xil_printf("\r\n");
	xil_printf("  Gateway IP:  "); print_ip4_addr(global_netif->gw);       xil_printf("\r\n");


	return XST_SUCCESS;	
}


/******************************************************************************
 * This creates all the buffers we'll use for sending/receiving data.  The 
 * application specifies the number of send buffers and their maximum size,
 * based on how much data it expects to produce and how often it will try to
 * send out data.  In addition, the application determines the maximum amount
 * of data it expects to receive between calls to lwip_lib_receive_data().
 * 
 * Note that this function cannot be called before mem_init() has been called,
 * which is done in lwip_lib_init().  Also, this function should probably be
 * called _before_ the timer interrupt is turned on, which is done in 
 * lwip_lib_start().
 * 
 * It is very easy to run out of memory here, as there needs to be enough
 * room on the heap for all these buffers.
 *****************************************************************************/
uint8 *lwip_lib_configure_buffers( uint8 numSendBufs, uint16 sendBufSize, uint16 recvBufSize ) {
	int i;

	// Our firstSentBufferIndex is a signed integer, so we have to limit the
	// number of send buffers to 127, which shouldn't be a problem.	
	if ( numSendBufs > 127 ) {
		xil_printf("Too many send buffers specified! (%d buffers).  Limiting to %d\r\n",numSendBufs,127);
		numSendBufs = 127;
	}
	
	if ( numSendBufs < 1 ) {
		xil_printf("Invalid number of send buffers specified.  Creating 1 send buffer\r\n",numSendBufs);
		numSendBufs = 1;
	}
	

	// Could allow recvBufSize=0 & then just drop any received data in recv_data_handler
	// (but we'd have to change the logic to ACK data even if we didn't copy it into our buffer)
	if ( sendBufSize == 0 || recvBufSize == 0 ) {
		xil_printf("Send buffers or receive buffer must have size between 1 and 2^16\r\n");
		return NULL;
	}
		
	if ( global_recv_buffer == NULL ) {
		global_recv_buffer = mem_malloc( recvBufSize );
	} else {
		// Already allocated?  Shouldn't happen...
		xil_printf("receive buffer already configured -- resizing to %d\r\n",recvBufSize);
		mem_realloc( global_recv_buffer, recvBufSize );
	}

	if ( global_recv_buffer == NULL ) {
		xil_printf("Out of memory (attemping to allocate receive buffer of size %d)\r\n",recvBufSize);
		return NULL;
	}
	
	// First, allocate enough room for all the send_buffer structs
	send_buffers = (struct send_buffer *)mem_malloc( numSendBufs * sizeof(struct send_buffer) );
	if ( send_buffers == NULL ) {
		xil_printf("Out of memory (attempting to allocate %d send buffers)\r\n",numSendBufs);
		return NULL;
	}
	
	// Then, for each send_buffer struct, allocate enough room for the data
	for ( i=0; i<numSendBufs; i++ ) {
		send_buffers[i].data = (uint8 *)mem_malloc(sendBufSize);
		if ( send_buffers[i].data == NULL ) {
			xil_printf("Out of memory (attemping to allocate send buffer %d of size %d)\r\n",i,sendBufSize);
			return NULL;
		}
		send_buffers[i].sentLength = 0;
		send_buffers[i].wasSent    = 0;
	}
	
	// Initialize all our globals
	MAX_RECV_BUFFER_SIZE = recvBufSize;
	SEND_BUFFER_SIZE     = sendBufSize;
	NUM_SEND_BUFFERS     = numSendBufs;
	firstSentBufferIndex = -1;
	currentWorkingBufferIndex = 0;

	// get_next_buffer() will just return send_buffers[0].data
	return get_next_buffer();
}


/******************************************************************************
 * Performs initial tcp tasks to prepare to accept connections on the specified
 * port
 * 
 * Also turns on the timer interrupts
 *****************************************************************************/
XStatus lwip_lib_start( uint16 tcp_port ) {

	struct tcp_pcb *pcb, *new_pcb;

	// First, need a new connection identifier
  	pcb = tcp_new();
  	if ( pcb == NULL ) {
  		xil_printf("lwip_lib_start: No memory available?\r\n");
  		return XST_FAILURE;
  	}
  	
  	// Then bind it to all local ip addrs (IP_ADDR_ANY) for my tcp_port
  	if ( tcp_bind(pcb, IP_ADDR_ANY, tcp_port) != ERR_OK ) {
  		xil_printf("lwip_lib_start: problem with tcp_bind.\r\n");
  		return XST_FAILURE; // don't know other error codes
  	}
  	
 	// Now we listen for incoming connections, calling our tcp accept function 
 	// (assigned next) whenever a new connection is accepted.  
 	// This returns a *new* pcb, and our old one is deallocated.
	// (So it doesn't seem like a good idea to have pcb = tcp_listen(pcb))
  	new_pcb = tcp_listen(pcb);
  	if ( new_pcb == NULL ) {
  		xil_printf("startup_tcp_conn: not enough memory for a new_pcb\r\n");
  		return XST_FAILURE;
  	}
  	
  	// lwip_lib_accept_handler is our (private) function that deals with new
  	// incoming connections.
  	tcp_accept(new_pcb, lwip_lib_accept_handler);

  	xil_printf("Listening for new connections on port %d\r\n", tcp_port);
  	
	// Now that we are ready to accept connections, we'll start the interrupt timer
	start_interrupt_timer();
  	
  	return XST_SUCCESS;
}


/******************************************************************************
 * Determines if there is an active connection or not
 * This depends on global_pcb being set correctly if connections are lost
 *****************************************************************************/
Xboolean lwip_lib_is_connected() {
	return (global_pcb == NULL) ? XFALSE : XTRUE;
}


/******************************************************************************
 * Closes the connection with the other side by calling our private
 * close_connection function 
 *****************************************************************************/
void lwip_lib_close_connection() {
	close_connection( global_pcb->callback_arg );
}


/******************************************************************************
 * This copies any recently received data into the applications buffer and then
 * resets the receive buffer so that it can accept new data
 * 
 * Interrupts are disabled while the data is being copied to avoid any possible
 * errors with new data arriving while were copying the buffer out
 *****************************************************************************/
uint16 lwip_lib_receive_data( uint8 *application_buffer ) {
	uint16 total_len;
	
	if ( global_recv_buffer_len <= 0 ) { // no recently received data
		return 0;
	}

	// Turning off interrupts so that our buffer doesn't change while we're copying it.
	microblaze_disable_interrupts();
	{
		if ( application_buffer != NULL ) {
			memcpy( application_buffer, global_recv_buffer, global_recv_buffer_len);
		}
	
		total_len = global_recv_buffer_len;

		// reset global_recv_buffer
		global_recv_buffer_len = 0;
	}
	microblaze_enable_interrupts();

	return total_len;
}


/******************************************************************************
 * This gives the current buffer data to the tcp_write function for enqueing
 * 
 * Because we don't use the copy option for tcp_write(), we must mark the
 * current buffer as outstanding and return a new buffer for the application
 * to use for storing data to send.
 * 
 * There are two error conditions the application will probably want to check
 * for after calling this function:
 * 
 *   +  return value == input buffer -- implies there was not enough room in 
 *      the send queue for the data (or some other tcp_write() error)
 *      The application can chose to wait and try resending, or it can reuse
 *      the input buffer and overwrite it with new data
 * 
 *   +  return value == NULL -- implies that there are no free buffers, 
 *      (i.e., all send buffers have been sent and none have been fully ACK'd)
 *      The application can chose to wait and check for a new valid buffer 
 *      (using lwip_lib_send_data(NULL,0), see below) or it could write to a 
 *      temporary location until a buffer is available.
 * 
 * Calling lwip_lib_send_data( NULL, 0 ) will not try to send data and will
 * just return the next un-sent buffer.  (However, it cannot be used to get a 
 * second data buffer to write to -- if the application already has a valid 
 * unsent buffer, that same buffer will be returned.)
 *****************************************************************************/
uint8 *lwip_lib_send_data( uint8 *data, uint16 len ) {
	uint8 *retBuf = NULL;	
	
	/*
		It appears that tcp_sndbuf, tcp_write, and tcp_output need to be 
		protected from interrupts, just like any time we access
		firstSentBufferIndex or currentWorkingBufferIndex.  So we will 
		interrupts off here and turn them back on just before we return.
	*/
	microblaze_disable_interrupts();

	if ( data == NULL ) {
		// The application doesn't have a valid buffer for data -- check if we
		// have a free buffer now (interrupts must be off during get_next_buffer)
		retBuf = get_next_buffer();
		goto return_here;
	}

	
	// First, check to make sure there is enough room to send our data
	// According to rawapi.txt, this should avoid tcp_write from having errors
	// Unfortunately, this does not seem to be the case.
	if ( len > tcp_sndbuf(global_pcb) ) {
		// Right now, we are not sending the data at all.  If this becomes a 
		// problem, we could try to break the data into pieces to send.
		xil_printf("Too much data to send!  (%d bytes to send; max=%d bytes)\r\n",len,tcp_sndbuf(global_pcb));
		retBuf = data;  // return input buffer, to signify that it was not sent
		goto return_here;
	}

	if ( tcp_write( global_pcb, data, len, 0 ) != ERR_OK ) {
		xil_printf("problem w/ tcp_write (sndbuf=%d, queuelen=%d, len=%d). trying tcp_output() first\r\n",tcp_sndbuf(global_pcb),global_pcb->snd_queuelen, len);
		// This call to tcp_output doesn't seem to fix our problem
		// It could probably be taken out ...
		if ( tcp_output( global_pcb ) != ERR_OK ) {
			xil_printf("Problem with tcp_output().  Probably out of memory.\r\n");
			retBuf = data;  // return input buffer, to signify that it was not sent
			goto return_here;
		}
		if ( tcp_write( global_pcb, data, len, 0 ) != ERR_OK ) {
			// We're supposed to wait and try again, but we can let the application
			// decide to do that or not
			xil_printf("Problem sending data (tried to send %d bytes)\r\n",len);
			retBuf = data;
			goto return_here;
		}
	}
	
	/*
		According to tcp_out.c comments, tcp_write() only enqueues the data to 
		be sent, in case more data is going to be sent soon (because it's 
		faster to send it all together).  We want our data sent out 
		immediately, so that we can reuse the buffer.
		
		According to the Citizenship Project using RAW API in LWIP, we should 
		call this if tcp_write() returns ERR_MEM to force the data to be sent,
		but it's unclear if this is sage advice.
		
		Thus, we call tcp_output to force the data to be sent, but we could try
		not using it, to see if it changes our throughput at all.
	*/
	if ( tcp_output( global_pcb ) != ERR_OK ) {
		// An error here is bad news -- we couldn't allocate a pbuf or
		// something like that.  It's unclear if waiting for data to be ACK'd
		// will solve this problem.
		xil_printf("Problem with tcp_output().  Probably out of memory.\r\n");
		retBuf = data;  // return input buffer, to signify that it was not sent
		goto return_here;
	}

	// Our buffer has at least been enqueued to be sent at this point.
		
	if ( data != send_buffers[currentWorkingBufferIndex].data ) {
		// The application did something weird.  It will probably screw us
		// up, but there's not much we can do about it.
		xil_printf("Sent strange buffer!  Correct execution not guarenteed\r\n");
	}

	if ( firstSentBufferIndex == -1 ) {
		//xil_printf("setting firstSentBufferIndex = %d\r\n",currentWorkingBufferIndex);
		firstSentBufferIndex = currentWorkingBufferIndex;
	}
	
	send_buffers[currentWorkingBufferIndex].wasSent    = 1;
	send_buffers[currentWorkingBufferIndex].sentLength = len;
	
	currentWorkingBufferIndex = (currentWorkingBufferIndex + 1) % NUM_SEND_BUFFERS;

	retBuf = get_next_buffer();
	
return_here:
	microblaze_enable_interrupts();
	return retBuf;
}




/*===========================================================================*/
/*                                                                           */
/*   Internal Functions                                                      */
/*                                                                           */
/*===========================================================================*/


/******************************************************************************
 * Returns the current data buffer for the application to use.  If all the
 * buffers are outstanding, NULL will be returned.
 * 
 * NOTE: Interrupts must be turned off during this function.
 *****************************************************************************/
static uint8 *get_next_buffer() {
	//xil_printf("Trying to return buffer for index %d\r\n",currentWorkingBufferIndex);
	if ( send_buffers[currentWorkingBufferIndex].wasSent == 0 ) {
		return send_buffers[currentWorkingBufferIndex].data;
	} else {
		// We have no free buffers . . .
		xil_printf("No free buffers for data\r\n");
		return NULL;
	}
}


/******************************************************************************
 * This is the tcp_accept() callback (so it needs to match the prototype as
 * specified in rawapi.txt).  This function gets called whenever a new client
 * connects to us.  We need to assign all our callback functions for this new
 * connection.
 * 
 * NOTE: We can only handle one connection at a time, so if another client
 * connects, we will kill off the first connection.
 *****************************************************************************/
static err_t lwip_lib_accept_handler(void *arg, struct tcp_pcb *newpcb, err_t err) {
	int i;
	struct connection_state *state;
	
	if ( global_pcb != NULL ) {
		xil_printf("Dropping current connection for new one!\r\n");
		// We want to pass the current global pcb's arg, not the new arg
		close_connection( global_pcb->callback_arg );
	}
	
	xil_printf("Accepting new connection\r\n");
	
	// Keeping one global pcb == one active connection
	global_pcb = newpcb;
	
	// Make sure global indexes are set appropriately:
	firstSentBufferIndex = -1;
	currentWorkingBufferIndex = 0;
	for ( i=0; i<NUM_SEND_BUFFERS; i++ ) {
		send_buffers[i].wasSent = 0;
		send_buffers[i].sentLength = 0;
	}

	// We don't really use the connection_state struct, but we keep it around
	// as an example, in case we want to add more functionality later.
	state = mem_malloc( sizeof( struct connection_state ) );
	if ( state == NULL ) {
		xil_printf("tcp_accept_handler: could not mem_malloc %d bytes\r\n", sizeof(struct connection_state));
		return ERR_MEM;
	}
	state->connected = 1;
	
	// Set our state struct as the argument to all our callback functions
	tcp_arg( newpcb, state );

	// Set up all our callback functions.  These will get called automatically
	// based on TCP events for this connection.
	tcp_recv( newpcb, lwip_lib_recv_data_handler );
	tcp_err(  newpcb, lwip_lib_err_handler  );
	tcp_poll( newpcb, lwip_lib_poll_handler, 1 );
	tcp_sent( newpcb, lwip_lib_sent_handler );

	tcp_setprio(newpcb, TCP_PRIO_MIN);

	xil_printf("Connection established\r\n");
	
	return ERR_OK;
}


/******************************************************************************
 * Don't need to worry about these unless we are going to allow the application
 * to initiate connections (rather than wait for someone to connect to us)
 * 
 * tcp_connect( pcb, {struct ip_addr *ipaddr}, port, lwip_lib_connected );
 * static err_t lwip_lib_connected(void *arg, struct tcp_pcb *pcb, err_t err) { }
 * 
 *****************************************************************************/


/******************************************************************************
 * This gets called whenever we receive new data or when the connection is
 * closed (signified by p=NULL).
 * 
 * This is assigned as a callback in lwip_lib_accept(), using tcp_recv().  As
 * such, it needs to match the prototype shown in rawapi.txt.
 * 
 * All we do here is copy the received data into a global_recv_buffer, which
 * can be collected by the application using lwip_lib_receive_data().
 *****************************************************************************/
static err_t lwip_lib_recv_data_handler(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err) {
	struct connection_state *state = arg;
	struct pbuf *q;
	uint16 total_bytes_received = 0;
	err_t retValue = err;
	
	// This signifies that the other side closed the connection
	if ( p == NULL ) {
		close_connection( state );
		return ERR_OK;
	}

	while ( p != NULL ) {
		q = p;
		
		if ( global_recv_buffer_len + q->len > MAX_RECV_BUFFER_SIZE ) {
			xil_printf("Received too much data!  current buffer size: %d  next packet size: %d\r\n", global_recv_buffer_len, q->len);
			if ( retValue == ERR_OK ) {
				// Override the err if it was set to OK -- otherwise, we'll
				// just return the original err
				retValue = XST_BUFFER_TOO_SMALL;  // defined in xstatus.h
			}
			// Instead of just quitting, we could copy as much data as will fit here
			break;
		}
		
		// Here we update our global buffer and length indicator
		// We have to copy the data because the pbuf is going to be freed.
		memcpy(global_recv_buffer+global_recv_buffer_len, q->payload, q->len);
		global_recv_buffer_len += q->len;
		
		total_bytes_received += q->len;
		
		// There may be more pbufs in the chain with more data
		p = pbuf_dechain(q);
		pbuf_free(q);
	}

	// Notify that the data was received (i.e., send an ACK)
	tcp_recved( global_pcb, total_bytes_received );
	
	xil_printf("Received data: %d bytes\r\n", total_bytes_received);	

	return retValue;
}


/******************************************************************************
 * This gets called whenever we receive an ACK for some of the data we sent
 * (or the initial ACK for establishing a connection).
 * 
 * This is assigned as a callback in lwip_lib_accept(), using tcp_send().  As 
 * such, it needs to match the prototype shown in rawapi.txt.
 * 
 * We need to determine how many of our outstanding sent buffers have been
 * successfully received by the other side so that we can mark them as being
 * available for handing off to the application again.
 * 
 * (This should effectively be called by the interrupt handler, so interrupts
 * should already be turned off.)
 *****************************************************************************/
static err_t lwip_lib_sent_handler(void *arg, struct tcp_pcb *tpcb, u16_t len) {
	err_t retVal = ERR_OK;  // (may be set to ERR_VAL, which is defined in lwip\src\include\lwip\err.h
	
	//xil_printf("Other side acknowledged receiving %d bytes\r\n",len);	
	//xil_printf("current firstSentBufferIndex = %d\r\n",firstSentBufferIndex);
	

	if ( (firstSentBufferIndex == -1) && (len > 0) ) {
		if ( len == 1 ) {
			// This is almost certainly the 1-byte ACK normally received at the
			// beginning of the connection, so it's OK.
		} else {
			xil_printf("**** STRANGE: Data acknowledged that we never sent (%d bytes)\r\n",len);
			// We'll ignore this weird condition and just continue.
		}
		return ERR_OK;
	}
	

	while ( len > 0 ) {
		// Note that we will not start this loop if firstSentBufferIndex == -1

		// We shouldn't have to make this check, but we'll leave it in for safety
		if ( send_buffers[firstSentBufferIndex].wasSent == 0 ) {
			xil_printf("**** STRANGE: least recently sent buffer wasn't sent? Probably an error...\r\n");
			firstSentBufferIndex = (firstSentBufferIndex + 1) % NUM_SEND_BUFFERS;
			if ( firstSentBufferIndex == currentWorkingBufferIndex ) {
				xil_printf("**** STRANGE: Can't find any sent buffers.  Probably an error...\r\n");
				firstSentBufferIndex = -1;
				retVal == ERR_VAL;
				break;
			}
			continue;
		}

		// Normal Situtation -- The least recently sent buffer was acknowledged
		// (at least partially)

		if ( send_buffers[firstSentBufferIndex].sentLength > len ) {
			// Only part of the buffer was ACK'd -- we'll have to wait for
			// another ACK to allow this buffer to be used again.
			send_buffers[firstSentBufferIndex].sentLength -= len;
			len = 0;  // exit loop on next iteration
		} else {
			// A full buffer was ACK'd, so we can mark it for use again.
			len -= send_buffers[firstSentBufferIndex].sentLength;
			send_buffers[firstSentBufferIndex].sentLength = 0;
			send_buffers[firstSentBufferIndex].wasSent = 0;
			firstSentBufferIndex = (firstSentBufferIndex + 1) % NUM_SEND_BUFFERS;

			if ( firstSentBufferIndex == currentWorkingBufferIndex ) {
				// If firstSentBufferIndex catches up to currentWorkingBufferIndex, 
				// we have no buffers that have been sent out.
				// (If currentWorkingBufferIndex catches up to firstSentBufferIndex,
				//  all the buffers have been sent out)
				firstSentBufferIndex = -1;
				if ( len > 0 ) {
					xil_printf("**** STRANGE: Extra data was acknowledged that we didn't send (leftover: %d bytes)\r\n",len);
					retVal = ERR_VAL;
				}
				break;
			}
		}
	}


	return retVal;
}


/******************************************************************************
 * This gets called if there is any connection error (e.g., ran out of memory)
 * 
 * This is assigned as a callback in lwip_lib_accept(), using tcp_err().  As
 * such, it needs to match the prototype shown in rawapi.txt
 * 
 * All we do is use our internal close_connection() function to try and reset
 * the global values.
 *****************************************************************************/
static void lwip_lib_err_handler(void *arg, err_t err) {
	xil_printf("Connection error -- closing connection\r\n");
	
	// close_connection() assumes a valid global pcb, which is a little dicey,
	// according to rawapi.txt: the reason we don't get a pcb argument here
	// is because it may have already been deallocated upon error.  Could that
	// affect our global pcb?  We'll check for a NULL pcb, so we should be OK.
	close_connection(arg);	
}


/******************************************************************************
 * This gets called at regular intervals by lwIP.
 * 
 * This is assigned as a callback in lwip_lib_accept(), using tcp_poll().  As
 * such, it needs to match the prototype shown in rawapi.txt
 * 
 * We're not using this functionality here because we currently do not need it.
 *****************************************************************************/
static err_t lwip_lib_poll_handler(void *arg, struct tcp_pcb *tpcb) {
	/*
	 * We could keep a count of how many times this function is called and
	 * firstSentBufferIndex == currentWorkingBufferIndex (indicating that we
	 * don't have any free buffers for new data) and then kill the connection
	 * if it appeared to no longer be useful.  But the tolerance for such a
	 * condition would be application-dependent, so we will leave it to the
	 * application to keep track of such conditions (i.e., when lwip_lib_send_data()
	 * returns NULL) and determine what it wants to do about it.
	 */
	
	/*
	 * Another standard technique in a poll callback is to try to resend data
	 * if tcp_write() failed.  In our current design, this isn't much of an
	 * issue.  If we did want to handle it, we could add another field to our
	 * send_buffer struct that indicated failed_to_send.  This would add some
	 * complication; for instance, we could not send another buffer until the
	 * failed_to_send buffer sent successfully (because we wouldn't be able to
	 * identify which buffer was ACK'd if we don't keep them in order).
	 * 
	 * This would add a fair amount of complication, and would probably add 
	 * confusion at the application layer without much added functionality.
	 */
}


/******************************************************************************
 * This is called if either side initiates a close connection or if our
 * lwip_lib_err_handler() gets called.
 * 
 * It deallocates the connection_state (passed in as the single parameter)
 * and the global_pcb.
 * 
 * NOTE: This is not set up to allow for re-connections -- they would probably
 * work fine, but it has not been tested.
 *****************************************************************************/
static void close_connection( void *arg ) {
	struct connection_state *state = arg;

	if ( state != NULL ) {
		mem_free( state );
	}

	if ( global_pcb != NULL ) {

		// Un-register the callbacks
		tcp_recv( global_pcb, NULL );
		tcp_err(  global_pcb, NULL );
		//tcp_poll( global_pcb, NULL, 1 );
		tcp_sent( global_pcb, NULL );
	
		// Close the connection and deallocate global_pcb
		if ( tcp_close( global_pcb ) != ERR_OK ) {
			xil_printf("Problem closing connection!  Trying tcp_abort()\r\n");
			tcp_abort( global_pcb );
		}
	
		// Reset global pcb
		global_pcb = NULL;
	}
	
	// Reset global receive buffer
	global_recv_buffer_len = 0;

	xil_printf("Connection closed\r\n");

	// If we need to turn off the interrupt timer, this should work, according
	// to drivers\tmrctr_v1_00_b\src\xtmrctr_low_level_example.c
	//XTmrCtr_mDisableIntr(XPAR_OPB_TIMER_1_BASEADDR, 0);

	// Here's another option for dealing with turning off our interrupt handler,
	// although it probably has the same effect as just calling
	// microblaze_disable_interrupts()
	//microblaze_register_handler(XNullHandler, NULL);

	return;
}


/******************************************************************************
 * Sets up the global network interface (global_netif) we will use for TCP
 * 
 * The IP must be specified, but subnet and gateway can be NULL, in which case
 * reasonable defaults will be used.
 *****************************************************************************/
static XStatus set_global_netif( uint8 ip[4], uint8 subnet[4], uint8 gateway[4] ) {
	struct ip_addr ipaddr, netmask, gw;
	//!!!jwh
	int netifSize;
	//!!!jwh
	unsigned char mac[] = {0x00, 0x0a, 0x35, 0x00, 0x01, 0x02};

	if ( ip == NULL ) {
		xil_printf("Must specify non-NULL IP!\r\n");
		return XST_FAILURE;
	}

	IP4_ADDR(&ipaddr, ip[0],ip[1],ip[2],ip[3]);

	if ( gateway != NULL ) {
		IP4_ADDR(&gw, gateway[0],gateway[1],gateway[2],gateway[3]);
	} else {
		// Default gateway will be based on the IP (probably: xxx.xxx.xxx.1)
		IP4_ADDR(&gw, ip[0],ip[1],ip[2], (ip[3]!=0x01) ? 0x01 : 0x02);
	}
	
	if ( subnet != NULL ) {
		if ( subnet[3] != 0x00 ) {
			xil_printf("Subnet does not end in 0.  Are you sure this is correct?\r\n");
		}
		IP4_ADDR(&netmask,subnet[0],subnet[1],subnet[2],subnet[3]); //Set subnet msk
	} else {
		// Default subnet == 255.255.255.0
		IP4_ADDR(&netmask,DEFAULT_SUBNET[0],DEFAULT_SUBNET[1],DEFAULT_SUBNET[2],DEFAULT_SUBNET[3]); //Set subnet msk
	}
	
	//!!!jwh (some debugging, doesn't affect behavior)
	netifSize = sizeof( struct netif );
	global_netif = (struct netif *)mem_malloc( netifSize );
//	global_netif = (struct netif *)mem_malloc( sizeof( struct netif ) );
	if ( global_netif == NULL ) {
		xil_printf("out of memory for default_netif\r\n");
		return XST_FAILURE;
	}

	//xil_printf("calling netif_add\r\n");
//!!!jwh
//   	global_netif = netif_add(global_netif, &ipaddr, &netmask, &gw, &XEmacIf_ConfigTable[0], 
//   						 xemacif_init, ip_input );

//!!!jwh above is replaced with this in lwip 1.3:
	if (!xemac_add(global_netif,
				   &ipaddr,
				   &netmask,
				   &gw,
				   mac,
				   XPAR_XPS_ETHERNETLITE_0_BASEADDR))
	{
		xil_printf("error adding N/W interface\n\r");
		return XST_FAILURE;
	}

	netif_set_default(global_netif);
	
	//!!!jwh
	/* specify that the network if is up */
	netif_set_up(global_netif);
	
	return XST_SUCCESS;
}

/******************************************************************************
 * Our simple interrupt handler.
 * 
 * If the interrupt as caused by the timer, we'll call the two functions that
 * have to be called at regular intervals for lwIP to work.
 *****************************************************************************/
static void lwip_lib_interrupt_handler (void* baseaddr_p) {

//!!!jwh -- replaced entire interrupt
//  int baseaddr = *(int *)baseaddr_p;
//   unsigned int csr;
		
  // Read timer 0 CSR to see if it raised the interrupt
  //csr = XTmrCtr_mGetControlStatusReg(XPAR_OPB_TIMER_1_BASEADDR, 0);
  
//  if (csr & XTC_CSR_INT_OCCURED_MASK) {
//
//	// It's not the best idea to call these functions from within the interrupt
//	// timer because it might take a while to return.  But the other option
//	// would be to force the application to call these at regular intervals,
//	// which is not as nice and clean of an interface.
//	xemacif_input( global_netif );
//	
//	// This will deal with calling tcp_fasttmr(), tcp_slowtmr()
//	my_tmr();
//     
//    // Clear the timer interrupt
//    //XTmrCtr_mSetControlStatusReg(XPAR_OPB_TIMER_1_BASEADDR, 0, csr);
//  } 

	static Xuint32 timerCnt = 0;
	Xuint32 regVal;
	
//	//clear the interrupt
	regVal = XTmrCtr_mGetControlStatusReg(TimerHandle.BaseAddress, 0);
	XTmrCtr_mSetControlStatusReg(TimerHandle.BaseAddress, 0, regVal);

	//print for debug
	timerCnt++;
	if (timerCnt > 1000)
	{
		timerCnt = 0;
//		print(".");
//		stats_display();
	}
	
	//maintain a ms_timer
	ms_timer++;
	
	// This will deal with calling tcp_fasttmr(), tcp_slowtmr()
	my_tmr();
	
	//	// It's not the best idea to call these functions from within the interrupt
//	// timer because it might take a while to return.  But the other option
//	// would be to force the application to call these at regular intervals,
//	// which is not as nice and clean of an interface.
	xemacif_input( global_netif );

}


/******************************************************************************
 * my_tmr - Called Periodically to dispatch TCP and ARP timers
 * 
 * This was stolen from Xilinx's echo server example, just like every other
 * lwIP project, ever.
 *****************************************************************************/
static void my_tmr(void) {

	// A static variable retains its value across calls to this function
	// So it's much nicer than a global value.
	static my_timer = 0;
	
	my_timer++;

   if(my_timer == 10) {
      my_timer = 0;
   }
   if(my_timer & 1) {
      /* Call tcp_fasttmr() every 2 ms, i.e.,
       * every other timer my_tmr() is called. */
      tcp_fasttmr();
   }
   if(my_timer == 0 || my_timer == 5) {
      /* Call tcp_slowtmr() every 5 ms, i.e.,
       * every fifth timer my_tmr() is called. */
      tcp_slowtmr();
      if (tcp_ticks%2000 == 0)
         /* Call etharp_tmr() every 20th call to tcp_slowtmr().
          * tcp_ticks is a global var defined in core/tcp.c */
         etharp_tmr();
   }
}

/******************************************************************************
 * Enables interrupts and starts the timer
 * 
 * The timer will trigger an interrupt at regular intervals to allow us to
 * look for network data and call the tcp_tmr routines.
 *****************************************************************************/
static void start_interrupt_timer() {
	//!!!jwh updated to use canonical defintion of hardware, hardware now
	//       requires ethernet interrupts, but these are init'ed by lwip

	// Canonical definitions for hardware
	//	XPAR_INTC_0_DEVICE_ID
	//	XPAR_INTC_0_EMACLITE_0_VEC_ID
	//	XPAR_INTC_0_TMRCTR_0_VEC_ID
	//
	//	XPAR_TMRCTR_0_DEVICE_ID
	//	XPAR_EMACLITE_0_DEVICE_ID
	
	Xuint32 currentCnt;
	XStatus Status;
	xil_printf("   Configuring timer interrupt\r\n");

#define ONE_MS (XPAR_CPU_CORE_CLOCK_FREQ_HZ / 1000)
	
	//Set up the timer
	XTmrCtr_SetOptions(&TimerHandle, 0, XTC_INT_MODE_OPTION | XTC_AUTO_RELOAD_OPTION | XTC_DOWN_COUNT_OPTION);
	XTmrCtr_SetResetValue(&TimerHandle, 0, ONE_MS);
	//XTmrCtr_SetResetValue(&TimerHandle, 0, 50000);
	XTmrCtr_Reset(&TimerHandle, 0);

	currentCnt = XTmrCtr_GetValue(&TimerHandle, 0);
	xil_printf("   Timer reset to create 1 ms timer: %d\r\n", currentCnt);
    
	XTmrCtr_Start(&TimerHandle, 0);

	//!!!jwh
	//Let LWIP turn on ethernet interrupts
	//LWIP CONNECTS THE ETHERNET HANDLER!!!
	//from xemacliteif.c
//#if NO_SYS
//	XIntc_RegisterHandler(xtopologyp->intc_baseaddr,
//			xtopologyp->intc_emac_intr,
//			(XInterruptHandler)XEmacLite_InterruptHandler,
//			xemaclitep);
//#else
//.
//.
//.
//	/* set Rx, Tx interrupt handlers */
//	XEmacLite_SetRecvHandler(xemaclitep, (void *)(xemac), xemacif_recv_handler);
//	XEmacLite_SetSendHandler(xemaclitep, (void *)(xemac), xemacif_send_handler);
//
//	/* enable Rx, Tx interrupts */
//    	XEmacLite_EnableInterrupts(xemaclitep);

    Status = XIntc_Connect(&InterruptHandle,
    					   XPAR_INTC_0_TMRCTR_0_VEC_ID,
                           (XInterruptHandler) lwip_lib_interrupt_handler,
                           (void *) &TimerHandle);
    if (Status != XST_SUCCESS)
    {
		print("Fail attaching timer!\n\r");
        return;
    }
	
	//Start all interrupts
    XIntc_Enable(&InterruptHandle, XPAR_INTC_0_EMACLITE_0_VEC_ID);
    XIntc_Enable(&InterruptHandle, XPAR_INTC_0_TMRCTR_0_VEC_ID);

    /*
     * Start the interrupt controller such that interrupts are enabled for
     * all devices that cause interrupts, specific real mode so that
     * the timer counter can cause interrupts thru the interrupt controller.
     */
    Status = XIntc_Start(&InterruptHandle, XIN_REAL_MODE);
    if (Status != XST_SUCCESS)
    {
        return;
    }
	
	//Now, enable interrupts on the processor
    /*
     * Enable the Interrupts on the Microblaze
     */
    //microblaze_enable_interrupts();

//!!!jwh -- old code. not registering the handler now, and not enabling
//       interrupts here. interrupts should be enabled by the main thread

  //	microblaze_register_handler(lwip_lib_interrupt_handler,NULL);
  // 	microblaze_enable_interrupts();
   
//!!!jwh -- original lwip_lib hw used OPB, not PLB-based hardware.
   
   // This is a pretty low-level way to interface with the timer controller
   // It was stolen from the echo server example from Xilinx.
   // We could probably check out drivers\tmrctr_v1_00_b\examples\xtmrctr_intr_example.c
   // for better ways of interfacing with the timer, but we know that this
   // works as desired.
      
   // For all of these XTmrCtr_* calls, the '0' is the timer #, so changing it 
   // to '1' should work to enable another timer, if desired.

   // Set the number of cycles the timer counts before interrupting
//#define ONE_MS (XPAR_CPU_CORE_CLOCK_FREQ_HZ / 1000)

//	XTmrCtr_mSetLoadReg(XPAR_OPB_TIMER_1_BASEADDR, 0, ONE_MS);
////	XTmrCtr_mSetLoadReg(XPAR_OPB_TIMER_1_BASEADDR, 0, 10000);
//
//   // Reset the timers, and clear interrupts
//   XTmrCtr_mSetControlStatusReg(XPAR_OPB_TIMER_1_BASEADDR, 0, XTC_CSR_INT_OCCURED_MASK | XTC_CSR_LOAD_MASK );
//   
//   // start the timers
//   XTmrCtr_mSetControlStatusReg(XPAR_OPB_TIMER_1_BASEADDR, 0, XTC_CSR_ENABLE_TMR_MASK | XTC_CSR_ENABLE_INT_MASK | XTC_CSR_AUTO_RELOAD_MASK | XTC_CSR_DOWN_COUNT_MASK);
   
   return;
}



/******************************************************************************
 * Simple function to pretty-print out a MAC address
 *****************************************************************************/
static void print_mac( uint8 mac[6] ) {
	xil_printf("%02X-%02X-%02X-%02X-%02X-%02X",mac[0],mac[1],mac[2],mac[3],mac[4],mac[5],mac[6]);
}


/******************************************************************************
 * Simple function to pretty-print out an IP address
 *****************************************************************************/
static void print_ip4_addr( struct ip_addr ip_addr ) {
	xil_printf("%d.%d.%d.%d",
           (unsigned int)(ntohl(ip_addr.addr) >> 24 & 0xff),
           (unsigned int)(ntohl(ip_addr.addr) >> 16 & 0xff),
           (unsigned int)(ntohl(ip_addr.addr) >> 8 & 0xff),
           (unsigned int)(ntohl(ip_addr.addr) & 0xff));
}

//!!!jwh We are using 100 MBPS anyways
///******************************************************************************
// * Unused function that purports to force the connection into 10Mbps mode
// * 
// * This probably has to be tailored to each specific ethernet PHY!  Especially
// * the XEmac_PhyRead() and XEmac_PhyWrite() functions.
// *****************************************************************************/
//static XStatus force10Mbps( ) {
//	// All stolen from Lwip_if, so we hope it works as advertised
//	// Actually, it is probably only going to work for a specific Ethernet PHY
//	
//	// Override auto-negotiation to force 10 Mbps link
//	XEmac *emac;
//	uint16 phy_data = 0;
//	Xboolean connected = XFALSE;
//	int i;
//	
//	emac = ((XEmacIf_Config*)(global_netif->state))->instance_ptr;
//	XEmac_PhyReset(emac);
//	XEmac_PhyWrite(emac, 3, 0, 0x100);
//
//	xil_printf("Waiting for Ethernet connection to PHY chip\r\n");
//
//	// Wait until PHY reports good link
//	// We only have to do this because we reset the PHY, right??
//	do {
//		XEmac_PhyRead(emac, 3, 1, &phy_data);
//		if (phy_data & 0x04) {
//			connected = XTRUE;
//		}
//
//		i++;
//#define LINK_TIMEOUT 100000
//	} while (i < LINK_TIMEOUT && connected == XFALSE);
//
//    // Check that a physical link was successfully established
//	if (connected) {
//		xil_printf("Done waiting for Ethernet connection, connection succeeded\r\n");
//		return XST_SUCCESS;
//	} else {
//		xil_printf("Done waiting for Ethernet connection, connection failed\r\n");
//		return XST_DEVICE_NOT_FOUND;
//	}
//}
