/* -*- Mode:C; Tab-width:4 -*- */
/*                                                                       */
/*                                                                       */
/*                      RESTRICTED RIGHTS LEGEND                         */
/*                                                                       */
/* Use, duplication, or disclosure by the Government is subject to       */
/* restrictions as set forth in subdivision (c)(1)(ii) of the Rights in  */
/* Technical Data and Computer Software clause at 252.227-7013.          */
/*                                                                       */
/*                    TEXAS INSTRUMENTS INCORPORATED.                    */
/*                            P.O. BOX 149149                            */
/*                         AUSTIN, TEXAS 78714-9149                      */
/*                              MS 2151                                  */
/*                                                                       */
/*  Copyright (C)   1987,1988,1989,1990 Texas Instruments Incorporated.  */
/*  All rights reserved.                                                 */
/*                                                                       */

/*

This version of sockets.c creates a standalone set of sockets to use inside of a mac application.
It is assumed that someone is polling a channel and sending the received packets to socket command.
It is also assumed that the variable trap_channel will have been set up before calling socket_initialize.

Anytime a port is allocated by bind it will have the channel number encoded in the high 4 bits of
the port number. Lisp will use this information to decide which channel to use to send reply data.
For instance: A program using channel 9 will have port numbers in the range of 0x9000 to 0x9fff allocated.
The portmapper channel is special cased to always be sent to channel 8. 

*/

#include <micronet-device.h>
#include "micronet-accessors.h"
#include <sockets.h>
#include <strings.h>
#include <errno.h>
#include <events.h>
#include <desk.h>
#include "::bsd:sys:time.h"
#include <AddinComm.h> 
#include <devices.h>
#include <utility.h>
#include <types.h>

static char sccsid[] = "%W% (TI) %E%";

/* The array of sockets. */
static struct sock sockets[NUMBER_OF_SOCKETS];
static int transmit_channel = 6;

/************************************************************/
/*                                                          */
/* Utility routines.                                        */
/*                                                          */
/************************************************************/

#define MICRONET_SMALLEST_PRESET_PORT 255
#define LARGEST_MICRONET_PORT_NUMBER 0xFFFF

/* Randomly generate a valid port number. */
static unsigned short
random_micronet_port_number()
{
  unsigned short port = 0;
  while((port <= MICRONET_SMALLEST_PRESET_PORT) || (port >= (LARGEST_MICRONET_PORT_NUMBER & 0xfff)))
	port = rand();
  /* or the channel number into the high order 4 bits. */
  return port | (transmit_channel << 12);
}

/* Make sure that a port number is not in use by another port. */
static Boolean
unique_micronet_port_number(port)
	 unsigned int port;
{
  int x;
  if((port >= LARGEST_MICRONET_PORT_NUMBER) || (port <= 0))
	return false;
  for(x=0; x<NUMBER_OF_SOCKETS; x++)
	{
	  struct micronet_addr *addr = (struct micronet_addr *) &sockets[x].src_addr;
	  if((sockets[x].src_addr.family == AF_MAC) && (addr->port == port))
		return false;
	}
  return true;
}

static unsigned int
generate_micronet_port_number()
{
  int port;
  
  while(!unique_micronet_port_number(port = random_micronet_port_number()));
  return port;
}

unsigned long select_word = 0;

Boolean 
socket_command(pkt)
	 acb *pkt;
{
  int x;

  for(x=0; x<NUMBER_OF_SOCKETS; x++)
	{		  
	  struct micronet_addr *addr = (struct micronet_addr *) &sockets[x].src_addr;
	  if(sockets[x].allocated &&
		 addr->port == destination_port(pkt) &&
		 addr->slot == destination_slot(pkt)) 
		{
		  q_element(&sockets[x].recv_queue, (q_elem *) pkt);
		  select_word |= 1<<x;
#ifdef DEBUG
		  if(debugFlag)
			printf("Queueing pkt %x for socket %x, port %x, select_word = %x\n", pkt, x, destination_port(pkt), select_word);
#endif
		  return true;
		}
	}
  
  /* If there is not a socket for this port then free the acb. Might put an error code in it later. */
#ifdef DEBUG
  if(debugFlag)
	printf("Throwing out pkt %x for port %x\n", pkt, destination_port(pkt));
#endif
  return_acb_server(pkt);
  return true;
}


/************************************************************/
/*                                                          */
/* Socket call handlers.                                    */
/*                                                          */
/************************************************************/

	 
socket_initialize()
{
	int x;
	extern unsigned short trap_channel;
	transmit_channel = trap_channel;
	for(x=0; x<NUMBER_OF_SOCKETS; x++)
	  sockets[x].allocated = false;

	select_word = 0;
}

int
select(width, readfds, writefds, execptfds, timeout)
	 int width, *readfds, *writefds, *execptfds;
	 struct timeval *timeout;
{
#pragma unused(width)				/* 11/22/89 sbw */

  unsigned int x, y, tmp_readfds;
  long tick_timeout = 0;

  /* Convert timeout to tickcount.*/
  if(timeout)
	tick_timeout = TickCount() + (16667 * (timeout->tv_usec)) + ((timeout->tv_sec) * 60);

  do
	{
		/* Sockets always ready for writing. */
	    if(writefds) *writefds = -1;
	  
		/* Sockets never get execptions. */
		if(execptfds)
		  *execptfds = 0;
		
		if(readfds)
		  tmp_readfds = select_word & *readfds;
		
		/* Count number of ready file descriptors. */
		for(x=0,y=0; y<32; y++)
		  if((readfds && (tmp_readfds & (1<<y))) ||
			 (writefds && (*writefds & (1<<y))))
			x++;
		
		if(tick_timeout && !x) event_handler(NULL, 0);
    } while(!x && tick_timeout && (TickCount() < tick_timeout));

  *readfds = tmp_readfds;
  
  return x;
}

/* bind - bind an address to a socket
	Returns 0 on success, -1 on failure
*/
int
bind(socket, addr, addrlen)
int socket;
struct micronet_addr *addr;
int addrlen;
{
  /* Only bind socket if it is not already bound. */
  if(!sockets[socket].src_addr.port)
	{
		if(!addr->port)
		  /* Get a port number if one is not assigned*/
		  {
			  if(!(addr->port = generate_micronet_port_number()))
				{
#ifdef DEBUG
					if(debugFlag)
					  printf("Error in bind: No ports available\n");  /* used to be DebugStr */
#endif
					errno = EIO;
					return EIO;
				}
			  /* else make sure that the one they passed is not in use. */
		  }
		else
		  {
			  if(!unique_micronet_port_number(addr->port))
				{
#ifdef DEBUG
					if(debugFlag)
					  printf("Error in bind: Port already in use\n");  /* used to be DebugStr */
#endif
					errno = EIO;
					return EIO;
				}
		  }
		
		/* If life is good put address in the socket. */
		bcopy(addr, &sockets[socket].src_addr, addrlen);
		
#ifdef DEBUG
		if(debugFlag)
		  printf("bind, socket = %d, port = %d, slot = %d\n", socket, sockets[socket].src_addr.port, sockets[socket].src_addr.slot);
#endif
		
	}
  else
	{
		
#ifdef DEBUG
		if(debugFlag)
		  printf("bind: socket already bound, socket = %d, port = %d, slot = %d\n",
				 socket, sockets[socket].src_addr.port, sockets[socket].src_addr.slot);
#endif	  
		bcopy(&sockets[socket].src_addr, addr, addrlen);
	}
  
  errno = 0;
  return SUCCESS;
}

/* getsockname - get socket name
	Returns 0 on success, -1 on failure
*/

int
getsockname(socket, addr, addrlen)
int socket;
struct sockaddr *addr;
int *addrlen;
{
	int size = MIN(*addrlen, sizeof(struct micronet_addr));
	
	bcopy(&sockets[socket].src_addr, addr, size);
	*addrlen = size;

#ifdef DEBUG
	if(debugFlag)
	  printf("getsockname, socket = %d\n", socket);
#endif

	errno = 0;
	return 0;
}


/* recvfrom - receive a message from a socket
	Returns number of bytes received, or -1 on failure
*/
int
recvfrom(socket, buf, len, flags, addr, addrlen)
int socket;
char *buf;
int len, flags;
struct micronet_addr *addr;
int *addrlen;
{
#pragma unused(flags, addrlen)			/* 11/22/89 sbw */

  acb *pkt;
  int received_data_length;

  if(!(pkt = (acb *) deq_element(&sockets[socket].recv_queue))) /* socket is always non blocking for now. */
	  return EWOULDBLOCK;

  if(sockets[socket].recv_queue.head == NULL)  
	select_word &= ~(1<<socket);

#ifdef DEBUG
  if(debugFlag)
	printf("Recv, socket = %d, dest = %x:%x, src= %x:%x, acb = %x, buf = %x\n",
           socket, parm_32b(pkt, 3), parm_32b(pkt, 2), parm_32b(pkt, 1), parm_32b(pkt, 0), pkt, buf);
#endif
		   
  received_data_length = MIN(parm_bytes(pkt), len);
  copy_parms_8b(pkt, buf, ACB_OVERHEAD_BYTE_COUNT, received_data_length, TO_ARRAY);
  
  /* Return the source address. */
  addr->family = AF_MAC;
  addr->slot = source_slot(pkt);
  addr->port = source_port(pkt);
  
  return_acb_server(pkt);
  errno = 0;
  return received_data_length;

}

/* recvfrom_acb - receive an acb from a socket
	Returns number of bytes received, or -1 on failure
*/
int
recvfrom_acb(socket, buf, first_byte_of_buffer, flags, addr, addrlen)
	 int socket, flags;
	 Ptr *first_byte_of_buffer;
	 acb **buf;
	 struct micronet_addr *addr;
int *addrlen;
{
#pragma unused(flags, addrlen)			/* 11/22/89 sbw */

  if(!(*buf = (acb *) deq_element(&sockets[socket].recv_queue))) /* socket is always non blocking for now. */
	  return EWOULDBLOCK;

  if(sockets[socket].recv_queue.head == NULL)  
	select_word &= ~(1<<socket);

  *first_byte_of_buffer = parm_8b_addr((*buf), ACB_OVERHEAD_BYTE_COUNT);

#ifdef DEBUG
  /*if(debugFlag)*/
	printf("Recv, socket = %d, dest = %x:%x, src= %x:%x, acb = %x, buf = %x\n",
           socket, destination_slot((*buf)), (unsigned long)destination_port((*buf)), (unsigned long)source_slot((*buf)), (unsigned long)source_port((*buf)), *buf, buf);
#endif
		   
  /* Return the source address. */
  addr->family = AF_MAC;
  addr->slot = source_slot((*buf));
  addr->port = source_port((*buf));
  
  errno = 0;
  return parm_bytes((*buf));

}

void
recvfrom_acb_return(pkt)
	 acb *pkt;
{
	return_acb_server(pkt);
}

/* sendto - send a message from a socket
	Returns number of bytes sent, or -1 on failure
*/

#define dest_channel(pkt) (destination_port(pkt) >> 12)

static void
transmit_packet_socket(pkt)
	 acb *pkt;
{
	int channel = dest_channel(pkt);

	/* This is here to special case the portmapper's channel. Many applications have its port number */
	/* hard coded without the channel number in the high 4 bits.                                     */
	if(destination_port(pkt) == PORTMAP_PORT)
	  channel = PORTMAP_CHANNEL;
	
	if(destination_slot(pkt) == DriverData->my_slot)
	  {
#ifdef DEBUG
		/*  if(debugFlag) */
			printf("Local transmission to port %x, from port %x, on channel %x\n", (unsigned long)destination_port(pkt), (unsigned long)source_port(pkt), channel);
#endif
		  transmit_packet_local(pkt, channel);
	  }
	else
	  {
#ifdef DEBUG
	/*	  if(debugFlag) */
			printf("Remote transmission to port %x, from port %x, on channel %x\n", (unsigned long)destination_port(pkt), (unsigned long)source_port(pkt), channel);
#endif
		  transmit_packet(pkt, channel);
	  }
}

int
sendto(socket, buf, len, flags, addr, addrlen)
     int socket;
     char *buf;
     int len, flags;
     struct micronet_addr *addr;
     int addrlen;
{
#pragma unused(flags, addrlen)			/* 11/22/89 sbw */

  acb *pkt;

  if(!(pkt = (acb *)get_acb_fast(len + ACB_OVERHEAD_BYTE_COUNT)))
	{
#ifdef DEBUG
		if(debugFlag)
		  printw("siocSend: An ACB could not be allocated from Lisp Machine memory."); /* used to be a DebugStr */
#endif
	  errno = EIO;
	  return NULL;
	}

  set_opcode(pkt, 1);
  
  /* Setup up source port. 		*/
  set_source_port(pkt, sockets[socket].src_addr.port);
  
  /* Setup up source host. 		*/
  set_source_slot(pkt, sockets[socket].src_addr.slot);
  
  /* Setup up destination port.	*/
  set_destination_port(pkt, addr->port);
  
  /* Setup up destination host.	*/
  set_destination_slot(pkt, addr->slot);
  
  /* Setup length.				*/
  set_parm_bytes(pkt, len);					
  
  copy_parms_8b(pkt, buf, ACB_OVERHEAD_BYTE_COUNT, len, TO_ACB);

  set_requestor_complete(pkt, true);

#ifdef DEBUG
	  if(debugFlag)
		printf("Sendto: socket %d, dest %x:%x, source = %x:%x, acb = %x, buffer = %x\n",
			   socket, parm_32b(pkt, 3), parm_32b(pkt, 2), parm_32b(pkt, 1), parm_32b(pkt, 0),
			   pkt, buf);
#endif	  


  transmit_packet_socket(pkt);
  
  errno = 0;
  return len;

}

int
sendto_acb(socket, pkt, len, flags, addr, addrlen)
     int socket;
     acb *pkt;
     int len, flags;
     struct micronet_addr *addr;
     int addrlen;
{
#pragma unused(flags, addrlen)			/* 11/22/89 sbw */

  set_opcode(pkt, 1);
  
  /* Setup up source port. 		*/
  set_source_port(pkt, sockets[socket].src_addr.port);
  
  /* Setup up source host. 		*/
  set_source_slot(pkt, sockets[socket].src_addr.slot);
  
  /* Setup up destination port.	*/
  set_destination_port(pkt, addr->port);
  
  /* Setup up destination host.	*/
  set_destination_slot(pkt, addr->slot);
  
  /* Setup length.				*/
  set_parm_bytes(pkt, len);					
  
  set_requestor_complete(pkt, true);

#ifdef DEBUG
	  /*if(debugFlag)*/
		printf("Sendto: socket %d, dest %x:%x, source = %x:%x, acb = %x\n",
			   socket, (unsigned long)source_slot(pkt), (unsigned long)source_port(pkt), (unsigned long)destination_slot(pkt), (unsigned long)destination_port(pkt), pkt);
#endif	  


  transmit_packet_socket(pkt);
  
  errno = 0;
  return len;

}

Ptr
sendto_acb_allocate(size, first_byte_of_buffer)
	 int size;
	 Ptr *first_byte_of_buffer; 
{
	acb *pkt = get_acb_fast(size);
	*first_byte_of_buffer = parm_8b_addr(pkt, ACB_OVERHEAD_BYTE_COUNT);
	set_requestor_complete(pkt, 1);
	return (Ptr) pkt;
}
     
/* socket - create an endpoint for communication
            Returns a new socket on success, -1 on failure
*/

int
socket(domain, type, protocol)
     int domain, type, protocol;
{
  int socket;

  /* Find a free socket. */
  for(socket=0; socket<NUMBER_OF_SOCKETS; socket++)
    if(!sockets[socket].allocated)
      break;

  if(socket >= NUMBER_OF_SOCKETS)
    /* Too many sockets allocated. */
	{
#ifdef DEBUG
	  printf("Too many sockets allocated.\n");	
#endif
	  errno = EIO;
	  return ERROR;
	}
  else
    {
#ifdef DEBUG
	  if(debugFlag)
		printf("Socket = %d\n", socket);
#endif
      sockets[socket].allocated = true;
	  sockets[socket].domain = domain;
      sockets[socket].type = type;
      sockets[socket].proto = protocol;
	  bzero(&sockets[socket].src_addr,sizeof(struct micronet_addr));

	  initialize_q(&sockets[socket].recv_queue);

	  errno = 0;
      return socket;
    }
}

sockclose(socket)
	 int socket;
{
  acb *pkt;
#ifdef DEBUG
  if(debugFlag)  
	printf("Closing socket %d\n", socket);
#endif

  if((socket > 0) && (socket < NUMBER_OF_SOCKETS) && sockets[socket].allocated)
	{
		/* Free any leftover pkts. */
		while(pkt = (acb *) deq_element(&sockets[socket].recv_queue))
		  {
			  return_acb_server(pkt);
#ifdef DEBUG
			  if(debugFlag)
				printf(">>>Freeing a pkt.\n");
#endif
		  }

		sockets[socket].allocated = false;
	}
  errno = 0;
  return 0;
}




