#define	    ident	"1B03"
/*
** +--------------------------------------------------------------+
** |                  Paul Scherrer Institute                     |
** |                     Computing Section                        |
** |                                                              |
** | This software may be used freely by non-profit organizations.|
** | It may be copied provided that the name of P.S.I. and of the |
** | author is included. Neither P.S.I. nor the author assume any |
** | responsibility for the use of this software outside of P.S.I.|
** +--------------------------------------------------------------+
**
** Module Name  . . . . . . . . : [...SINQHM]LWL_SERVER.C
**
** Author   . . . . . . . . . . : D. Maden
** Date of creation . . . . . . : Oct 1996
**
** Updates:
**  1A01  1-Oct-1996 DM	    Initial version.
**---------------------------------------------------------------------------
**  LWL_SERVER.C is the main program of a SINQ Histogram Memory process
**  which will run in a front-end processor, accept a TCP/IP connection
**  and transfer received data into the LWL (Licht-Wellen-Leiter) Test
**  generator.
**
**  To compile this program for VxWorks on PSS123, use:
**
	    ccvx lwl_server.c
** where
	    ccvx = ccppc -O0		    \
		    -mcpu=603		    \
		    -I${WIND_BASE}/target/h \
		    -fno-builtin	    \
		    -fno-for-scope	    \
		    -nostdinc		    \
		    -DCPU=PPC603	    \
		    -D_GNU_TOOL		    \
		    -gdwarf -c -Wimplicit

**  and, on the target,
**
**   -> ld < lwl_server.o
**   -> taskSpawn "lwl_server", 100, 8, 20000, lwl_server, \
**					    <port>, <base-addr>, <verbose>
**					      ^		^	    ^
**					      |		|	    |
**     		         default = 3501 ------+		|	    |
**			 default = 0x1800 --------------+	    |
**			 verbose mode if non-zero ------------------+
**
**  To suppress VME access, e.g. for test purposes, compile with the
**  option
**		-D_NO_VME
**====================================================================
*/
#include    <vxWorks.h>
#include    <taskLib.h>
#include    <msgQLib.h>
#include    <sockLib.h>
#include    <hostLib.h>
#include    <inetLib.h>
#include    <sysLib.h>
#include    <vme.h>

#include    <stdlib.h>
#include    <stdio.h>
#include    <unistd.h>
#include    <errno.h>
#include    <netinet/in.h>
#include    <arpa/inet.h>
#include    <sys/types.h>
#include    <sys/socket.h>

/*** #include    <sys/file.h>	***/	/* Get POSIX file definitions. */

#include    <signal.h>		/* Get POSIX signal definitions. */
#include    <sys/mman.h>	/* Get POSIX mem mgmnt definitions. */
#include    <sys/wait.h>	/* Get POSIX wait definitions. */

#include    "vmio10_def.h"
/*
**====================	Global Definitions =====================================
*/
#define	    DFLT_PORT	    3501
#define	    DFLT_BASE_ADDR  0x1800
/*
**====================	Global Variables =====================================
*/
  char	    *LwlBase;
  char	    Lwl_dummy[64];	/* Dummy array - used only when VME access ..
				** .. is disabled. */

  int	    Lwl_cnct_skt = 0;	/* Connection socket */
  int	    Lwl_rw_skt = 0;		/* Read/write socket */

  char	    Lwl_inet_node[20];
  char	    Lwl_inet_addr[20];
  int	    Lwl_my_errno;
/*

**=============================================================================
**  Local routines.
**				    Forward references ...
*/
    void    lwl_enable ();
    void    lwl_failInet (char *text);
    void    lwl_getErrno (int *his_errno);
    void    lwl_resetcio ();
    void    lwl_setup_inet_info ();
    void    lwl_vmio_byte_send (char bit8, char lobits);
    void    lwl_vmio_init ();
    void    lwl_vmio_send (char nbytes, char *buff);
    void    lwl_vmio_tg_reset ();
    void    lwl_vmio_transp_init ();
/*
**-----------------------------------------------------------------*/
  void	lwl_enable () {
/*	==========
**			output/input enable for IC6 ,IC7
*/
    LwlBase[VMIO_AB_CNTL] = 0x01;
    LwlBase[VMIO_AB_CNTL] = 0x94;
    LwlBase[VMIO_CD_CNTL] = 0x01;
    LwlBase[VMIO_CD_CNTL] = 0x94;
  }
/*
**-----------------------------------------------------------------*/
  void lwl_failInet (char *text) {
/*     ============
**		    Output the given text and exit the process.
*/
    int    my_errno;

    lwl_getErrno (&my_errno);
    printf ("\n### Internet Error ###\n");
    printf ("                   ### errno = %d.\n", my_errno);
    perror (text);
    exit (EXIT_FAILURE);
  }
/*
**-----------------------------------------------------------------*/
  void	lwl_getErrno (int *his_errno) {
/*      ============
*/
    *his_errno = errno;	    /* Make copy of errno   */
    return;
  }
/*
**-----------------------------------------------------------------*/
  void	lwl_resetcio () {
/*	============
**			Software reset for cio
*/
    char    aux;

    aux = LwlBase[VMIO_AB_CNTL];
    LwlBase[VMIO_AB_CNTL] = 0x00;

    aux = LwlBase[VMIO_AB_CNTL];
    LwlBase[VMIO_AB_CNTL] = 0x00;
    LwlBase[VMIO_AB_CNTL] = 0x01;
    LwlBase[VMIO_AB_CNTL] = 0x00;
	
    aux = LwlBase[VMIO_CD_CNTL];
    LwlBase[VMIO_CD_CNTL] = 0x00;
    aux = LwlBase[VMIO_CD_CNTL];
    LwlBase[VMIO_CD_CNTL] = 0x00;
    LwlBase[VMIO_CD_CNTL] = 0x01;
    LwlBase[VMIO_CD_CNTL] = 0x00;
  }
/*
**-----------------------------------------------------------------*/
  void lwl_setup_inet_info () {
/*     ===================
**                  Get Internet Nodename and Address from System.
*/
    int			    status;
    struct in_addr	    my_addr;

    status = gethostname (Lwl_inet_node, sizeof (Lwl_inet_node));
    if (status != 0) sprintf (Lwl_inet_node, "Undefined");

    my_addr.s_addr = hostGetByName (Lwl_inet_node);
    inet_ntoa_b (my_addr, Lwl_inet_addr);
  }
/*
**-----------------------------------------------------------------*/
  void	lwl_vmio_byte_send (char bit8, char lobits) {
/*	==================
**			    Send a byte (well, 9 bits really) via the VMIO10.
*/
    char    aux;

    do {				/* Wait for Output Reg. empty */
      LwlBase[VMIO_AB_CNTL] = 0X09;	/* Select Port B status reg .. */
      aux = LwlBase[VMIO_AB_CNTL];	/* .. and read it. */
    } while ((aux & 0x08) == 0);	/* Test its "empty" bit */

    LwlBase[VMIO_PORT_C] = bit8;	/* Send Bit-8 */
    LwlBase[VMIO_PORT_B] = lobits;	/* Output the other bits */
  }
/*
**-----------------------------------------------------------------*/
  void	lwl_vmio_init () {
/*	=============
**			initialise VMIO10 Module
*/
    LwlBase[VMIO_CD_CNTL] = 0x2A;	/* Port D %00101010 */
    LwlBase[VMIO_CD_CNTL] = 0x00;	/* %00000000, non inverting */
    LwlBase[VMIO_CD_CNTL] = 0x2B;	/* %00101011 */
    LwlBase[VMIO_CD_CNTL] = 0x0F;	/* %00001111, Run, EF0_S, Q2, Q1, Q0 */

    LwlBase[VMIO_AB_CNTL] = 0x2A;	/* Port B */
    LwlBase[VMIO_AB_CNTL] = 0x00;
    LwlBase[VMIO_AB_CNTL] = 0x2B;
    LwlBase[VMIO_AB_CNTL] = 0x00;
	
    LwlBase[VMIO_AB_CNTL] = 0x22;	/* Port A %00100010 */
    LwlBase[VMIO_AB_CNTL] = 0x00;	/* %00000000, non inverting */
    LwlBase[VMIO_AB_CNTL] = 0x23;	/* %00100011 */
    LwlBase[VMIO_AB_CNTL] = 0x00;	/* %00000000, all output */
	
    LwlBase[VMIO_CD_CNTL] = 0x22;	/* Port C */
    LwlBase[VMIO_CD_CNTL] = 0x00;
    LwlBase[VMIO_CD_CNTL] = 0x23;
    LwlBase[VMIO_CD_CNTL] = 0x00;
  }
/*
**-----------------------------------------------------------------*/
  void	lwl_vmio_send (
/*	=============			Send bytes to the LWL.
*/		    char    nbytes,
		    char    *buff) {
/*
**		The bytes are buff[0] to buff[nbytes-1]. buff[0] gives the
**		headerbit for buff[1]. The remainder are sent with the 9th
**		bit clear.
*/
    int	    i;
    char    aux;
		/*
		** ---------- loop sending the bytes ---------------
		*/
    LwlBase[VMIO_PORT_C] = 0;			    /* Mode = 0 */

    if (nbytes < 2) return;
    lwl_vmio_byte_send (buff[0], buff[1]); /* 2 bytes define the first "LWL-byte" */

    if (nbytes < 3) return;

    for (i = 2; i < nbytes; i++) {     /* The remaining bytes have Bit-8 = 0 */
      lwl_vmio_byte_send (0x00, buff[i]);
    }
  }
/*
**-----------------------------------------------------------------*/
  void	lwl_vmio_tg_reset () {
/*	=================
**			Initialise VMIO10 Test Generator Piggyback
*/
    LwlBase[VMIO_PORT_AB] = 0x02;	/* Clear Write, Strobe */
    LwlBase[VMIO_PORT_C] = 0x00;	/* Mode = 0 -> R,W = High */
    LwlBase[VMIO_PORT_C] = 0x07;	/* Mode = reset */
    LwlBase[VMIO_PORT_C] = 0x00;	/* Mode = 0 */
  }
/*
**-----------------------------------------------------------------*/
  void	lwl_vmio_transp_init () {
/*	====================
**			Initialise VMIO10 ready for transparent
**			transmission of data.
*/
		/*
		** ---------- initialize i/o ---------------
		*/
    LwlBase[VMIO_AB_CNTL] = 0x28;	/* Port B output port-Mode Spec. Reg */
    LwlBase[VMIO_AB_CNTL] = 0x80;
    	
    LwlBase[VMIO_AB_CNTL] = 0x29;	/* Port B handshake interlock */
    LwlBase[VMIO_AB_CNTL] = 0x00;
  }
/*==============================================================================

**  Main line program
**  ------------------
**============================================================================*/
  int lwl_server (int port, int base_addr, int verbose,
		  int arg4, int arg5, int arg6, int arg7, int arg8, int arg9,
		  int suspend) {
/*============================================================================*/
    STATUS		status;
    char	       *my_task_name;
    int			i, j, n_bytes_got, bytes_to_get;
    int			msg_cnt = 0;
    char		*p_addr;
    int			cl_port;
    struct sockaddr_in	lcl_sockname;
    struct sockaddr_in	rmt_sockname;
    int			rmt_sockname_len;

    char		nbytes, buff[256], *p_buff;
/*============================================================================
*/
    my_task_name = taskName (taskIdSelf());
    if (suspend != 0) {
      printf ("\n%s: Suspending ...\n", my_task_name);
      taskSuspend (0);	    /* We've been told to suspend ourself, ..
			    **  .. presumably for debug reasons.
			    */
      printf ("\n%s:   ... suspension ended!\n", my_task_name);
    }
/*============================================================================
**	Perform the initialisation of global variables ...
*/
    if (Lwl_cnct_skt != 0) {
      printf ("\007Lwl_cnct_skt = %d -- still open? ... ", Lwl_cnct_skt);
      status = close (Lwl_cnct_skt);
      if (status != ERROR) taskDelay (30);  /* Pause, maybe, to let system ..
					    ** .. tidy up. */
      printf ("it has been closed!\n");
      Lwl_cnct_skt = 0;
    }
    if (Lwl_rw_skt != 0) {
      printf ("\007Lwl_rw_skt   = %d -- still open? ...", Lwl_rw_skt);
      status = close (Lwl_rw_skt);
      if (status != ERROR) taskDelay (30);  /* Pause, maybe, to let system ..
					    ** .. tidy up. */
      printf ("it has been closed!\n");
      Lwl_rw_skt = 0;
    }
/*============================================================================
**		Get a port number to use. If the argument "port" is not
**		a valid port number (not in range 128 to 32767), use DFLT_PORT.
*/
    if ((port < 128) || (port > 32767)) port = DFLT_PORT;
/*============================================================================
**		Get the VME base address of the Test Generator Module.
**		If the argument "base_addr" is zero, use DFLT_BASE_ADDR.
*/
    if (base_addr == 0) base_addr = DFLT_BASE_ADDR;
#ifdef _NO_VME
    printf ("LWL_SERVER: Warning, VME access disabled.\n");
    LwlBase = Lwl_dummy;
#else
    status = sysBusToLocalAdrs (VME_AM_USR_SHORT_IO,
				    (char *) base_addr, &LwlBase);
    if (status != OK) {
      printf ("Error status from sysBusToLocalAdrs\n");
      return ERROR;
    }
#endif	    /* _NO_VME */
		    /*
		    ** Initialise the Test Generator Module.
		    */
    lwl_resetcio ();
    lwl_vmio_init ();
    lwl_enable ();
    lwl_vmio_tg_reset ();
/*============================================================================
**	Create a TCP/IP socket for receiving connection requests, bind it and
**	set it to listen.
*/
    Lwl_cnct_skt = socket (AF_INET, SOCK_STREAM, 0);
    if (Lwl_cnct_skt == -1) lwl_failInet ("\nLWL_SERVER -- Cnct-Socket socket error");

    lcl_sockname.sin_family = AF_INET;
    lcl_sockname.sin_port   = htons (port);
    lcl_sockname.sin_addr.s_addr = 0;
    status = bind (Lwl_cnct_skt, (struct sockaddr *) &lcl_sockname,
					  sizeof (lcl_sockname));
    if (status != 0) lwl_failInet ("\nLWL_SERVER -- Cnct-Socket bind error");

    status = listen (Lwl_cnct_skt, 5);
    if (status != 0) lwl_failInet ("\nLWL_SERVER -- Cnct-Socket listen error");

    lwl_setup_inet_info ();    /* Find out who we are  */
    printf ("\nLWL_SERVER Ident \"%s\" started on %s (%s). Port %d.\n",
			    ident, Lwl_inet_node, Lwl_inet_addr, port);
    if (verbose != 0) printf ("Verbose mode\n");
		    /*
		    **===============================================
		    **	    Loop accepting connections from clients.
		    */
    while (TRUE) {
      rmt_sockname_len = sizeof (rmt_sockname);
      Lwl_rw_skt = accept (Lwl_cnct_skt, (struct sockaddr *) &rmt_sockname,
						&rmt_sockname_len);
      if (Lwl_rw_skt <= 0) {
        lwl_getErrno (&Lwl_my_errno);
	close (Lwl_cnct_skt);
	Lwl_cnct_skt = 0;
	lwl_failInet ("\nLWL_SERVER -- Cnct-Socket accept error");	/* No return */
      }

      p_addr = inet_ntoa (rmt_sockname.sin_addr);
      cl_port = ntohs (rmt_sockname.sin_port);
      printf ("\nLWL_SERVER_%d: Connected to Node %s, Port %d.\n",
				port, p_addr, cl_port);

      lwl_vmio_transp_init ();  /* Ensure the VMIO10 module is in correct mode. */
		    /*
		    **=======================================================
		    **	    Loop reading messages from client.
		    */
      n_bytes_got = recv (Lwl_rw_skt, &nbytes, 1, NULL);
      while ((n_bytes_got == 1) && (nbytes != 0)) {
	if (verbose != 0) printf ("\nLWL:%3d", nbytes);
			/*
			** See if it's a "special" request (0x80 to 0xff).
			*/
	if ((nbytes & 0x80) == 0) {
	  msg_cnt++;
	  bytes_to_get = nbytes;
	  p_buff = buff;
	  while (bytes_to_get > 0) {
	    n_bytes_got = recv (Lwl_rw_skt, p_buff, bytes_to_get, NULL);
	    if (n_bytes_got != bytes_to_get) {
	      if (n_bytes_got <= 0) {
		printf ("\nLWL_SERVER: recv error on message %d; "
			" recv status = %d\n", n_bytes_got);
		printf ("   This is fatal - closing connection to %s.\n",
					    inet_ntoa (rmt_sockname.sin_addr));
		close (Lwl_rw_skt);
		Lwl_rw_skt = 0;
		close (Lwl_cnct_skt);
		Lwl_cnct_skt = 0;
		return ERROR;
	      }
	    }
	    bytes_to_get -= n_bytes_got;
	    p_buff += n_bytes_got;
	  }
	  if (verbose != 0) {
	    for (i = 0; i < nbytes; i++) {
	      if ((i % 16 == 0) && (i != 0)) {
		printf ("\n        %02x", buff[i]);
	      }else {
		printf (" %02x", buff[i]);
	      }
	    }
	    printf ("\n");
	  }
	  lwl_vmio_send (nbytes, buff);	    /* Send the bytes to the LWL. */
	}else if (nbytes == 0x81) {
	  printf ("\nLWL_SERVER: message count set to zero\n");
	  msg_cnt = 0;
	}else if (nbytes == 0x82) {
	  printf ("\nLWL_SERVER: message count = %d\n", msg_cnt);
	}else {
          printf ("\nLWL_SERVER: unrecognised request received -- exiting!\n");
	  printf ("            Request byte = 0x%x\n", nbytes);
	  close (Lwl_rw_skt);
	  Lwl_rw_skt = 0;
	  close (Lwl_cnct_skt);
	  Lwl_cnct_skt = 0;
	  return ERROR;
	}
	n_bytes_got = recv (Lwl_rw_skt, &nbytes, 1, NULL);
      }
		    /*
		    **	    End of loop reading messages from client.
		    **===============================================
		    */
      if (n_bytes_got <= 0) {
	printf ("\nLWL_SERVER -- R/W-Socket recv error.\n");
	printf ("              Closing connection to %s.\n",
					inet_ntoa (rmt_sockname.sin_addr));
      }else {
	printf ("\nLWL_SERVER -- client %s has terminated his connection.\n",
					inet_ntoa (rmt_sockname.sin_addr));
      }
      close (Lwl_rw_skt);
      Lwl_rw_skt = 0;
    }
		    /*
		    **	    End of loop accepting connections from clients.
		    **======================================================
		    */
    return OK;
  }
/*========================================= End of LWL_SERVER.C ========*/




