/* $Header: /home/cobweb3/surendar/networks/RCS/physical.c,v 2.2 1993/09/02 23:13:18 surendar Exp $ */

/*
 * Module:	This module supports the physical layer functions.
 * Author:	$Author: surendar $ 
 * 		Dept. of Computer Sciences 
 * 		Worcester Polytechnic Institute. 
 * Date:	$Date: 1993/09/02 23:13:18 $ 
 */

/* 
 * $Log: physical.c,v $
 * Revision 2.2  1993/09/02  23:13:18  surendar
 * Latest Version
 *
 * Revision 2.1  1993/01/29  08:09:07  surendar
 * Assignment II release
 *
 * Revision 1.3  1993/01/29  08:06:15  surendar
 * Prepare for assignment II
 *
 * Revision 1.2  1993/01/14  19:07:36  surendar
 * Checked in for Assignment I
 * 
 */

#ifndef lint
static char rcs_ident[] = "$Header: /home/cobweb3/surendar/networks/RCS/physical.c,v 2.2 1993/09/02 23:13:18 surendar Exp $";
#endif /* lint */

#include	<stdio.h>
#include	<sys/types.h>
#include	<sys/time.h>
#include	<sys/param.h>
#include	<sys/socket.h>
#include	<netdb.h>
#include	<netinet/in.h>
#include	<sys/un.h>
#include	<sys/stat.h>
#include	<fcntl.h>

#if HAVE_STRING_H
#include	<string.h>
#endif /* HAVE_STRING_H */

#if HAVE_STDARG_H
#include	<stdarg.h>
#endif /* HAVE_STDARG_H */
#include	"defs.h"
#include	"extern.h"

static queue_t *pl_queue = NULL;	/* Queue used by Physical layer */

/*
 * ====================================================================
 *
 * Function: \section{noise}
 *
 * Arguments: buf - Buffer to add noise to
 * 	   len - length of the buffer
 * 	   rate- Error rate [ 0 .. 10000 ]
 * Return: void
 *
 * Description: This function adds noise to the input buffer. Depending on the
 * error rate, noise is added as bit errors (not bursty). It could knock out
 * any bit in the data.
 *
 * ===================================================================
 */
static void
    noise(caddr_t *buf, size_t *len, long rate)
{
    int count, bit;
    caddr_t ptr;
    char pattern;
    size_t new_len ;
    
#ifdef TOOMUCH
    if ((random() % 10000) < rate)
	new_len = *len + 5 - (random() % 5);	/* Length can grow or shrink by two */
    else
	new_len = *len;

    if (new_len != *len)
	{
	    *buf = (caddr_t) realloc(*buf, new_len);
	    *len = new_len;
	}
#endif				/* TOO MUCH */

    if ((random() % 10000) < rate) /* Do we add noise to this frame */
	{
	    for (count = (random() % 16); /* Lets simulate bursty errors */
		 count;
		 count--)
		{
		    bit = random() % (*len * 8);	/* Find a random location */
		    					/* in buffer */ 
		    ptr = *buf + (bit / 8);
		    pattern = 0x1 << (bit % 8);
		    if (*ptr & pattern)	/* Toggle that bit */
			*ptr &= ~pattern;
		    else
			*ptr |= ~pattern;
		}
	}
}


/*
 * ====================================================================
 *
 * Function: pl_send
 *
 * Arguments: buf - Buffer to send
 * 	   len - Length of the buffer
 *
 * Return: Error status
 *
 * Description: This function sends the data over the actual wire(not until
 * project II). Noise is added, packets are dropped (project II) and data is
 * queued up otherwise.
 *
 * ===================================================================
 */
static err_t
    pl_send(caddr_t buf, size_t len)
{
    float rate;			/* Delay rate */
    unsigned long data_len;
    
    if (len <= 0 || buf == NULL) /* Error input? */
	return(nerrno = Failure);

    rate = (float) len / (float) phys_control.speed;

    log(20, "Delay in pl_send()");
    delay(rate);		/* Wait for so much time to account for */
				/* transmission delay */

    noise(&buf, &len, phys_control.error_rate); /* Add random noise to this */
					      /* packet */ 

    if ((random() & 0xff) == phys_control.error_rate)	/* Drop the packet */
							/* totally */ 
	return(Success);
    
#ifdef PROJECT_I
    enqueue(&pl_queue, buf, len);		/* queue this packet */
#else
    dump("SEND_DATA", buf, len);
    data_len = htonl(len);
    
    if (write(phys_forward, &data_len, sizeof(data_len)) <= 0)
	return(Failure);
    
    if (write(phys_forward, buf, len) <= 0)
	return(Failure);
#endif /* PROJECT_I */
    
    return(Success);
}


/*
 * ====================================================================
 *
 * Function: pl_recv
 *
 * Arguments: buf - Pointer to recieve data from other host [ not till project II]
 * 	   len - Pointer to store the length actually read
 * 	   delay - in microseconds to wait for data
 * Return: Error status
 *
 * Description: Receive data from the other end and return as addr in buf. The
 * buffer is allocated using malloc() and the calling function will free() it
 * when appropriate.
 *
 * ===================================================================
 */
static err_t
    pl_recv(void **buf, size_t *len, long ignore_delay)
{
    err_t status;
    float delay_time;
    fd_set readfds;
    struct timeval timeout;
    unsigned long data_len, cur_len;
    
    if (buf == NULL || len == NULL)
	return(nerrno = Failure);

    *buf = NULL;
#ifdef PROJECT_I
    status = dequeue(&pl_queue, buf, len, phys_control.error_rate);
    
    delay_time = phys_control.error_rate * ((random() % 4) ? 0: 1) * 0.001;
    delay(delay_time);		/* Arbitrary delays in physical */
				/* transmission channel */

#else /* PROJECT_I */
    status = Success;
    FD_ZERO(&readfds);
    FD_SET(phys_reverse, &readfds);
    timeout.tv_sec = ignore_delay / 1000000;
    timeout.tv_usec = ignore_delay % 1000000;

    if (select (phys_reverse + 1, &readfds, NULL, NULL, &timeout) == 1)
	{
	    char read_buf[1024];

	    if ((read(phys_reverse, &data_len, sizeof(data_len))) <= 0)
		fatal("Connection lost??");

	    cur_len = *len = data_len = ntohl(data_len);
	    
	    for (cur_len = 0; cur_len < data_len; cur_len++)
		if (read(phys_reverse, &read_buf[cur_len], 1) <= 0)
		    fatal("Connection lost??");
	    
	    *buf = (void *) malloc (*len);
	    memcpy(*buf, &read_buf[0], *len);
    
	    delay_time = phys_control.error_rate *
		((random() % 4) ? 0: 1) * 0.001;
	    log(20, "Delay in pl_recv()");

	    delay(delay_time);		/* Arbitrary delays in physical */
					/* transmission channel */
		dump("RECV_DATA", *buf, *len);

	}
    else
	status = E_Nodata;
#endif /* PROJECT_I */

    if (status != Success)
	nerrno = status;

    return(status);
}


/*
 * ====================================================================
 *
 * Function: pl_process
 *
 * Arguments: -
 *
 * Return: - 
 *
 * Description: The main() function for the physical layer. All control is
 * done from here.
 *
 * ===================================================================
 */
void
    pl_process(void)
{
    void *buf;
    err_t ret_value;
    int	sockfd;
    size_t len;
    struct sockaddr_in serv_addr,cli_addr;
    char print_buf[132];
    
    log (10, "Starting Physical layer");

    if (Server)
	{
	    if ((sockfd = socket(AF_INET, SOCK_STREAM, IP)) < 0)
		fatal("Cannot create socket");

	    bzero((char *) &serv_addr, sizeof(serv_addr));
	    serv_addr.sin_family = AF_INET;
	    serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	    serv_addr.sin_port = htons(UNIQUE_KEY);	/* Some port */

	    sprintf(print_buf, "Connecting to port %d", UNIQUE_KEY);
	    log(10, print_buf);

	    if (bind(sockfd,(struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0)
		fatal("Cannot bind socket. Another server running?");
    
	    listen(sockfd,1);
	    
	    len = sizeof(cli_addr);
	    
	    if ((phys_reverse = accept(sockfd, (struct sockaddr *)& cli_addr,
				       (int *)&len)) < 0)
		fatal("Cannot accept request");

	    /* Not necessary, but what the heck */
	    len = sizeof(cli_addr);
	    
	    if ((phys_forward = accept(sockfd, (struct sockaddr *)& cli_addr,
				       (int *)&len)) < 0)
		fatal("Cannot accept request");
	    
	    if (read(phys_reverse,
		     &phys_control,
		     sizeof(phys_control)) != sizeof(phys_control))
		fatal("Protocol establishment failed");

#ifdef DEBUG
	    dump("CTRL", (void *)&phys_control, sizeof(phys_control));
#endif
	    if (write(phys_forward, "Hello", 5) < 0)
		fatal("Protocol establishment failed");

	    
	    log(20, phys_control.dest);
	    
	    memcpy(phys_ptr, &phys_control, sizeof(phys_control));
	    phys_ptr->valid = 0x1010fefe;
	    srandom(phys_control.seed);	/* Seed set through command line switches */
	    
	    close(sockfd);
	    log(20, "Server specific code done");
	}
    else
	{
	    srandom(phys_control.seed);	/* Seed set through command line switches */

	    /*Not necssary, but what the heck */
	    log(20, desthost);
	    if ((phys_forward = establish_link( desthost, UNIQUE_KEY)) < 0)
		fatal("Server not up?? Run netcp -S on the dest host");
	    log(20, "Forward connection setup");

	    if ((phys_reverse = establish_link( desthost, UNIQUE_KEY)) < 0)
		fatal("Server not up?? Run netcp -S on the dest host");
	    log(20, "Reverse connection setup");
	    log(20, phys_control.dest);

#ifdef DEBUG
	    dump("CTRL", (void *)&phys_control, sizeof(phys_control));
#endif				/* DEBUG */
	    write(phys_forward, &phys_control, sizeof(phys_control));

	    if (read(phys_reverse, &print_buf[0], 5) != 5)
		fatal("Protocol establishment failed");

#ifdef DEBUG
	    dump("PROT", &print_buf[0], 5);
#endif				/* DEBUG */
	    if (memcmp(&print_buf[0], "Hello", 5) != 0)
		fatal("Protocol establishment failed");
	    log(20, "Client specific code done");
	}
    
    while (1)
	{
	    log (20,"Anything from Data link layer?");
	    if ((ret_value = dlpl_recv(&buf, &len, 0)) == Success)	/* Anything */
								/* from DL? */
		{
		    dump("RECV", buf, len);
		    log(20,"Yes, stuff from DL");
		    pl_send(buf, len); /* Send it down on the wire */
		    free(buf);
		}

	    log (20,"Anything from Physical link layer?");
	    if (pl_recv(&buf, &len, 0) == Success)	/* Anything from wire */
		{
		    log(20,"Yes, stuff for DL");
		    pldl_send(buf, len); /* Send it up to DL */
		    free(buf);
		}
	}
}


