/*		V24 (RS232) Serial line Transport Service for RPC       ts_v24.c
**		=================================================
**
**  This module provides a transport service for the RPC system developed
**  by CERN/DD Online group in 1986-90.
**
**  This particular module uses RS232 lines (V24) with its own built-in
**  protocol.
**  See the RPC User Manual for details of address syntax.
**
**  See also the RPC internals manual, which describes how modules
**  similar to this one work, also giving flow charts and message formats,
**  and describing the character encoding and protocol.
**
**  Authors:
**	Roberto Bagnara (CERN, PISA) wrote this in Pascal originally.
**	Tim Berners-Lee (CERN/CN) translated into C and maintained it.
**
**  History:
**	 5 May 90   Code extracted from general ts.c module. (TBL)
**
**  Compilation switches:
**
*/


/*	Module parameters:
**	-----------------
**
**  These may be undefined and redefined by syspec.h
*/
# define WILDCARD '*'	    /* Wildcard used in addressing */

# define INITIAL_RETRY_TIME 500
# define BACKOFF_LIMIT 3000


#include <stdio.h>
#include "syspec.h"
#define TS			/*  Require visibility of TS interface */
#define TS_INTERNALS		/*  Require visibility of TS internals */
#include "rpcrts.h"		/*  Define all general RPC data structures   */
#include "rpc_code.h"		/* Coding convention macros */

/*
**      Definitions for pointers
*/
#define NIL_MESSAGE (rpc_message *)0
extern char *malloc();
extern void free();
extern char *strncpy();


/*                      External Routines
**			-----------------
*/
 
extern void rpc_new();                  /* Allocate rpc_message */
extern void rpc_dispose();              /* Deallocate rpc_message */
extern void rpc_get_string();		/* Get Pascal or C string parameter */
 
#ifdef AST				/* Always used by CATS !!!!!! */
extern void sys_setast();		/* En/Disable ASTs */
rpc_status ts_when_receive();           /* forward (ASTs only) */
#endif



/*   Genuine module-global variables
**		    ----------------
*/

PRIVATE    v24_buffer		    send_buffer;
PRIVATE    v24_chan_info_array	    v24_chan_info;
PRIVATE    char *		    buffer_pointer;

/*      Global variables converted from Pascal intermediate level
**      variables:
*/
rpc_message	    *G94_mes;	/* All for V24 */
socket_type	    G88_socket;
socket_type	    G92_socket;

/*			    V24 routines
**			    ------------
*/

/*	Update Cyclic Redundancy Check or Checksum	   compute_block_check()
**	------------------------------------------
**
**  On entry,
**	rpc_byte		is the byte to be included in the check
**	block-check_type	indicates whether a checksum or CRC is used
**	computed_block_check	points to the running total
**
**  On exit,
**	*computed_block-check	has been updated.
*/

#ifdef __STDC__
PRIVATE void compute_block_check(
    rpc_byte		octet,
    block_check_type	block_check_used,
    unsigned int        *computed_block_check)
#else
PRIVATE void compute_block_check( octet, block_check_used, computed_block_check)
    rpc_byte		octet;
    block_check_type	block_check_used;
    unsigned int        *computed_block_check;
#endif
{
   unsigned int q, c, crc;

   if (block_check_used == checksum) {
      (*computed_block_check) = ((*computed_block_check) + octet) & 0xFF;
#ifdef BLOCK_CHECK_TRACE
      if (rpc_trace)
         UTRACE(tfp, " block_check = %lh %02x\n",
		(*computed_block_check), octet);
#endif
      }
   else {
      c = octet;
      crc = *computed_block_check;
      q = (crc ^ c ) & 0xF;
      crc = (crc >> 4) ^ (q * 4225);
      q = (crc ^ (c >> 4)) & 0xF;
      crc = (crc >> 4) ^ ( q * 4225);
      *computed_block_check = crc;
      }
   return;
}

/*	Open V24 physical channel
**	-------------------------
**
*/
/*  mychannel is modified to path number (-JRR?) */

#ifdef __STDC__
PRIVATE rpc_status rpc_v24_open(channel mychannel)
#else
PRIVATE rpc_status rpc_v24_open(mychannel)
   channel	mychannel;
#endif
{
    register v24_chan_info_type *cha = &v24_chan_info[mychannel];
   
    if (!((cha->v24_usage)++)) {
#ifdef OSK
	int istatus = v24_open(mychannel, cha->speed, cha->traffic, rpc_trace);
	if (istatus < 0)	    /* @@@ bug in OSK code */
	    return RPC_S_TS_INTERNAL_ERROR;
	else return RPC_S_NORMAL;
#else
	return v24_open(mychannel, cha->speed, cha->traffic, rpc_trace);
#endif
    } else return RPC_S_NORMAL;
}

/*__________________________________________________________________________
 *
 *	    Procedure:	Close V24 physical channel
 *
 */

 void
rpc_v24_close(mychannel, pstatus)
   channel	mychannel;
   rpc_status   *pstatus;
{
      register v24_chan_info_type *chan = &v24_chan_info[mychannel];

      if (--(chan->v24_usage) == 0) {
         *pstatus = v24_close(mychannel, rpc_trace);
      } else
         *pstatus = RPC_S_NORMAL;
}


/*__________________________________________________________________________
 *
 *	    Function:	Get byte from V24 channel
 *
 */

 rpc_status
get_byte(octet, chan, timeout)
   rpc_byte   *	octet;
   channel	chan;
   int		timeout;
{
   int len, istat;

   (*octet) = 0;
   len =1;
   istat = v24_get(octet,len,timeout,chan,rpc_trace);
   if (istat <= 0)
      return RPC_S_TIMEOUT;
   else
      return RPC_S_NORMAL;
}

/*__________________________________________________________________________
 *
 *	    Function:	Send raw byte down a V24 channel
 *
 */

 BOOLEAN
send_byte(octet, chan)
   rpc_byte   *octet;
   channel   chan;
{
   return (v24_send(octet,	/* Buffer address */
		    1,		/* Buffer length */
		    chan,
		    rpc_trace) >0);
}

/*__________________________________________________________________________
 *
 *	    Procedure:	Encode as necessary and send byte down V24 channel
 *
 */

 void
send_byte_encoded(octet, do_block_check)
   rpc_byte   octet;
   BOOLEAN   do_block_check;
{
   {
      register v24_chan_info_type *chan =
			&v24_chan_info[(G88_socket)->mdp.v24.soc_chan];

      if (do_block_check) 
         compute_block_check(octet, chan->block_check_used, &(chan->computed_block_check));
      if (chan->traffic == eight_bit)
         if ((octet == RPC_V24_STARTOFPACKET) || (octet == RPC_V24_ESC_7E))
         {
            *buffer_pointer++ = RPC_V24_ESC_7E;
            *buffer_pointer++ = (octet ^ 64);
         } else {
            *buffer_pointer++ = octet;
         }
      else
         if ((octet <= 92)) {
            *buffer_pointer++ = octet + 32;
         } else
            if ((octet <= 185)) {
               *buffer_pointer++ = RPC_V24_ESC_7D;
               *buffer_pointer++ = octet - 61;
            } else {
               *buffer_pointer++ = RPC_V24_ESC_7E;
               *buffer_pointer++ = octet - 154;
            }
   }
}

/*__________________________________________________________________________
 *
 *	    Procedure:	
 *
 */

 void
rpc_v24_send_message(status, socket, pmessage, bufsize)
   rpc_status	        *status;
   socket_type		socket;
   rpc_message_pointer  pmessage;
   rpc_long		bufsize;
{
   register int   d_index;
   int   d_start;
   rpc_byte   octet;

   G88_socket = socket;
   {
      register struct socket_struct *soc = socket;

      {
         register v24_chan_info_type *chan = &v24_chan_info[soc->mdp.v24.soc_chan];

/*         channel tc;
         int ln;      out */

	 *status = RPC_S_NORMAL;
         if (rpc_trace) {
            UTRACE(tfp, "Send V24 mess line %1d, link %1d, len %1d\n",
		    soc->mdp.v24.soc_chan,
		    soc->mdp.v24.soc_link_number,
		    bufsize);
            }
         v24_clear(soc->mdp.v24.soc_chan);

/*  Request to Send/Clear to send character exchange
 */

         if ((chan->rtscts_enabled))
            do {
               if (!send_byte(RPC_V24_RTS, soc->mdp.v24.soc_chan))
                  goto L99;
               *status = get_byte(&octet, soc->mdp.v24.soc_chan, 100);
               if (*status == RPC_S_FRAMING)
                  goto L99;
               else
		  if ((*status == RPC_S_NORMAL) && (octet >= 32))
                     if ((octet != RPC_V24_CTS)) {
			*status = RPC_S_FRAMING;
                        goto L99;
                     }
	    } while (!((*status == RPC_S_NORMAL) && (octet == RPC_V24_CTS)));


         chan->computed_block_check = 0;
         send_buffer[0] = RPC_V24_STARTOFPACKET;
	 buffer_pointer = (char *)&send_buffer[1];/* Point to next empty byte */
         if (bufsize < 128) {
            chan->block_check_used = checksum;
            send_byte_encoded((rpc_byte)(bufsize + 128), TRUE);
         } else {
            chan->block_check_used = crc_ccitt;
            send_byte_encoded((rpc_byte)(bufsize / 256), TRUE);
            send_byte_encoded((rpc_byte)(bufsize % 256), TRUE);
         }
         send_byte_encoded((rpc_byte)(soc->mdp.v24.soc_link_number), TRUE);
         {
            register rpc_message *mes = pmessage;

            if ((mes->body.which == CALL_MESSAGE)
                && (mes->body.cal.program_number.h.h == 0)
                && (mes->body.cal.program_number.h.l == 0)
                && (mes->body.cal.program_number.l.h == 0)
                && (mes->body.cal.program_number.l.l < 24)
                && (mes->body.cal.version_number.h == 0)
                && (mes->body.cal.version_number.l == 0)
                && (mes->body.cal.procedure_number.h == 0)) {
               send_byte_encoded((rpc_byte)
		    ( ((mes->body.cal.program_number.l.l + 8) * 8)
		      + mes->body.cal.call_transaction_id.l), TRUE);
               send_byte_encoded((rpc_byte)(mes->body.cal.procedure_number.l),
		     TRUE);
               d_start = 12;
            } else {
               send_byte_encoded((rpc_byte)
#ifdef LSBFIRST
		  ((mes->body.which>>5) + mes->body.cal.call_transaction_id.l),
#else
		  ((mes->body.which<<3) + mes->body.cal.call_transaction_id.l),
#endif
		     TRUE);
               d_start = 4;
            }
            for (d_index = d_start; d_index<=bufsize-1 ; d_index++)
                     send_byte_encoded(mes->body.rpc_b[d_index], TRUE);
         }
         if (chan->block_check_used == checksum) 
            send_byte_encoded((rpc_byte)(chan->computed_block_check), FALSE);
         else {
            send_byte_encoded((rpc_byte)(chan->computed_block_check / 256),
		   FALSE);
            send_byte_encoded((rpc_byte)(chan->computed_block_check % 256),
		   FALSE);
         }
         v24_send(  send_buffer,			    /* Address */
		    buffer_pointer - (char *)send_buffer,   /* Length */
		    soc->mdp.v24.soc_chan,
		    rpc_trace);
      L99:
         if (rpc_trace)
	    if (*status == RPC_S_NORMAL)
               UTRACE(tfp, "RPC/TS: V24 message sent \n");
            else
               UTRACE(tfp, "Error sending V24 message.\n");
      }
   }
}

/*__________________________________________________________________________
 *
 *	    Function:	V24 Packet Reader State Machine	
 *
 */

 packet_reader_states
rpc_v24_reader_automaton(octet)
   rpc_byte   octet;
{
      int   V24_mess_type;
      register v24_chan_info_type *chan =
			&v24_chan_info[G92_socket->mdp.v24.soc_chan];
      register rpc_message * mes = G94_mes;

/*
      if ((chan->traffic == seven_bit))
         octet = (unsigned)(octet & 127);
*/
      if ((octet == RPC_V24_STARTOFPACKET)) {
         chan->data_index = 0;
         chan->block_check_enabled = FALSE;
         chan->packet_reader_state = got_marker;
         chan->byte_reader_state = cleared;
      } else
         if (((int)(chan->packet_reader_state) >= (int)(idle))) {
            chan->packet_reader_state = idle;
            if ((chan->rtscts_enabled) && (octet == RPC_V24_RTS))
               if (!send_byte(RPC_V24_CTS, G92_socket->mdp.v24.soc_chan))
                  chan->packet_reader_state = C1_abort;
         } else {
            if ((chan->traffic == eight_bit))
               if ((octet == RPC_V24_ESC_7E))
                  chan->byte_reader_state = got_escape_7e;
               else {
                  if ((chan->byte_reader_state == got_escape_7e)) {
                     octet = (unsigned)(octet ^ 64);
                     chan->byte_reader_state = cleared;
                  }
               }
            else {
               if ((octet == RPC_V24_ESC_7D))
                  chan->byte_reader_state = got_escape_7d;
               else
                  if ((octet == RPC_V24_ESC_7E))
                     chan->byte_reader_state = got_escape_7e;
                  else
                     if ((chan->byte_reader_state == got_escape_7d)) {
                        octet = octet + 61;
                        chan->byte_reader_state = cleared;
                     } else
                        if ((chan->byte_reader_state == got_escape_7e)) {
                           octet = octet + 154;
                           chan->byte_reader_state = cleared;
                        } else
                           octet = octet - 32;
            }

            if ((chan->byte_reader_state == cleared)) {
               if ((chan->block_check_enabled))
                  compute_block_check(octet, chan->block_check_used, &chan->computed_block_check);
               switch ((int)(chan->packet_reader_state)) {
                 case got_marker:
                  if (octet & 0x80) {	    /* Bit 7 flags "short message" */
                     chan->block_check_used = checksum;
                     chan->message_size = octet & 0x7f ;
                     chan->packet_reader_state = got_length;
                  } else {		    /*	Long message */
                     chan->block_check_used = crc_ccitt;
                     chan->message_size = octet << 8;
                     chan->packet_reader_state = getting_length;
                  }
                  chan->computed_block_check = 0;
                  chan->block_check_enabled = TRUE;
                  compute_block_check(octet, chan->block_check_used, &chan->computed_block_check);
                  break ;
                 case getting_length:
                  chan->message_size = chan->message_size + octet;
                  if ((chan->message_size > RPC_BUFFER_SIZE))
                     chan->packet_reader_state = got_garbled_packet;
                  else
                     chan->packet_reader_state = got_length;
                  break ;
                 case got_length:
                  chan->link_number = octet;
                  chan->packet_reader_state = got_link_number;
                  break ;

                 case got_link_number:
                  mes->body.cal.call_transaction_id.h = 0;
                  mes->body.cal.call_transaction_id.l = (unsigned)(octet & 7);
                  V24_mess_type = octet >> 3;
                  if ((V24_mess_type >= 8)) {
                     mes->body.which = CALL_MESSAGE;
                     mes->body.cal.program_number.h.h = 0;
                     mes->body.cal.program_number.h.l = 0;
                     mes->body.cal.program_number.l.h = 0;
                     mes->body.cal.program_number.l.l = V24_mess_type - 8;
                     mes->body.cal.version_number.h = 0;
                     mes->body.cal.version_number.l = 0;
                     mes->body.cal.procedure_number.h = 0;
                     chan->packet_reader_state = getting_proc_number;
                  } else {
#ifdef LSBFIRST
                     mes->body.which = V24_mess_type<<8;
#else
                     mes->body.which = V24_mess_type;
#endif
                     if ((chan->message_size > 4)) {
                        chan->data_index = 4;
                        chan->packet_reader_state = getting_data;
                     } else {
                        chan->block_check_enabled = FALSE;
                        chan->packet_reader_state = got_data;
                     }
                  }
                  break ;

                 case getting_proc_number:
                  mes->body.cal.procedure_number.l = octet;
                  if ((chan->message_size > 12)) {
                     chan->data_index = 12;
                     chan->packet_reader_state = getting_data;
                  } else {
                     chan->block_check_enabled = FALSE;
                     chan->packet_reader_state = got_data;
                  }
                  break ;

                 case getting_data:
                  mes->body.rpc_b[chan->data_index] = octet;
                  if ((chan->data_index == chan->message_size - 1)) {
                     chan->block_check_enabled = FALSE;
                     chan->packet_reader_state = got_data;
                  }
                  chan->data_index = chan->data_index + 1;
                  break ;

                 case got_data:
                  if ((chan->block_check_used == checksum)) {
                     chan->received_block_check = octet;
                     if ((chan->received_block_check == chan->computed_block_check))
                        chan->packet_reader_state = got_good_packet;
                     else
                        chan->packet_reader_state = got_garbled_packet;
                  } else {
                     chan->received_block_check = octet * 256;
                     chan->packet_reader_state = getting_block_check;
                  }
                  break ;

                 case getting_block_check:
                  chan->received_block_check = chan->received_block_check + octet;
                  if ((chan->received_block_check == chan->computed_block_check))
                     chan->packet_reader_state = got_good_packet;
                  else
                     chan->packet_reader_state = got_garbled_packet;
                  break ;
               }
            }
         }
      return chan->packet_reader_state;
}


/*		Receive a mesaage			       v24_get_message()
**		-----------------
**
**	This is a raw packet read on the RS232 channel, which may pick
**  up a corruption or a timeout.
*/
#ifdef __STDC__
PRIVATE rpc_status rpc_v24_get_message(
   rpc_message	        *mes,
   int			timeout)
#else
PRIVATE rpc_status rpc_v24_get_message(mes, timeout)
   rpc_message	        *mes;
   int			timeout;
#endif
{
   rpc_byte   octet;
   packet_reader_states   automaton_state;
   BOOLEAN   again;

/*  Set up global variables for automata to use
*/
   socket_type soc = G92_socket = mes->m_socket;
   G94_mes = mes;

   CTRACE(tfp, "Waiting for V24 message...\n");
   FLUSH_TRACE 
   {
      register v24_chan_info_type *chan = &v24_chan_info[soc->mdp.v24.soc_chan];

      do {
         again = FALSE;
         chan->packet_reader_state = idle;
         chan->byte_reader_state = cleared;
         chan->data_index = 0;
         chan->message_size = 0;
         automaton_state = idle;

/*  Get a packet:
*/
         do {
            mes->m_status = get_byte(&octet, (G92_socket)->mdp.v24.soc_chan,
		    timeout);
            if (mes->m_status == RPC_S_NORMAL) 
               automaton_state = rpc_v24_reader_automaton(octet);
         } while (!((mes->m_status != RPC_S_NORMAL)
		    || ((int)(automaton_state) >= (int)(got_good_packet))));

/*  Decode status:
*/
         if (mes->m_status == RPC_S_NORMAL)
            if ((automaton_state == got_garbled_packet)
	    || (automaton_state == C1_abort))
	       mes->m_status = RPC_S_FRAMING;
            else
               if (soc->mdp.v24.soc_link_number != chan->link_number)
                  again = TRUE;

         mes->m_index = chan->message_size;

/*  Print trace message:
*/
         if (rpc_trace)
            if (mes->m_status == RPC_S_NORMAL) {
	        UTRACE(tfp, "RPC/TS: V24 message received ok, link number %1d\n",
			    chan->link_number);
                /* hex_message(mes, mes->m_index);  see ts_receive*/

            } else {
               UTRACE(tfp, "RPC/TS: Error receiving V24 message:\n");
	       if ((mes->m_status == RPC_S_TIMEOUT))
                  UTRACE(tfp, "  Timeout Limit Expired\n");
               else
                  if ((automaton_state == got_garbled_packet))
                     if ((mes->m_index > RPC_BUFFER_SIZE))
                        UTRACE(tfp, "  Illegal Length Received: %1d\n", 
			    mes->m_index);
                     else {
                        UTRACE(tfp, 
	        "  Wrong Block Check Received, Computed: %4x, Received: %4x\n",
			    chan->computed_block_check,
			    chan->received_block_check);
                     }
                  else
                     if ((automaton_state == C1_abort))
                        UTRACE(tfp, "  Error Sending CTS\n");
                     else
                        UTRACE(tfp, "  Unknown Reason\n");
               if ((mes->m_index > 0) && (mes->m_index <= RPC_BUFFER_SIZE)) {
                  UTRACE(tfp, "  Original packet length could be %1d\n",
					    mes->m_index);
               }
               if ((chan->data_index > 0)) {
                  UTRACE(tfp, "  Incomplete message dump follows,");
                  hex_message(G94_mes, chan->data_index);
               }
            } /* end if trace, if not normal */

      } while (again);
   }
   return mes->m_status;
}

/*	Check for Duplicate Message				 duplicate_v24()
**	---------------------------
*/

PRIVATE BOOLEAN
duplicate_v24(pmessage)
   rpc_message_pointer   pmessage;
{

      register rpc_message_pointer mes = pmessage;

      if (     (mes->body.which == RETURN_MESSAGE)
	    || (mes->body.which == REJECT_MESSAGE)) {
         {
            register struct socket_struct *soc = mes->m_socket;

            if (soc->soc_last_call_sent)
               if ((mes->body.cal.call_transaction_id.l 
                  != soc->soc_last_call_sent->body.cal.call_transaction_id.l)) {
CTRACE(tfp, "RPC/TS: *** Duplicated reply received: Discarded ***\n");
		  return TRUE;
               }
         }
      } else
         if ((mes->body.which == CALL_MESSAGE)) {
            {
               register struct socket_struct *soc = mes->m_socket;

               if (soc->soc_last_reply_sent)
                  if (mes->body.cal.call_transaction_id.l)
                     if (mes->body.cal.call_transaction_id.l ==
                          soc->soc_last_reply_sent->
			  body.ret.return_transaction_id.l) {
                        rpc_v24_send_message(
			    &mes->m_status,
			    mes->m_socket,
			    soc->soc_last_reply_sent,
			    soc->soc_last_reply_sent->m_index);
CTRACE(tfp, "RPC/TS: Duplicated call received, Last reply retransmitted ***\n");
                        return TRUE;
                     }
            }
         }
      return FALSE;
}

/*	Return RPC address of this socket		        v24_my_address()
**	---------------------------------
**
**  This routine returns a string indicating the RPC address of the socket.
**
**  On entry,
**	soc	    should be a valid socket pointer
**	addrstr	    points to a buffer to contain a string.
**	addrlen    indicates the length of the string buffer
**
**  On exit,
**	*addrstr    has the contact address for an external node wishing to contact
**		    this socket written into it, zero terminated, without the
**		    protocol on the end.
*/
#ifdef __STDC__
PRIVATE rpc_status v24_my_address(
    socket_type	    soc,
    chr *	    addrstr,
    int		    addrlen)
#else
PRIVATE rpc_status v24_my_address(soc, addrstr, addrlen)
    socket_type	    soc;
    char *	    addrstr;
    int		    addrlen;
#endif
{
    return RPC_S_NOT_IMPLEMENTED;
}

/*	Return RPC address of peer		              v24_peer_address()
**	--------------------------
**
**  On entry,
**	soc	    should be a valid socket pointer
**	address	    points to a buffer to contain an rpc_name.
**
**  On exit,
**	*address    has the contact address for the peer node.
**		    blank filled.
**		    26 useful characters in the address.
*/
#ifdef __STDC__
PRIVATE rpc_status v24_peer_address(
    socket_type	    soc,
    char * 	    addrstr,
    int		    addrlen)
#else
PRIVATE rpc_status v24_peer_address(soc, addrstr, addrlen)
    socket_type	    soc;
    char *	    addrstr;
    int		    addrlen;
#endif
{
    return RPC_S_NOT_IMPLEMENTED;
}

/*      Send a message over the given socket			     v24_write()
**	------------------------------------
**
** On entry,
**      pmessage        points to a pointer to the message, with fields:
**	    m_next	    (undefined)
**	    m_socket	    valid
**	    m_index	    Length of message to be sent in bytes,
**			    exclusing any bytes needed for the protocol.
**	    m_status	    (undefined)
**
** On exit,
**
**      The pointer to the message may have been changed. In this case,
**      it will point to a message with the m_socket and m_status fields
**      set, but without the data.  This routine is therefore DESTRUCTIVE.
**
**	returns:	Status of operation.
*/
#ifdef __STDC__
PRIVATE rpc_status v24_write(rpc_message_pointer *ppmessage)
#else
PRIVATE rpc_status v24_write(ppmessage)
    rpc_message_pointer *ppmessage;
#endif
{
    rpc_status		status;

    register rpc_message *mes = *ppmessage;
    register struct socket_struct *soc = mes->m_socket;
 
    mes->m_status = check_socket(soc);
    if (BAD(mes->m_status)) return mes->m_status;

    if (rpc_trace) {
	    UTRACE(tfp, "RPC/v24: Sending message, ");
	    hex_message(mes, mes->m_index);
    }

    if ((mes->body.which == CALL_MESSAGE)) {
	mes->body.cal.call_transaction_id.l =
			 soc->soc_next_call_tid;
	soc->soc_next_call_tid =
			 soc->soc_next_call_tid % 7 + 1;
    }

    rpc_v24_send_message(&mes->m_status, mes->m_socket, mes,
				(rpc_long) RPC_BUFFER_SIZE);

    if (((*ppmessage)->body.which == RETURN_MESSAGE)
    || ((*ppmessage)->body.which == REJECT_MESSAGE)) {
        rpc_message_pointer temp_pointer;

	if ((soc->soc_last_reply_sent == NIL_MESSAGE)) {
	    rpc_new(&soc->soc_last_reply_sent, RPC_BUFFER_SIZE);
	    soc->soc_last_reply_sent->m_socket = soc;
	    soc->soc_last_reply_sent->m_status = RPC_S_NORMAL;
	}
	temp_pointer = (*ppmessage);
	(*ppmessage) = soc->soc_last_reply_sent;
	soc->soc_last_reply_sent = temp_pointer;
    }
    else
    if ((*ppmessage)->body.which == CALL_MESSAGE) {
	register rpc_message_pointer temp_pointer;

	if ((soc->soc_last_call_sent == NIL_MESSAGE)) {
	   rpc_new(&soc->soc_last_call_sent, RPC_BUFFER_SIZE);
	   soc->soc_last_call_sent->m_socket = soc;
	   soc->soc_last_call_sent->m_status = RPC_S_NORMAL;
	}

	temp_pointer = *ppmessage;
	*ppmessage = soc->soc_last_call_sent;
	soc->soc_last_call_sent = temp_pointer;
    }

    return (*ppmessage)->m_status;
} 

/*      Receive message						      v24_read()
**	---------------
**
**  This procedure waits for a message to arrive on a socket and returns the
**  message. A suitable message buffer is passed to the routine, but a
**  different one may be returned. If the socket passed is the master socket
**  for mutiple connections, then the message recieved will carry the socket
**  number of the slave socket on which the message actually arrived.
**
** On entry:
**      ppmessage       is pointer to pointer to buffer, fields:
**	    m_socket	    The socket to be used - possibly a master socket
**	    m_index	    don't care
**	    m_status	    don't care
**	    m_next	    don't care
**
**      timeout         -1 for infinite, 0 for poll, else in units of 10ms
**
** On exit,
**	returns		The status of the operation.
**      *pmessage       may have been modified to point to another message
**                      from the pool. Fields:
**	    m_socket	    The (possible slave) socket the msg came on.
**	    m_status	    The status of the operation (= return value).
**	    m_index	    The number of bytes recieved if good status
**	    m_next	    junk.
**
*/
#ifdef __STDC__
PRIVATE rpc_status v24_read(
        rpc_message_pointer *ppmessage,
        int		    timeout)
#else
PRIVATE rpc_status v24_read(ppmessage, timeout)
        rpc_message_pointer *ppmessage;
        int		    timeout;
#endif
{
    int		    waited;
    int		    backoff;
    BOOLEAN	    again;
    rpc_status	    status;

    register struct socket_struct *soc = (*ppmessage)->m_socket;

    if (soc->soc_last_call_sent == NULL)
        backoff = timeout;
    else {
        waited = 0;
        backoff = 500;
    }
    do {
        again = FALSE;
        if (soc->soc_last_call_sent != NULL)  {
	  if ((timeout >= 0))
	     if ((waited + backoff > timeout))
		backoff = timeout - waited;
	  waited = waited + backoff;
        }

        status = rpc_v24_get_message(*ppmessage, backoff);

        if (status == RPC_S_FRAMING) {
	  again = TRUE;
	  CTRACE(tfp, 
"RPC/TS/v24: GARBLED PACKET RECEIVED, Waiting for retransmission ***\n");
        }
        else if (status == RPC_S_TIMEOUT) {
	     if (rpc_trace)
		UTRACE(tfp, "RPC/TS/V24: Timeout period expired\n");
	     if ((soc->soc_last_call_sent != NIL_MESSAGE))
		if (((timeout < 0) || (waited < timeout))) {
		   if ((backoff < BACKOFF_LIMIT / 2))
		      backoff = backoff + backoff;
		   else
		      backoff = BACKOFF_LIMIT;
		   rpc_v24_send_message(&status, soc,
			soc->soc_last_call_sent,
			soc->soc_last_call_sent->m_index);
		   CTRACE(tfp, 
		    "RPC/TS/v24: Last message retransmitted\n");
		   again = TRUE;
		} else
		   CTRACE(tfp, 
		"RPC/TS/V24: Total timeout expired: stop trying ***\n");
	} else
	if (GOOD(status))
		again = duplicate_v24((*ppmessage));
    } while (again);

/*  If we have got a valid reply, we can dispose of the last call
**  we sent out.
*/
    if (soc->soc_last_call_sent != NULL)
       if (((*ppmessage)->body.which == RETURN_MESSAGE)
	  || ((*ppmessage)->body.which == REJECT_MESSAGE)) {
	  rpc_dispose(soc->soc_last_call_sent);
	  soc->soc_last_call_sent = NULL;
    }

    (*ppmessage)->m_status = status;
    return status;
}

/*      Asynchronous Receive
**	--------------------
**
** On entry,
**      pmessage        is the address of a suitable message buffer
**      action          is the address of the routine to be called
**      user_1          is not used at the moment.
**
** On exit,
**      the parameters are stored only.  Exits immediately.
**
** On arrival of the next message:
**      The routine *action() is called, and passed the message as a parameter.
**      If the buffer *pmessage is not used, it will be reused elsewhere and
**      a different buffer passed to *action().
*/
#ifdef __STDC__
PRIVATE rpc_status v24_aread(
        rpc_message_pointer     pmessage,
        rpc_pointer		action,
        rpc_integer		user_1)
#else
PRIVATE rpc_status v24_aread(pmessage, action, user_1)
        rpc_message_pointer     pmessage;
        rpc_pointer		action;
        rpc_integer		user_1;
#endif
{
    return(RPC_S_NOT_IMPLEMENTED);
} /* v24_aread */

/*	Open a transport connection				      v24_open()
**	---------------------------
**
**  On entry,
**	soc points to a recently created socket, fields as follows:
**
**	    soc_last_call_sent	    NULL
**	    soc_last_reply_sent	    NULL
**	    soc_next-call_tid	    0
**	    soc_protocol		    valid pointer to protocol structure
**
**	    and otherwise uninitialised.
**
**	tsap	points to a zero-terminated string representing the address
**		(with no .v24 on it)
*/

#ifdef __STDC__
PRIVATE rpc_status v24_open(socket_type soc, char * tsap)
#else
PRIVATE rpc_status v24_open(soc, tsap)
    socket_type	soc;
    char	*tsap;
#endif
{

/*  We have to put the values of some parameters into temporary variables
**  because we do not yet know which channel to load them into.
*/
    traffic_type    tmp_traffic;
    BOOLEAN	    tmp_rtscts;
    int		    tmp_speed;
    char	    *p = tsap;	    /* Pointer to string */

    rpc_status status = RPC_S_NORMAL;  /* To be returned */

    tmp_traffic = eight_bit;
    tmp_rtscts = FALSE;
    tmp_speed = 0;

    soc->mdp.v24.soc_link_number = 0;
    soc->mdp.v24.soc_chan = 0;
    {	int a;				/* Coerce all to uppercase */
	for (a=0; tsap[a]; a++)
	    tsap[a] = toupper(tsap[a]);
    }
    for (;*p;) {
	switch ((int)(*p++)) {
	    case '0':
	     break;
	    case 'L':
	     tmp_traffic = seven_bit;
	     break ;
	    case 'B':
	     tmp_traffic = eight_bit;
	     break ;
	    case 'R':
	     tmp_rtscts = TRUE;
	     break ;
	    case 'F':
	     tmp_rtscts = FALSE;
	     break ;
	    case 'C':
	     soc->mdp.v24.soc_link_number =
		parse_cardinal(&status, &p, 255);
	     break ;
	    case 'S': tmp_speed =
		parse_cardinal(&status, &p, 19200);
	     break ;

	    case 'T': 		    /* Terminal number Tnnn */
	     if (*p != 'T')	    /* Allow TTnnn instead of Tnnn */
		    soc->mdp.v24.soc_chan = parse_cardinal(
				   &status, &p, V24_CHANNELS-1);
#ifdef __MSDOS__
/*	For MSDOS, COMn: numbers start from 1, but our 'chan'
numbers (and parameters to async_xxx) start at 0. */
	     soc->mdp.v24.soc_chan--;
#endif
	     break ;

	    case ':':
	     break ;
	    case '_':
	     break ;
	    default:
	     status = RPC_S_SYNTAX_4;  /* Invalid option */
      } /*switch*/
    } /*for*/

    { /*with*/
      register v24_chan_info_type *chan =
		    &v24_chan_info[soc->mdp.v24.soc_chan];

      chan->traffic = tmp_traffic;
      chan->rtscts_enabled = tmp_rtscts;
      chan->speed = tmp_speed;
    } /*with*/

    if (GOOD(status)) return rpc_v24_open(soc->mdp.v24.soc_chan);
    else return status;
}

/*	Close a Transport Connection				     v24_close()
**	----------------------------
**
**  On entry,
**	soc	Must be a socket opened by v24_open().
**
**  On exit,
**	returns	a status of the close operation.
*/

#ifdef __STDC__
PRIVATE rpc_status v24_close(socket_type soc)
#else
PRIVATE rpc_status v24_close(soc)
    socket_type	soc;
#endif
{
    rpc_status status;
    rpc_v24_close(soc->mdp.v24.soc_chan, &status);
    return status;
} /* v24_close */

/*	Data structure defining this medium:
**	-----------------------------------
*/

PRIVATE rpc_protocol v24_protocol = {
    0,				/* Link (not yet used) */
    "v24",			/* Name of medium used in address strings */
    0,				/* No, not reliable */
    v24_open,
    v24_close,
    v24_write,
    v24_read,
    v24_aread,
    v24_my_address,
    v24_peer_address,
};


/*	Register this protocol with the RPC system
**	------------------------------------------
*/

#ifdef __STDC__
rpc_status use_v24(void)
#else
rpc_status use_v24()
#endif
{
    {	register int        s;
	for (s=0; s<V24_CHANNELS ; s++) {
	     register v24_chan_info_type *chan = &v24_chan_info[s];

	     chan->v24_usage = 0;
	     chan->packet_reader_state = idle;
	     chan->byte_reader_state = cleared;
	}
    }

    rpc_use(v24_protocol);
}

