/*

Relay Software
--------------

NOTE: The PLATFORM constant selects the target system as follows:

NOTE: After you install Dynamic C, and before you compile this code, edit
BIOS\RABBITBIOS.C and change XMEM_RESERVE_SIZE to 0x4000L or greater.

The Relay (Version 7+) accepts connections on a single IP port, only
one connection at a time. Clients can connect, exercise the driver, and read
and write to the configuration file inn EEPROM. Clients cannot change the
currently-active configuration. To activate the configuration stored in
EEPROM, you must reset the driver with the hardware reset switch. If you
want to return the EEPROM configuration file to the factory default, then
you hold the configuration switch down and reset the driver. Hold the
configuration switch down for at least three seconds after you release
the hardware reset switch. The factory default configuration is defined by the
constants beginning with the prefix "MY_".

The Relay (Version 7+) allows clients to set the global variable logged_in with
a LOGIN message accompanied by the a string matching the global password string.
The global variable security_level, which is also a configuration parameter,
determines when the client is required to log in. With security level zero, the
client is never required to log in. With security level one or higher, the client
must log in to read from or write to the configuration files. With security
level two or higher, the client must log in to execute any message other than
a LOGIN message.

Version 8 adds the MAC_READ instruction, to which the Relay responds by
transmitting its MAC address. Version 8 corrects a bug in the buffered TCP/IP
read routine, and guarantees that closing the TCP/IP socket will always cause
the Relay to return to its listening state. Verion 8 corrects a bug in the
security level, so that trying to execute any instruction other than a LOGIN
when the security level is 2 and you have not yet logged in results in an
immediate closing of the socket, and a return to the listening state.

Version 9 slows down the controller interface by switching to seven wait
states instead of 3. This allows it to work in the A2064A and A2064X boards
without modification. We add the STREAM_DELETE job, which writes a constant
byte value repeatedly to the same address.

Version 10 adds comments and cleans up TCPIP error handling and socket closure.
In particular, when the relay receives an invalid message start code, it closes
the existing socket immediately, instead of flushing the socket and waiting for
more input.

Version 11 is compatible with Dynamic C Version 9.21, and backwards-compatible
with Dynamic C Version 7.33. To get the code to work on DC9.21, we had to
declare as static variables all main() variable that we pass to our in-line
assembler code. The compiled code is marginally faster at TCPIP data transfer
with DC9.21 than with DC7.33.

Version 12 begins by improving the implementation of carriage returns in the
debug reporting. We move the controller interface configuration into a
separate routine, which we comment more heavily. We speed up the stream read
assembler loop slightly by using ld a,(hl) instead of ld a,0xE000.

Version 12 continues with the addition of the zero-length call to our buffered
socket read routine. The zero-length call clears the TCPIP socket buffer. Only
when the buffer is clear will tcp_tick inform us that a socket has been closed
or broken. We use the zero-length call in BYTE_POLL to abort when the client
closes the socket. Until now, all versions of this code would fail to abort
from a BYTE_POLL whenever there was incoming data still to be read out of the
TCPIP socket.

Version 12 continues with the addition of a ping to the default router at
start-up. This ping serves to advertise the RCM's MAC address to the router,
and so bring the router up to date if we have just switched one RCM for another
on the same physical socket, and assigned both the same IP address. As part
of this addition to the code, we eliminate tcp_config and replace it with
calls to ifconfig.

Version 12 continues with multi-platform support through the PLATFORM
constant.

Version 12 continues with improvements to the response to RAM buffer overflow
and messages whose content length exceeds the input buffer length.

*/

// Set PLATFORM to select the target system for compilation.
// 1: A2037E (RCM2200 on LWDAQ Driver with Ethernet Interface)
// 1: A2064A (RCM2200 on TCPIP-VME Interface)
// 2: A2064F (RCM3200 on TCPIP-VME Interface)
#define PLATFORM 1

// Version and debug settings. Starting with Version 12, we
// determined that leaving the RCM disables the debug text
// output automatically when it runs without its programming
// connector. All printf calls are ignored. The default values
// of REPORT and REPORT_TCP are now 1, so debug text appears
// if and only if you are running the RCM through its programming
// connetor.
#define VERSION_NUM 12 // current version number
#define REPORT 1 // set to 1 to debug message receiver
#define REPORT_TCP 1 // set to 1 to debug TCP/IP buffer

// Default configuration
#define TCPCONFIG 0 // we define our own configuration
#define USE_ETHERNET 1 // use ethernet, not wireless
#define MY_IP_ADDRESS "10.0.0.37" // IP of driver
#define MY_GATEWAY "10.0.0.1" // IP of gateway
#define MY_NETMASK "255.255.255.0" // IP mask
#define MY_PORT 90 // IP connection port
#define MY_PASSWORD "LWDAQ" // password for access
#define MY_SECURITY 1 // security level
#define MY_TIMEOUT 0 // tcpip timeout seconds (0=infinite)
#define MY_ID "not_assigned" // driver serial number
#define MY_TIMESTAMP "00000000000000"// null time stamp

// Ethernet configuration
#define ETH_MAXBUFS 3
#define ETH_MTU 1514
#define BUFF_SIZE (ETH_MTU-40)
#define RAM_BUFF_SIZE (6*BUFF_SIZE)
#define TCP_BUF_SIZE (6*BUFF_SIZE)
#define CHECK_TCP_COUNT 1000

// LWDAQ messages
#define START_CODE 0xA5 // correct value for start code
#define CLOSE_CODE 0x04 // close connection character
#define END_CODE 0x5A // correct value for end code
#define START_OFFSET 0
#define ID_OFFSET 1
#define CLEN_OFFSET 5
#define CONTENT_OFFSET 9
#define FRAME_SIZE 10
#define VERSION_READ 0x00000000
#define BYTE_READ 0x00000001
#define BYTE_WRITE 0x00000002
#define STREAM_READ 0x00000003
#define DATA_RETURN 0x00000004
#define BYTE_POLL 0x00000005
#define LOGIN 0x00000006
#define CONFIG_READ 0x00000007
#define CONFIG_WRITE 0x00000008
#define MAC_READ 0x00000009
#define STREAM_DELETE 0x0000000A


#if PLATFORM == 2
   // Enable the auxilary I/O data bus for Type-2 platforms.
   #define PORTA_AUX_IO
#endif

// Controller registers
#define CS_ADDR 40 // configuration switch address

// EEPROM file system.
// In Bios\RabbitBios.c set XMEM_RESERVE_SIZE to 0x4000L.
#define FS2_USE_PROGRAM_FLASH 16 // kbytes for file system
#define CONFIG_LENGTH 1024 // bytes for config file buffer
#define CONFIG_FILE_NAME 10 // a numerical name for the config file
#define SEPCHARS " :\n,;=" // separator characters in config file

// General
#define BITS_PER_BYTE 8 // number of buts per byte
#define MAC_LENGTH 6 // bytes per MAC address

// Libraries
#memmap xmem
#use "dcrtcp.lib"
#use "fs2.lib"

// function prototypes
long flip_bytes(long);
void write_controller_byte(char,char);
char read_controller_byte(char);
int read_relay_configuration();
void configure_controller_interface();
int write_relay_configuration(char* contents);
int buffered_socket_read(tcp_Socket* s, byte* dp, int len);

// global variables
char logged_in;
int ip_port,tcp_timeout,security_level;
char password[32];
char configuration[CONFIG_LENGTH];
char tcp_buffer[RAM_BUFF_SIZE];
int tcp_first,tcp_available;


/*
main listens for a TCPIP connection, and then attends to that
connection while rejecting all others. When the socket closes,
main goes back to listening.
*/
main()
{
	// These variables we will use in assembly code, so they must be
	// static. The compiler must be able to translate the name into
	// a number for the assembly instructions.
	static char value,count_lb,count_hb;
	static char out_buffer[BUFF_SIZE];
	static int io_addr;

	// tcpip socket variables must be static.
	static tcp_Socket socket;

	// These variables will not be used in assembler, so they need
	// not be static.
	char code,register_addr;
	char in_buffer[BUFF_SIZE];
	char message[255];
	int socket_open,byte_num,status;
	long in_length,content_length,in_message_id;
	long *long_ptr;
	longword router_addr;
	char router_addr_string[20];

	// Set up the I/O registers to communicate with the controller
	// logic chip.
	configure_controller_interface();

	// Start up and initialize the TCPIP stack.
	sock_init();

	// Configure the LWDAQ TCP/IP interface.
	strcpy(password,MY_PASSWORD);
	ip_port=MY_PORT;
	security_level=MY_SECURITY;
	tcp_timeout=MY_TIMEOUT;

	// Read relay configuration file.
	read_relay_configuration();

	// Ping the router to register MAC address.
	ifconfig(IF_ANY,IFG_ROUTER_DEFAULT,&router_addr,IFS_END);
	inet_ntoa(router_addr_string,router_addr);
	if (REPORT) printf("Attmpting to ping router at %s.\n",
		router_addr_string);
	tcp_tick(NULL);
	status=_ping(router_addr,0);
	tcp_tick(NULL);
   if (REPORT) {
	   if (status==-1) printf("Could not resolve router hardware address.\n");
	   if (status==0) printf("Ping succeeded.\n");
	   if (status==1) printf("Failed sending packet to router.\n");
   }

	while (1) {
		// Reset LWDAQ interface variables.
		logged_in=0;
		tcp_first=-1;
		tcp_available=0;
		status=0;

		if (REPORT) printf("Listening for connection on port %d...\n",ip_port);
		tcp_listen(&socket,ip_port,0,0,NULL,0);
		sock_wait_established(&socket,0,NULL,&status);
		if (REPORT) printf("Connection established...\n");

		while (1) {
			if (tcp_available==0) {
				if (REPORT) printf("Waiting for input...\n");
				sock_wait_input(&socket,tcp_timeout,NULL,&status);
			}

			buffered_socket_read(&socket,&code,sizeof(code));
			if (code!=START_CODE) {
				if (code==CLOSE_CODE) {
					if (REPORT) printf("Close code received, closing socket.\n");
				} else {
					if (REPORT) printf("Invalid start code, closing socket.\n");
				}
				sock_close(&socket);
				break;
			}

			buffered_socket_read(&socket,(char*)&in_message_id,sizeof(in_message_id));
			in_message_id=flip_bytes(in_message_id);

			buffered_socket_read(&socket,(char*)&in_length,sizeof(in_length));
			in_length=flip_bytes(in_length);
			if (in_length>BUFF_SIZE) {
				if( REPORT ) printf("Message content too long, closing socket.\n");
				sock_close(&socket);
				break;
			}

			if (in_length>0) buffered_socket_read(&socket,in_buffer,(int) in_length);

			buffered_socket_read(&socket,&code,sizeof(code));
			if (code!=END_CODE) {
				if (REPORT) printf("Invalid end code, closing socket.\n");
				sock_close(&socket);
				break;
			}

			if ((security_level>=2) && (logged_in==0) && ((int) in_message_id != LOGIN)) {
				if (REPORT) printf("LOGIN required for security level %d, closing socket.\n",
					security_level);
				sock_close(&socket);
				break;
			}

			switch ((int) in_message_id) {

				case VERSION_READ:{
					if (REPORT) printf("VERSION_READ\n");
					out_buffer[START_OFFSET]=START_CODE;
					long_ptr=(long*)&out_buffer[ID_OFFSET];
					*long_ptr=flip_bytes(DATA_RETURN);
					long_ptr=(long*)&out_buffer[CLEN_OFFSET];
					*long_ptr=flip_bytes(sizeof(long));
					long_ptr=(long*)&out_buffer[CONTENT_OFFSET];
					*long_ptr=flip_bytes(VERSION_NUM);
					out_buffer[CONTENT_OFFSET+sizeof(long)]=END_CODE;
					sock_write(&socket,out_buffer,FRAME_SIZE+sizeof(long));
					break;
				}

				case BYTE_READ:{
					register_addr=in_buffer[3];
					value=read_controller_byte(register_addr);
					if (REPORT) printf("BYTE_READ from %d of %d.\n",register_addr,value);
					out_buffer[START_OFFSET]=START_CODE;
					long_ptr=(long*)&out_buffer[ID_OFFSET];
					*long_ptr=flip_bytes(DATA_RETURN);
					long_ptr=(long*)&out_buffer[CLEN_OFFSET];
					*long_ptr=flip_bytes(sizeof(char));
					out_buffer[CONTENT_OFFSET]=value;
					out_buffer[CONTENT_OFFSET+sizeof(char)]=END_CODE;
					sock_write(&socket,out_buffer,FRAME_SIZE+sizeof(char));
					break;
				}

				case BYTE_WRITE:{
					register_addr=in_buffer[3];
					value=in_buffer[4];
					write_controller_byte(register_addr,value);
					if (REPORT) printf("BYTE_WRITE to %d of %d.\n",register_addr,value);
					break;
				}

				case STREAM_READ:{
 					// The register address will be the lower six bits specified
					// in the stream read register address byte.
					register_addr=in_buffer[3] & 0x3F;

               // Set up the stream read according to PLATFORM. See
               // controller interface configuration routine for more
               // information.
					#if PLATFORM == 1
               io_addr=0xE000;
               WrPortI(PADR,NULL,(0x40 | register_addr));
               #endif
               #if PLATFORM == 2
               io_addr=(0xC000 | register_addr);
               WrPortI(PEB3R,NULL,0xFF);
               #endif

					// Start to compose the return message using the output
					// buffer. We copy the content length from the input buffer.
					// We determine the number of bytes requested by looking at
					// the input buffer. We have to flip the byte order in the
					// input buffer for this processor.
					out_buffer[START_OFFSET]=START_CODE;
					long_ptr=(long*)&out_buffer[ID_OFFSET];
					*long_ptr=flip_bytes(DATA_RETURN);
					long_ptr=(long*)&in_buffer[4];
					content_length=flip_bytes(*long_ptr);
					long_ptr=(long*)&out_buffer[CLEN_OFFSET];
					*long_ptr=flip_bytes(content_length);
					if (REPORT)
					printf("STREAM_READ from %d of length %lu...\n",
						register_addr,content_length);

 					// Transmit the return message header.
					sock_write(&socket,out_buffer,9);

					// Enter the stream read loop, which uses assembler
					// code at its core.
					while (content_length>0) {
						if (content_length>BUFF_SIZE) {
							count_hb=(char) ((BUFF_SIZE & 0x0000FF00) >> BITS_PER_BYTE);
							count_lb=(char) (BUFF_SIZE & 0x000000FF);
						}
						else {
							count_hb=(char) ((content_length & 0x0000FF00) >> BITS_PER_BYTE);
							count_lb=(char) (content_length & 0x000000FF);
						}

						#asm
						ld de,out_buffer
						ld hl,count_hb
						ld c,(hl)
						ld a,c
						cp a,0
						jr z,remainder_sr

						ld b,0x00
						ld hl,(io_addr)
						outer_loop_sr:
							inner_loop_sr:
								ioe ld a,(hl)		;5
								ld (de),a			;7
								inc de				;2
								dec b					;2
							jr nz,inner_loop_sr	;5 total 21 clocks/byte
							dec c
						jr nz,outer_loop_sr

						remainder_sr:

						ld hl,count_lb
						ld b,(hl)
						ld a,b
						cp a,0
						jr z,done_sr

						ld hl,(io_addr)
						loop_sr:
							ioe ld a,(hl)
							ld (de),a
							inc de
							dec b
						jr nz,loop_sr

						done_sr:
						#endasm

						if (content_length>BUFF_SIZE) {
							sock_write(&socket,out_buffer,BUFF_SIZE);
							content_length=content_length-BUFF_SIZE;
						}
						else {
							sock_write(&socket,out_buffer,(int) content_length);
							content_length=0;
						}
					}

					out_buffer[0]=END_CODE;
					sock_write(&socket,out_buffer,1);
					break;
				}

				case STREAM_DELETE:{
					// The register address will be the lower six bits specified
					// in the stream read register address byte.
					register_addr=in_buffer[3] & 0x3F;

               // In the A2037E and A2064A, the IO Address lies within the
					// range 0xE000 to 0xFFFF so that we provoke a pulse on PE7,
               // which we are use as our read and write strobe. In the
               // A2064F, the IO Address lies within 0xC000 and we use PE6 as
               // the strobe. In the A2037E and A2064A, we write the register
               // address to Parallel Port A, as well as a 1 in bit 6 to
               // indicate a read cycle. In the A2064F we Set !CW, or PE3,
               // to 0 to indicate a read cycle, but don't have to write
               // the register address to the parallel port.
					#if PLATFORM == 1
               io_addr=0xE000;
               WrPortI(PADR,NULL,register_addr);
               #endif
               #if PLATFORM == 2
               io_addr=(0xC000 | register_addr);
               WrPortI(PEB3R,NULL,0x00);
               #endif

					// Obtain content length from the input buffer.
					// We have to flip the byte order. We read the
					// delete value from the input buffer also. We will
					// write this value to the target ram portal, and
					// so delete existing data in the target ram.
					long_ptr=(long*)&in_buffer[4];
					content_length=flip_bytes(*long_ptr);
					value=in_buffer[8];
					if (REPORT)
						printf("STREAM_DELETE to %d of length %lu, over-writing with %d...\n",
							register_addr,content_length,value);

					// Enter the stream delete loop, which uses assembler
					// code at its core.
					while (content_length>0) {
						if (content_length>BUFF_SIZE) {
							count_hb=(char) ((BUFF_SIZE & 0x0000FF00) >> BITS_PER_BYTE);
							count_lb=(char) (BUFF_SIZE & 0x000000FF);
						}
						else {
							count_hb=(char) ((content_length & 0x0000FF00) >> BITS_PER_BYTE);
							count_lb=(char) (content_length & 0x000000FF);
						}

						#asm
						ld hl,count_hb
						ld c,(hl)
						ld a,c
						cp a,0
						jr z,remainder_sd

						ld hl,value
						ld a,(hl)
						ld b,0x00
						ld hl,(io_addr)

						outer_loop_sd:
							inner_loop_sd:
								ioe ld (hl),a		;5
								dec b					;2
							jr nz,inner_loop_sd	;5 total 12 clocks/byte.
							dec c
						jr nz,outer_loop_sd

						remainder_sd:
						ld hl,count_lb
						ld b,(hl)
						ld a,b
						cp a,0
						jr z,done_sd

						ld hl,value
						ld a,(hl)

						ld hl,(io_addr)
						loop_sd:
							ioe ld (hl),a
							dec b
						jr nz,loop_sd

						done_sd:
						#endasm

						if (content_length>BUFF_SIZE) {
							content_length=content_length-BUFF_SIZE;
						}
						else {
							content_length=0;
						}
					}

					break;
				}

				case BYTE_POLL:{
					register_addr=in_buffer[3];
					value=in_buffer[4];
					if (REPORT) printf("POLL_BYTE of %d for %d...\n",register_addr,value);
					while (read_controller_byte(register_addr) != value) {
						status=buffered_socket_read(&socket,NULL,0);
							if (status<0) goto sock_err;
					}
					break;
				}

				case LOGIN:{
					if (REPORT) printf("LOGIN\n");
					if (in_length<1) {
						if (REPORT) printf("Failed with empty password.\n");
						break;
					}
					if (!strcmp(password,in_buffer)) {
						logged_in=1;
					} else {
						logged_in=0;
						if (REPORT) printf("Failed with password: %s.\n",in_buffer);
					}
					out_buffer[START_OFFSET]=START_CODE;
					long_ptr=(long*)&out_buffer[ID_OFFSET];
					*long_ptr=flip_bytes(DATA_RETURN);
					long_ptr=(long*)&out_buffer[CLEN_OFFSET];
					*long_ptr=flip_bytes(sizeof(char));
					out_buffer[CONTENT_OFFSET]=logged_in;
					out_buffer[CONTENT_OFFSET+sizeof(char)]=END_CODE;
					sock_write(&socket,out_buffer,FRAME_SIZE+sizeof(char));
					break;
				}

				case CONFIG_READ:{
					if (REPORT) printf("CONFIG_READ\n");
					out_buffer[START_OFFSET]=START_CODE;
					long_ptr=(long*)&out_buffer[ID_OFFSET];
					*long_ptr=flip_bytes(DATA_RETURN);
					if ((logged_in==1) || (security_level==0)) {
						content_length=strlen(configuration)+1;
						long_ptr=(long*)&out_buffer[CLEN_OFFSET];
						*long_ptr=flip_bytes(content_length);
						strcpy((char*)&out_buffer[CONTENT_OFFSET],configuration);
					} else {
						if (REPORT) printf("Rejected: not logged in.\n");
						strcpy(message,"Log in to read the configuration file.\n");
						content_length=strlen(message)+1;
						long_ptr=(long*)&out_buffer[CLEN_OFFSET];
						*long_ptr=flip_bytes(content_length);
						strcpy((char*)&out_buffer[CONTENT_OFFSET],message);
					}
					out_buffer[CONTENT_OFFSET + (int) content_length]=END_CODE;
					sock_write(&socket,out_buffer,FRAME_SIZE + (int) content_length);
					if (REPORT) printf("Transmitted %d characters.\n",content_length);
					break;
				}

				case CONFIG_WRITE:{
					if (REPORT) printf("CONFIG_WRITE\n");
					if ((logged_in==1) || (security_level==0)) {
						if (in_length0) {
			if (REPORT_TCP)
         	tcp_available=tcp_available+num_read;
         	printf("Read %d bytes into RAM buffer, now %d available.\n",
					num_read,tcp_available);
		}
		if (!tcp_tick(s)) {
			if (REPORT_TCP) printf("Connection broken.\n");
			return -1;
		}
		return 0;
	}

	// The following code handles requests for one or more bytes.
	if (tcp_available>=len) {
		memcpy(dp,&tcp_buffer[tcp_first],len);
		tcp_first=tcp_first+len;
		tcp_available=tcp_available-len;
	} else {
		tcp_remaining=len;
		tcp_destination=dp;
		if (tcp_available>0) {
			memcpy(tcp_destination,&tcp_buffer[tcp_first],tcp_available);
			tcp_remaining=tcp_remaining-tcp_available;
			tcp_destination=tcp_destination+tcp_available;
			if (REPORT_TCP) printf("Read last %d available, need %d more.\n",
				tcp_available,tcp_remaining);
		}
		tcp_available=0;
		tcp_first=-1;
		while (tcp_remaining>0) {
			if (REPORT_TCP) printf("Waiting for %d bytes...\n",tcp_remaining);
			while (tcp_available==0) {
				tcp_available=sock_fastread(s,&tcp_buffer[0],BUFF_SIZE);
				if (!tcp_tick(s)) {
					if (REPORT_TCP) printf("Connection broken.\n");
					return -1;
				}
			}
			if (REPORT_TCP) printf("Received %d bytes.\n",tcp_available);
			if (tcp_available>=tcp_remaining) {
				memcpy(tcp_destination,&tcp_buffer[0],tcp_remaining);
				tcp_first=tcp_remaining;
				tcp_available=tcp_available-tcp_remaining;
				tcp_remaining=0;
			} else {
				memcpy(tcp_destination,&tcp_buffer[0],tcp_available);
				tcp_destination=tcp_destination+tcp_available;
				tcp_remaining=tcp_remaining-tcp_available;
				tcp_available=0;
			}
		}
	}
	return len;
}

/*
read_relay_configuration reads the configuration parameters
out of EEPROM, assuming the EEPROM has been initialized and
written already. If the EEPROM file system has not been created,
this routine creates it. If there is no configuration file
existing, it uses default values for all parameters, which you
will find declared above as global tcp_firsts. If the user is
pressing the driver's configuration switch, the routine detects
this and creates the default configuration file.
*/
int read_relay_configuration() {
	int rc,fn,lx;
	File config_file;
	static char scratch[CONFIG_LENGTH];
	char* parameter;
	char* value;

	lx=fs_get_flash_lx();
	if (REPORT) printf("Starting EEPROM file system...\n",lx);
	rc=fs_init(0,0);
	if (rc) {
		if (REPORT) {
			printf("Failed with error %d.\n",errno);
			if (errno==EINVAL) printf("The reserveblocks parameter was non-zero.\n");
			if (errno==EIO) printf("I/O error. This indicates a hardware problem.\n");
			if (errno==ENOMEM) printf("Insufficient memory for required buffers.\n");
			if (errno==ENOSPC) printf("No recognized flash or RAM memory device available.\n");
		}
		return 1;
	}

	if (!read_controller_byte(CS_ADDR)) {
		if (REPORT) printf("Formatting EEPROM disk...\n");
		rc=lx_format(lx,0);
		if (rc) {
			if (REPORT) printf("Failed with error %d.\n",errno);
			return 2;
		}
	}

	if (REPORT) printf("Disk capacity is %ld \n",fs_get_lx_size(lx,1,0));
	if (REPORT) printf("Available space is %ld \n",fs_get_lx_size(lx,0,0));
	fs_set_lx(lx,lx);

	if (!read_controller_byte(CS_ADDR)) {
		if (REPORT) printf("Creating configuration file...\n");
		rc=fcreate(&config_file,CONFIG_FILE_NAME);
		if (rc) {
			if (REPORT) printf("Failed with error %d.\n",errno);
			return 3;
		}
		if (REPORT) printf("Writing configuration file...\n");
		rc=fopen_wr(&config_file,CONFIG_FILE_NAME);
		if (rc) {
			if (REPORT) printf("Failed with error %d.\n",errno);
			return 4;
		}
		sprintf(configuration,"lwdaq_relay_configuration:\n");
		sprintf(scratch,"operator: relay_version_%d\n",VERSION_NUM);
		strcat(configuration,scratch);
		sprintf(scratch,"configuration_time: %s\n",MY_TIMESTAMP);
		strcat(configuration,scratch);
		sprintf(scratch,"password: %s\n",MY_PASSWORD);
		strcat(configuration,scratch);
		sprintf(scratch,"driver_id: %s\n",MY_ID);
		strcat(configuration,scratch);
		sprintf(scratch,"ip_addr: %s\n",MY_IP_ADDRESS);
		strcat(configuration,scratch);
		sprintf(scratch,"ip_port: %d\n",MY_PORT);
		strcat(configuration,scratch);
		sprintf(scratch,"tcp_timeout: %d\n",MY_TIMEOUT);
		strcat(configuration,scratch);
		sprintf(scratch,"security_level: %d\n",MY_SECURITY);
		strcat(configuration,scratch);
		sprintf(scratch,"gateway_addr: %s\n",MY_GATEWAY);
		strcat(configuration,scratch);
		sprintf(scratch,"subnet_mask: %s\n",MY_NETMASK);
		strcat(configuration,scratch);
		fwrite(&config_file,configuration,strlen(configuration)+1);
		fclose(&config_file);
	}

	if (REPORT) printf("Reading configuration file...\n");
	rc=fopen_rd(&config_file,CONFIG_FILE_NAME);
	if (rc) {
		if (REPORT) printf("Failed with error %d.\n",errno);
		return 5;
	}
	fseek(&config_file,0,SEEK_SET);
	rc=fread(&config_file,configuration,CONFIG_LENGTH);
	fclose(&config_file);

	strcpy(scratch,configuration);

	parameter=strtok(scratch,SEPCHARS);
	if (strcmp(parameter,"lwdaq_relay_configuration")) {
		if (REPORT) printf("Error: contents start with <%s>.\n",parameter);
		return 6;
	}

	parameter=strtok(NULL,SEPCHARS);
	value=strtok(NULL,SEPCHARS);
	while ((parameter != NULL) && (value != NULL)) {
		if (!strcmp(parameter,"operator")) {
			if (REPORT) printf("Configuration supplied by %s\n",value);
		}
		if (!strcmp(parameter,"configuration_time")) {
			if (REPORT) printf("Configuration time stamp is %s\n",value);
		}
		if (!strcmp(parameter,"driver_id")) {
			if (REPORT) printf("Driver identification is %s\n",value);
		}
		if (!strcmp(parameter,"password")) {
			strcpy(password,value);
			if (REPORT) printf("Setting %s to %s\n",parameter,value);
		}
		if (!strcmp(parameter,"ip_port")) {
			ip_port=atoi(value);
			if (REPORT) printf("Setting %s to %d\n",parameter,ip_port);
		}
		if (!strcmp(parameter,"ip_addr")) {
			tcp_config("MY_IP_ADDRESS",value);
			ifconfig(IF_ETH0,IFS_DOWN,IFS_IPADDR,aton(value),IFS_UP,IFS_END);
			if (REPORT) printf("Setting %s to %s\n",parameter,value);
		}
		if (!strcmp(parameter,"gateway_addr")) {
			ifconfig(IF_ETH0,IFS_DOWN,IFS_ROUTER_SET,aton(value),IFS_UP,IFS_END);
			if (REPORT) printf("Setting %s to %s\n",parameter,value);
		}
		if (!strcmp(parameter,"subnet_mask")) {
			ifconfig(IF_ETH0,IFS_DOWN,IFS_NETMASK,aton(value),IFS_UP,IFS_END);
			if (REPORT) printf("Setting %s to %s\n",parameter,value);
		}
			if (!strcmp(parameter,"security_level")) {
			security_level=atoi(value);
			if (REPORT) printf("Setting %s to %d\n",parameter,security_level);
		}
		if (!strcmp(parameter,"tcp_timeout")) {
			tcp_timeout=atoi(value);
			if (REPORT) printf("Setting %s to %d\n",parameter,tcp_timeout);
		}
		parameter=strtok(NULL,SEPCHARS);
		value=strtok(NULL,SEPCHARS);
	}

	return 0;
}

/*
write_relay_configuration writes a string to the relay
configuration file. It does not check the string to see
if it is in the correct format. If the format is not
correct, a subsequent read_relay_configuration will fail.
*/
int write_relay_configuration(char* contents) {
	int rc,lx;
	File config_file;
	static char scratch[CONFIG_LENGTH];
	char* parameter;
	char* value;

	if (REPORT) printf("Writing new contents to configuration file...\n");
	rc=fopen_wr(&config_file,CONFIG_FILE_NAME);
	if (rc) {
		if (REPORT) printf("Failed with error %d.\n",errno);
		return 3;
	}
	fseek(&config_file,0,SEEK_SET);
	fwrite(&config_file,contents,strlen(contents)+1);
	fclose(&config_file);

	strcpy(scratch,contents);
	parameter=strtok(scratch,SEPCHARS);
	if (REPORT) printf("%s:\n",parameter);
	parameter=strtok(NULL,SEPCHARS);
	value=strtok(NULL,SEPCHARS);
	while ((parameter != NULL) && (value != NULL)) {
		if (REPORT) printf("%s: %s\n",parameter,value);
		parameter=strtok(NULL,SEPCHARS);
		value=strtok(NULL,SEPCHARS);
	}
	return 0;
}