
int eth_trace = 0;
#define ETH_DBUG 0
#define ETH_TRACE if(eth_trace) printf

#if APELLEgWM
#include "apelle.h"
int db_eth = -1;
#endif

#define ANR_DBUG 0
#define ANR_INLINE 1	    /* Use inline assembler instead of extern module */

/*
**  RAWETH.C --- 14-Nov-1989   - Specific ETHERNET Code for the IBM-PC
*/

#include <dos.h>
#include <stdio.h>
#include <string.h>

#define TS                      /* We want TS layer visibility */
#include "syspec.h"
#include "rpcrts.h"
#include "rpc_code.h"

#if ANR_DBUG
#define ethspy(a, b)	*(unsigned *)MK_FP(0xB800, a) = b
#else
#define ethspy(a, b)
#endif

/* ============= TS / AST specific information ============== */

#define   PROTECT  asm cli
#define UNPROTECT  asm sti

#define NO_AST_FLAG (rpc_pointer)0
#define SET_AST_FLAG (rpc_pointer)1

#define NIL_SOCKET_PTR (socket_type)0
#define NIL_MESSAGE_PTR (rpc_message_pointer)0

volatile
socket_type     eth_rxq;        /* Queue of sockets waiting for ETH packets */

int eth_usage;                  /* Usage count for ETH */

extern void eth_ast_service();

/* ============= BICC specific information ========================== */

#define INT 0x5B

#define ACB_LEN 54	/* minus 1 */
#define TCB_LEN 68	/* minus 1 */
#define LG_STACK 4096
#define M_LDATA 1500    /*  Maximum packet length Blue Book MAC  */

/* Command codes */

#define CD_M_ACTIVATE_SAP   0xfa
#define CD_M_DEACTIVATE_SAP 0Xfb
#define CD_L_DATA_SEND      0xf4
#define CD_STATUS           0xb3
#define CF_M_ACTIVATE_SAP   0x12
#define CF_M_DEACTIVATE_SAP 0X14
#define ID_M_DATA           0X0a

/* Error codes returned by the Multi Protocol Support routines */

#define MPS_COMMAND_SUCCESS      0
#define MPS_ILLEGAL_BUF_LEN      1
#define MPS_ILLEGAL_COMMAND      3
#define MPS_CALL_TIMEOUT         5
#define MPS_MORE_DATA_COMING     6
#define MPS_VCID_INVALID         8
#define MPS_NO_RESOURCE_AVAIL    9
#define MPS_VIRT_CIRCUIT_DOWN   10
#define MPS_COMMAND_CANCELLED   11
#define MPS_VIRT_CIRCUIT_ABORT  24
#define MPS_INTERFACE_BUSY      33
#define MPS_TOO_MANY_COMMANDS   34
#define MPS_NO_CMND_TO_CANCEL   36
#define MPS_LSAP_ERROR          66
#define MPS_BAD_PROTOCOL_TYPE   67
#define MPS_ALL_MAC_GRABBED     68
#define MPS_TYPE_FIELD_CLASH    69
#define MPS_NO_CAN_CORRESPOND   70
#define MPS_FACILITY  98        /* Facility code for MPS routines */
#define MPS_SEVERITY   2        /* Severity level = ERROR         */


#define EA_LENGTH	    6
#define EA_OFFSET	    9
#define EA_STRLEN           12


typedef char BOOLEEN;
typedef unsigned char BYTE;
typedef unsigned int WORD;

typedef char m_data[M_LDATA];


/*---------------Structure used for the TCB (see BICC spec)-------------*/

typedef struct {
   BYTE     command;
   BYTE     cid;
   BYTE     vcid;
   WORD     len;
   char far *baddr;
   BOOLEEN  expedited;
   BOOLEEN  cancelable;
   char     laddr[16];
   char     raddr[16];
   int (far *async)();
   char     lnet[4];
   char     rnet[4];
   BYTE     rto;
   BYTE     sto;
   char     res2[8];
   char     cmdext;
   WORD     status;
} TCB;

/*---------------Structure used for the ACB (see BICC spec)-------------*/

typedef struct {
   BYTE     cid;
   BYTE     vcid;
   BYTE     err;
   BYTE     cmd;
   WORD     len;
   char     raddr[16];
   char     rnet[4];
   char     laddr[16];
   char     lnet[4];
   WORD     status;
   BYTE     eventcode;
   char far *baddr;
   BOOLEEN  expedited;
} ACB;


TCB          mytcb;
TCB * ptcb = &mytcb;

rpc_status	eth_status;

courier_word typef_start  = {0x50,0x00};
courier_word typef_end    = {0x51,0xFF};
courier_word typef        = {0x50,0x50};

rpc_ethernet_address my_eth_adr;
rpc_ethernet_address remote_eth_adr;

void rpc_eth_receive_service();

/*************************************************/
/*  Asynchronous Notification Routine - C Part   */
/*         (Internal Procedure)                  */
/*************************************************/

PUBLIC
void anr(ACB * ptacb)

{
	int i, sentbyme;
	char * ptr = ptacb -> baddr;
	
#if ANR_DBUG
	printf("   ANR error %x\n", ptacb -> err);
	printf("   ANR command %x\n", ptacb -> cmd);
	printf("   ANR eventcode %x\n", ptacb -> eventcode);
#endif
	if ((ptacb -> err == 0)
		&& (ptacb -> cmd == CD_M_ACTIVATE_SAP)
		&& (ptacb -> eventcode == ID_M_DATA)) {

		/* Don't catch my OWN messages */

		sentbyme = 1;
		for (i = 0; i < EA_LENGTH; i++)
			if (ptacb -> raddr[i + EA_OFFSET] != my_eth_adr[i])
				sentbyme = 0;
	}

	if (!sentbyme) {
#if ANR_DBUG
		printf("---> %d bytes received\n", ptacb -> len);
		for (i=0; i<ptacb -> len; i++)
			printf("%02x ", (*ptr++)&0xff);
		printf("\n");
#endif
		rpc_eth_receive_service(ptacb);
	}
} 


#if ANR_INLINE
/********************************************************************/
/*       Asynchronous Notification Routine  - Assembler Part        */
/*                     (Internal Procedure)                         */
/********************************************************************/

#define STACK_SIZE 1024
int save[2];
char stack[STACK_SIZE];

PRIVATE
int far asyncf(void)

	{
		/* Restore the data segment value */
	asm        mov     ax, DGROUP
	asm        mov     ds, ax

		/* Save the stack values (SS:SP) */
	asm        mov     [save], sp
	asm        mov     ax, ss
	asm        mov     [save + 2], ax

		/* Give new stack values */
	asm	  cli
	asm       lea     ax, [stack + STACK_SIZE]
	asm	  and	   ax, 0xFFFE
	asm       mov	   sp, ax
	asm        mov     ax, DGROUP
	asm        mov     ss, ax
	asm	  sti
	
		/* Push as parameter the ACB pointer and call ANR routine */
	asm        push    es
	asm        push    bx
	asm        call    _anr

		/* Restore the former stack values */
	asm        mov     sp, [save]
	asm        mov     ax, [save + 2]
	asm        mov     ss, ax
	}

#else
extern int far asyncf();
#endif

/*****************************************************************/ 
/*         Call the Resident Communication System                */ 
/*                 (Internal Function)                           */ 
/*****************************************************************/ 

PRIVATE
BYTE mps_interface(TCB * tcb)

{
   union REGS inreg, outreg;
   struct SREGS segreg;

   tcb -> async = asyncf;

   segread(&segreg);
   segreg.es = segreg.ds;
   inreg.x.bx = (unsigned int) tcb;

   int86x(INT, &inreg, &outreg, &segreg);

   return(outreg.h.al);

}

/**************************************************************/ 
/*                 Fills a TCB with 00x                       */
/*                 (Internal Function)                        */ 
/**************************************************************/ 

PRIVATE
void tcb_init(char * tcb)
{ 
  memset(tcb, 0, TCB_LEN);
} 


/**************************************************************/ 
/*          Get the BICC Ethernet address                     */ 
/*              (Internal Function)                           */ 
/**************************************************************/ 

PUBLIC
BYTE get_local_eaddr(rpc_ethernet_address eaddr)
{ 
   int  i;
   BYTE  retval;
   long addr;
   BYTE user_buffer[80];

   tcb_init((char *)ptcb); 

   ptcb -> command = CD_STATUS;
   ptcb -> len = 16;

   ptcb -> baddr = (char far *) user_buffer;

   retval = mps_interface(ptcb);

   for (i = 0; i < EA_LENGTH; i++)
      eaddr[i] = user_buffer[i + EA_OFFSET];

   return retval;
}

/**************************************************************/ 
/* Reserves a typefield interval to listen to                 */ 
/*                 (External Function)                        */ 
/**************************************************************/ 

PRIVATE
BYTE bic_m_activate_sap(courier_word typefield_low,
			courier_word typefield_high,
			BYTE tcb_id)

{ 
     unsigned long tpf, tpf1, tpf2;

     tcb_init((char *)ptcb);
   
     ptcb -> command = CD_M_ACTIVATE_SAP;
     ptcb -> cid = tcb_id;
     ptcb -> len = 0;

     tpf1 = (typefield_low.h  << 8) + typefield_low.l;
     tpf2 = (typefield_high.h  << 8) + typefield_high.l;
     tpf = tpf1 + (tpf2 << 16);

     ptcb -> baddr = (char far *) tpf;

     return mps_interface(ptcb);
  
} 

/**************************************************************/ 
/* Reserves a typefield interval to listen to                 */ 
/*                 (External Function)                        */ 
/**************************************************************/ 

PRIVATE
BYTE bic_m_deactivate_sap(courier_word typefield_low,
			  courier_word typefield_high,
			  BYTE tcb_id)

{ 
     BYTE stat;
     unsigned long tpf, tpf1, tpf2;

     tcb_init((char *)ptcb);
   
     ptcb -> command = CD_M_DEACTIVATE_SAP;
     ptcb -> cid = tcb_id;
     ptcb -> len = 0;

     tpf1 = (typefield_low.h  << 8) + typefield_low.l;
     tpf2 = (typefield_high.h  << 8) + typefield_high.l;
     tpf = tpf1 + (tpf2 << 16);

     ptcb -> baddr = (char far *) tpf;

     stat = mps_interface(ptcb);

  	CTRACE(tfp, "DEACTIVATE returned %x \n", (int)stat);

    return stat;
} 


PRIVATE
BYTE bic_m_data_send(rpc_ethernet_address dest,
		     rpc_ethernet_address source,
		     unsigned short       typefield,
		     char		  *data_ptr,
		     int		  data_len)

{ 
   int i;

     tcb_init((char *)ptcb);
   
     ptcb -> command = CD_L_DATA_SEND;
     ptcb -> cid = 33;
     ptcb -> len = data_len;

     ptcb -> baddr = (char far *) data_ptr;
    
     for (i = 0; i < EA_LENGTH; i++) {
	ptcb -> laddr[i + EA_OFFSET] = source[i];
	ptcb -> raddr[i + EA_OFFSET] = dest[i];
        } 

     ptcb -> status = typefield;

     return mps_interface(ptcb);
} 

#if ETH_DBUG
/* ================== RPC_TS_DEBUGGING_UTILITIES ========================== */

/*
**
**	Print out socket descriptor
**	---------------------------
**
**
*/
PRIVATE dump_socket(socket_type soc)
{
  if (rpc_trace) {

	if (soc == NIL_SOCKET_PTR) {
		UTRACE(tfp,"Dumping NIL SOCKET !!!\n");
		return;
	}

    UTRACE(tfp,"    Socket at %lx:\n", (long int) soc);
    UTRACE(tfp,"        next @            %lx\n",   (long int)soc->soc_next);
    UTRACE(tfp,"        astadr =          %lx\n",   (long int)soc->soc_astadr);
    UTRACE(tfp,"        astprm =          %lx\n",   (long int)soc->soc_astprm);
    UTRACE(tfp,"        last call sent @  %lx\n",   (long int)soc->soc_last_call_sent);
    UTRACE(tfp,"        last reply sent @ %lx\n",   (long int)soc->soc_last_reply_sent);

    UTRACE(tfp,"        medium =          ");
    switch(soc->soc_medium) {

    case invalid_medium:
	UTRACE(tfp, "INVALID (ZERO)\n");
	break;
    case v24:
	UTRACE(tfp, "V24\n");
	break;
    case ether:
	UTRACE(tfp, "ETHERNET\n");

	UTRACE(tfp, "        Address filter: %2x_%2x_%2x_%2x_%2x_%2x %2x_%2x\n",
		    soc->mdp.soc_ether.soc_addr_filter[0],
		    soc->mdp.soc_ether.soc_addr_filter[1],
		    soc->mdp.soc_ether.soc_addr_filter[2],
		    soc->mdp.soc_ether.soc_addr_filter[3],
		    soc->mdp.soc_ether.soc_addr_filter[4],
		    soc->mdp.soc_ether.soc_addr_filter[5],
		    soc->mdp.soc_ether.soc_type_filter.h,
		    soc->mdp.soc_ether.soc_type_filter.l);
	UTRACE(tfp, "        Address mask:   %2x_%2x_%2x_%2x_%2x_%2x %4x\n",
		    soc->mdp.soc_ether.soc_addr_mask[0],
		    soc->mdp.soc_ether.soc_addr_mask[1],
		    soc->mdp.soc_ether.soc_addr_mask[2],
		    soc->mdp.soc_ether.soc_addr_mask[3],
		    soc->mdp.soc_ether.soc_addr_mask[4],
		    soc->mdp.soc_ether.soc_addr_mask[5],
		    soc->mdp.soc_ether.soc_type_mask);

	UTRACE(tfp, "        Header of last packet received on this socket:\n");
	UTRACE(tfp, "           To:     %2x_%2x_%2x_%2x_%2x_%2x\n",
		    soc->mdp.soc_ether.soc_rx_dest_addr[0],
		    soc->mdp.soc_ether.soc_rx_dest_addr[1],
		    soc->mdp.soc_ether.soc_rx_dest_addr[2],
		    soc->mdp.soc_ether.soc_rx_dest_addr[3],
		    soc->mdp.soc_ether.soc_rx_dest_addr[4],
		    soc->mdp.soc_ether.soc_rx_dest_addr[5]);
	UTRACE(tfp, "           From:   %2x_%2x_%2x_%2x_%2x_%2x\n",
		    soc->mdp.soc_ether.soc_rx_source_addr[0],
		    soc->mdp.soc_ether.soc_rx_source_addr[1],
		    soc->mdp.soc_ether.soc_rx_source_addr[2],
		    soc->mdp.soc_ether.soc_rx_source_addr[3],
		    soc->mdp.soc_ether.soc_rx_source_addr[4],
		    soc->mdp.soc_ether.soc_rx_source_addr[5]);
	UTRACE(tfp, "           Ptype:  %2x_%2x\n",
		    soc->mdp.soc_ether.soc_rx_protocol_type.h,
		    soc->mdp.soc_ether.soc_rx_protocol_type.l);
	break;
    decnet:
	UTRACE(tfp, "DECNET\n");
	break;
    ieee:
	UTRACE(tfp, "IEEE\n");
	break;
    x25:
	UTRACE(tfp, "X25\n");
	break;
    drb:
	UTRACE(tfp, "DRB/CHI/HVIOR link\n");
	break;
    udp:
	UTRACE(tfp, "UDP/IP\n");
	break;
    tcp:
	UTRACE(tfp, "TCP/IP\n");
	break;
    default:
	UTRACE(tfp,"ILLEGAL: hex %x\n", (int)soc->soc_medium);
    } /*switch */

    UTRACE(tfp,"\n");
  } /* end if trace */
} /* dump socket */


/*	Dump RPC Message Internal bits			    dump_message()
**	------------------------------
**
*/
void dump_message(rpc_message_pointer mes)
{
  int	i;
  if (rpc_trace) {
    UTRACE(tfp, "    Message at %lx:\n", (long int)mes);
    UTRACE(tfp, "        Next at     %lx",	(long int)mes->m_next);
    UTRACE(tfp, "        Status is   %lx\n",	(long int)mes->m_status);
    UTRACE(tfp, "        Socket at   %lx",	(long int)mes->m_socket);
    UTRACE(tfp, "        Index is    %lx\n",	(long int)mes->m_index);
    UTRACE(tfp, "        Protocol header:\n");
    UTRACE(tfp, "            ");
    for(i=0; i<16; i++)
	UTRACE(tfp, "%2x ", (int)(((unsigned char*)(&mes->header))[i]));
    UTRACE(tfp, "\n");
    UTRACE(tfp, "\n");
  }
}

/* ================ RPC_TS_RAW_ETHERNET_UTILITIES ========================== */
#endif


PUBLIC
void sys_setast(int flag)
{
	if (flag == 0)
		PROTECT;
	else
		UNPROTECT;
}

extern long BiosTicks();

PRIVATE
void rpc_wait(rpc_pointer * event, int timeout)
{
	long int	time_on_entry;
	int             current_time;

	ETH_TRACE("RPC_WAIT called with %x\n", timeout);

	if (timeout != 0)
		{
		if (timeout > 0)
			{
			/* Get current time in ticks from BIOS */
			time_on_entry = BiosTicks();
			do
				{
				/* Wait for a maximum of 'timeout' 10th of ms */
				/* There are 18.2 ticks per second */
				current_time = (int)(((BiosTicks() -
					time_on_entry) * 100L) / 18L);
			}
			while ((*event == NO_AST_FLAG)
			   && (current_time < timeout));    /* Time out! */
		}
		else    /* if timeout < 0 wait for EVER */
			{
			do { } while (*event == NO_AST_FLAG);
			}
	}
}


PRIVATE
void queue_add_head(volatile socket_type * queue, socket_type item)
{ 
    PROTECT;   /*  Critical code start  */

    item->soc_next = *queue; 
    *queue = item; 

    UNPROTECT;   /*  Critical code end  */
} 


PRIVATE
void queue_add_tail(volatile socket_type * queue, socket_type item)
{ 
    socket_type  scan; 

    PROTECT;   /*  Critical code start  */

    item->soc_next = NIL_SOCKET_PTR;
    if (*queue == NIL_SOCKET_PTR) *queue = item;
    else { 
	 for (scan = *queue; scan->soc_next; scan = scan->soc_next) ;
	 scan->soc_next = item;
    }
    UNPROTECT;   /*  Critical code end  */
} 


PRIVATE
void queue_remove(volatile socket_type * queue, socket_type item)
{
    socket_type  scan; 

    PROTECT;   /*  Critical code start  */

    if (*queue == item) *queue = item->soc_next;
    else { 
	 scan = *queue;
	 while (scan && (scan->soc_next != item))
		 scan = scan->soc_next;
	 if (scan)
		 scan->soc_next = item->soc_next;
    }

    UNPROTECT;   /*  Critical code end  */
} 


/*        Convert BICC Status to 32-bit RPC Status
	  ========================================
*/

PRIVATE
rpc_status bicc_stat(BYTE bicc_completion)
{ 
    rpc_status retval;
    
    if (bicc_completion == MPS_COMMAND_SUCCESS)
       retval = RPC_S_NORMAL;
    else 
       retval = 
       (MPS_SEVERITY + (8 * bicc_completion) + 65536 * (2048 + MPS_FACILITY));
    return (retval);
} 

/* ================ RPC_TS_RAW_ETHERNET_INTERFACE ========================= */


/*      Procedure:      Initialise the ETHERNET I/O
**	---------	---------------------------
**
**  This routine is called once only for this process, to initialise
**  any process-wide things.
**
** On exit,
**	return	    gives the return status
*/   

PUBLIC
rpc_status rpc_eth_init(void)
{
#if APELLEgWM
	/* Open ETHERNET debug window */
	if ((db_eth = WCreateWindow("ETH debug", W_TEXT, 15, 60, BLACKonIWHITE)) < 0)
		CltFatal("Error opening ETH Debug Window %d ",db_eth);
	if (eth_trace)
		WMapRaised(db_eth);
#endif
	eth_usage = 0;
	return RPC_S_NORMAL;
}

/*      Procedure:      Initialise the ETHERNET I/O for one channel
**	---------	-------------------------------------------
**
** On entry,
**	psocket	    points to a socket descriptor which has been set up
**		    with the correct ethernet addreess, type fields and masks.
** On exit,
**	return	    gives the return status
*/   

PUBLIC
rpc_status rpc_eth_open(socket_type psocket)
{
	rpc_status eth_status;
	int i;

	ETH_TRACE("RPC_ETH_OPEN called\n");

	eth_status = RPC_S_NORMAL;
        eth_usage++;
	if (eth_usage == 1) {
		eth_rxq = NIL_SOCKET_PTR;         /* Clear all pending ASTs */

#if ZAPPED
		eth_status = (rpc_status)
		   get_local_eaddr(my_eth_adr);
#endif

		eth_status = (rpc_status)
		   bic_m_activate_sap(typef_start, typef_end, 1);

		if (eth_status == 0) eth_status = RPC_S_NORMAL;
	}

/*	In order not to miss any packets, we immediately allocate a
**	receive buffer.  This may be filled at any time by the ANR.
*/
	rpc_new(&psocket->soc_astprm, RPC_BUFFER_SIZE);	/* Get a rx buffer */
	psocket->soc_astprm->m_socket = psocket;	/* Link msg back to socket */
	psocket->soc_astadr = NO_AST_FLAG;		/* mark buffer as empty */
	queue_add_tail(&eth_rxq, psocket);	/* Make receive active */

	return eth_status;
}


/*
**      Procedure:      DeInitialise the ethernet i/o for one channel
**	---------	---------------------------------------------
**
** This procedure in fact checks whether the very last open is being done,
** in which case it performs any global deinitialisation which may be required.
**
** On entry,
**	psocket	    points to a socket descriptor.
** On exit,
**	return	    gives the return status
*/   

/*

NOTE:   If an asynchronous receive is in progress, then the buffer passed
        to the RPC system will be disposed of.  This is because the
        buffer may have been swapped many times with the one the user
	originally provided, so he has no way of knowing which one to dispose of

	This may not be safe if a call arrives just as the cancellation
	occurs !!!
 */ 

PUBLIC
rpc_eth_close(socket_type psocket)
{
	rpc_status eth_status;
        socket_type  scan; 
	BYTE active;

	ETH_TRACE( "RPC_ETH_CLOSE called\n");

/*	Deactivate reception of packets
*/
	queue_remove(&eth_rxq, psocket);

/*	Dispose of any buffer waiting for an async mess
*/
	if (psocket->soc_astprm != NIL_MESSAGE_PTR)
	    	rpc_dispose(psocket->soc_astprm);

/*	Dispose of any record of last call made if any
*/
	if (psocket->soc_last_call_sent != NIL_MESSAGE_PTR) {
		rpc_dispose(psocket->soc_last_call_sent);
		psocket->soc_last_call_sent = NIL_MESSAGE_PTR;
	}

/*	Dispose of last reply to EACH address
*/
	while (psocket->soc_last_reply_sent != NIL_MESSAGE_PTR) {
		register rpc_message_pointer temp;
		PROTECT;
		temp = psocket->soc_last_reply_sent;
		psocket->soc_last_reply_sent =
			psocket->soc_last_reply_sent->m_next;
		rpc_dispose(temp);
		UNPROTECT;
	}

	eth_status = RPC_S_NORMAL;
	eth_usage--;
	if (eth_usage == 0) {

		eth_status = bicc_stat(
		   bic_m_deactivate_sap(typef_start, typef_end, 1)
		   );
	}
	return eth_status;
}


/*      Procedure:      Send ethernet packet
**	---------	--------------------	
**
**  On entry,
**	socket	    points to an initialised socket.
**
**      buffer	    points to a packet. The destination field and protocol type
**		    in the packet header are correct.
**	    m_socket	is valid
**	    m_index	is the number of bytes (excluding the header)
**		    The source address is not yet filled in.
**	bufsize     gives the number of bytes to be transmitted, EXCLUDING the
**		    ethernet header.
**  On exit,
**	return      Gives the status: if good, the packet has been sent.
**
*/

PUBLIC
rpc_status rpc_eth_send(rpc_message_pointer pmessage)
{
	int bufsize = (int)pmessage->m_index;
	rpc_status eth_status;
	unsigned short typefield;
	int i;

	typefield =        (unsigned short)
			   ((pmessage->header.ether.ptype.h << 8) +
			    pmessage->header.ether.ptype.l);

	ETH_TRACE( "RPC_ETH_SEND called\n");

	eth_status = bicc_stat(
	   bic_m_data_send( pmessage->header.ether.dest,
			   my_eth_adr,
			   typefield,
			   pmessage -> body.rpc_b,
			   bufsize) );

	return eth_status;

}
 

/*
**      Procedure:      Wait for ethernet message
**
** This procedure receives one ethernet message on the given socket.
** The packets are filtered by source address and protocol type according
** to the way the socket has been set up (wildcard or not).
**
** On entry,
**
**	ppmessage   points to a pointer to a suitable message buffer with
**		    the following fields set up:
**
**      m_socket    Pointer to socket descriptor
**
**      timeout	    may be -1 for infinite timeout, or 0 for poll.
**		    else units of microseconds.
**
** On return,
**
**      return	    returns error code or normal or timeout.
**      *pmessage   may have been changed, ie the message swapped for another.
**      **pmessage  has the header and data fields both filled in.
**      m_index     gives the number of data bytes in the message.
**      m_socket    pointer to socket descriptor
*/

PUBLIC
rpc_status rpc_eth_receive(rpc_message_pointer * ppmessage, int timeout)
{
	register rpc_message *mes = *ppmessage;
	register socket_type soc =  mes->m_socket;
	rpc_status               eth_status;

	ETH_TRACE("RPC_RECEIVE called with %x\n", timeout);

/* 	Wait for Soc_Astadr <> 0 or timeout
*/
	rpc_wait(&soc->soc_astadr, timeout);

/*      Now we have to be careful that the timeout might occur at the same time
**	as the message arrives.
*/
	if (soc->soc_astadr) {
		rpc_message_pointer temp;

		PROTECT;
		temp = soc->soc_astprm;			/* Swap buffers */
		soc->soc_astprm = *ppmessage; 		/*   to get data */
		soc->soc_astadr = NO_AST_FLAG;		/* Mark as empty */
		UNPROTECT;
		*ppmessage = temp;
		return RPC_S_NORMAL;
	} else {
		return RPC_S_TIMEOUT;
	}
	/*NOTREACHED*/
}


/***************************************************************************
**
**      Procedure:      Set up handler for next message to arrive
**                      (Only if ASTs supported)
**
** On entry,
**	pmessage    points to an available message buffer, fields as follows:
**	    m_index	maximum length to be read
**	    m_status	(undefined)
**	    m_next	(undefined)
**	    m_socket	points to valid socket descriptor, includes fields
**		soc_user_ast	address of AST completion routine
*/

PUBLIC
rpc_status rpc_eth_when_receive(rpc_message_pointer pmessage)
{
 
	ETH_TRACE("ETH_WHEN_RECEIVE called\n");

	return RPC_S_NOT_IMPLEMENTED;		/* yet */
}



/*        Incoming Packet Handler
	  =======================
	  
This routine is called from the asynchronous notification routine (ANR)
*/

PRIVATE
void rpc_eth_receive_service(ACB * ptacb)
{ 
    socket_type scan;
    int          i;

    ethspy((80*23 + 70)*2, 0x6901);

    for (scan = eth_rxq; scan != NIL_SOCKET_PTR; scan = scan->soc_next) {
    	
    	BOOLEAN match = TRUE;
    	int i;
	for (i = 0; i < EA_LENGTH; i++) {
		if ((scan->mdp.soc_ether.soc_addr_mask[i] != 0)
		&& (scan->mdp.soc_ether.soc_addr_filter[i]
				      != ptacb->raddr[i + EA_OFFSET]))
		    match = FALSE; /* Wrong source address */
	}
    	if (!match) break; /* wrong source address */
    	
/*  Protocol types match ?
*/

	if(
	       (scan->mdp.soc_ether.soc_type_mask == 0)	/* Wildcard */
	    || ((scan->mdp.soc_ether.soc_type_filter.l
	 	    == ((char)(ptacb->status & 0xff)))
	         && (scan->mdp.soc_ether.soc_type_filter.h
	     	    == ((char)(ptacb->status >> 8))))
	   ) /* if */ {

/*  Copy packet data (a pity we have to)
*/

#if ANR_DBUG
		printf("Copying Data ... AST\n");
	 	{
			char * ptr = ptacb -> baddr;
			int i;
			printf("-----> %d bytes received\n", ptacb -> len);
			for (i=0; i<ptacb -> len; i++)
				printf("%02x ", (*ptr++)&0xff);
			printf("\n");
		}
#endif
		memcpy(scan->soc_astprm->body.rpc_b, ptacb->baddr, ptacb->len);

		for (i = 0; i < EA_LENGTH; i++) {

			/*  Copy packet header  */

			scan->soc_astprm->header.ether.source[i] =
						 ptacb->raddr[i + EA_OFFSET];
			scan->soc_astprm->header.ether.dest[i] =
						 ptacb->laddr[i + EA_OFFSET];
		}

		scan->soc_astprm->header.ether.ptype.l =
						 ptacb->status & 0xff;
		scan->soc_astprm->header.ether.ptype.h =
						 ptacb->status << 8;
		scan->soc_astprm->m_index = (rpc_long)ptacb->len;


/*  Inform the waiting task that the buffer is valid
*/

		if (scan->soc_astadr == NO_AST_FLAG) {
			scan->soc_astadr = SET_AST_FLAG;
			ethspy((80*23 + 73)*2, 0x6943);
		}
		else
		if (scan->soc_astadr == SET_AST_FLAG)
			ethspy((80*23 + 74)*2, 0x694C);	
		else {
			ethspy((80*23 + 75)*2, 0x6941);
			(*(scan->soc_astadr))(scan->soc_astprm); /* call AST */
		}
	} /* if */
    } /* for */

    ethspy((80*23 + 79)*2, 0x6952);
} /* rpc_eth_receive_service */

