[comp.os.minix] Reposting of #39

ast@cs.vu.nl (Andy Tanenbaum) (07/23/88)

: This is a shar archive.  Extract with sh, not csh.
: This archive ends with exit, so do not worry about trailing junk.
: --------------------------- cut here --------------------------
PATH=/bin:/usr/bin:/usr/ucb
echo Extracting 'amoeba.c'
sed 's/^X//' > 'amoeba.c' << '+ END-OF-FILE ''amoeba.c'
X/*
X**	Minix /dev/amoeba driver
X**
X**	This file contains two kernel servers: amint_task and amoeba_task.
X**	amoeba_task implements transactions for user tasks and amint_task
X**	handles asynchronous events such as timeouts, incoming ethernet
X**	packets and interrupts.
X**
X**	An amoeba_task is permanently assigned to a process until a transaction
X**	is complete.  If you do a getreq then the kernel task remains
X**	allocated until after the putrep or the server dies.
X**	There is a limit of one operation at a time except in the case that a
X**	getreq is followed by a trans.
X**
X**	The value of curtask is only correct if there is non-preemptive
X**	scheduling of kernel tasks.  It is reset by am_sleep when it returns
X**	which keeps it pointing to the correct place.
X**
X**	Lines marked HACK are of doubtful portability but produce efficient
X**	code.
X*/
X
X#define NDEBUG
X
X#include "../h/const.h"
X#include "../h/type.h"
X#include "../h/com.h"
X#include "../h/error.h"
X#include "../h/callnr.h"
X#include "../h/signal.h"
X#include "const.h"
X#include "type.h"
X#include "proc.h"
X#include "glo.h"	/* need definition of cur_proc */
X
X#include "../h/amoeba.h"
X#undef umap
X#include "../h/amparam.h"
X#include "global.h"
X#define	MPX
X#define	TRANS
X#include "task.h"
X#include "assert.h"
X#include "internet.h"
X#include "etherformat.h"
X#include "byteorder.h"
X
X/* amoeba task table - can't alloc memory in minix kernel */
XPUBLIC struct task	am_task[AM_NTASKS];
X
X#ifndef NONET
X
X#define	ETH_HDRS	(sizeof (Framehdr))
X#define	HSZ		(ETH_HDRS + HEADERSIZE) /* watchout for alignment */
X#define	FAKESITENO	0xff			/* to bluff trans.c */
X#define	MAPENTRIES	127
X/* two hacks for speed */
X#define	EANULL(a)	NullPort((port *) (a))			/* HACK! */
X#define	EACMP(a, b)	PortCmp((port *) (a), (port *) (b))	/* HACK! */
X
XPRIVATE Etherpacket	Packet;	/* the latest arrived amoeba ethernet packet */
XPRIVATE phys_bytes	Bufaddr;/*physical address of Packet */
X
XPRIVATE phys_bytes	Inptr;	/* used by pickoff() & getall() to copy data */
XPRIVATE	unsigned	Insiz;	/* total size of received packet */
XPRIVATE phys_bytes	Outptr;	/* pointer to pos currently building packet */
XPRIVATE	unsigned	Outsiz;	/* size of currently building packet */
XPRIVATE phys_bytes	Xmtbuf;	/* Pointer to current ethernet write buffer */
XPRIVATE	Eth_addr	Myaddr;	/* ether address of this host */
XPRIVATE Eth_addr	Gwaddr;	/* ether address of pronet gateway */
X/* broadcast address for ethernet. see next comment. */
XPRIVATE	Eth_addr	Broadcastaddr = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
X/* the 128th map entry is to hold the broadcast address.  can you say HACK? */
XPRIVATE Eth_addr	Eamap[MAPENTRIES+1];
X
X#endif
X
X
X/*===========================================================================*
X *                            amoeba_task                                    *
X *===========================================================================*/
XPUBLIC
Xamoeba_task()
X{
X    void	am_reply();
X    message	mess;
X    int		mytask;
X
X    mytask = AMOEBA_CLASS - cur_proc;
X    curtask = &am_task[mytask];  /* make me the current amoeba task */
X    am_init();
X    while (TRUE)
X    {
X	receive(ANY, &mess);
X	curtask = &am_task[mytask];  /* make me the current amoeba task */
X	switch (mess.AM_OP)
X	{
X	case AM_TRANS:
X	    do_trans(&mess);
X	    break;
X	case AM_GETREQ:
X	    do_getreq(&mess);
X	    break;
X	case AM_PUTREP:
X	    do_putrep(&mess);
X	    break;
X	default:
X	    if (mess.m_source >= 0)
X		am_reply(TASK_REPLY, mess.m_source, mess.AM_PROC_NR, 1, EINVAL);
X	    break;
X	} /* end switch */
X    } /* end while */
X}
X
X
X/*===========================================================================*
X *                                am_init                                    *
X *===========================================================================*/
XPRIVATE int
Xam_init()
X{
X/* non-pre-emptive scheduling is assumed here for initialisation! */
X    static int Initialised;
X
X    if (Initialised == 0)
X    {		    /* set up the ethernet driver and init the tables */
X	Initialised++;
X	uppertask = &am_task[AM_NTASKS];
X	ntask = AM_NTASKS;
X#ifndef NONET
X	net_init();
X#endif
X	transinit();
X	portinit();
X    }
X    trinit();
X    curtask->mx_flags = RUNNABLE;
X}
X
X
X/*===========================================================================*
X *                             amint_task                                    *
X *===========================================================================*/
XPUBLIC
Xamint_task()
X{
X    phys_bytes	umap();
X    message	mess;
X    struct task * t;
X
X#ifndef NONET
X    Bufaddr = umap(proc_addr(AMINT_CLASS), D, (vir_bytes)&Packet, (vir_bytes)HSZ);
X#endif
X    set_timer();	/* start the netsweep timer */
X    while (TRUE)
X    {
X	receive(ANY, &mess);
X	switch (mess.AM_OP)
X	{
X	case ETHER_ARRIV:	/* an ethernet packet arrived */
X	    do_arrive(&mess);
X	    break;
X	case AM_TIMEOUT:	/* run transaction sweepers every 0.1 secs */
X	    netsweep();
X	    portsweep();
X	    set_timer();	/* reset the timeout */
X	    break;
X	case AM_PUTSIG:		/* user typed a del or a quit or a kill */
X	    sendsig(&am_task[mess.AM_COUNT], 1);
X	    break;
X	case AM_TASK_DIED:	/* a user task died while doing an operation */
X	    t = &am_task[mess.AM_COUNT];
X	    if (t->mx_active)	/* if transaction record is still valid */
X	    {
X		destroy(t);	/* then destroy it */
X		t->mx_proc_nr = 0;
X		t->mx_active = 0;
X		t->mx_flags = 0;
X	    }
X	    break;
X	default:
X	    break;
X	}
X    }
X}
X
X
X/*===========================================================================*
X *                             set_timer                                     *
X *===========================================================================*/
XPRIVATE
Xset_timer()
X{
X    message	mess;
X    int		am_runsweep();
X
X    mess.m_type = SET_ALARM;
X    mess.CLOCK_PROC_NR = AMINT_CLASS;
X    mess.DELTA_TICKS = HZ/10;		/* every 0.1 seconds ! */
X    mess.FUNC_TO_CALL = am_runsweep;
X    sendrec(CLOCK, &mess);
X}
X
X
X/*===========================================================================*
X *                            am_runsweep                                    *
X *===========================================================================*/
XPRIVATE
Xam_runsweep()
X{
X    message	mess;
X
X    mess.AM_OP = AM_TIMEOUT;
X    send(AMINT_CLASS, &mess);
X}
X
X
X/*===========================================================================*
X *                               do_trans                                    *
X *===========================================================================*/
XPRIVATE
Xdo_trans(m_ptr)
Xmessage *	m_ptr;
X{
X    header *	am_starttask();
X    void	am_endtask();
X    unshort	trans();
X    void	am_reply();
X
X    unshort	ret;
X    header *	hdr;
X    Trpar	param;	/* parameter block for transaction */
X
X#define	req	param.tp_par[0]
X#define	rep	param.tp_par[1]
X
X/* copy in parameter block */
X    if (get_param(m_ptr, &param) == 0)
X    {
X	am_reply(TASK_REPLY, m_ptr->m_source, m_ptr->AM_PROC_NR, 1, EFAULT);
X	return;
X    }
X/* copy header in */
X    hdr = am_starttask(m_ptr);
X    if (get_header(m_ptr, req.p_hdr, hdr) == 0)
X    {
X	am_endtask();
X	am_reply(TASK_REPLY, m_ptr->m_source, m_ptr->AM_PROC_NR, 1, EFAULT);
X	return;
X    }
X/* reply to FS to suspend luser task */
X    am_reply(TASK_REPLY, m_ptr->m_source, m_ptr->AM_PROC_NR, 0, SUSPEND);
X/* start locate timer */
X    (void)am_timeout(param.tp_maxloc);
X/* call trans */
X    ret = trans(hdr, req.p_buf, req.p_cnt, hdr, rep.p_buf, rep.p_cnt);
X/* copy header to luser task (trans already copied the data) */
X    if ((short)ret >= 0 && put_header(m_ptr->AM_PROC_NR, hdr, rep.p_hdr) == 0)
X    {
X	am_endtask();
X	am_reply(AM_REVIVE, m_ptr->m_source, m_ptr->AM_PROC_NR, 1, EFAULT);
X	return;
X    }
X    am_endtask();
X/* revive luser task */
X    am_reply(AM_REVIVE, m_ptr->m_source, m_ptr->AM_PROC_NR, 1, (int)ret);
X}
X
X
X/*===========================================================================*
X *                              do_getreq                                    *
X *===========================================================================*/
XPRIVATE
Xdo_getreq(m_ptr)
Xmessage *	m_ptr;
X{
X    unshort	getreq();
X    header *	am_starttask();
X    void	am_endtask();
X    void	am_reply();
X
X    Trpar	param;
X    unshort	ret;
X    header *	hdr;
X    int		free;
X
X/* copy parameter block for getreq */
X    if (get_param(m_ptr, &param) == 0)
X    {
X	am_reply(TASK_REPLY, m_ptr->m_source, m_ptr->AM_PROC_NR, 1, EFAULT);
X	return;
X    }
X/* copy header in */
X    hdr = am_starttask(m_ptr);
X    if (get_header(m_ptr, param.tp_par[0].p_hdr, hdr) == 0)
X    {
X	am_endtask();
X	am_reply(TASK_REPLY, m_ptr->m_source, m_ptr->AM_PROC_NR, 1, EFAULT);
X	return;
X    }
X/* reply to FS to suspend luser task */
X    am_reply(TASK_REPLY, m_ptr->m_source, m_ptr->AM_PROC_NR, 0, SUSPEND);
X/* call getreq */
X    ret = getreq(hdr, param.tp_par[0].p_buf, param.tp_par[0].p_cnt);
X    free = 0;
X    if ((short)ret < 0)	/* getreq failed */
X    {
X	free = 1;
X	am_endtask();
X    }
X    else    /* copy header to luser task (getreq already copied the data) */
X	if (put_header(m_ptr->AM_PROC_NR, hdr, param.tp_par[0].p_hdr) == 0)
X	{
X	    free = 1;
X	    ret = EFAULT;
X	    am_endtask();
X	}
X/* restart luser task but don't free the kernel task! */
X    am_reply(AM_REVIVE, m_ptr->m_source, m_ptr->AM_PROC_NR, free, (int)ret);
X}
X
X
X/*===========================================================================*
X *                              do_putrep                                    *
X *===========================================================================*/
XPRIVATE
Xdo_putrep(m_ptr)
Xmessage *	m_ptr;
X{
X    unshort	putrep();
X    void	am_reply();
X
X    Trpar	param;
X    header *	hdr;
X    unshort	ret;
X
X/* copy in parameter block */
X    if (get_param(m_ptr, &param) == 0)
X    {
X	am_reply(TASK_REPLY, m_ptr->m_source, m_ptr->AM_PROC_NR, 1, EFAULT);
X	return;
X    }
X/* make sure that there was a getrequest */
X    if (!curtask->mx_active || curtask->mx_proc_nr != m_ptr->AM_PROC_NR)
X    {
X	am_reply(TASK_REPLY, m_ptr->m_source, m_ptr->AM_PROC_NR, 1, (int)FAIL);
X	return;
X    }
X/* copy in header */
X    hdr = &curtask->mx_hdr;
X    if (get_header(m_ptr, param.tp_par[0].p_hdr, hdr) == 0)
X    {
X	am_endtask();
X	am_reply(TASK_REPLY, m_ptr->m_source, m_ptr->AM_PROC_NR, 1, EFAULT);
X	return;
X    }
X/* tell FS to suspend luser task */
X    am_reply(TASK_REPLY, m_ptr->m_source, m_ptr->AM_PROC_NR, 0, SUSPEND);
X/* send reply */
X    ret = putrep(hdr, param.tp_par[0].p_buf, param.tp_par[0].p_cnt);
X    am_endtask();
X/* restart luser task */
X    am_reply(AM_REVIVE, m_ptr->m_source, m_ptr->AM_PROC_NR, 1, (int)ret);
X}
X
X
X/*===========================================================================*
X *                              do_arrive                                    *
X *===========================================================================*/
XPRIVATE
Xdo_arrive(m_ptr)
Xmessage *	m_ptr;
X{
X#ifndef NONET
X/*
X**	we already know it is an amoeba packet, otherwise we wouldn't get here.
X**	copy in the ethernet header, the internet header and the amoeba header
X**	and then call packet handle.  getall and pickoff do the rest!
X*/
X    phys_copy(m_ptr->AM_PADDR, Bufaddr, (long)HSZ);
X
X/* fix amoeba size field - NB. the following is a macro */
X    dec_s_le(&Packet.ep_fr.f_ah.ph_size);
X/* if packethandle succeeds it calls netenable itself! */
X    if (check(&Packet))	/* then give it to the transaction layer */
X    {
X	Inptr = m_ptr->AM_PADDR + HSZ;	/* pointer to the data after headers */
X	Insiz = Packet.ep_fr.f_ah.ph_size;
X	Packet.ep_fr.f_ah.ph_size -= ETH_HDRS;
X	Packet.ep_fr.f_ah.ph_dstnode = FAKESITENO;
X	if (!pkthandle(&Packet.ep_fr.f_ah, Packet.ep_data))
X	    am_netenable();
X    }
X    else
X	am_netenable();
X#endif
X}
X
X
X/*===========================================================================*
X *                               am_reply                                    *
X *===========================================================================*/
XPRIVATE void
Xam_reply(code, replyee, proc_nr, free_it, status)
Xint	code;			/* TASK_REPLY or revive */
Xint	replyee;		/* destination address for the reply */
Xint	proc_nr;		/* to whom the reply should go */
Xint	free_it;		/* if a getreq, then don't free this task */
Xint	status;			/* reply code */
X{
X/* send reply to process doing a trans, getreq or putrep or anything else */
X    message	a_mess;
X
X    a_mess.m_type = AM_SYSCALL;
X    a_mess.AM_OP = code;
X    a_mess.AM_FREE_IT = (long)free_it;
X    a_mess.AM_PROC_NR = proc_nr;
X    a_mess.AM_STATUS = status;
X    send(replyee, &a_mess);
X}
X
X
X/*===========================================================================*
X *                           am_starttask                                    *
X *===========================================================================*/
XPRIVATE header *
Xam_starttask(m_ptr)
Xmessage *	m_ptr;
X{
X/* check to see if already doing a transaction for this user */
X    if (curtask->mx_active && curtask->mx_proc_nr == m_ptr->AM_PROC_NR)
X    {
X	assert(curtask->mx_flags & RUNNABLE);
X	assert(!(curtask->mx_flags & NESTED));
X	curtask->mx_flags |= NESTED;
X    }
X    else
X    {
X	curtask->mx_proc_nr = m_ptr->AM_PROC_NR;
X	curtask->mx_active = 1;
X	curtask->mx_flags = RUNNABLE;
X    }
X    return &curtask->mx_hdr;
X}
X
X
X/*===========================================================================*
X *                             am_endtask                                    *
X *===========================================================================*/
XPRIVATE void
Xam_endtask()
X{
X    if (curtask->mx_flags & NESTED)
X	curtask->mx_flags &= ~NESTED;
X    else
X    {
X	am_cleanup();
X	curtask->mx_flags = 0;
X	curtask->mx_active = 0;
X    }
X}
X
X
X/*===========================================================================*
X *                             get_param                                     *
X *===========================================================================*/
XPRIVATE
Xget_param(m_ptr, param)
Xmessage *	m_ptr;
XTrpar *		param;
X{
X    phys_bytes	umap();
X    phys_bytes	src;	/* physical address of parameter block */
X    phys_bytes  dst;	/* kernel buffer */
X
X/* copy parameter block for trans */
X    if (m_ptr->AM_COUNT != (int)sizeof (Trpar) ||
X	(src = umap(proc_addr(m_ptr->AM_PROC_NR), D,
X		(vir_bytes)m_ptr->AM_ADDRESS, (vir_bytes)sizeof (Trpar))) == 0)
X	return 0;
X    dst = umap(proc_addr(cur_proc), D, (vir_bytes)param,
X					(vir_bytes)sizeof (Trpar));
X    phys_copy(src, dst, (long)sizeof (Trpar));
X    return 1;
X}
X
X
X/*===========================================================================*
X *                             get_header                                    *
X *===========================================================================*/
XPRIVATE
Xget_header(m_ptr, h_src, h_dest)
Xmessage *	m_ptr;
Xheader *	h_src;
Xheader *	h_dest;
X{
X/* get amoeba header from user space */
X    phys_bytes	umap();
X    phys_bytes	src;	/* user's header */
X    phys_bytes	dst;	/* kernel buffer for header */
X
X    if ((src = umap(proc_addr(m_ptr->AM_PROC_NR), D, (vir_bytes)h_src,
X					(vir_bytes)sizeof (header))) == 0)
X	return 0;
X    dst = umap(proc_addr(cur_proc), D, (vir_bytes)h_dest,
X					(vir_bytes)sizeof (header));
X    phys_copy(src, dst, (long)sizeof (header));
X    return 1;
X}
X
X
X/*===========================================================================*
X *                             put_header                                    *
X *===========================================================================*/
XPRIVATE
Xput_header(proc_nr, h_src, h_dest)
Xint		proc_nr;
Xheader *	h_src;
Xheader *	h_dest;
X{
X/* write an amoeba header into user space */
X    phys_bytes	umap();
X    phys_bytes	src;
X    phys_bytes	dst;
X
X    if ((dst = umap(proc_addr(proc_nr), D, (vir_bytes)h_dest,
X					(vir_bytes)sizeof (header))) == 0)
X	return 0;
X    src = umap(proc_addr(cur_proc), D, (vir_bytes)h_src,
X					(vir_bytes)sizeof (header));
X    phys_copy(src, dst, (long)sizeof (header));
X    return 1;
X}
X
X
X/*
X**	routines which are needed by trans.c
X*/
X
X/*===========================================================================*
X *                                 am_umap                                   *
X *===========================================================================*/
XPUBLIC phys_bytes
Xam_umap(a, b, c)
Xstruct task * a;
Xvir_bytes b;
Xvir_bytes c;
X{
X/* the umap in trans.c needs to be converted to minix umap */
X    phys_bytes	umap();
X
X    return umap(proc_addr(a->mx_proc_nr), D, b, c);
X}
X
X
X/*===========================================================================*
X *                                am_psig                                    *
X *===========================================================================*/
XPUBLIC
Xam_psig(t, sig)
Xstruct task *	t;
Xunshort		sig;
X{
X/* should propagate if between a g & p and remember sig! */
X    sendsig(t, (char)sig);	/* propagate signal to servers */
X    cause_sig(t->mx_proc_nr, SIGAMOEBA);
X}
X
X
X/*
X** sleep and wakeup don't fit into the amoeba model too well.
X** the following are hacks and don't give true sleep and wakeup
X** semantics.  they also do not take account of interrupts but seem to work.
X*/
X
X/*===========================================================================*
X *                               am_sleep                                    *
X *===========================================================================*/
Xam_sleep(addr)
Xevent_t	addr;
X{
X    message	mess;
X    struct task *	c;
X
X    c = curtask;
X    receive(ANY, &mess);
X    if (mess.AM_ADDRESS != addr)
X	printf("am_sleep: woken badly %x %x\n", mess.ADDRESS, addr);
X    curtask = c;
X    return 0;
X}
X
X
X/*===========================================================================*
X *                               am_wakeup                                   *
X *===========================================================================*/
Xam_wakeup(addr)
Xevent_t addr;
X{
X    message mess;
X    int tasknr;
X
X    mess.AM_ADDRESS = addr;	
X    tasknr = ((struct task *)addr - am_task); /* HACK */
X    if (am_task[tasknr].mx_active)  /* don't wake it up if it is dead! */
X	send(AMOEBA_CLASS - tasknr, &mess);
X}
X
X
X#ifndef NDEBUG
X
X#define PRINTABLE(c) (((c) >= ' ' && (c) <= '~') ? (c) : '?')
X
X
X/*===========================================================================*
X *                                  prport                                   *
X *===========================================================================*/
XPUBLIC
Xprport(p)
Xport * p;
X{
X    int i;
X
X    for (i = 0; i < PORTSIZE; i++)
X		printf("%c", PRINTABLE(p->_portbytes[i]));
X}
X
X#endif
X
X
X#ifndef NONET
X
X/*===========================================================================*
X *                              interinit                                    *
X *===========================================================================*/
XPUBLIC address
Xinterinit()
X{
X    return 0xFF;
X}
X
X
X/*===========================================================================*
X *                                  check                                    *
X *===========================================================================*/
XPRIVATE
Xcheck(p)
XEtherpacket *	p;
X{
X/* make sure that an ethernet packet is a valid amoeba packet */
X    if (p->ep_fr.f_ah.ph_srcnode == 0)	/* from an ethernet host */
X    {
X	if ((p->ep_fr.f_ah.ph_srcnode = ealookup(&p->ep_fr.f_srcaddr)) == 0)
X	{
X	    printf("ethernet mapping table overflow\n");
X	    return 0;
X	}
X    }
X    else	/* was the packet from ProNet? */
X#ifdef PRONET
X    {
X	if (p->ep_fr.f_ah.ph_srcnode & ETHERBITS)
X	    return 0;
X    /* a packet from the pronet gateway */
X	if (EANULL(&Gwaddr))
X	{
X	    Gwaddr = p->ep_fr.f_srcaddr;
X	    pr_addr("Gateway to pronet at", &Gwaddr);
X	}
X	else
X	    if (!EACMP(&Gwaddr, &p->ep_fr.f_srcaddr))
X		pr_addr("Second gateway claims to be at", &p->ep_fr.f_srcaddr);
X    }
X#else
X	return 0;
X#endif PRONET
X    return 1;
X}
X
X
X/*===========================================================================*
X *                                pr_addr                                    *
X *===========================================================================*/
XPRIVATE
Xpr_addr(s, p)
Xchar *		s;
XEth_addr *	p;
X{
X/* print an ethernet address */
X    printf("%s %x:%x:%x:%x:%x:%x\n", s,
X	p->e[0] & 0xff,
X	p->e[1] & 0xff,
X	p->e[2] & 0xff,
X	p->e[3] & 0xff,
X	p->e[4] & 0xff,
X	p->e[5] & 0xff);
X}
X
X
X/*===========================================================================*
X *                             am_puthead                                    *
X *===========================================================================*/
XPUBLIC
Xam_puthead(dst, src, ident, seq, type, size)
Xaddress	dst;
Xaddress	src;
Xchar	ident;
Xchar	seq;
Xchar	type;
Xunshort	size;
X{
X    phys_bytes	umap();
X    phys_bytes	eth_getbuf();
X
X    unshort	totalsize;
X    char	dstnode;
X    Framehdr	fh;
X    phys_bytes	phd;
X
X    totalsize = size + sizeof (Framehdr);
X    compare(totalsize, <=, 1514);
X    fh.f_ah.ph_dstnode = dstnode = lobyte(dst);
X    if ((dstnode & ETHERBITS) == 0)
X    {
X	assert(!EANULL(&Gwaddr));
X	fh.f_dstaddr = Gwaddr;
X    }
X    else	/* broadcast is also handled here! */
X	fh.f_dstaddr = Eamap[dstnode & 0x7f];
X    fh.f_srcaddr = Myaddr;
X    fh.f_proto = AMOEBAPROTO;
X    enc_s_be(&fh.f_proto);
X
X    fh.f_ah.ph_srcnode = 0;
X    fh.f_ah.ph_dsttask = hibyte(dst);
X    fh.f_ah.ph_srctask = hibyte(src);
X    fh.f_ah.ph_ident = ident;
X    fh.f_ah.ph_seq = seq;
X    fh.f_ah.ph_type = type;
X    fh.f_ah.ph_flags = 0;
X    fh.f_ah.ph_size = totalsize;
X    enc_s_le(&fh.f_ah.ph_size);
X
X    if ((Xmtbuf = eth_getbuf()) != 0)
X    {
X	phd = umap(proc_addr(cur_proc), D, (vir_bytes)&fh, (vir_bytes)sizeof (Framehdr));
X	phys_copy(phd, Xmtbuf, (long)sizeof (Framehdr));
X	if (size == 0)
X	    eth_write(Xmtbuf, 60);
X	else
X	{
X	    Outsiz = sizeof (Framehdr);
X	    Outptr = Xmtbuf + sizeof (Framehdr);
X	}
X    }
X    else
X	Outptr = 0;
X}
X
X
X/*===========================================================================*
X *                                am_gall                                    *
X *===========================================================================*/
XPUBLIC
Xam_gall()	/* getall in trans.c */
X{
X/*
X**  copy in any bytes not already copied into packet! We've already copied
X**  the first HSZ bytes!
X**  Bufaddr points to the local buffer Packet and Inptr points to the current
X**  position in the buffer on the ethernet card.
X*/
X    long size;
X
X    if ((size = (long)Insiz - HSZ) > 0)
X	phys_copy(Inptr, Bufaddr+HSZ, size);
X}
X
X
X/*===========================================================================*
X *                             am_do_append                                  *
X *===========================================================================*/
XPUBLIC
Xam_doappend(data, size, dosend)
Xphys_bytes	data;
Xunshort		size;
Xint		dosend;
X{
X/* add more data to current ethernet output packet */
X    if (Outptr == 0)	/* previous puthead failed */
X	return;
X    phys_copy(data, Outptr, (long)size);
X    Outptr += size;
X    Outsiz += size;
X    if (dosend)
X    {
X	if (Outsiz < 60)
X	    Outsiz = 60;
X	eth_write(Xmtbuf, (int)Outsiz);
X    }
X}
X
X
X/*===========================================================================*
X *                             am_pickoff                                    *
X *===========================================================================*/
XPUBLIC
Xam_pickoff(data, size)
Xphys_bytes	data;
Xunsigned	size;
X{
X    phys_copy(Inptr, data, (long)size);
X    Inptr += size;
X}
X
X
X/*===========================================================================*
X *                             am_append                                     *
X *===========================================================================*/
XPUBLIC
Xam_append(data, size, dosend)
Xphys_bytes	data;	/* not really a phys_bytes! really a vir_bytes */
Xunshort		size;
Xint		dosend;
X{
X    phys_bytes	paddr;
X    phys_bytes	umap();
X
X    paddr = umap(proc_addr(cur_proc), D, (vir_bytes)data, (vir_bytes)size);
X    am_doappend(paddr, size, dosend);
X}
X
X
X/*===========================================================================*
X *                             am_phys_copy                                  *
X *===========================================================================*/
XPUBLIC
Xam_phys_copy(s, d, size)
Xvir_bytes	s;
Xphys_bytes	d;
Xphys_bytes	size;
X{
X/*
X** the phys_copy in trans.c needs a little help in places since kernel
X** virtual address need to be umapped under minix
X*/
X    phys_bytes	umap();
X    phys_bytes	ps;
X
X    ps = umap(proc_addr(cur_proc), D, (vir_bytes)s, (vir_bytes)size);
X    phys_copy(ps, d, size);
X}
X
X
X/*===========================================================================*
X *                               ealookup                                    *
X *===========================================================================*/
XPRIVATE
Xealookup(addr)
XEth_addr *	addr;
X{
X    int		index;
X    int		i;
X    Eth_addr *	mep;
X
X    index = addr->e[5] & 0x7f;	/* hash it */
X    if (index >= MAPENTRIES)
X	index = 0;
X    i = index;
X    do
X    {
X	mep = &Eamap[i];
X	if (EACMP(addr, mep))
X	    return i | ETHERBITS;
X	if (EANULL(mep))
X	{
X	    *mep = *addr;
X	    return i | ETHERBITS;
X	}
X	if (++i >= MAPENTRIES)
X	    i = 0;
X    } while (i != index);
X    return 0;
X}
X
X
X/*
X** the following routines provide the interface to the ethernet driver
X*/
X
X
X/*===========================================================================*
X *                                net_init                                   *
X *===========================================================================*/
XPUBLIC
Xnet_init()
X{
X    int pkt_arr();  /* called by ethernet driver when packet arrives */
X    int pkt_sent(); /* called by ethernet driver when packet was sent */
X    int eth_init(); /* initialise ethernet driver */
X
X    Eamap[MAPENTRIES] = Broadcastaddr;
X    epl_init();
X    etheraddr(&Myaddr);
X    pr_addr("Etheraddr:", &Myaddr);
X    eth_init(&Myaddr, pkt_arr, pkt_sent);
X}
X
X
X/*
X** some special hack-defines because physical addresses are stored in a long
X** and virtual addresses are not
X*/
X
X#define	PROTO_OFFSET	((int) &((Etherpacket *) 0)->ep_fr.f_proto)
X
XPRIVATE message arr_mess;
X
X/*===========================================================================*
X *                                 pkt_arr                                   *
X *===========================================================================*/
XPRIVATE
Xpkt_arr(addr, count)
Xphys_bytes addr;
Xint count;
X{
X/*
X** This routine is called when an ethernet interrupt occurs.
X** It must select appropriate amoeba task to give the packet to.
X** NB: we are only interested in amoeba packets!
X*/
X    short	getbint();
X    short	protocol;
X
X    protocol = getbint(addr + PROTO_OFFSET);
X#ifdef ALTAMOEBAPROTO
X    if (protocol == AMOEBAPROTO || protocol == ALTAMOEBAPROTO)
X#else
X    if (protocol == AMOEBAPROTO)
X#endif
X    {
X	arr_mess.AM_OP = ETHER_ARRIV;
X	arr_mess.AM_PADDR = addr;
X	arr_mess.AM_COUNT = count;
X	interrupt(AMINT_CLASS, &arr_mess);
X    }
X    else	/* not an amoeba packet, so give it back */
X	eth_release(addr);
X}
X
X
X/*===========================================================================*
X *                                 pkt_sent                                  *
X *===========================================================================*/
X/*ARGSUSED*/
XPRIVATE
Xpkt_sent (addr)
Xphys_bytes addr;
X{
X/*
X** This is never called. The ethernet driver busy waits!  It is here for
X** compatibility with the ethernet driver
X*/
X}
X
X
X/*===========================================================================*
X *                             am_netenable                                  *
X *===========================================================================*/
XPUBLIC
Xam_netenable()	/* release the last received message */
X{
X	eth_release((phys_bytes)arr_mess.AM_PADDR);
X}
X
X#endif NONET
+ END-OF-FILE amoeba.c
chmod 'u=rw,g=r,o=r' 'amoeba.c'
set `wc -c 'amoeba.c'`
count=$1
case $count in
27724)	:;;
*)	echo 'Bad character count in ''amoeba.c' >&2
		echo 'Count should be 27724' >&2
esac
echo Extracting 'amstat.h'
sed 's/^X//' > 'amstat.h' << '+ END-OF-FILE ''amstat.h'
Xstruct amstat {
X	long ams_clfail;
X	long ams_svfail;
X	long ams_clcrash;
X	long ams_rxcl;
X	long ams_rxsv;
X	long ams_trans;
X	long ams_loctrans;
X	long ams_remtrans;
X	long ams_getreq;
X	long ams_putrep;
X	long ams_naks;
X};
+ END-OF-FILE amstat.h
chmod 'u=rw,g=r,o=r' 'amstat.h'
set `wc -c 'amstat.h'`
count=$1
case $count in
215)	:;;
*)	echo 'Bad character count in ''amstat.h' >&2
		echo 'Count should be 215' >&2
esac
echo Extracting 'assert.h'
sed 's/^X//' > 'assert.h' << '+ END-OF-FILE ''assert.h'
X/****************************************************************************
X *									    *
X * (c) Copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands *
X *									    *
X *    This product is part of the  Amoeba  distributed operating system.    *
X *    Permission is hereby granted to use it exclusively for educational    * 
X *    and research purposes.  It may also be freely duplicated and given    *
X *    to others for educational and research purposes only.  All other use  *
X *    requires written permission from the copyright owner.		    *
X *									    *
X *    Requests for such permissions may be sent to              	    *
X *									    *
X *									    *
X *		Dr. Andrew S. Tanenbaum					    *
X *		Dept. of Mathematics and Computer Science		    *
X *		Vrije Universiteit					    *
X *		De Boelelaan 1081					    *
X *		1081 HV Amsterdam					    *
X *		The Netherlands						    *
X *									    *
X/****************************************************************************/
X
X#define NDEBUG
X#ifdef NDEBUG
X#define assert(e)	/* NOTHING */
X#define compare(a,t,b)	/* NOTHING */
X#else
X#ifdef lint
X#define assert(e)	use(e)
X#define compare(a,t,b)	use(a, b)
X#else lint
X#define assert(x)	if (!(x)) printf("assertion failed in %s at %d\n", __FILE__, __LINE__)
X#define compare(a,t,b)	if (!((a) t (b))) \
X			printf("comparison failed in %s at %d (%D)\n", \
X						 __FILE__, __LINE__, a)
X/*
X#define assert(e)	do if (!(e)) badassertion(__FILE__,__LINE__); while (0)
X#define compare(a,t,b)	do if (!((a) t (b))) \
X				badcompare(__FILE__,__LINE__, (long) (a)); \
X			while (0)
X*/
X#endif lint
X#endif NDEBUG
+ END-OF-FILE assert.h
chmod 'u=rw,g=r,o=r' 'assert.h'
set `wc -c 'assert.h'`
count=$1
case $count in
1621)	:;;
*)	echo 'Bad character count in ''assert.h' >&2
		echo 'Count should be 1621' >&2
esac
echo Extracting 'byteorder.h'
sed 's/^X//' > 'byteorder.h' << '+ END-OF-FILE ''byteorder.h'
X/*
X * set of macros to do inplace byteorder changes
X * The dec_* routines decode a short (_s) or long (_l) from little endian(_le)
X * or bigendian(_be) to native format.
X * The enc_* are similar for native to net format
X */
X
X
X/* littleendian version for ibm pc */
X
X#define dec_s_le(s)	/* nothing */
X#define dec_s_be(s)	(*(s))=((((*(s))>>8)&0xFF)|(((*(s))&0xFF)<<8))
X
X#define dec_l_le(l)	/* nothing */
X#define dec_l_be(l)	(*(l))=((((*(l))>>24)&0xFF)|(((*(l))>>8)&0xFF00)|(((*(l))<<8)&0xFF0000)|(((*(l))<<24)&0xFF000000))
X
X#define enc_s_le(s)	/* nothing */
X#define enc_s_be(s)	dec_s_be(s)
X
X#define enc_l_le(l)	/* nothing */
X#define enc_l_be(l)	dec_l_be(l)
+ END-OF-FILE byteorder.h
chmod 'u=rw,g=r,o=r' 'byteorder.h'
set `wc -c 'byteorder.h'`
count=$1
case $count in
654)	:;;
*)	echo 'Bad character count in ''byteorder.h' >&2
		echo 'Count should be 654' >&2
esac
echo Extracting 'conf.c'
sed 's/^X//' > 'conf.c' << '+ END-OF-FILE ''conf.c'
X#include "../h/type.h"
X#include "../h/amoeba.h"
X#include "global.h"
X#include "conf.h"
X
X/*
X**	the following hack is imported from task.c under Amoeba to declare
X**	a few pointers to the task table
X*/ 
X#define extern
X
X#include "task.h"
X
X#undef extern
X
X/************************************************************************/
X/*	TRANS CONFIGURATION						*/
X/************************************************************************/
X
X/*
X**	various variables used for transactions
X*/
X
Xport	NULLPORT;	/* used in trans.c, declared here for compatability */
Xlong	ticker;		/* bogus global used by trans.c for statistics */
X
X#ifndef NONET
X
Xunshort minloccnt	= MINLOCCNT;
Xunshort maxloccnt	= MAXLOCCNT;
X
Xunshort retranstime	= RETRANSTIME;
Xunshort crashtime	= CRASHTIME;
Xunshort clientcrash	= CLIENTCRASH;
X
Xunshort maxretrans	= MAXRETRANS;
Xunshort mincrash	= MINCRASH;
Xunshort maxcrash	= MAXCRASH;
X
X#endif NONET
+ END-OF-FILE conf.c
chmod 'u=rw,g=r,o=r' 'conf.c'
set `wc -c 'conf.c'`
count=$1
case $count in
902)	:;;
*)	echo 'Bad character count in ''conf.c' >&2
		echo 'Count should be 902' >&2
esac
echo Extracting 'conf.h'
sed 's/^X//' > 'conf.h' << '+ END-OF-FILE ''conf.h'
X/*
X**	constants used for configuring amoeba transactions
X*/
X#define MINLOCCNT	   5	/* locate message sent every dsec */
X#define MAXLOCCNT	 100	/* locate message sent every MAXLOCCNT dsec */
X
X#define RETRANSTIME	   5	/* retransmission time in dsec */
X#define CRASHTIME	 100	/* crash timer in dsec */
X#define CLIENTCRASH	 500	/* client must probe within this time */
X
X#define MAXRETRANS	  10	/* max. number of transmissions */
X#define MINCRASH	   5	/* enquiry sent MINCRASH times during recv */
X#define MAXCRASH	  10	/* enquiry sent MAXCRASH times during serv */
X
X#define	NPORTS		  16	/* # ports in portcache */
+ END-OF-FILE conf.h
chmod 'u=rw,g=r,o=r' 'conf.h'
set `wc -c 'conf.h'`
count=$1
case $count in
610)	:;;
*)	echo 'Bad character count in ''conf.h' >&2
		echo 'Count should be 610' >&2
esac
echo Extracting 'dp8390.c'
sed 's/^X//' > 'dp8390.c' << '+ END-OF-FILE ''dp8390.c'
X
X#include "../h/const.h"
X
X#ifndef NONET
X
X#include "../h/error.h"
X#include "../h/type.h"
X#include "../h/com.h"
X#include "const.h"
X#include "internet.h"
X#include "etherformat.h"
X#include "dp8390.h"
X#include "dp8390info.h"
X#include "dp8390stat.h"
X#include "assert.h"
X
X/* macros for device I/O */
X#define PIC_enable()	port_out(INT_CTL,ENABLE)
X
X#define input(devaddr, dp_register) \
X	inbyte((vir_bytes)&((union dp8390reg *) devaddr)->dp_pg0rd.dp_register)
X#define input1(devaddr, dp_register) \
X	inbyte((vir_bytes)&((union dp8390reg *) devaddr)->dp_pg1rdwr.dp_register)
X#define output(devaddr, dp_register, value) \
X	outbyte((vir_bytes)&((union dp8390reg *) devaddr)->dp_pg0wr.dp_register, value)
X#define output1(devaddr, dp_register, value) \
X	outbyte((vir_bytes)&((union dp8390reg *) devaddr)->dp_pg1rdwr.dp_register, value)
X
X#define MAX_WAIT	10000
X
X#ifdef DPSTAT
Xstruct dpstat dpstat;
X#endif
X
Xstatic int (*bufread)();	/* call when packet came in */
Xstatic int (*bufwritten)();	/* call when packet has been written */
X
Xstatic disabled;
Xstatic phys_bytes curopacket;	/* packet being transmitted */
Xstatic phys_bytes Curbuff;	/* address of next read buffer to release */
X
X
Xstatic
Xchipinit(myaddr)
XEth_addr *myaddr;
X{
X	register vir_bytes device;
X
X	device = dp8390info.dpi_devaddr;
X	output(device, dp_cr, CR_PS_P0|CR_DM_ABORT);	/* back to main register set */
X	output(device, dp_pstart, dp8390info.dpi_pstart);
X	output(device, dp_pstop, dp8390info.dpi_pstop);
X	output(device, dp_bnry, dp8390info.dpi_pstart);
X	output(device, dp_rcr, RCR_MON);
X	output(device, dp_tcr, TCR_NORMAL);
X	output(device, dp_dcr, DCR_BYTEWIDE|DCR_8BYTES);
X        output(device, dp_rbcr0, 0);
X        output(device, dp_rbcr1, 0);
X        output(device, dp_isr, 0xFF);
X	output(device, dp_cr, CR_PS_P1|CR_DM_ABORT);	/* switch to register set 1 */
X	output1(device, dp_par0, myaddr->e[0]);
X	output1(device, dp_par1, myaddr->e[1]);
X	output1(device, dp_par2, myaddr->e[2]);
X	output1(device, dp_par3, myaddr->e[3]);
X	output1(device, dp_par4, myaddr->e[4]);
X	output1(device, dp_par5, myaddr->e[5]);
X	output1(device, dp_curr, dp8390info.dpi_pstart+1);
X        output1(device, dp_cr, CR_PS_P0|CR_DM_ABORT);
X        output(device, dp_rcr, RCR_AB);
X        input(device, dp_cntr0);
X        input(device, dp_cntr1);
X        input(device, dp_cntr2);
X
X#ifdef TRMTINT
X	output(device, dp_imr, IMR_TXEE|IMR_PTXE|IMR_PRXE|IMR_CNTE|IMR_OVWE);
X#endif    
X	output(device, dp_imr, IMR_PRXE|IMR_CNTE|IMR_OVWE);
X	output(device, dp_cr, CR_STA|CR_DM_ABORT);		/* fire it up */
X}
X
X/*
X * Interrupt handling
X */
X
Xstatic
Xdp_xmit_intr()
X#ifdef TRMTINT
X{
X	register tsr;
X
X	if (curopacket == 0) {
X		printf("Bogus transmit interrupt\n");
X		STINC(ds_btint);
X		return;
X	}
X	tsr = input(dp8390info.dpi_devaddr, dp_tsr);
X	if (tsr&TSR_PTX)
X		STINC(ds_written);	/* It went OK! */
X	if (tsr&TSR_DFR)
X		STINC(ds_deferred);
X	if (tsr&TSR_COL)
X		STINC(ds_collisions);
X	if (tsr&TSR_ABT)
X		STINC(ds_xcollisions);
X	if (tsr&TSR_CRS) {
X		printf("Ethernet carrier sense lost\n");
X		STINC(ds_carlost);
X	}
X	if (tsr&TSR_FU) {
X		printf("Ethernet Fifo Underrun\n");
X		STINC(ds_fifo);
X	}
X	if (tsr&TSR_CDH) {
X		printf("Ethernet Heartbeat failure\n");
X		STINC(ds_heartbeat);
X	}
X	if (tsr&TSR_OWC) {
X		printf("Ethernet late collision\n");
X		STINC(ds_lcol);
X	}
X	(*bufwritten)(curopacket);
X	curopacket = 0;
X}
X#else
X{}
X#endif
X
X
Xstatic
Xrecvintr()
X{
X	register vir_bytes device;
X	register phys_bytes paddr;
X	struct rcvdheader pkthead;
X	char pageno, curr, next;
X	int length;
X
X	device = dp8390info.dpi_devaddr;
X	pageno=input(device, dp_bnry)+1;
X        if (pageno == dp8390info.dpi_pstop)
X            pageno = dp8390info.dpi_pstart;
X	while (!(disabled)) {
X		output(device, dp_cr, CR_PS_P1);/* switch to register set 1 */
X		curr = input1(device, dp_curr);
X		output1(device, dp_cr, CR_PS_P0);/* back to main register set */
X		if (pageno==curr)
X			break;
X		STINC(ds_read);
X		paddr = dp8390info.dpi_membase+(pageno<<8);
X		getheader(paddr, &pkthead);
X		next = pkthead.rp_next;
X		if (pkthead.rp_status&RSR_PRX) {
X			if (next < pageno && next > dp8390info.dpi_pstart) {
X				/*
X				 * We copy end of packet to avoid break.
X				 */
X				phys_copy(dp8390info.dpi_membase+
X					   (dp8390info.dpi_pstart<<8),
X					  dp8390info.dpi_membase+
X					   (dp8390info.dpi_pstop<<8),
X					  (phys_bytes) (next-dp8390info.dpi_pstart)<<8);
X			}
X			length = (pkthead.rp_rbcl&0xFF)|(pkthead.rp_rbch<<8);
X			Curbuff = paddr + sizeof (pkthead);
X			disabled = 1;
X			(*bufread)(Curbuff, length-4);
X		}
X		pageno = pkthead.rp_next;
Xif (pageno >= dp8390info.dpi_pstop || pageno < dp8390info.dpi_pstart)
X	 printf("page no %x\n", pageno);
X		assert(pageno >= dp8390info.dpi_pstart);
X		assert(pageno < dp8390info.dpi_pstop);
X	}
X}			
X
Xstatic
Xcntintr()
X{
X	register vir_bytes device;
X        int n;
X
X        printf("dp8390: counter overflow\n");		/*DEBUG*/
X	device = dp8390info.dpi_devaddr;
X        n = input(device, dp_cntr0);
X	STADD(ds_fram, n);
X        n = input(device, dp_cntr1);
X	STADD(ds_crc, n); 
X        n =input(device, dp_cntr2);
X	STADD(ds_lost, n);
X}
X
Xdp8390_int()
X{
X	register isr;
X	register vir_bytes device;
X
X	PIC_enable();
X	device = dp8390info.dpi_devaddr;
X	for(isr=input(device, dp_isr); isr&(ISR_OVW|ISR_PRX|ISR_PTX|ISR_CNT);
X						isr=input(device, dp_isr)) {
X		if (isr&ISR_OVW) {
X			printf("OVW, do something\n");
X			output(device, dp_isr, ISR_OVW);	/* ack */
X		}
X		if (isr&ISR_PTX) {
X			dp_xmit_intr();
X			output(device, dp_isr, ISR_PTX);	/* ack */
X		}
X		if (isr&ISR_TXE) {
X			dp_xmit_intr();
X			output(device, dp_isr, ISR_TXE);	/* ack */
X		}
X		if (isr&ISR_PRX) {
X			recvintr();
X			output(device, dp_isr, ISR_PRX);	/* ack */
X		}
X		if (isr&ISR_CNT) {
X			cntintr();
X			output(device, dp_isr, ISR_CNT);	/* ack */
X		}
X	}
X}
X
Xeth_init(etheraddr, br, bw)
XEth_addr *etheraddr;
Xint (*br)(), (*bw)();
X{
X	bufread = br;
X	bufwritten = bw;
X	epl_init();		/* activate on board memory */
X	chipinit(etheraddr);	/* start ethernet controller chip */
X}
X
X
X
Xeth_write(bufaddr, bufcnt)
Xphys_bytes bufaddr;
X{
X	int bpageno;
X	register vir_bytes device;
X
X	device = dp8390info.dpi_devaddr;
X/*	assert(curopacket==0);  */
X	assert(((bufaddr-dp8390info.dpi_membase)&0xFF)==0);
X	assert(bufcnt >= 60);	/* magic Ethernet requirement */
X/*	assert(bufcnt <= 1514); /* another one */
X	bpageno = ((bufaddr-dp8390info.dpi_membase)>>8) & 0xFF;
X	curopacket = bufaddr;
X	output(device, dp_tpsr, bpageno);
X	output(device, dp_tbcr1, bufcnt>>8);
X	output(device, dp_tbcr0, bufcnt&0xFF);
X	output(device, dp_cr, CR_TXP);		/* there it goes */
X}
X
Xeth_release(bufaddr) 
Xphys_bytes bufaddr;
X{
X	register vir_bytes device;
X	register phys_bytes paddr;
X	struct rcvdheader pkthead;
X	char pageno;
X	int old_state;
X
X	device = dp8390info.dpi_devaddr;
X	paddr = bufaddr-sizeof(pkthead);
X	assert(((paddr-dp8390info.dpi_membase)&0xFF)==0);
X	getheader(paddr, &pkthead);
X        pageno = pkthead.rp_next;
X        if (pageno == dp8390info.dpi_pstart)
X            pageno = dp8390info.dpi_pstop;
X	if (bufaddr != Curbuff)
X	    panic("eth_release: bad order", NO_NUM);
X	output(device, dp_bnry, pageno-1);
X	disabled = 0;
X	old_state = lock();
X	recvintr();
X	restore(old_state);
X}
X
Xphys_bytes
Xeth_getbuf()
X{
X	int t_cnt;
X	register vir_bytes device;
X	register tsr;
X
X	device = dp8390info.dpi_devaddr;
X
X        t_cnt = 0;
X        while (input(device,dp_cr)&CR_TXP) {
X            if (t_cnt++ > MAX_WAIT)
X                printf("transmitter frozen\n"); 
X		return (phys_bytes)0;
X	}       
X
X#ifndef TRMTINT
X#ifdef DPSTAT
X	tsr = input(device, dp_tsr);
X	if (tsr&TSR_PTX)
X		STINC(ds_written);	/* It went OK! */
X	if (tsr&TSR_DFR)
X		STINC(ds_deferred);
X	if (tsr&TSR_COL)
X		STINC(ds_collisions);
X	if (tsr&TSR_ABT)
X		STINC(ds_xcollisions);
X	if (tsr&TSR_CRS) {
X		printf("Ethernet carrier sense lost\n");
X		STINC(ds_carlost);
X	}
X	if (tsr&TSR_FU) {
X		printf("Ethernet Fifo Underrun\n");
X		STINC(ds_fifo);
X	}
X	if (tsr&TSR_CDH) {
X		printf("Ethernet Heartbeat failure\n");
X		STINC(ds_heartbeat);
X	}
X	if (tsr&TSR_OWC) {
X		printf("Ethernet late collision\n");
X		STINC(ds_lcol);
X	}
X#endif
X#endif
X	return dp8390info.dpi_tbuf;	/* return pointer to xmit buffer */
X}
X
X#else NONET
X
XPUBLIC
Xdp8390_int()
X{
X}
X
X#endif NONET
X
X#ifdef i8088
X
XPUBLIC
Xeth_stp()
X{
X/* called from reboot() (klib88.s) to stop the ethernet */
X#ifndef NONET
X	output(dp8390info.dpi_devaddr, dp_cr, CR_STP|CR_DM_ABORT);
X
X#endif
X}
X
X#endif i8088
+ END-OF-FILE dp8390.c
chmod 'u=rw,g=r,o=r' 'dp8390.c'
set `wc -c 'dp8390.c'`
count=$1
case $count in
8277)	:;;
*)	echo 'Bad character count in ''dp8390.c' >&2
		echo 'Count should be 8277' >&2
esac
echo Extracting 'dp8390.h'
sed 's/^X//' > 'dp8390.h' << '+ END-OF-FILE ''dp8390.h'
X/*
X * National Semiconductor DP8390 Network Interface Controller
X */
X
Xtypedef
Xunion dp8390reg {
X	struct pg0rd {			/* Page 0, for reading ------------- */
X		char	dp_cr;		/* Read side of Command Register     */
X		char	dp_clda0;	/* Current Local Dma Address 0       */
X		char	dp_clda1;	/* Current Local Dma Address 1       */
X		char	dp_bnry;	/* Boundary Pointer                  */
X		char	dp_tsr;		/* Transmit Status Register          */
X		char	dp_ncr;		/* Number of Collisions Register     */
X		char	dp_fifo;	/* Fifo ??                           */
X		char	dp_isr;		/* Interrupt Status Register         */
X		char	dp_crda0;	/* Current Remote Dma Address 0      */
X		char	dp_crda1;	/* Current Remote Dma Address 1      */
X		char	dp_dum1;	/* unused                            */
X		char	dp_dum2;	/* unused                            */
X		char	dp_rsr;		/* Receive Status Register           */
X		char	dp_cntr0;	/* Tally Counter 0                   */
X		char	dp_cntr1;	/* Tally Counter 1                   */
X		char	dp_cntr2;	/* Tally Counter 2                   */
X	} dp_pg0rd;
X	struct pg0wr {			/* Page 0, for writing ------------- */
X		char	dp_cr;		/* Write side of Command Register    */
X		char	dp_pstart;	/* Page Start Register               */
X		char	dp_pstop;	/* Page Stop Register                */
X		char	dp_bnry;	/* Boundary Pointer                  */
X		char	dp_tpsr;	/* Transmit Page Start Register      */
X		char	dp_tbcr0;	/* Transmit Byte Count Register 0    */
X		char	dp_tbcr1;	/* Transmit Byte Count Register 1    */
X		char	dp_isr;		/* Interrupt Status Register         */
X		char	dp_rsar0;	/* Remote Start Address Register 0   */
X		char	dp_rsar1;	/* Remote Start Address Register 1   */
X		char	dp_rbcr0;	/* Remote Byte Count Register 0      */
X		char	dp_rbcr1;	/* Remote Byte Count Register 1      */
X		char	dp_rcr;		/* Receive Configuration Register    */
X		char	dp_tcr;		/* Transmit Configuration Register   */
X		char	dp_dcr;		/* Data Configuration Register       */
X		char	dp_imr;		/* Interrupt Mask Register           */
X	} dp_pg0wr;
X	struct pg1rdwr {		/* Page 1, read/write -------------- */
X		char	dp_cr;		/* Command Register                  */
X		char	dp_par0;	/* Physical Address Register 0       */
X		char	dp_par1;	/* Physical Address Register 1       */
X		char	dp_par2;	/* Physical Address Register 2       */
X		char	dp_par3;	/* Physical Address Register 3       */
X		char	dp_par4;	/* Physical Address Register 4       */
X		char	dp_par5;	/* Physical Address Register 5       */
X		char	dp_curr;	/* Current Page Register             */
X		char	dp_mar0;	/* Multicast Address Register 0      */
X		char	dp_mar1;	/* Multicast Address Register 1      */
X		char	dp_mar2;	/* Multicast Address Register 2      */
X		char	dp_mar3;	/* Multicast Address Register 3      */
X		char	dp_mar4;	/* Multicast Address Register 4      */
X		char	dp_mar5;	/* Multicast Address Register 5      */
X		char	dp_mar6;	/* Multicast Address Register 6      */
X		char	dp_mar7;	/* Multicast Address Register 7      */
X	} dp_pg1rdwr;
X} dp8390;
X
X/* Bits in dp_cr */
X
X#define CR_STP		0x01		/* Stop: software reset              */
X#define CR_STA		0x02		/* Start: activate NIC               */
X#define CR_TXP		0x04		/* Transmit Packet                   */
X#define CR_DMA		0x38		/* Mask for DMA control              */
X#	define CR_DM_NOP	0x00	/* DMA: No Operation                 */
X#	define CR_DM_RR		0x08	/* DMA: Remote Read                  */
X#	define CR_DM_RW		0x10	/* DMA: Remote Write                 */
X#	define CR_DM_SP		0x18	/* DMA: Send Packet                  */
X#	define CR_DM_ABORT	0x20	/* DMA: Abort Remote DMA Operation   */
X#define CR_PS		0xC0		/* Mask for Page Select              */
X#	define CR_PS_P0		0x00	/* Register Page 0                   */
X#	define CR_PS_P1		0x40	/* Register Page 1                   */
X#	define CR_PS_T0		0x80	/* Test Mode Register Map ??         */
X#	define CR_SP_T1		0xC0	/* Test Mode Register Map ??         */
X
X/* Bits in dp_isr */
X
X#define ISR_PRX		0x01		/* Packet Received with no errors    */
X#define ISR_PTX		0x02		/* Packet Transmitted with no errors */
X#define ISR_RXE		0x04		/* Receive Error                     */
X#define ISR_TXE		0x08		/* Transmit Error                    */
X#define ISR_OVW		0x10		/* Overwrite Warning                 */
X#define ISR_CNT		0x20		/* Counter Overflow                  */
X#define ISR_RDC		0x40		/* Remote DMA Complete               */
X#define ISR_RST		0x80		/* Reset Status                      */
X
X/* Bits in dp_imr */
X
X#define IMR_PRXE	0x01		/* Packet Received iEnable           */
X#define IMR_PTXE	0x02		/* Packet Transmitted iEnable        */
X#define IMR_RXEE	0x04		/* Receive Error iEnable             */
X#define IMR_TXEE	0x08		/* Transmit Error iEnable            */
X#define IMR_OVWE	0x10		/* Overwrite Warning iEnable         */
X#define IMR_CNTE	0x20		/* Counter Overflow iEnable          */
X#define IMR_RDCE	0x40		/* DMA Complete iEnable              */
X
X/* Bits in dp_dcr */
X
X#define DCR_WTS		0x01		/* Word Transfer Select              */
X#	define DCR_BYTEWIDE	0x00	/* WTS: byte wide transfers          */
X#	define DCR_WORDWIDE	0x01	/* WTS: word wide transfers          */
X#define DCR_BOS		0x02		/* Byte Order Select                 */
X#	define DCR_LTLENDIAN	0x00	/* BOS: Little Endian                */
X#	define DCR_BIGENDIAN	0x02	/* BOS: Big Endian                   */
X#define DCR_LAS		0x04		/* Long Address Select               */
X#define DCR_BMS		0x08		/* Burst Mode Select                 */
X#define DCR_AR		0x10		/* Autoinitialize Remote             */
X#define DCR_FTS		0x60		/* Fifo Threshold Select             */
X#	define DCR_2BYTES	0x00	/* 2 bytes                           */
X#	define DCR_4BYTES	0x40	/* 4 bytes                           */
X#	define DCR_8BYTES	0x20	/* 8 bytes                           */
X#	define DCR_12BYTES	0x60	/* 12 bytes                          */
X
X/* Bits in dp_tcr */
X
X#define TCR_CRC		0x01		/* Inhibit CRC                       */
X#define TCR_ELC		0x06		/* Encoded Loopback Control          */
X#	define TCR_NORMAL	0x00	/* ELC: Normal Operation             */
X#	define TCR_INTERNAL	0x02	/* ELC: Internal Loopback            */
X#	define TCR_0EXTERNAL	0x04	/* ELC: External Loopback LPBK=0     */
X#	define TCR_1EXTERNAL	0x06	/* ELC: External Loopback LPBK=1     */
X#define TCR_ATD		0x08		/* Auto Transmit                     */
X#define TCR_OFST	0x10		/* Collision Offset Enable (be nice) */
X
X/* Bits in dp_tsr */
X
X#define TSR_PTX		0x01		/* Packet Transmitted (without error)*/
X#define TSR_DFR		0x02		/* Transmit Deferred                 */
X#define TSR_COL		0x04		/* Transmit Collided                 */
X#define TSR_ABT		0x08		/* Transmit Aborted                  */
X#define TSR_CRS		0x10		/* Carrier Sense Lost                */
X#define TSR_FU		0x20		/* Fifo Underrun                     */
X#define TSR_CDH		0x40		/* CD Heartbeat                      */
X#define TSR_OWC		0x80		/* Out of Window Collision           */
X
X/* Bits in tp_rcr */
X
X#define RCR_SEP		0x01		/* Save Errored Packets              */
X#define RCR_AR		0x02		/* Accept Runt Packets               */
X#define RCR_AB		0x04		/* Accept Broadcast                  */
X#define RCR_AM		0x08		/* Accept Multicast                  */
X#define RCR_PRO		0x10		/* Physical Promiscuous              */
X#define RCR_MON		0x20		/* Monitor Mode                      */
X
X/* Bits in dp_rsr */
X
X#define RSR_PRX		0x01		/* Packet Received Intact            */
X#define RSR_CRC		0x02		/* CRC Error                         */
X#define RSR_FAE		0x04		/* Frame Alignment Error             */
X#define RSR_FO		0x08		/* FIFO Overrun                      */
X#define RSR_MPA		0x10		/* Missed Packet                     */
X#define RSR_PHY		0x20		/* Multicast Address Match !!        */
X#define RSR_DIS		0x40		/* Receiver Disabled                 */
X
X
Xstruct rcvdheader {
X	char	rp_status;		/* Copy of rsr                       */
X	char	rp_next;		/* Pointer to next packet            */
X	char	rp_rbcl;		/* Receive Byte Count Low            */
X	char	rp_rbch;		/* Receive Byte Count High           */
X};
+ END-OF-FILE dp8390.h
chmod 'u=rw,g=r,o=r' 'dp8390.h'
set `wc -c 'dp8390.h'`
count=$1
case $count in
8035)	:;;
*)	echo 'Bad character count in ''dp8390.h' >&2
		echo 'Count should be 8035' >&2
esac
echo Extracting 'dp8390info.h'
sed 's/^X//' > 'dp8390info.h' << '+ END-OF-FILE ''dp8390info.h'
X/*
X * parameters for driver for
X * National Semiconductor DP8390 Network Interface Controller
X */
X
Xextern
Xstruct dp8390info {
X	vir_bytes	dpi_devaddr;	/* device address */
X	char		dpi_pstart;	/* start of recv ring */
X	char		dpi_pstop;	/* end of recv ring */
X	phys_bytes	dpi_membase;	/* memory address of page 0 */
X	phys_bytes	dpi_tbuf;	/* memory address of transmit buffer */
X} dp8390info;
X
+ END-OF-FILE dp8390info.h
chmod 'u=rw,g=r,o=r' 'dp8390info.h'
set `wc -c 'dp8390info.h'`
count=$1
case $count in
402)	:;;
*)	echo 'Bad character count in ''dp8390info.h' >&2
		echo 'Count should be 402' >&2
esac
echo Extracting 'dp8390stat.h'
sed 's/^X//' > 'dp8390stat.h' << '+ END-OF-FILE ''dp8390stat.h'
X#ifdef DPSTAT
X/* statistics from dp8390 */
Xstruct dpstat {
X	long	ds_read;	/* packets read */
X	long	ds_written;	/* packets written */
X        long    ds_fram;	/* Input framing errors */
X        long    ds_crc;		/* Input CRC errors */
X	long	ds_lost;	/* Packets lost */
X	long	ds_btint;	/* Bogus transmit interrupts */
X	long	ds_deferred;	/* Deferred packets */
X	long	ds_collisions;	/* Packets collided at least once */
X	long	ds_xcollisions;	/* Aborts due to excessive collisions */
X	long	ds_carlost;	/* Carrier sense lost */
X	long	ds_fifo;	/* Fifo underrun */
X	long	ds_heartbeat;	/* Heart beat failure */
X	long	ds_lcol;	/* Late collisions */
X};
X#define STINC(x) dpstat.x++
X#define STADD(x,y) dpstat.x += y
X#else
X#define STINC(x)        /* nothing */
X#define STADD(x,y)      /* nothing */
X#endif DPSTAT
+ END-OF-FILE dp8390stat.h
chmod 'u=rw,g=r,o=r' 'dp8390stat.h'
set `wc -c 'dp8390stat.h'`
count=$1
case $count in
821)	:;;
*)	echo 'Bad character count in ''dp8390stat.h' >&2
		echo 'Count should be 821' >&2
esac
echo Extracting 'eplinfo.h'
sed 's/^X//' > 'eplinfo.h' << '+ END-OF-FILE ''eplinfo.h'
X/*
X * parameters for initialisation of 
X * Western Digital Ethercard Plus, or WD8003E
X */
X
Xextern
Xstruct eplinfo {
X	vir_bytes	epi_devaddr;	/* device address */
X} eplinfo;
X
+ END-OF-FILE eplinfo.h
chmod 'u=rw,g=r,o=r' 'eplinfo.h'
set `wc -c 'eplinfo.h'`
count=$1
case $count in
181)	:;;
*)	echo 'Bad character count in ''eplinfo.h' >&2
		echo 'Count should be 181' >&2
esac
echo Extracting 'etherformat.h'
sed 's/^X//' > 'etherformat.h' << '+ END-OF-FILE ''etherformat.h'
X/* Format of packets on the Ethernet */
X
X#define AMOEBAPROTO 0x8145		/* Official Ethernet protocol number */
X
X#define ETHERBITS	0x80		/* These addresses on Ethernet */
X
Xtypedef struct
X{
X    char e[6];
X} Eth_addr;
X
Xtypedef struct
X{
X	Eth_addr	 f_dstaddr;
X	Eth_addr	 f_srcaddr;
X	unshort		 f_proto;
X	struct pktheader f_ah;
X} Framehdr;
X	
Xtypedef struct
X{
X	Framehdr	ep_fr;
X	char		ep_data[1490];
X} Etherpacket;
+ END-OF-FILE etherformat.h
chmod 'u=rw,g=r,o=r' 'etherformat.h'
set `wc -c 'etherformat.h'`
count=$1
case $count in
404)	:;;
*)	echo 'Bad character count in ''etherformat.h' >&2
		echo 'Count should be 404' >&2
esac
echo Extracting 'etherplus.c'
sed 's/^X//' > 'etherplus.c' << '+ END-OF-FILE ''etherplus.c'
X#include "../h/const.h"
X#include "../h/type.h"
X#include "../h/com.h"
X#include "const.h"
X#include "dp8390.h"
X#include "internet.h"
X#include "etherformat.h"
X#include "etherplus.h"
X#include "dp8390info.h"
X#include "eplinfo.h"
X#include "assert.h"
X
X/* macros for device I/O */
X
X#define input(devaddr, ep_register) \
X	inbyte((vir_bytes)&((struct eplusreg *) devaddr)->ep_register)
X#define output(devaddr, ep_register, value) \
X	outbyte((vir_bytes)&((struct eplusreg *) devaddr)->ep_register, value)
X
Xepl_init() {
X	register vir_bytes device;
X	register sum;
X
X	device = eplinfo.epi_devaddr;
X	assert((dp8390info.dpi_membase&0x81FFF)==0x80000);
X	sum =
X		input(device, epl_ea5) +
X		input(device, epl_ea4) +
X		input(device, epl_ea3) +
X		input(device, epl_ea2) +
X		input(device, epl_ea1) +
X		input(device, epl_ea0) +
X		input(device, epl_res2) +
X		input(device, epl_chksum);
X	if ((sum&0xFF) != 0xFF)
X		panic("No ethernet board", NO_NUM);
X	output(device, epl_ctlstatus, CTL_RESET);
X	output(device, epl_ctlstatus, CTL_MENABLE|((dp8390info.dpi_membase>>13)&CTL_MEMADDR));
X}
X
X
Xetheraddr(eaddr) Eth_addr *eaddr; {
X	register vir_bytes device;
X
X	device = eplinfo.epi_devaddr;
X	eaddr->e[0] = input(device, epl_ea0);
X	eaddr->e[1] = input(device, epl_ea1);
X	eaddr->e[2] = input(device, epl_ea2);
X	eaddr->e[3] = input(device, epl_ea3);
X	eaddr->e[4] = input(device, epl_ea4);
X	eaddr->e[5] = input(device, epl_ea5);
X}
+ END-OF-FILE etherplus.c
chmod 'u=rw,g=r,o=r' 'etherplus.c'
set `wc -c 'etherplus.c'`
count=$1
case $count in
1390)	:;;
*)	echo 'Bad character count in ''etherplus.c' >&2
		echo 'Count should be 1390' >&2
esac
echo Extracting 'etherplus.h'
sed 's/^X//' > 'etherplus.h' << '+ END-OF-FILE ''etherplus.h'
X/*
X * Western Digital Ethercard Plus, or WD8003E card
X *
X * This information seems to be guarded like the crown jewels
X */
X
Xstruct eplusreg {
X	char	epl_ctlstatus;		/* Control(write) and status(read)   */
X	char	epl_res1[7];
X	char	epl_ea0;		/* Most significant eaddr byte       */
X	char	epl_ea1;
X	char	epl_ea2;
X	char	epl_ea3;
X	char	epl_ea4;
X	char	epl_ea5;		/* Least significant eaddr byte      */
X	char	epl_res2;
X	char	epl_chksum;		/* sum from epl_ea0 upto here is 0xFF   */
X	dp8390	epl_dp8390;		/* NatSemi chip                      */
X};
X
X/* Bits in epl_ctlstatus */
X
X#define CTL_RESET	0x80		/* Software Reset                    */
X#define CTL_MENABLE	0x40		/* Memory Enable                     */
X#define CTL_MEMADDR	0x3F		/* Bits SA18-SA13, SA19 implicit 1   */
X
X#define STA_IIJ		0x7		/* Interrupt Indication Jumpers      */
+ END-OF-FILE etherplus.h
chmod 'u=rw,g=r,o=r' 'etherplus.h'
set `wc -c 'etherplus.h'`
count=$1
case $count in
826)	:;;
*)	echo 'Bad character count in ''etherplus.h' >&2
		echo 'Count should be 826' >&2
esac
echo Extracting 'exception.h'
sed 's/^X//' > 'exception.h' << '+ END-OF-FILE ''exception.h'
X#define CRASH		((unshort) 0xFF)
+ END-OF-FILE exception.h
chmod 'u=rw,g=r,o=r' 'exception.h'
set `wc -c 'exception.h'`
count=$1
case $count in
32)	:;;
*)	echo 'Bad character count in ''exception.h' >&2
		echo 'Count should be 32' >&2
esac
echo Extracting 'global.h'
sed 's/^X//' > 'global.h' << '+ END-OF-FILE ''global.h'
X/****************************************************************************/
X/*									    */
X/* (c) Copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands */
X/*									    */
X/*    This product is part of the  Amoeba  distributed operating system.    */
X/*									    */
X/*    Permission to use, sell, duplicate or disclose this software must be  */
X/* obtained in writing.  Requests for such permissions may be sent to	    */
X/*									    */
X/*									    */
X/*		Dr. Andrew S. Tanenbaum					    */
X/*		Dept. of Mathematics and Computer Science		    */
X/*		Vrije Universiteit					    */
X/*		Postbus 7161						    */
X/*		1007 MC Amsterdam					    */
X/*		The Netherlands						    */
X/*									    */
X/****************************************************************************/
X
X#define KERNEL		0
X#define USER		1
X
Xtypedef unshort address;
Xtypedef int func;
X
X#define bufptr		vir_bytes
X
X#define NOWHERE		((address) 0)
X#define SOMEWHERE	((address) -1)
X#define NILVECTOR	((func (*)()) 0)
X
X#ifdef lint
X#define ABSPTR(t, c)	(use(c), (t) 0)
X#else
X#define ABSPTR(t, c)	((t) (c))
X#endif
X
X#define bit(b)		(1 << (b))	/* simulate type 'bit' */
X
X#define lobyte(x)	((unshort) (x) & 0xFF)
X#define hibyte(x)	((unshort) (x) >> 8)
X#define concat(x, y)	((unshort) (x) << 8 | (unshort) (y) & 0xFF)
X
X#define sizeoftable(t)	(sizeof(t) / sizeof((t)[0]))
+ END-OF-FILE global.h
chmod 'u=rw,g=r,o=r' 'global.h'
set `wc -c 'global.h'`
count=$1
case $count in
1354)	:;;
*)	echo 'Bad character count in ''global.h' >&2
		echo 'Count should be 1354' >&2
esac
echo Extracting 'internet.h'
sed 's/^X//' > 'internet.h' << '+ END-OF-FILE ''internet.h'
X/****************************************************************************/
X/*									    */
X/* (c) Copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands */
X/*									    */
X/*    This product is part of the  Amoeba  distributed operating system.    */
X/*									    */
X/*    Permission to use, sell, duplicate or disclose this software must be  */
X/* obtained in writing.  Requests for such permissions may be sent to	    */
X/*									    */
X/*									    */
X/*		Dr. Andrew S. Tanenbaum					    */
X/*		Dept. of Mathematics and Computer Science		    */
X/*		Vrije Universiteit					    */
X/*		Postbus 7161						    */
X/*		1007 MC Amsterdam					    */
X/*		The Netherlands						    */
X/*									    */
X/****************************************************************************/
X
X#define PACKETSIZE	1490	/* network packet size - sizeof(framehdr) */
X
X#define BROADCAST	((address) 0xFF)
X
X#define TYPE		0x0F	/* message types */
X#define   LOCATE	0x01
X#define   HERE		0x02
X#define   REQUEST	0x03
X#define   REPLY		0x04
X#define   ACK		0x05
X#define   NAK		0x06
X#define	  ENQUIRY	0x07
X#define   ALIVE		0x08
X#define	  DEAD		0x09
X
X#define LAST		0x10	/* flags */
X#define RETRANS		0x20
X
Xstruct pktheader {
X	char	ph_dstnode;	/* 0: destination node */
X	char	ph_srcnode;	/* 1: source node */
X	char	ph_dsttask;	/* 2: destination task */
X	char	ph_srctask;	/* 3: source task */
X	char	ph_ident;	/* 4: transaction identifier */
X	char	ph_seq;		/* 5: fragment no. */
X	unshort	ph_size;	/* 6: total size of this packet */
X	char	ph_flags;	/* 8: some flags (not used) */
X	char	ph_type;	/* 9: locate, here, data, ack or nak (!= 0) */
X};
X
X#define ph_signal	ph_seq
X
X#define NOSEND		0
X#define SEND		1
X
X#define DONTKNOW	0
X#define LOCAL		1
X#define GLOBAL		2
X
X#define siteaddr(x)	lobyte(x)
X#define tasknum(x)	hibyte(x)
X
X#define pktfrom(ph)	((unshort) (ph->ph_srctask<<8 | ph->ph_srcnode & 0xFF))
X#define pktto(ph)	((unshort) (ph->ph_dsttask<<8 | ph->ph_dstnode & 0xFF))
+ END-OF-FILE internet.h
chmod 'u=rw,g=r,o=r' 'internet.h'
set `wc -c 'internet.h'`
count=$1
case $count in
1958)	:;;
*)	echo 'Bad character count in ''internet.h' >&2
		echo 'Count should be 1958' >&2
esac
echo Extracting 'makefile'
sed 's/^X//' > 'makefile' << '+ END-OF-FILE ''makefile'
X# The kernel dir contains xt_wini.c and at_wini.c.  Before running make
X# you must copy one of these to wini.c, depending on which controller you
X# have.  If you do not have a hard disk, you MUST choose one of them at random.
X# On a PC, cpp and cem are in /lib and will be removed to make space while
X# linking the kernel.  On an AT, they are in /usr/lib are are not removed.
X# This is because they have to be in /lib on a PC; the diskette is too small
X# for them to be in /usr/lib.  You can change this by editing commands/cc.c.
X#
X# Normally, MINIX scrolls the screen using the 6845's registers.  However,
X# on some EGA cards (those that are not IBM compatible), the 6845 is not
X# properly emulated.  On these machines, it is necessary to scroll in
X# software by copying. This is much slower, but it works. The CFLAGS flags are:
X#
X#  -Di8088	- required on an 8088/80286/80386 CPU; forbidden on a 68000
X#  -F		- run cpp and cem sequentially (used when memory is tight)
X#  -T.		- put temporaries in working directory (when RAM disk is small)
X#  -DAM_KERNEL	- includes code for Amoeba transactions
X#  -DNONET	- single machine, no Ethernet available (requires -DAM_KERNEL)
X#  -DSTATISTICS	- include code to collect Ethernet statistics
X#
X# Machines wishing to use Amoeba transactions should add -DAM_KERNEL to CFLAGS.
X# If there is no ethernet connection then also add -DNONET.  The ethernet
X# driver included in this distribution is for the Western Digital Ethercard
X# Plus.  If you want ethernet statistics then add -DSTATISTICS to CFLAGS.
X#
X# When making a kernel on a machine with limited RAM disk, it may be 
X# necessary to use -T. or even remove /lib/cem and /lib/cpp during asld.
X#
XCFLAGS	= -DAM_KERNEL -Di8088  -F -I../../kernel -I.
XA	= ../h
XH	= ../../h
XK	= ../../kernel
XLIB	= /usr/lib
X
XOBJ =	../../kernel/mpx88.s main.s tty.s floppy.s wini.s system.s proc.s \
X	clock.s memory.s printer.s amoeba.s conf.s dp8390.s util.s \
X	etherplus.s portcache.s trans.s table.s ../../kernel/klib88.s dmp.s
X
X
XCOBJS =	main.s tty.s floppy.s wini.s system.s proc.s clock.s memory.s \
X	amoeba.s conf.s dp8390.s util.s etherplus.s portcache.s trans.s \
X	printer.s table.s dmp.s
X
X
Xkernel:	makefile $(OBJ) $(LIB)/libc.a
X	@echo "Start linking Kernel."
X	@asld -o kernel $(OBJ) $(LIB)/libc.a $(LIB)/end.s
X	@echo "Kernel done.  "
X
Xclean:
X	rm -f $(COBJS) kernel
X
Xclock.s:	$K/const.h $K/type.h $H/const.h $H/type.h
Xclock.s:	$H/callnr.h
Xclock.s:	$H/com.h
Xclock.s:	$H/error.h
Xclock.s:	$H/signal.h
Xclock.s:	$K/glo.h
Xclock.s:	$K/proc.h
Xclock.s:	$K/clock.c
X	$(CC) $(CFLAGS) -c $K/clock.c
X
Xdmp.s:		$K/const.h $K/type.h $H/const.h $H/type.h
Xdmp.s:		$H/callnr.h
Xdmp.s:		$H/com.h
Xdmp.s:		$H/error.h
Xdmp.s:		$K/glo.h
Xdmp.s:		$K/proc.h
Xdmp.s:		$K/dmp.c
X	$(CC) $(CFLAGS) -c $K/dmp.c
X
Xfloppy.s:	$K/const.h $K/type.h $H/const.h $H/type.h
Xfloppy.s:	$H/callnr.h
Xfloppy.s:	$H/com.h
Xfloppy.s:	$H/error.h
Xfloppy.s:	$K/glo.h
Xfloppy.s:	$K/proc.h
Xfloppy.s:	$K/floppy.c
X	$(CC) $(CFLAGS) -c $K/floppy.c
X
Xmain.s:		$K/const.h $K/type.h $H/const.h $H/type.h
Xmain.s:		$H/callnr.h
Xmain.s:		$H/com.h
Xmain.s:		$H/error.h
Xmain.s:		$K/glo.h
Xmain.s:		$K/proc.h
Xmain.s:		$K/main.c
X	$(CC) $(CFLAGS) -c $K/main.c
X
Xmemory.s:	$K/const.h $K/type.h $H/const.h $H/type.h
Xmemory.s:	$H/callnr.h
Xmemory.s:	$H/com.h
Xmemory.s:	$H/error.h
Xmemory.s:	$K/proc.h
Xmemory.s:	$K/memory.c
X	$(CC) $(CFLAGS) -c $K/memory.c
X
Xprinter.s:	$K/const.h $K/type.h $H/const.h $H/type.h
Xprinter.s:	$H/callnr.h
Xprinter.s:	$H/com.h
Xprinter.s:	$H/error.h
Xprinter.s:	$K/proc.h
Xprinter.s:	$K/glo.h
Xprinter.s:	$K/printer.c
X	$(CC) $(CFLAGS) -c $K/printer.c
X
Xproc.s:		$K/const.h $K/type.h $H/const.h $H/type.h
Xproc.s:		$H/callnr.h
Xproc.s:		$H/com.h
Xproc.s:		$H/error.h
Xproc.s:		$K/glo.h
Xproc.s:		$K/proc.h
Xproc.s:		$K/proc.c
X	$(CC) $(CFLAGS) -c $K/proc.c
X
Xsystem.s:	$K/const.h $K/type.h $H/const.h $H/type.h
Xsystem.s:	$H/callnr.h
Xsystem.s:	$H/com.h
Xsystem.s:	$H/error.h
Xsystem.s:	$H/signal.h
Xsystem.s:	$K/glo.h
Xsystem.s:	$K/proc.h
Xsystem.s:	$K/system.c
X	$(CC) $(CFLAGS) -c $K/system.c
X
Xtable.s:	$K/const.h $K/type.h $H/const.h $H/type.h $H/com.h
Xtable.s:	$K/glo.h
Xtable.s:	$K/proc.h
Xtable.s:	$K/table.c
X	$(CC) $(CFLAGS) -c $K/table.c
X
Xtty.s:	$K/const.h $K/type.h $H/const.h $H/type.h
Xtty.s:	$H/callnr.h
Xtty.s:	$H/com.h
Xtty.s:	$H/error.h
Xtty.s:	$H/sgtty.h
Xtty.s:	$H/signal.h
Xtty.s:	$K/glo.h
Xtty.s:	$K/proc.h
Xtty.s:	$K/tty.c
X	$(CC) $(CFLAGS) -c $K/tty.c
X
Xwini.s:	$K/const.h $K/type.h $H/const.h $H/type.h
Xwini.s:	$H/callnr.h
Xwini.s:	$H/com.h
Xwini.s:	$H/error.h
Xwini.s:	$K/proc.h
Xwini.s:	$K/wini.c
X	$(CC) $(CFLAGS) -c $K/wini.c
X
Xamoeba.s: $A/amoeba.h
Xamoeba.s: $A/host_os.h
Xamoeba.s: $A/amparam.h
Xamoeba.s: $H/signal.h
Xamoeba.s: $H/type.h
Xamoeba.s: ./assert.h
Xamoeba.s: ./byteorder.h
Xamoeba.s: $K/const.h
Xamoeba.s: ./dp8390info.h
Xamoeba.s: ./etherformat.h
Xamoeba.s: $K/glo.h
Xamoeba.s: ./global.h
Xamoeba.s: ./internet.h
Xamoeba.s: ./mpx.H
Xamoeba.s: ./portcache.H
Xamoeba.s: $K/proc.h
Xamoeba.s: ./task.h
Xamoeba.s: ./trans.H
Xamoeba.s: $K/type.h
Xamoeba.s: amoeba.c
Xconf.s: $A/amoeba.h
Xconf.s: $A/host_os.h
Xconf.s: $H/type.h
Xconf.s: ./conf.h
Xconf.s: ./global.h
Xconf.s: ./mpx.H
Xconf.s: ./portcache.H
Xconf.s: ./task.h
Xconf.s: ./trans.H
Xconf.s: conf.c
Xdp8390.s: $H/com.h
Xdp8390.s: $H/const.h
Xdp8390.s: $H/error.h
Xdp8390.s: $H/type.h
Xdp8390.s: ./assert.h
Xdp8390.s: $K/const.h
Xdp8390.s: ./dp8390.h
Xdp8390.s: ./dp8390info.h
Xdp8390.s: ./dp8390stat.h
Xdp8390.s: ./etherformat.h
Xdp8390.s: ./internet.h
Xdp8390.s: dp8390.c
Xetherplus.s: $H/com.h
Xetherplus.s: $H/const.h
Xetherplus.s: $H/type.h
Xetherplus.s: ./assert.h
Xetherplus.s: $K/const.h
Xetherplus.s: ./dp8390.h
Xetherplus.s: ./dp8390info.h
Xetherplus.s: ./eplinfo.h
Xetherplus.s: ./etherformat.h
Xetherplus.s: ./etherplus.h
Xetherplus.s: ./internet.h
Xetherplus.s: etherplus.c
Xportcache.s: $A/amoeba.h
Xportcache.s: $H/const.h
Xportcache.s: $A/host_os.h
Xportcache.s: $H/type.h
Xportcache.s: ./assert.h
Xportcache.s: ./conf.h
Xportcache.s: $K/const.h
Xportcache.s: ./global.h
Xportcache.s: ./internet.h
Xportcache.s: ./mpx.H
Xportcache.s: ./portcache.H
Xportcache.s: ./task.h
Xportcache.s: ./trans.H
Xportcache.s: portcache.c
Xtrans.s: $A/amoeba.h
Xtrans.s: $H/const.h
Xtrans.s: $A/host_os.h
Xtrans.s: $H/type.h
Xtrans.s: ./amstat.h
Xtrans.s: ./assert.h
Xtrans.s: ./byteorder.h
Xtrans.s: $K/const.h
Xtrans.s: ./exception.h
Xtrans.s: ./global.h
Xtrans.s: ./internet.h
Xtrans.s: ./mpx.H
Xtrans.s: ./portcache.H
Xtrans.s: ./task.h
Xtrans.s: ./trans.H
Xtrans.s: trans.c
Xutil.s: $H/com.h
Xutil.s: $H/const.h
Xutil.s: $H/type.h
Xutil.s: ./assert.h
Xutil.s: $K/const.h
Xutil.s: ./dp8390.h
Xutil.s: ./dp8390info.h
Xutil.s: ./eplinfo.h
Xutil.s: $K/proc.h
Xutil.s: $K/type.h
Xutil.s: util.c
+ END-OF-FILE makefile
chmod 'u=rw,g=r,o=r' 'makefile'
set `wc -c 'makefile'`
count=$1
case $count in
6567)	:;;
*)	echo 'Bad character count in ''makefile' >&2
		echo 'Count should be 6567' >&2
esac
echo Extracting 'mpx.H'
sed 's/^X//' > 'mpx.H' << '+ END-OF-FILE ''mpx.H'
X/****************************************************************************/
X/*									    */
X/* (c) Copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands */
X/*									    */
X/*    This product is part of the  Amoeba  distributed operating system.    */
X/*									    */
X/*    Permission to use, sell, duplicate or disclose this software must be  */
X/* obtained in writing.  Requests for such permissions may be sent to	    */
X/*									    */
X/*									    */
X/*		Dr. Andrew S. Tanenbaum					    */
X/*		Dept. of Mathematics and Computer Science		    */
X/*		Vrije Universiteit					    */
X/*		Postbus 7161						    */
X/*		1007 MC Amsterdam					    */
X/*		The Netherlands						    */
X/*									    */
X/****************************************************************************/
X
Xstruct mpx {
X	short	MX_active;		/* is a transaction in progress */
X	unshort	MX_flags;		/* flags - see below */
X	int	MX_proc_nr;		/* task identifier */
X	header	MX_hdr;			/* storage space for header */
X} tk_mpx;
X
X#ifdef MPX
X
X#define mx_flags	tk_mpx.MX_flags
X#define mx_active	tk_mpx.MX_active
X#define mx_proc_nr	tk_mpx.MX_proc_nr
X#define	mx_hdr		tk_mpx.MX_hdr
X
X
X/* bits in flags: */
X#define RUNNABLE	bit(0)		/* task is runnable */
X#define NESTED		bit(1)		/* nested getreq, trans or putrep */
X#define	BETWEEN		bit(2)		/* between getreq and putrep */
X#else
X
X#define tk_mpx		tk_dummy	/* other modules must not touch it */
X
X#endif
+ END-OF-FILE mpx.H
chmod 'u=rw,g=r,o=r' 'mpx.H'
set `wc -c 'mpx.H'`
count=$1
case $count in
1427)	:;;
*)	echo 'Bad character count in ''mpx.H' >&2
		echo 'Count should be 1427' >&2
esac
echo Extracting 'portcache.H'
sed 's/^X//' > 'portcache.H' << '+ END-OF-FILE ''portcache.H'
X/****************************************************************************/
X/*									    */
X/* (c) Copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands */
X/*									    */
X/*    This product is part of the  Amoeba  distributed operating system.    */
X/*									    */
X/*    Permission to use, sell, duplicate or disclose this software must be  */
X/* obtained in writing.  Requests for such permissions may be sent to	    */
X/*									    */
X/*									    */
X/*		Dr. Andrew S. Tanenbaum					    */
X/*		Dept. of Mathematics and Computer Science		    */
X/*		Vrije Universiteit					    */
X/*		Postbus 7161						    */
X/*		1007 MC Amsterdam					    */
X/*		The Netherlands						    */
X/*									    */
X/****************************************************************************/
X
Xstruct portcache {
X	address PE_location;
X	struct task *PE_link;
X} tk_portcache;
X
X#ifdef PORTCACHE
X
X#define pe_location	tk_portcache.PE_location
X#define pe_link		tk_portcache.PE_link
X
X#else 
X
X#define tk_portcache	tk_dummy	/* other modules must not touch it */
X
X#endif
X
X#define NOWAIT		0
X#define WAIT		1
X
X#define LOOK		0
X#define DELETE		1
+ END-OF-FILE portcache.H
chmod 'u=rw,g=r,o=r' 'portcache.H'
set `wc -c 'portcache.H'`
count=$1
case $count in
1139)	:;;
*)	echo 'Bad character count in ''portcache.H' >&2
		echo 'Count should be 1139' >&2
esac
echo Extracting 'portcache.c'
sed 's/^X//' > 'portcache.c' << '+ END-OF-FILE ''portcache.c'
X/****************************************************************************/
X/*									    */
X/* (c) Copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands */
X/*									    */
X/*    This product is part of the  Amoeba  distributed operating system.    */
X/*									    */
X/*    Permission to use, sell, duplicate or disclose this software must be  */
X/* obtained in writing.  Requests for such permissions may be sent to	    */
X/*									    */
X/*									    */
X/*		Dr. Andrew S. Tanenbaum					    */
X/*		Dept. of Mathematics and Computer Science		    */
X/*		Vrije Universiteit					    */
X/*		Postbus 7161						    */
X/*		1007 MC Amsterdam					    */
X/*		The Netherlands						    */
X/*									    */
X/****************************************************************************/
X
X#define NDEBUG
X#define PORTCACHE
X
X/*
X * This module does the port management. It keeps track of the local servers
X * doing a ``getreq'' on a port, local clients waiting for a server on some
X * port, and interlocal servers addressed by some port. This last category of
X * ports may be forgotten, or may be incorrect.
X *
X * The following routines are defined:
X *	portinstall(port, where, wait);
X *	portlookup(port, wait, delete);
X *	portremove(port, location);
X *	portquit(port, task);
X *
X * ``Portinstall'' is called when a port is assumed to be at location
X * ``where.'' If ``wait'' is set, the port is local.
X * ``Portlookup'' is called to find a port in the cache. If ``wait'' is
X * set, the routine will block until the port is found. If ``delete'' is
X * set, the port must be removed when it is found.
X * ``Portremove'' removes a port from the cache which is thought
X * of to be at the specified location.
X * When a port doesn't have to be located anymore for some task, ``portquit''
X * takes care of that.
X */
X
X#include "../h/const.h"
X#include "../h/type.h"
X#include "../h/amoeba.h"
X#include "const.h"
X#include "global.h"
X#include "task.h"
X#include "internet.h"
X#include "assert.h"
X
Xextern struct task	task[];
X
X#ifdef STATISTICS
X#include "portstat.h"
X
Xstruct portstat portstat;
X#define STINC(x)	portstat.x++
X#else
X#define STINC(x)
X#endif
X
X#include "conf.h"
X
X#define LOGHASH		   5	/* log sizeof hash table of local ports */
X
Xstruct porttab {
X	port p_port;			/* the port this entry is about */
X	unshort p_idle;			/* idle timer */
X	address p_location;		/* where is it? (0 = being located) */
X	address p_asker;		/* address of someone interested */
X	struct porttab *p_nextport;	/* port with same hash value */
X	struct task *p_tasklist;	/* list of tasks */
X};
X
X#define NILPORTTAB	((struct porttab *) 0)
X
X#define NHASH		(1 << LOGHASH)
X#define HASHMASK	(NHASH - 1)
X
X#define hash(p)		(* (unshort *) (p) & HASHMASK)
X
X/* MINIX can't allocate memory in the kernel at run-time
Xextern unshort nport;
XPRIVATE struct porttab *porttab, *lastport, *hashtab[NHASH], *portfree;
X*/
XPRIVATE struct porttab porttab[NPORTS];
XPRIVATE struct porttab *lastport, *hashtab[NHASH], *portfree;
X
X#ifndef NONET
X#ifndef NOCLIENT
X
X#define NLOCATE            8    /* max. number of ports to locate */
XPRIVATE port loctab[NLOCATE];
XPRIVATE unshort loccnt, loctim, locthissweep;
Xextern unshort minloccnt, maxloccnt;
X
X#endif
X#endif
X
X/* Allocate an entry in the hash table at location ``ht.'' */
Xstatic struct porttab *allocate(ht, p)
Xstruct porttab **ht;
Xport *p;
X{
X  register struct porttab *pt;
X
X  STINC(pts_alloc);
X  if ((pt=portfree) == 0) {
X	STINC(pts_full);
X	portpurge();		/* total cleanup, not likely to happen */
X	if ((pt=portfree) == 0)
X		return 0;
X  }
X  portfree = pt->p_nextport;
X  pt->p_nextport = *ht;
X  *ht = pt;
X  pt->p_port = *p;
X  return pt;
X}
X
X/* Install a port in the hash table.  If ``wait'' is set, the location will
X * be this machine and is certain.  If not, the location is somewhere else
X * and uncertain.
X */
Xportinstall(p, where, wait)
Xregister port *p;
Xaddress where;
X{
X  register struct porttab **ht, *pt;
X  register struct task *t;
X  extern address local;
X
X  ht = &hashtab[hash(p)];
X  for (pt = *ht; pt != NILPORTTAB; pt = pt->p_nextport)
X	if (PortCmp(&pt->p_port, p)) {
X		if (pt->p_location == SOMEWHERE) {
X			compare(pt->p_tasklist, !=, NILTASK);
X			do {
X				t = pt->p_tasklist;
X				t->pe_location = where;
X				STINC(pts_wakeup);
X				wakeup((event_t) &t->pe_location);
X			} while ((pt->p_tasklist = t->pe_link) != NILTASK);
X		}
X#ifndef NOCLIENT
X		else if (siteaddr(pt->p_location) == local && !wait &&
X							pt->p_tasklist != 0)
X			return;
X#endif
X		break;
X	}
X  if (pt == NILPORTTAB && (pt = allocate(ht, p)) == NILPORTTAB)
X#ifndef NOCLIENT
X	if (wait)
X#endif
X		panic("portcache full for servers", 0x8000);
X#ifndef NOCLIENT
X	else		/* no room left, so forget it */
X		return;
X#endif
X  pt->p_location = where;
X#ifndef NOCLIENT
X  if (wait) {	/* task is going to await a client, so leave it immortal */
X#endif
X	compare(area(where), ==, LOCAL);
X	t = &task[tasknum(where)];
X	t->pe_location = where;
X	t->pe_link = pt->p_tasklist;
X	pt->p_tasklist = t;
X#ifndef NONET
X	if (pt->p_asker != NOWHERE) {
X		STINC(pts_here);
X		hereport(pt->p_asker, p, (unsigned )1);
X		pt->p_asker = NOWHERE;
X	}
X#endif
X#ifndef NOCLIENT
X  }
X#endif
X  pt->p_idle = 0;
X}
X
X#ifndef NONET
X#ifndef NOCLIENT
X/* Broadcast a locate message
X */
Xstatic sendloc(){
X  register struct porttab *pt;
X  register unsigned n = 0;
X
X  if (locthissweep) {
X	/* During this clocktick we already sent out a broadcast packet.
X	 * To prevent buggy userprograms from creating a broadcast storm
X	 * we do not send another one, we just prepare for it to be done 
X	 */
X	 STINC(pts_nolocate);
X	 loccnt = maxloccnt;
X	 return;
X  }
X  for (pt = porttab; pt < lastport; pt++)
X	if (pt->p_location == SOMEWHERE) {
X		loctab[n++] = pt->p_port;
X		if (n == NLOCATE)
X			break;
X	}
X  if (n) {
X	locthissweep = 1;
X	whereport(loctab, n);	/* send out the broadcast locate */
X  } else
X	loctim = 0;	/* No locates necessary anymore */
X  loccnt = 0;
X}
X
X#endif NOCLIENT
X#endif NONET
X
X/* Look whether port p is in the portcache.  You can specify whether you
X * want to wait for the information and whether you want to delete it.
X */
Xaddress portlookup(p, wait, del)
Xregister port *p;
X{
X  register struct porttab **ht, *pt;
X  register struct task *c, *t;
X  register address location;
X
X  STINC(pts_lookup);
X  ht = &hashtab[hash(p)];
X  for (pt = *ht; pt != NILPORTTAB; pt = pt->p_nextport)
X	if (PortCmp(&pt->p_port, p)) {	/* found it */
X		location = pt->p_location;
X		switch (area(location)) {
X		case LOCAL:		/* local server */
X			if (pt->p_tasklist == 0)
X				break;
X			if (del) {	/* remove it */
X				pt->p_tasklist = pt->p_tasklist->pe_link;
X				if ((t = pt->p_tasklist) != NILTASK)
X					pt->p_location = t->pe_location;
X			}
X			pt->p_idle = 0;
X			STINC(pts_flocal);
X			return(location);
X
X		case GLOBAL:		/* remote server */
X			compare(pt->p_tasklist, ==, NILTASK);
X			pt->p_idle = 0;
X			STINC(pts_fglobal);
X			return(location);
X		
X		case DONTKNOW:		/* somebody else wants to know too */
X			compare(pt->p_tasklist, !=, NILTASK);
X			break;
X		
X		default:
X			assert(0);
X		}
X		break;
X	}
X  /* The port wasn't in the port cache */
X#ifndef NOCLIENT
X  if (wait) {		/* wait for it */
X	if (pt == NILPORTTAB && (pt = allocate(ht, p)) == NILPORTTAB)
X		panic("portcache full for clients", 0x8000);
X	pt->p_location = SOMEWHERE;
X	c = curtask;
X	c->pe_link = pt->p_tasklist;
X	pt->p_tasklist = c;
X#ifndef NONET
X	STINC(pts_locate);
X	sendloc();
X	loctim = minloccnt;
X#endif
X	c->pe_location = SOMEWHERE;
X	if (sleep((event_t) &c->pe_location))
X		assert(pt->p_tasklist != c);
X	pt->p_idle = 0;
X	return(c->pe_location);
X  }
X  else
X#endif NOCLIENT
X	return(NOWHERE);
X}
X
X/* Port p isn't at location ``location'' anymore */
Xportremove(p, location)
Xport *p;
Xaddress location;
X{
X  register struct porttab *pt, **ht;
X  register struct task *t;
X
X  for (ht = &hashtab[hash(p)]; (pt= *ht) != NILPORTTAB; ht = &pt->p_nextport)
X	if (PortCmp(&pt->p_port, p)) {
X		if (pt->p_location == location) {
X			if ((t = pt->p_tasklist) != NILTASK) {
X				compare(area(location), ==, LOCAL);
X				compare(t->pe_location, ==, location);
X				if ((pt->p_tasklist = t->pe_link) != NILTASK) {
X					pt->p_location =
X						pt->p_tasklist->pe_location;
X					break;
X				}
X			} else {
X				*ht = pt->p_nextport;
X				pt->p_location = NOWHERE;	/* for dump */
X				pt->p_nextport = portfree;
X				portfree = pt;
X			}
X		}
X		else if ((t = pt->p_tasklist) != NILTASK)
X			while (t->pe_link != NILTASK)
X				if (t->pe_link->pe_location == location) {
X					t->pe_link = t->pe_link->pe_link;
X					break;
X				}
X				else
X					t = t->pe_link;
X		return;
X	}
X}
X
X#ifndef NOCLIENT
X/* Task ``s'' isn't interested anymore */
Xportquit(p, s)
Xregister port *p;
Xregister struct task *s;
X{
X  register struct porttab *pt, **ht;
X  register struct task *t;
X
X  for (ht = &hashtab[hash(p)]; (pt= *ht) != NILPORTTAB; ht = &pt->p_nextport)
X	if (PortCmp(&pt->p_port, p)) {
X		if (pt->p_location != SOMEWHERE)
X			return;
X		if ((t = pt->p_tasklist) == s) {
X			if ((pt->p_tasklist = s->pe_link) == NILTASK) {
X				*ht = pt->p_nextport;
X				pt->p_location = NOWHERE;	/* for dump */
X				pt->p_nextport = portfree;
X				portfree = pt;
X			}
X			s->pe_location = NOWHERE;
X			wakeup((event_t) &s->pe_location);
X		}
X		else {
X			while (t != NILTASK)
X				if (t->pe_link == s) {
X					t->pe_link = s->pe_link;
X					s->pe_location = NOWHERE;
X					wakeup((event_t) &s->pe_location);
X					break;
X				}
X				else
X					t = t->pe_link;
X		}
X		return;
X	}
X}
X#endif NOCLIENT
X
X#ifndef NONET
X
X#define NHERE 8
XPRIVATE port heretab[NHERE];
X
Xportask(asker, ptab, nports)		/* handle locate request */
Xaddress asker;
Xregister port *ptab;
Xunsigned nports;
X{
X  register port *p,*q;
X  register struct porttab **ht, *pt;
X  register unsigned nfound;
X
X  STINC(pts_portask);
X  nfound = 0; q = heretab;
X  for(p=ptab; nports--; p++) {
X	ht = &hashtab[hash(p)];
X	for (pt = *ht; pt != NILPORTTAB; pt = pt->p_nextport)
X		if (PortCmp(&pt->p_port, p)) {	/* found it */
X			if (area(pt->p_location) == LOCAL) {
X				if (pt->p_tasklist == 0) {
X					/* just record someone was interested */
X					pt->p_asker = asker;
X					break;
X				}
X				if (nfound < NHERE) {
X					*q++ = *p;
X					nfound++;
X				}
X				pt->p_idle = 0;
X			}
X		}
X  }
X  if (nfound) {
X	STINC(pts_portyes);
X	hereport(asker, heretab, nfound);
X  }
X}
X#endif
X
X#ifndef NDEBUG
Xportdump(){
X  register struct porttab *pt;
X  register struct task *t;
X
X  printf("\n PORT  LOCATION IDLE TASKS\n");
X  for (pt = porttab; pt < lastport; pt++)
X	if (pt->p_location != NOWHERE) {
X		prport(&pt->p_port);
X		printf("   %4x   %4d", pt->p_location, pt->p_idle);
X		for (t = pt->p_tasklist; t != NILTASK; t = t->pe_link)
X			printf(" {%d, %x}", t - task, t->pe_location);
X		printf("\n");
X	}
X}
X#endif
X
X/* Initialize tables and free list */
Xportinit(){
X  register struct porttab *pt;
X
X/*  MINIX can't allocate data in the kernel at run-time
X  extern char *aalloc();
X
X  porttab = (struct porttab *) aalloc(nport * sizeof(struct porttab), 0);
X  lastport = &porttab[nport];
X*/
X  lastport = &porttab[NPORTS];
X  for (pt = porttab; pt < lastport; pt++) {
X	pt->p_nextport = portfree;
X	portfree = pt;
X  }
X}
X
X/* called when freelist was empty, will throw away all mortal ports */
Xportpurge() {
X  register struct porttab *pt,**ht,**htp;
X 
X  for (htp=hashtab; htp< &hashtab[NHASH]; htp++) {
X	ht = htp;
X	while ((pt = *ht) != 0) {
X		if (pt->p_tasklist == 0){
X			*ht = pt->p_nextport;
X			pt->p_location = NOWHERE;	/* for dump */
X			pt->p_nextport = portfree;
X			portfree = pt;
X		} else
X			ht = &pt->p_nextport;
X	}
X  }
X}
X
X#define MAXSWEEP	 3000	/* dseconds maximum idle time for port */
X#define SWEEPRESOLUTION	  100	/* accuracy */
X
Xportsweep() {
X  register struct porttab *pt,**ht,**htp;
X  static unshort cnt;
X
X#ifndef NOCLIENT
X#ifndef NONET
X  locthissweep = 0;
X  if (loctim && ++loccnt > loctim) {              /* send a locate message */
X	STINC(pts_relocate);
X        sendloc();
X        loctim <<= 1;
X        if (loctim > maxloccnt)
X                loctim = maxloccnt;
X	locthissweep = 0;
X  }
X#endif NONET
X#endif
X  if (++cnt<SWEEPRESOLUTION)
X	return;
X  for (htp=hashtab; htp< &hashtab[NHASH]; htp++) {
X	ht = htp;
X	while ((pt = *ht) != 0) {
X		if (pt->p_tasklist == 0 && (pt->p_idle += cnt) > MAXSWEEP) {
X			*ht = pt->p_nextport;
X			pt->p_location = NOWHERE;	/* for dump */
X			pt->p_nextport = portfree;
X			portfree = pt;
X			STINC(pts_aged);
X		} else
X			ht = &pt->p_nextport;
X	}
X  }
X  cnt=0;
X}
+ END-OF-FILE portcache.c
chmod 'u=rw,g=r,o=r' 'portcache.c'
set `wc -c 'portcache.c'`
count=$1
case $count in
12260)	:;;
*)	echo 'Bad character count in ''portcache.c' >&2
		echo 'Count should be 12260' >&2
esac
echo Extracting 'portstat.h'
sed 's/^X//' > 'portstat.h' << '+ END-OF-FILE ''portstat.h'
Xstruct portstat {
X	long pts_alloc;
X	long pts_aged;
X	long pts_full;
X	long pts_wakeup;
X	long pts_here;
X	long pts_lookup;
X	long pts_flocal;
X	long pts_fglobal;
X	long pts_portask;
X	long pts_portyes;
X	long pts_locate;
X	long pts_nolocate;
X	long pts_relocate;
X};
+ END-OF-FILE portstat.h
chmod 'u=rw,g=r,o=r' 'portstat.h'
set `wc -c 'portstat.h'`
count=$1
case $count in
255)	:;;
*)	echo 'Bad character count in ''portstat.h' >&2
		echo 'Count should be 255' >&2
esac
echo Extracting 'task.h'
sed 's/^X//' > 'task.h' << '+ END-OF-FILE ''task.h'
X/****************************************************************************/
X/*									    */
X/* (c) Copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands */
X/*									    */
X/*    This product is part of the  Amoeba  distributed operating system.    */
X/*									    */
X/*    Permission to use, sell, duplicate or disclose this software must be  */
X/* obtained in writing.  Requests for such permissions may be sent to	    */
X/*									    */
X/*									    */
X/*		Dr. Andrew S. Tanenbaum					    */
X/*		Dept. of Mathematics and Computer Science		    */
X/*		Vrije Universiteit					    */
X/*		Postbus 7161						    */
X/*		1007 MC Amsterdam					    */
X/*		The Netherlands						    */
X/*									    */
X/****************************************************************************/
X
X#ifdef BUFFERED				/* HACK */
X#define BUFSIZE		100
X#define NETBUF		((buffer) -1)
X
Xtypedef unshort buffer;
X#endif
X
Xstruct task {
X
X#include "mpx.H"			/* mpx module */
X#include "trans.H"			/* trans module */
X#include "portcache.H"			/* portcache module */
X	char *tk_aux;			/* auxiliary pointer */
X	/* really a hack to make process task more efficient */
X
X};
X
Xextern struct task *curtask, *uppertask;
Xextern unshort ntask;
X
X#define NILTASK		((struct task *) 0)
+ END-OF-FILE task.h
chmod 'u=rw,g=r,o=r' 'task.h'
set `wc -c 'task.h'`
count=$1
case $count in
1259)	:;;
*)	echo 'Bad character count in ''task.h' >&2
		echo 'Count should be 1259' >&2
esac
echo Extracting 'trans.H'
sed 's/^X//' > 'trans.H' << '+ END-OF-FILE ''trans.H'
X/****************************************************************************/
X/*									    */
X/* (c) Copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands */
X/*									    */
X/*    This product is part of the  Amoeba  distributed operating system.    */
X/*									    */
X/*    Permission to use, sell, duplicate or disclose this software must be  */
X/* obtained in writing.  Requests for such permissions may be sent to	    */
X/*									    */
X/*									    */
X/*		Dr. Andrew S. Tanenbaum					    */
X/*		Dept. of Mathematics and Computer Science		    */
X/*		Vrije Universiteit					    */
X/*		Postbus 7161						    */
X/*		1007 MC Amsterdam					    */
X/*		The Netherlands						    */
X/*									    */
X/****************************************************************************/
X
Xstruct trans {
X	char		TS_state;	/* see below */
X	char		TS_flags;	/* several flags - see below */
X	char		TS_clident;	/* ident number for client */
X	char		TS_svident;	/* ident number for server */
X	char		TS_ident;	/* transaction identifier */
X	char		TS_seq;		/* fragment sequence number */
X	char		TS_count;	/* max. number timer may expire */
X	char		TS_signal;	/* signal being sent to the server */
X	unshort		TS_addr;	/* network address of this task */
X	unshort		TS_timer;	/* timer, decremented every sweep */
X	unshort		TS_cltim;	/* client crash timer */
X	address		TS_client;	/* if serving: who's the client */
X	address		TS_server;	/* if in trans: who's the server */
X	port		TS_portcache;	/* this port was used the last time */
X	header	       *TS_rhdr;	/* saved param in getreq or trans */
X	header	       *TS_xhdr;	/* saved param in putrep or trans */
X	bufptr		TS_rbuf;	/* receiver buffer */
X	bufptr		TS_xbuf;	/* transmitter buffer */
X	unshort		TS_rcnt;	/* size of rbuf */
X	unshort		TS_xcnt;	/* size of xbuf */
X	unshort		TS_offset;	/* offset in buffer */
X	unshort		TS_maxloc;	/* max. location time in seconds */
X	long		TS_totloc;	/* total location time in ticks */
X	long		TS_totsvr;	/* total server time in ticks */
X#ifdef BUFFERED
X	address		TS_sender;	/* task that sent the buffer */
X	char	       *TS_savehdr;	/* saved header pointer */
X	buffer		TS_buffer;	/* buffer */
X	unshort		TS_bufcnt;	/* buffer size */
X	unshort		TS_what;	/* REQUEST or REPLY */
X#endif
X} tk_trans;
X
X#ifdef TRANS
X
X#define ts_state	tk_trans.TS_state
X#define ts_flags	tk_trans.TS_flags
X#define ts_clident	tk_trans.TS_clident
X#define ts_svident	tk_trans.TS_svident
X#define ts_ident	tk_trans.TS_ident
X#define ts_seq		tk_trans.TS_seq
X#define ts_timer	tk_trans.TS_timer
X#define ts_count	tk_trans.TS_count
X#define ts_signal	tk_trans.TS_signal
X#define ts_addr		tk_trans.TS_addr
X#define ts_cltim	tk_trans.TS_cltim
X#define ts_client	tk_trans.TS_client
X#define ts_server	tk_trans.TS_server
X#define ts_portcache	tk_trans.TS_portcache
X#define ts_rhdr		tk_trans.TS_rhdr
X#define ts_xhdr		tk_trans.TS_xhdr
X#define ts_rbuf		tk_trans.TS_rbuf
X#define ts_xbuf		tk_trans.TS_xbuf
X#define ts_rcnt		tk_trans.TS_rcnt
X#define ts_xcnt		tk_trans.TS_xcnt
X#define ts_offset	tk_trans.TS_offset
X#define ts_maxloc	tk_trans.TS_maxloc
X#define	ts_totloc	tk_trans.TS_totloc
X#define ts_totsvr	tk_trans.TS_totsvr
X#define ts_sender	tk_trans.TS_sender
X#define ts_savehdr	tk_trans.TS_savehdr
X#define ts_buffer	tk_trans.TS_buffer
X#define ts_bufcnt	tk_trans.TS_bufcnt
X#define ts_what		tk_trans.TS_what
X
X/* possible values of ts_state */
X#define IDLE		0
X#define SENDING		1
X#define DONE		2
X#define ACKED		3
X#define NACKED		4
X#define FAILED		5
X#define WAITBUF		6
X#define RECEIVING	7
X#define ABORT		8
X#define MEMFAULT	9
X
X/* possible flags in ts_flags */
X#define LOCATING	bit(0)	/* blocked in trans locating a port */
X#define PUTREQ		bit(1)	/* blocked in trans sending a request */
X#define GETREQ		bit(2)	/* blocked in getreq */
X#define PUTREP		bit(3)	/* blocked in putrep */
X#define GETREP		bit(4)	/* blocked in trans getting a reply */
X#define SERVING		bit(5)	/* running between getreq and putrep */
X
X#else 
X
X#define tk_trans	tk_dummy	/* other modules must not touch it */
X
X#endif
+ END-OF-FILE trans.H
chmod 'u=rw,g=r,o=r' 'trans.H'
set `wc -c 'trans.H'`
count=$1
case $count in
4005)	:;;
*)	echo 'Bad character count in ''trans.H' >&2
		echo 'Count should be 4005' >&2
esac
echo Extracting 'trans.c'
sed 's/^X//' > 'trans.c' << '+ END-OF-FILE ''trans.c'
X/****************************************************************************/
X/*									    */
X/* (c) Copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands */
X/*									    */
X/*    This product is part of the  Amoeba  distributed operating system.    */
X/*									    */
X/*    Permission to use, sell, duplicate or disclose this software must be  */
X/* obtained in writing.  Requests for such permissions may be sent to	    */
X/*									    */
X/*									    */
X/*		Dr. Andrew S. Tanenbaum					    */
X/*		Dept. of Mathematics and Computer Science		    */
X/*		Vrije Universiteit					    */
X/*		Postbus 7161						    */
X/*		1007 MC Amsterdam					    */
X/*		The Netherlands						    */
X/*									    */
X/****************************************************************************/
X
X#define NDEBUG
X#define TRANS
X
X/*
X * This module implements the transaction mechanism. The transaction
X * calls are:
X *	getreq(header, buffer, count);
X *	putrep(header, buffer, count);
X *	trans(hdr1, buf1, cnt1, hdr2, buf2, cnt2);
X *
X * ``Getreq'' is called by servers that want to wait for a request.
X * ``Putrep'' is called by servers that what to send a reply to a client.
X * ``Trans'' is called by clients that want to send a request to a server.
X * Requests are addressed by the ``h_port'' field in the header structure.
X * Replies are automatically sent back to the corresponding client. Between
X * a ``getreq'' and a ``putrep'' the server may not call ``getreq''. All
X * the three calls are blocking.
X *
X#ifndef NONET
X * The following network interface routines must be defined externally:
X *
X *	puthead(dest, source, ident, seq, type, size);
X *	append(data, size, send);
X *	pickoff(data, size);
X *
X * ``Puthead'' is called first when a packet has to be sent. Among other
X * things the destination and the size are specified. If this size is zero,
X * the packet must be sent immediately.
X * ``Append'' is called when more data must be appended to a packet. The
X * ``send'' parameter is set when the packet can be sent.
X * ``Pickoff'' is called when a packet has arrived. The specified number
X * of bytes must copied to the specified data buffer.
X *
X * When a packet arrives, the local routine ``handle'' must be called:
X *	handle(dest, source, ident, seq, type, size, hdr);
X * ``Hdr'' contains the first HEADERSIZE data bytes. With a call to
X * ``getall'' this buffer is enlarged to contain the full packet.
X#endif NONET
X */
X
X#include "../h/const.h"
X#include "../h/type.h"
X#include "../h/amoeba.h"
X#include "const.h"
X#undef IDLE
X#include "byteorder.h"
X#include "exception.h"
X#include "global.h"
X#include "task.h"
X#include "internet.h"
X#include "amstat.h"
X#include "assert.h"
X
X#define	send		amsend
X#define	received	amreceived
Xextern struct task	task[];
X
X#define FIRSTSIZE	(PACKETSIZE - HEADERSIZE)
X
X#define MAXCNT		30000		/* maximum buffer size */
X
X#define taskptr(to)	(&task[tasknum(to)])
X
Xextern port NULLPORT;
X
X#ifndef NONET
X#ifndef NOCLIENT
Xextern unshort maxcrash;
X#endif NOCLIENT
Xextern unshort retranstime, crashtime, clientcrash;
Xextern unshort maxretrans, mincrash;
X#endif
X
Xaddress local;
Xextern long ticker;
Xextern address portlookup();
Xextern phys_bytes umap();
X
X#ifdef STATISTICS
Xstruct amstat amstat;
X#endif
X#ifdef BUFFERED
X
X/* Some simple routines to test ``BUFFERED transactions''
X */
X
XPRIVATE char bufs[10][BUFSIZE];
XPRIVATE char used[11];
X
X#define NOBUF	((buffer) 0)
X
Xbuffer allocbuf(){
X  register buffer b;
X
X  for (b = 1; b <= 10; b++)
X	if (!used[b]) {
X		used[b] = 1;
X		return(b);
X	}
X#ifndef NDEBUG
X  printf("out of buffers\n");
X#endif
X  return(NOBUF);
X}
X
Xfreebuf(b)
Xbuffer b;
X{
X  assert(used[b]);
X  used[b] = 0;
X}
X
Xputbuf(b, addr, size)
Xbuffer b;
Xvir_bytes addr;
Xunshort size;
X{
X  register phys_bytes pb;
X
X  assert(used[b]);
X  if ((pb = umap(curtask, addr, (vir_bytes) size)) == 0)
X	return(0);
X  phys_copy((phys_bytes) bufs[b - 1], pb, (phys_bytes) size);
X  return(1);
X}
X
Xgetbuf(b, addr, size)
Xbuffer b;
Xvir_bytes addr;
Xunshort size;
X{
X  register phys_bytes pb;
X
X  assert(used[b]);
X  if ((pb = umap(curtask, addr, (vir_bytes) size)) == 0)
X	return(0);
X  phys_copy(pb, (phys_bytes) bufs[b - 1], (phys_bytes) size);
X  return(1);
X}
X
X#endif BUFFERED
X
X/* return where the ``location'' can be found:
X *	DONTKNOW:	the location is unknown;
X *	LOCAL:		it's on this machine;
X *	GLOBAL:		it's probably somewhere on the net.
X */
Xarea(location)
Xregister address location;
X{
X  if (location == SOMEWHERE)
X	return(DONTKNOW);
X  if (siteaddr(location) == local)
X	return(LOCAL);
X#ifdef NONET
X  assert(0);
X  /*NOTREACHED*/
X#else
X  return(GLOBAL);
X#endif
X}
X
X/* (re)start getreq
X */
Xstatic getreqstart(t)
Xregister struct task *t;
X{
X  t->ts_state = WAITBUF;
X  t->ts_seq = 0;
X  t->ts_offset = 0;
X  portinstall(&t->ts_rhdr->h_port, t->ts_addr, WAIT);
X}
X
X#ifndef NONET	/*--------------------------------------------------*/
X
Xstatic headerfromnet(hdr)
Xheader *hdr;
X{
X  dec_s_be(&hdr->h_command);
X  dec_s_be(&hdr->h_size);
X  dec_l_be(&hdr->h_offset);
X  dec_s_be(&hdr->h_extra);
X}
X
Xstatic headertonet(hdr)
Xheader *hdr;
X{
X  enc_s_be(&hdr->h_command);
X  enc_s_be(&hdr->h_size);
X  enc_l_be(&hdr->h_offset);
X  enc_s_be(&hdr->h_extra);
X}
X
X/* A locate message has arrived. This routine handles it by looking the
X * ports up in the cache, and if it finds some there that are local it
X * sends a reply back with those ports.
X */
X
Xstatic locreq(ph, ptab)         /* handle locate request */
Xregister struct pktheader *ph;
Xregister char *ptab;
X{
X  getall();
X  portask(pktfrom(ph), (port *) ptab, ph->ph_size/PORTSIZE);
X  netenable();
X}
X
X/* called from portcache.c */
Xhereport(asker, ptab, nports)
Xaddress asker;
Xport *ptab;
Xunsigned nports;
X{
X
X  nports *= PORTSIZE;
X  puthead(asker, local, 0, 0, HERE, nports);
X  append((phys_bytes) ptab, nports, SEND);
X}
X
X#ifndef NOCLIENT
X
X/* called from portcache.c */
Xwhereport(ptab, nports)
Xport *ptab;
Xunsigned nports;
X{
X  nports *= PORTSIZE;
X  puthead(BROADCAST, local, 0, 0, LOCATE, nports);
X  append((phys_bytes) ptab, nports, SEND);
X}
X
X/* start getrep
X */
Xstatic getrepstart(t)
Xregister struct task *t;
X{
X  t->ts_flags &= ~PUTREQ;
X  t->ts_state = WAITBUF;
X  t->ts_seq = 0;
X  t->ts_offset = 0;
X  t->ts_flags |= GETREP;
X  t->ts_timer = crashtime;
X  t->ts_count = maxcrash;
X}
X
X/* A reply on a locate message has arrived. Store the ports in the cache.
X */
Xstatic here(ph, ptab)	/* handle locate reply */
Xregister struct pktheader *ph;
Xregister char *ptab;
X{
X  register port *p;
X  register unshort from = pktfrom(ph);
X
X  getall();
X  p = ABSPTR(port *, ptab + ph->ph_size);
X  while ((char *) --p >= ptab)
X	portinstall(p, from, NOWAIT);
X  netenable();
X}
X
X/* After I've enquired about the health of a server, the processor answered
X * that it's fine. Thank goodness. But keep asking.
X */
Xstatic alive(ph)
Xregister struct pktheader *ph;
X{
X  register struct task *t = &task[ph->ph_dsttask & 0xFF];
X
X  netenable();
X  if (t->ts_server == pktfrom(ph) && t->ts_svident == ph->ph_ident &&
X						(t->ts_flags & GETREP)) {
X	t->ts_timer = crashtime;
X	t->ts_count = maxcrash;
X	t->ts_signal = 0;
X  }
X}
X
X/* After I've enquired about the health of a server, the processor answered
X * that it's dead. Too bad. Notify client.
X */
Xstatic dead(ph)
Xregister struct pktheader *ph;
X{
X  register struct task *t = &task[ph->ph_dsttask & 0xFF];
X
X  netenable();
X  if (t->ts_server == pktfrom(ph) && t->ts_svident == ph->ph_ident &&
X		(t->ts_state == WAITBUF || t->ts_state == RECEIVING ||
X		 t->ts_state == SENDING) &&
X		(t->ts_flags & (PUTREQ | GETREP))) {
X	t->ts_timer = 0;
X	t->ts_state = FAILED;
X	wakeup((event_t) &t->ts_state);
X  }
X}
X#endif NOCLIENT
X
X/* Someone enquires how a server doing. Inform him. A signal may be sent
X * along with the enquiry. Handle that.
X */
Xstatic enquiry(ph)
Xregister struct pktheader *ph;
X{
X  register unshort from = pktfrom(ph), to = pktto(ph);
X  register struct task *t = &task[ph->ph_dsttask & 0xFF];
X
X  netenable();
X  if (t->ts_client == from && t->ts_clident == ph->ph_ident) {
X  	if (t->ts_flags & SERVING) {
X		t->ts_cltim = clientcrash;
X		puthead(from, to, ph->ph_ident, 0, ALIVE, 0);
X		if (ph->ph_signal != 0)
X			putsig(t, (unshort) (ph->ph_signal & 0xFF));
X
X	}
X  }
X  else
X	puthead(from, to, ph->ph_ident, 0, DEAD, 0);
X}
X
X/* Send a fragment of a packet. If it's the first, insert the header.
X */
Xstatic sendfragment(t, what)
Xstruct task *t;
Xunshort what;
X{
X  register address to;
X  register short size = t->ts_xcnt - t->ts_offset;
X  register phys_bytes xbuf;
X
X#ifndef NOCLIENT
X  if (t->ts_flags & PUTREQ) {
X	to = t->ts_server;
X	what |= REQUEST;
X  }
X  else
X#endif
X  {
X	to = t->ts_client;
X	what |= REPLY;
X  }
X  if (t->ts_seq == 0) {		/* first fragment--append header */
X	if (size <= FIRSTSIZE)		/* first and last */
X		what |= LAST;
X	else {				/* first but not last */
X		size = (size + HEADERSIZE - 1) % PACKETSIZE - HEADERSIZE + 1;
X		if (size < 0)
X			size = 0;
X	}
X	puthead(to, t->ts_addr, t->ts_ident, t->ts_seq, (char) what,
X						(unshort) size + HEADERSIZE);
X	headertonet(t->ts_xhdr);
X	append((phys_bytes) t->ts_xhdr, HEADERSIZE, size == 0 ? SEND : NOSEND);
X	headerfromnet(t->ts_xhdr);
X  }
X  else {			/* not first */
X	if (size <= PACKETSIZE)		/* last but not first */
X		what |= LAST;
X	else				/* not first and not last */
X		size = PACKETSIZE;
X	puthead(to, t->ts_addr, t->ts_ident, t->ts_seq, (char) what,
X							(unshort) size);
X  }
X/*
X** a change from original trans code because on an ibmpc kernel virtual
X** addresses are different from kernel physical.  therefore we must
X** replace call to append with am_doappend().
X*/
X  if (size != 0)
X	if ((xbuf = umap(t, (vir_bytes) (t->ts_xbuf + t->ts_offset),
X						(vir_bytes) size)) == 0)
X		return(0);
X	else
X		am_doappend(xbuf, (unshort) size, SEND);
X  return(1);
X}
X
X/* Send a message. Call sendfragment to send the first fragment. When an
X * acknowledgement arrives, the interrupt routine handling it will send
X * the next fragment, if necessary.
X */
Xstatic send(){
X  register struct task *c = curtask;
X  register unshort what = 0;
X
X  c->ts_state = SENDING;
X#ifdef NOCLIENT
X  c->ts_ident = c->ts_clident;
X#else
X  c->ts_ident = c->ts_flags & PUTREQ ? c->ts_svident : c->ts_clident;
X#endif
X  c->ts_seq = 0;
X  c->ts_count = maxretrans;
X  do {
X	if (!sendfragment(c, what)) {
X		c->ts_state = MEMFAULT;
X		return;
X	}
X	if (c->ts_state == SENDING) {
X		c->ts_timer = retranstime;
X		if (sleep((event_t) &c->ts_state)) {
X			c->ts_timer = 0;
X			c->ts_state = ABORT;
X			return;
X		}
X	}
X	if (c->ts_state == ACKED) {
X		c->ts_state = SENDING;
X		what = 0;
X	}
X	else /* if (c->ts_state == SENDING) */
X		what = RETRANS;
X  } while (c->ts_state == SENDING);
X}
X
X/* An acknowledgement for a fragment arrived. If it wasn't the last fragment,
X * send the next. Else if it was a putrep, restart the task. Else it was the
X * putreq part of a transaction, start up the getrep part.
X */
Xstatic gotack(ph)
Xregister struct pktheader *ph;
X{
X  register unshort to = pktto(ph), from = pktfrom(ph);
X  register struct task *t = &task[ph->ph_dsttask & 0xFF];
X
X  netenable();
X  if (t->ts_state == SENDING && t->ts_ident == ph->ph_ident &&
X						t->ts_seq == ph->ph_seq) {
X	if (ph->ph_seq == 0) {		/* first fragment */
X		register short size = t->ts_xcnt - t->ts_offset;
X		
X#ifndef NOCLIENT
X		if (t->ts_flags & PUTREQ)
X			t->ts_server = from;
X		else
X#endif
X		if (t->ts_client != from)
X			return;
X		if (size > 0)
X			size = (size + HEADERSIZE - 1) % PACKETSIZE
X							- HEADERSIZE + 1;
X		if (size > 0)
X			t->ts_offset += size;
X	}
X	else
X		t->ts_offset += PACKETSIZE;
X	t->ts_timer = 0;
X	if (t->ts_offset >= t->ts_xcnt)		/* ack for last fragment */
X#ifndef NOCLIENT
X		if (t->ts_flags & PUTREQ) {	/* in a transaction */
X			getrepstart(t);
X			if (t->ts_signal != 0)
X				puthead(from, to, ph->ph_ident, t->ts_signal,
X								ENQUIRY, 0);
X		}
X		else
X#endif
X		{				/* putrep done */
X			assert(t->ts_flags & PUTREP);
X			t->ts_state = DONE;
X			wakeup((event_t) &t->ts_state);
X		}
X	else {
X		t->ts_seq++;
X#ifdef BUFFERED
X		t->ts_timer = 0;
X		t->ts_count = maxretrans;
X		t->ts_state = ACKED;
X		wakeup((event_t) &t->ts_state);
X#else
X		if (sendfragment(t, 0)) {
X			t->ts_timer = retranstime;
X			t->ts_count = maxretrans;
X		}
X		else {
X			t->ts_timer = 0;
X			t->ts_state = MEMFAULT;
X			wakeup((event_t) &t->ts_state);
X		}
X#endif BUFFERED
X	}
X  }
X}
X
X#ifndef NOCLIENT
X/* A nak has arrived. Obviously the server was not at the assumed address.
X * Wake up the task, to do a new locate.
X */
Xstatic gotnak(ph)
Xregister struct pktheader *ph;
X{
X  register struct task *t = &task[ph->ph_dsttask & 0xFF];
X
X  netenable();
X  if (t->ts_state == SENDING && t->ts_ident == ph->ph_ident && t->ts_seq == 0
X	  && (t->ts_flags & PUTREQ) && t->ts_server == (ph->ph_srcnode & 0xFF)) {
X	t->ts_timer = 0;
X	t->ts_state = NACKED;
X	wakeup((event_t) &t->ts_state);
X  }
X}
X#endif NOCLIENT
X
X/* A fragment has arrived. Get the data and see if more fragments are expected.
X */
Xstatic received(t, what, size, hdr)
Xstruct task *t;
Xchar what, *hdr;
Xunshort size;
X{
X  register unshort cnt = t->ts_rcnt - t->ts_offset, n;
X  register phys_bytes rbuf;
X
X  if (cnt > size)
X	cnt = size;
X  if (cnt != 0) {
X	rbuf = umap(t, (vir_bytes) (t->ts_rbuf+t->ts_offset), (vir_bytes) cnt);
X	if (rbuf == 0) {
X		netenable();
X		t->ts_timer = 0;
X		t->ts_state = MEMFAULT;
X		wakeup((event_t) &t->ts_state);
X		return;
X	}
X	t->ts_offset += cnt;
X	if (t->ts_seq != 0) {	/* copy the ``header'' */
X		n = cnt < HEADERSIZE ? cnt : HEADERSIZE;
X/* kernel virtual != kernel physical under minix */
X		am_phys_copy((vir_bytes)hdr, rbuf, (phys_bytes) n);
X		rbuf += n;
X		cnt -= n;
X	}
X	if (cnt != 0)
X		pickoff(rbuf, cnt);
X  }
X  netenable();
X  if (what & LAST) {
X	t->ts_timer = 0;
X	t->ts_state = DONE;
X#ifndef BUFFERED
X	wakeup((event_t) &t->ts_state);
X#endif
X  }
X  else {
X	t->ts_seq++;
X	t->ts_timer = crashtime;
X	t->ts_count = mincrash;
X	t->ts_state = RECEIVING;
X  }
X}
X
X#ifdef BUFFERED
X
X/* A network fragment is available. Wake up the waiting task.
X */
Xstatic gotbuffer(t, from, ident, what, size)
Xregister struct task *t;
Xaddress from;
Xchar ident, what;
Xunshort size;
X{
X  assert(t->ts_state == WAITBUF || t->ts_state == RECEIVING);
X  t->ts_sender = from;
X  t->ts_ident = ident;
X  t->ts_buffer = NETBUF;
X  t->ts_bufcnt = size;
X  t->ts_what = what;
X  t->ts_timer = 0;	/* shouldn't be necessary, but can't hurt */
X  wakeup((event_t) &t->ts_state);
X}
X
X#endif BUFFERED
X
X/* See if someone is handling the request for `from.'
X */
Xstatic struct task *find(from, ident)
Xaddress from;
Xchar ident;
X{
X  register struct task *t;
X
X  for (t = task; t < uppertask; t++)
X	if ((t->ts_flags & (GETREQ | SERVING | PUTREP)) &&
X				t->ts_client == from && t->ts_clident == ident)
X		return(t);
X  return(NILTASK);
X}
X
X/* A request packet has arrived. Find out which task this packet should go to.
X * Send an acknowledgement if it can't do any harm.
X */
Xstatic gotrequest(ph, hdr)
Xregister struct pktheader *ph;
Xheader *hdr;
X{
X  register struct task *t;
X  register unsigned /* unshort */ from = pktfrom(ph), to, size = ph->ph_size;
X
X  if (ph->ph_seq == 0)	/* only the first fragment is really interesting */
X	if ((ph->ph_type & RETRANS) &&
X				(t = find(from, ph->ph_ident)) != NILTASK) {
X		netenable();		/* ack got lost, send it again */
X		puthead(from, t->ts_addr, ph->ph_ident, ph->ph_seq, ACK, 0);
X	}
X	else {
X		register address location;
X
X		location = portlookup(&hdr->h_port, NOWAIT, DELETE);
X		if (location == NOWHERE || siteaddr(location) != local) {
X			netenable();
X			puthead(from, pktto(ph), ph->ph_ident, 0, NAK, 0);
X			return;
X		}
X		size -= HEADERSIZE;
X		t = taskptr(location);
X		t->ts_client = from;
X		t->ts_clident = ph->ph_ident;
X		*t->ts_rhdr = *hdr;
X		headerfromnet(t->ts_rhdr);
X#ifdef BUFFERED
X		gotbuffer(t, from, ph->ph_ident, ph->ph_type, size);
X#else
X		if ((ph->ph_type & (LAST | RETRANS)) != LAST)
X			puthead(from, location, ph->ph_ident, 0, ACK, 0);
X		received(t, ph->ph_type, size, (char *) 0);
X#endif BUFFERED
X	}
X  else {	/* seq != 0 */
X	to = pktto(ph);
X	t = &task[ph->ph_dsttask & 0xFF];
X	if (t->ts_state != RECEIVING || ph->ph_seq != t->ts_seq) {
X		netenable();
X		puthead(from, to, ph->ph_ident, ph->ph_seq, ACK, 0);
X	}
X	else {
X#ifdef BUFFERED
X		t->ts_savehdr = (char *) hdr;
X		gotbuffer(t, from, ph->ph_ident, ph->ph_type, size);
X#else
X		puthead(from, to, ph->ph_ident, ph->ph_seq, ACK, 0);
X		received(t, ph->ph_type, size, (char *) hdr);
X#endif BUFFERED
X	}
X  }
X}
X
X#ifndef NOCLIENT
X/* A reply packet has arrived. Send an acknowledgement if it can't do any
X * harm.
X */
Xstatic gotreply(ph, hdr)
Xregister struct pktheader *ph;
Xheader *hdr;
X{
X  register unshort from = pktfrom(ph), to = pktto(ph), size = ph->ph_size;
X  register struct task *t = &task[ph->ph_dsttask & 0xFF];
X
X  if (ph->ph_ident != t->ts_svident || ph->ph_seq != t->ts_seq)
X	t = NILTASK;
X  else if ((t->ts_flags & GETREP) == 0)
X	if (t->ts_flags & PUTREQ) {	/* ack for request got lost */
X		compare(t->ts_ident, ==, ph->ph_ident);
X		getrepstart(t);		/* start the getrep */
X		t->ts_signal = 0;
X	}
X	else
X		t = NILTASK;
X  if (t != NILTASK) {
X	if (ph->ph_seq == 0) {
X		*t->ts_rhdr = *hdr;
X		headerfromnet(t->ts_rhdr);
X		size -= HEADERSIZE;
X	}
X	else if (t->ts_state != RECEIVING)
X		t = NILTASK;
X  }
X  if (t == NILTASK) {
X	netenable();
X	puthead(from, to, ph->ph_ident, ph->ph_seq, ACK, 0);
X  }
X  else {
X#ifdef BUFFERED
X	t->ts_savehdr = (char *) hdr;
X	gotbuffer(t, from, ph->ph_ident, ph->ph_type, size);
X#else
X	puthead(from, to, ph->ph_ident, ph->ph_seq, ACK, 0);
X	received(t, ph->ph_type, size, (char *) hdr);
X#endif BUFFERED
X  }
X}
X#endif NOCLIENT
X
X/* A packet has arrived. Call an appropiate routine, after checking some
X * things.
X */
Xpkthandle(ph, hdr)
Xregister struct pktheader *ph;
Xregister char *hdr;
X{
X  if (ph->ph_dsttask < ntask && ph->ph_size <= PACKETSIZE)
X	switch (ph->ph_type & TYPE) {
X  	case LOCATE:
X		if (ph->ph_size == 0 || ph->ph_ident != 0 || ph->ph_seq != 0)
X			break;
X		if ((ph->ph_size % PORTSIZE) != 0)
X			break;
X		locreq(ph, hdr);
X		return(1);
X	case REQUEST:
X		if (ph->ph_seq == 0 && ph->ph_size < HEADERSIZE)
X			break;
X		gotrequest(ph, ABSPTR(header *, hdr));
X		return(1);
X	case ACK:
X		if (ph->ph_size != 0)
X			break;
X		gotack(ph);
X		return(1);
X	case ENQUIRY:
X		if (ph->ph_size != 0)
X			break;
X		enquiry(ph);
X		return(1);
X#ifndef NOCLIENT
X	case HERE:
X		if (ph->ph_size == 0 || ph->ph_ident != 0 || ph->ph_seq != 0)
X			break;
X		if ((ph->ph_size % PORTSIZE) != 0)
X			break;
X		here(ph, hdr);
X		return(1);
X	case REPLY:
X		if (ph->ph_seq == 0 && ph->ph_size < HEADERSIZE)
X			break;
X		gotreply(ph, ABSPTR(header *, hdr));
X		return(1);
X	case NAK:
X		if (ph->ph_size != 0 || ph->ph_seq != 0)
X			break;
X		gotnak(ph);
X		return(1);
X	case ALIVE:
X		if (ph->ph_size != 0 || ph->ph_seq != 0)
X			break;
X		alive(ph);
X		return(1);
X	case DEAD:
X		if (ph->ph_size != 0 || ph->ph_seq != 0)
X			break;
X		dead(ph);
X		return(1);
X#endif
X	case 0:
X		return(0);
X  }
X  return(0);
X}
X
X#ifdef notdef  /* don't need this for minix */
Xhandle(to, from, ident, seq, what, size, hdr)	/* compatibility */
Xaddress to, from;
Xchar ident, seq, what;
Xunshort size;
Xchar *hdr;
X{
X  struct pktheader ph;
X
X  ph.ph_dstnode = siteaddr(to);
X  ph.ph_srcnode = siteaddr(from);
X  ph.ph_dsttask = tasknum(to);
X  ph.ph_srctask = tasknum(from);
X  ph.ph_ident = ident;
X  ph.ph_seq = seq;
X  ph.ph_size = size;
X  ph.ph_type = what;
X  return pkthandle(&ph, hdr);
X}
X
X#endif
X
X/* A timer has gone off too many times. See what went wrong. Restart the task.
X */
Xstatic failed(t)
Xregister struct task *t;
X{
X  assert(t->ts_flags & (GETREQ | PUTREP | GETREP | PUTREQ));
X#ifndef NDEBUG
X  if (t->ts_flags & (GETREQ | PUTREP))
X	printf("%x: client %x failed (%d)\n", t - task,
X					t->ts_client, t->ts_state);
X  if (t->ts_flags & (GETREP | PUTREQ))
X	printf("%x: server %x failed (%d)\n", t - task,
X					t->ts_server, t->ts_state);
X#endif
X#ifdef STATISTICS
X  if (t->ts_flags & (GETREQ | PUTREP))
X	amstat.ams_clfail++;
X  if (t->ts_flags & (GETREP | PUTREQ))
X	amstat.ams_svfail++;
X#endif
X  switch (t->ts_state) {
X  case SENDING:		/* Message didn't arrive */
X	t->ts_state = FAILED;
X	assert(t->ts_flags & (PUTREQ | PUTREP));
X	break;
X  case WAITBUF:		/* server site has crashed */
X	assert(t->ts_flags & GETREP);
X  case RECEIVING:
X#ifndef NOCLIENT
X	if (t->ts_flags & GETREP)
X		t->ts_state = FAILED;
X	else
X#endif
X	{
X		getreqstart(t);	/* client failed, restart getreq */
X		return;
X	}
X	break;
X  default:
X	assert(0);
X  }
X  wakeup((event_t) &t->ts_state);
X}
X
X/* A timer went off. See what is wrong.
X */
Xstatic again(t)
Xregister struct task *t;
X{
X  switch (t->ts_state) {
X  case SENDING:			/* retransmit */
X#ifdef STATISTICS
X  if (t->ts_flags & (GETREQ | PUTREP))
X	amstat.ams_rxcl++;
X  if (t->ts_flags & (GETREP | PUTREQ))
X	amstat.ams_rxsv++;
X#endif
X#ifdef BUFFERED
X	wakeup((event_t) &t->ts_state);
X#else
X	if (!sendfragment(t, RETRANS))
X		assert(0);
X	t->ts_timer = retranstime;
X#endif
X	break;
X  case WAITBUF:			/* Check if the server is still alive */
X	assert(t->ts_flags & GETREP);
X  case RECEIVING:
X#ifndef NOCLIENT
X	if (t->ts_flags & GETREP)  /* See if the other side is still there */
X		puthead(t->ts_server, t->ts_addr, t->ts_svident,
X						t->ts_signal, ENQUIRY, 0);
X#endif
X	t->ts_timer = retranstime;
X	break;
X  default:
X	assert(0);
X  }
X}
X
X#endif NONET	/*--------------------------------------------------*/
X
X/* First check all the timers. If any went off call the appropiate routine.
X * Then see if there are ports to locate.
X */
Xnetsweep(){
X  register struct task *t;
X
X  for (t = task; t < uppertask; t++) {
X	if (t->ts_timer != 0 && --t->ts_timer == 0)	/* timer expired */
X#ifndef NOCLIENT
X		if (t->ts_flags & LOCATING)
X			portquit(&t->ts_xhdr->h_port, t);
X#endif
X#ifndef NONET
X#ifndef NOCLIENT
X		else
X#endif
X		{
X			compare(t->ts_count, !=, 0);
X			if (--t->ts_count == 0)		/* serious */
X				failed(t);
X			else				/* try again */
X				again(t);
X			break;
X		}
X	if (t->ts_cltim != 0 && --t->ts_cltim == 0) {	/* client crashed */
X#ifdef STATISTICS
X		amstat.ams_clcrash++;
X#endif
X		putsig(t, CRASH);
X	}
X#endif NONET
X  }
X}
X
X#ifdef BUFFERED
X
X/*  Data has arrived. Get it. If there's more, wait for it.
X */
Xstatic recvbuf(){
X  register struct task *c = curtask, *t;
X
X  c->ts_state = WAITBUF;
X  for (;;) {
X#ifndef NONET
X	if (c->ts_buffer == NETBUF) {		/* something from the net */
X		c->ts_buffer = NOBUF;
X		puthead(c->ts_sender, c->ts_addr, c->ts_ident, c->ts_seq,
X								ACK, 0);
X		received(c, (char) c->ts_what, c->ts_bufcnt, c->ts_savehdr);
X		if (c->ts_state != RECEIVING)
X			return;
X	}
X	else
X#endif NONET
X	if (c->ts_buffer != NOBUF && !putbuf(c->ts_buffer, (vir_bytes)
X				(c->ts_rbuf + c->ts_offset), c->ts_bufcnt)) {
X#ifndef NDEBUG
X		printf("%x: bad rbuf (received from %x)\n",
X						c->ts_addr, t->ts_addr);
X#endif
X		c->ts_state = MEMFAULT;		/* receiver fails */
X		freebuf(c->ts_buffer);
X		c->ts_buffer = NOBUF;
X		if (c->ts_sender != NOWHERE) {
X			t = taskptr(c->ts_sender);
X			t->ts_state = FAILED;
X			wakeup((event_t) &t->ts_state);
X		}
X		return;
X	}
X	else {		/* local copy done */
X		c->ts_offset += c->ts_bufcnt;
X		if (c->ts_bufcnt < BUFSIZE) {	/* last buffer */
X			if (c->ts_buffer != NOBUF) {
X				freebuf(c->ts_buffer);
X				c->ts_buffer = NOBUF;
X			}
X			c->ts_state = DONE;
X			return;
X		}
X		else {			/* more buffers expected */
X			compare(c->ts_sender, !=, NOWHERE);
X			c->ts_state = RECEIVING;
X			t = taskptr(c->ts_sender);
X			compare(t->ts_state, ==, SENDING);
X			assert(t->ts_flags & (PUTREQ | PUTREP));
X			wakeup((event_t) &t->ts_state);
X		}
X	}
X	if (sleep((event_t) &c->ts_state)) {		/* wait for rest */
X		portremove(&c->ts_rhdr->h_port, c->ts_addr);
X		if (c->ts_buffer != NOBUF) {
X			freebuf(c->ts_buffer);
X			c->ts_buffer = NOBUF;
X		}
X		c->ts_state = ABORT;
X		return;
X	}
X  }
X}
X
X#endif BUFFERED
X
X/* The transaction is local. This routine does the sending.
X */
Xstatic sendbuf(t)
Xregister struct task *t;
X{
X  register struct task *c = curtask;
X  register unshort cnt = t->ts_rcnt < c->ts_xcnt ? t->ts_rcnt : c->ts_xcnt;
X#ifdef BUFFERED
X  register unshort size;
X  buffer allocbuf();
X
X  c->ts_state = SENDING;
X  t->ts_sender = c->ts_addr;
X  if (cnt == 0)
X	t->ts_buffer = NOBUF;
X  else if ((t->ts_buffer = allocbuf()) == NOBUF) {
X	c->ts_xcnt = FAIL;
X	c->ts_state = FAILED;
X	if (!(t->ts_flags & GETREQ)) {
X		t->ts_state = FAILED;	/* trans fails */
X		wakeup((event_t) &t->ts_state);
X	}
X	return;
X  }
X  do {
X	if ((size = cnt) != 0) {
X		if (size > BUFSIZE)
X			size = BUFSIZE;
X		if (!getbuf(t->ts_buffer, (vir_bytes) (c->ts_xbuf +
X						c->ts_offset), size)) {
X#ifndef NDEBUG
X			printf("%x: bad xbuf (sending to %x)\n",
X						c->ts_addr, t->ts_addr);
X#endif
X			freebuf(t->ts_buffer);
X			t->ts_buffer = NOBUF;
X			c->ts_xcnt = FAIL;		/* for putrep */
X			c->ts_state = MEMFAULT;
X			if (!(t->ts_flags & GETREQ)) {
X				t->ts_state = FAILED;	/* trans fails */
X				wakeup((event_t) &t->ts_state);
X			}
X			break;
X		}
X	}
X	else if (t->ts_buffer != NOBUF) {
X		freebuf(t->ts_buffer);
X		t->ts_buffer = NOBUF;
X	}
X	t->ts_bufcnt = size;
X	assert(t->ts_state == WAITBUF || t->ts_state == RECEIVING);
X	wakeup((event_t) &t->ts_state);
X	if (size != BUFSIZE) {		/* last buffer */
X		t->ts_sender = NOWHERE;
X		c->ts_state = DONE;
X		break;
X	}
X	cnt -= size;
X	c->ts_offset += size;
X	if (sleep((event_t) &c->ts_state))
X		c->ts_state = ABORT;
X  } while (c->ts_state == SENDING);
X#else
X  compare(t->ts_state, ==, WAITBUF);
X  if (cnt != 0) {
X	register phys_bytes rbuf, xbuf;
X
X	if ((rbuf = umap(t, t->ts_rbuf, (vir_bytes) cnt)) == 0) {
X#ifndef NDEBUG
X		printf("bad rbuf (%X,%X)\n", t->ts_rbuf, (vir_bytes) cnt);
X#endif
X		c->ts_state = FAILED;
X		c->ts_xcnt = FAIL;
X		t->ts_state = MEMFAULT;
X		wakeup((event_t) &t->ts_state);
X		return;
X	}
X	if ((xbuf = umap(c, c->ts_xbuf, (vir_bytes) cnt)) == 0) {
X#ifndef NDEBUG
X		printf("bad xbuf (%X,%X)\n", c->ts_xbuf, (vir_bytes) cnt);
X#endif
X		c->ts_state = MEMFAULT;
X		c->ts_xcnt = FAIL;
X		if (t->ts_flags & GETREQ)
X			getreqstart(t);	/* client failed, restart getreq */
X		else {
X			t->ts_state = FAILED;
X			wakeup((event_t) &t->ts_state);
X		}
X		return;
X	}
X	phys_copy(xbuf, rbuf, (phys_bytes) cnt);
X  }
X  t->ts_offset = cnt;
X  t->ts_state = DONE;
X  wakeup((event_t) &t->ts_state);
X  c->ts_state = DONE;
X#endif BUFFERED
X}
X
X#ifndef NOCLIENT
X/* The transaction is local. Send the request to the server.
X */
Xstatic recvrequest(){
X  register address to;
X  register struct task *c = curtask, *t;
X
X  if ((to = portlookup(&c->ts_xhdr->h_port, NOWAIT, DELETE)) == NOWHERE)
X	return(0);
X#ifndef NONET
X  if (siteaddr(to) != local)
X	return(0);
X#endif
X  t = taskptr(to);
X  c->ts_server = to;
X  t->ts_client = c->ts_addr;
X  t->ts_clident = c->ts_svident;
X  *t->ts_rhdr = *c->ts_xhdr;
X  sendbuf(t);
X  return(c->ts_xcnt != FAIL);
X}
X#endif
X
X/* A task calls this routine when it wants to be blocked awaiting a request.
X * It specifies the header containing the port to wait for, a buffer where
X * the data must go and the size of this buffer. Getreq returns the size of
X * the request when one arrives.
X */
Xunshort getreq(hdr, buf, cnt)
Xheader *hdr;
Xbufptr buf;
Xunshort cnt;
X{
X  register struct task *c = curtask;
X
X  if (c->ts_flags != 0 || cnt > MAXCNT)
X	return(FAIL);
X  if (NullPort(&hdr->h_port))
X	return(FAIL);
X#ifdef STATISTICS
X  amstat.ams_getreq++;
X#endif
X  c->ts_rhdr = hdr;
X  c->ts_rbuf = buf;
X  c->ts_rcnt = cnt;
X  c->ts_flags |= GETREQ;
X  getreqstart(c);
X  if (sleep((event_t) &c->ts_state)) {
X	portremove(&hdr->h_port, c->ts_addr);
X	c->ts_state = ABORT;
X  }
X#ifdef BUFFERED
X  if (c->ts_state == WAITBUF)
X	recvbuf();
X#endif
X  c->ts_flags &= ~GETREQ;
X  switch (c->ts_state) {
X  case DONE:
X	c->ts_flags |= SERVING;
X#ifndef NONET
X	if (area(c->ts_client) != LOCAL)
X		c->ts_cltim = clientcrash;
X#endif NONET
X	cnt = c->ts_offset;
X	break;
X  case ABORT:
X	cnt = ABORTED;
X	break;
X  case MEMFAULT:
X	cnt = BADADDRESS;
X	break;
X  default:
X	assert(0);
X  }
X  c->ts_state = IDLE;
X  return(cnt);
X}
X
X/* A task wants to send a reply to its client. Putrep returns the size of
X * the reply.
X */
Xunshort putrep(hdr, buf, cnt)
Xheader *hdr;
Xbufptr buf;
Xunshort cnt;
X{
X  register struct task *c = curtask;
X
X  if (c->ts_flags != SERVING)
X	return(FAIL);
X  c->ts_flags &= ~SERVING;
X  if (cnt > MAXCNT)
X	return(FAIL);
X#ifdef STATISTICS
X  amstat.ams_putrep++;
X#endif
X  c->ts_cltim = 0;
X  c->ts_xhdr = hdr;
X  c->ts_xbuf = buf;
X  c->ts_xcnt = cnt;
X  c->ts_offset = 0;
X  c->ts_flags |= PUTREP;
X#ifndef NONET
X  if (siteaddr(c->ts_client) != local)
X	send();
X  else
X#endif
X  {		/* local transaction */
X	register struct task *t = taskptr(c->ts_client);
X
X	if (t->ts_server == c->ts_addr) {
X		*t->ts_rhdr = *hdr;
X		sendbuf(t);
X	}
X  }
X  c->ts_flags &= ~PUTREP;
X  if (c->ts_state == MEMFAULT)
X	cnt = BADADDRESS;
X  c->ts_state = IDLE;
X  return(cnt);
X}
X
X#ifndef NOCLIENT
X/* Somebody wants to contact a server, and wait for a reply. The port this
X * server should listen to is specified in the first header. The reply
X * comes in the second. Trans returns the size of the reply, or FAIL if
X * a transaction fails after the server has been located.
X */
Xunshort trans(hdr1, buf1, cnt1, hdr2, buf2, cnt2)
Xheader *hdr1, *hdr2;
Xbufptr buf1, buf2;
Xunshort cnt1, cnt2;
X{
X  register struct task *c = curtask;
X
X  if ((c->ts_flags & ~SERVING) || cnt1 > MAXCNT || cnt2 > MAXCNT)
X	return(FAIL);
X  if (NullPort(&hdr1->h_port))
X	return(FAIL);
X#ifdef STATISTICS
X  amstat.ams_trans++;
X#endif
X  for (;;) {
X	c->ts_state = IDLE;
X	c->ts_xhdr = hdr1; c->ts_xbuf = buf1; c->ts_xcnt = cnt1;
X	c->ts_rhdr = hdr2; c->ts_rbuf = buf2; c->ts_rcnt = cnt2;
X	c->ts_signal = 0;
X	if (!PortCmp(&c->ts_portcache, &hdr1->h_port)) {
X		c->ts_flags |= LOCATING;
X		c->ts_timer = c->ts_maxloc;
X		c->ts_totloc -= ticker;
X		c->ts_server = portlookup(&hdr1->h_port, WAIT, LOOK);
X		c->ts_totloc += ticker;
X		c->ts_timer = 0;
X		c->ts_flags &= ~LOCATING;
X		switch (c->ts_server) {
X		case NOWHERE:		/* server not found */
X			c->ts_portcache = NULLPORT;
X			return(c->ts_signal == 0 ? NOTFOUND : ABORTED);
X		case SOMEWHERE:
X			c->ts_portcache = NULLPORT;
X			return(FAIL);
X		}
X		c->ts_portcache = hdr1->h_port;
X	}
X#ifdef notdef
X	else
X		c->ts_server = siteaddr(c->ts_server);
X#endif
X	c->ts_svident++;
X	c->ts_offset = 0;
X	c->ts_flags |= PUTREQ;
X#ifndef NONET
X	if (siteaddr(c->ts_server) != local) {
X#ifdef STATISTICS
X		amstat.ams_remtrans++;
X#endif
X		c->ts_totsvr -= ticker;
X		send();
X		c->ts_flags &= ~PUTREQ;
X#ifdef BUFFERED
X		if (c->ts_state == WAITBUF)
X			recvbuf();	/* await the reply */
X#endif
X		c->ts_totsvr += ticker;
X		if (c->ts_state == NACKED || c->ts_state == FAILED) {
X			portremove(&hdr1->h_port, siteaddr(c->ts_server));
X			c->ts_portcache = NULLPORT;
X		}
X		if (c->ts_state != NACKED)
X			break;
X#ifdef STATISTICS
X		amstat.ams_naks++;
X#endif
X		c->ts_portcache = NULLPORT;
X	}
X	else
X#endif NONET
X	if (recvrequest()) {	/* local transaction */
X#ifdef STATISTICS
X		amstat.ams_loctrans++;
X#endif
X		c->ts_flags &= ~PUTREQ;
X		c->ts_flags |= GETREP;
X		c->ts_totsvr -= ticker;
X		c->ts_offset = 0;
X		c->ts_state = WAITBUF;
X		if (c->ts_signal != 0) {
X			putsig(taskptr(c->ts_server),
X					(unshort) (c->ts_signal & 0xFF));
X			c->ts_signal = 0;
X		}
X		if (sleep((event_t) &c->ts_state))
X			c->ts_state = ABORT;
X#ifdef BUFFERED
X		else
X			recvbuf();
X#endif
X		c->ts_totsvr += ticker;
X		break;
X	}
X	else {		/* too bad, try again */
X		c->ts_flags &= ~PUTREQ;
X		if (c->ts_state == MEMFAULT)
X			break;
X	}
X	c->ts_portcache = NULLPORT;
X	if (c->ts_signal != 0) {
X		c->ts_state = ABORT;
X		break;
X	}
X  }
X  c->ts_signal = 0;
X  c->ts_flags &= ~(PUTREQ | GETREP);
X  if (c->ts_state == DONE) {
X	c->ts_state = IDLE;
X	return c->ts_offset;
X  }
X#ifndef NDEBUG
X  printf("trans failed with %x (state = %d; command = %d; port ",
X			c->ts_server, c->ts_state, c->ts_xhdr->h_command);
X  prport(&c->ts_xhdr->h_port);
X  printf(")\n");
X#endif
X  switch (c->ts_state) {
X  case FAILED:
X  case ABORT:
X	cnt2 = FAIL;
X	break;
X  case MEMFAULT:
X	cnt2 = BADADDRESS;
X	break;
X  default:
X	assert(0);
X  }
X  c->ts_state = IDLE;
X  return(cnt2);
X}
X#endif NOCLIENT
X
X/* If doing a transaction, send a signal to the server. For a remote server,
X * the signal is sent along with enquiries. If it's still locating a server,
X * abort that.  If it isn't doing a transaction, but blocked in a getreq,
X * abort that.
X */
Xsendsig(t, signal)
Xregister struct task *t;
Xchar signal;
X{
X#ifndef NOCLIENT
X  if (t->ts_flags & (LOCATING | PUTREQ | GETREP))
X	t->ts_signal = signal;
X  if (t->ts_flags & LOCATING)
X	portquit(&t->ts_xhdr->h_port, t);
X  else if (t->ts_state == WAITBUF)
X	if (t->ts_flags & GETREQ) {
X		portremove(&t->ts_rhdr->h_port, t->ts_addr);
X		t->ts_state = ABORT;
X		wakeup((event_t) &t->ts_state);
X	}
X	else if (signal != 0 && (t->ts_flags & GETREP))
X#ifndef NONET
X		if (area(t->ts_server) != LOCAL)
X			puthead(t->ts_server, t->ts_addr, t->ts_svident,
X							signal, ENQUIRY, 0);
X		else
X#endif NONET
X		{
X			putsig(taskptr(t->ts_server),
X						(unshort) (signal & 0xFF));
X			t->ts_signal = 0;
X		}
X#endif NOCLIENT
X}
X
X#ifndef NOCLIENT
X/* Abort anything task s is doing. If the task is serving somebody, notify
X * it that the server has failed.
X */
Xdestroy(s)
Xregister struct task *s;
X{
X  register struct task *t;
X
X  sendsig(s, (char) CRASH);
X  if (s->ts_flags & SERVING)
X#ifndef NONET
X	if (area(s->ts_client) != LOCAL) {
X		puthead(s->ts_client, s->ts_addr, s->ts_clident, 0, DEAD, 0);
X		s->ts_cltim = 0;
X	}
X	else
X#endif
X	{
X#ifndef NDEBUG
X		printf("%x destroyed, %x victim\n", s->ts_addr, s->ts_client);
X#endif
X		t = taskptr(s->ts_client);
X		if (t->ts_state == WAITBUF) {
X			assert(t->ts_flags & GETREP);
X			t->ts_timer = 0;
X			t->ts_state = FAILED;
X			wakeup((event_t) &t->ts_state);
X		}
X	}
X  s->ts_timer = 0;
X  if (s->ts_flags & (LOCATING|GETREQ|GETREP|PUTREQ|PUTREP)) {
X	s->ts_state = ABORT;
X	wakeup((event_t) &s->ts_state);
X  }
X  else {
X	s->ts_state = IDLE;
X	s->ts_flags = 0;
X  }
X  s->ts_server = s->ts_client = 0;
X  s->ts_portcache = NULLPORT;
X}
X
X/* Clean up the mess.
X */
Xcleanup(){
X  register struct task *c = curtask;
X
X  compare(c->ts_state, ==, IDLE);
X  destroy(c);
X}
X#endif
X
X
X/* Limit the maximum locate time & service time. 0 is infinite.
X */
Xunshort timeout(maxloc)
Xunshort maxloc;
X{
X  unshort oldloc = curtask->ts_maxloc;
X
X  curtask->ts_maxloc = maxloc;
X  return(oldloc);
X}
X
X#ifndef NDEBUG
Xtransdump(){
X  register struct task *t;
X  static char *states[] = {
X	"IDLE", "SENDING", "DONE", "ACKED", "NACKED", "FAILED",
X	"WAITBUF", "RECEIVING", "ABORT", "MEMFAULT"
X  };
X  static struct ftab {
X	unshort flag;
X	char *name;
X  } ftab[] = {
X	{ GETREQ,	"GETREQ"  }, { SERVING,	 "SERVING" },
X	{ PUTREP,	"PUTREP"  }, { LOCATING, "LOCATE"  },
X	{ PUTREQ,	"PUTREQ"  }, { GETREP,	 "GETREP"  },
X  };
X  register struct ftab *p;
X
X  printf("\nTK STATE    CTM TIM CNT  CLT  SRV CLI SVI SEQ SIG FLAGS\n");
X  for (t = task; t < uppertask; t++) {
X	if (t->ts_state == IDLE && t->ts_flags == 0) {
X		compare(t->ts_cltim, ==, 0);
X		compare(t->ts_timer, ==, 0);
X		compare(t->ts_signal, ==, 0);
X		continue;
X	}
X	printf("%2d %9s%3d %3d %3d %4x %4x %3d %3d %3d %3d", t - task,
X		states[t->ts_state], t->ts_cltim, t->ts_timer, t->ts_count,
X		t->ts_client, t->ts_server, t->ts_clident & 0xFF,
X		t->ts_svident & 0xFF, t->ts_seq & 0xFF, t->ts_signal & 0xFF);
X	for (p = ftab; p < &ftab[sizeoftable(ftab)]; p++)
X		if (t->ts_flags & p->flag)
X			printf(" %s", p->name);
X	if (t->ts_flags & (GETREQ | LOCATING | GETREP)) {
X		printf("  '");
X		prport(t->ts_flags & GETREQ ? &t->ts_rhdr->h_port
X							: &t->ts_xhdr->h_port);
X		printf("'");
X	}
X	printf("\n");
X  }
X}
X#endif NDEBUG
X
Xtrinit(){
X  curtask->ts_addr = ((curtask - task) << 8 | local);
X}
X
X/* Get the site address.
X */
Xtransinit(){
X#ifdef NONET
X  local = 1;
X#else
X  extern address interinit();
X
X  local = siteaddr(interinit());
X/*
X  netenable();
X*/
X#endif NONET
X}
X
X#ifndef NDEBUG
Xamdump()
X{
X#ifdef STATISTICS
X  printf("\nAmoeba statistics:\n");
X  printf("clfail  %7D ", amstat.ams_clfail);
X  printf("svfail  %7D ", amstat.ams_svfail);
X  printf("clcrash %7D ", amstat.ams_clcrash);
X  printf("rxcl    %7D ", amstat.ams_rxcl);
X  printf("rxsv    %7D\n",amstat.ams_rxsv);
X  printf("trans   %7D ", amstat.ams_trans);
X  printf("local   %7D ", amstat.ams_loctrans);
X  printf("remote  %7D ", amstat.ams_remtrans);
X  printf("getreq  %7D ", amstat.ams_getreq);
X  printf("putrep  %7D\n",amstat.ams_putrep);
X  printf("naks    %7D\n",amstat.ams_naks);
X#endif
X}
X#endif
+ END-OF-FILE trans.c
chmod 'u=rw,g=r,o=r' 'trans.c'
set `wc -c 'trans.c'`
count=$1
case $count in
35869)	:;;
*)	echo 'Bad character count in ''trans.c' >&2
		echo 'Count should be 35869' >&2
esac
echo Extracting 'util.c'
sed 's/^X//' > 'util.c' << '+ END-OF-FILE ''util.c'
X#include "../h/const.h"
X#include "../h/type.h"
X#include "../h/com.h"
X#include "const.h"
X#include "type.h"
X#include "proc.h"
X#include "dp8390.h"
X#include "assert.h"
X#include "dp8390info.h"
X#include "eplinfo.h"
X
X#define  PIC_enable()	port_out(INT_CTL, ENABLE)
X
Xextern char get_byte();
X
Xstruct eplinfo eplinfo = {0x280};
X
Xstruct dp8390info dp8390info = {0x290, 6, 27, 0xC4000, 0xC4000};
X
Xinbyte(port)
X    vir_bytes port;
X{
X    int value;
X
X    port_in(port, &value);
X    return value;
X}
X
Xoutbyte(port, value)
X    vir_bytes port;
X    int value;
X{
X    port_out(port, value);
X}
X
Xgetheader(paddr, pkthead)
X    phys_bytes paddr;
X    struct rcvdheader *pkthead;
X{
X    vir_bytes  seg;
X
X    assert((paddr&0xFFF0000F)==0L);
X    seg = paddr>>4;
X    pkthead->rp_status = get_byte(seg,0);
X    pkthead->rp_next = get_byte(seg,1);
X    pkthead->rp_rbcl = get_byte(seg,2);
X    pkthead->rp_rbch = get_byte(seg,3);
X}
X
X
Xshort
Xgetbint(paddr)
X    phys_bytes paddr;
X{
X    vir_bytes seg,offset;
X
X    seg = paddr >> 4;
X    offset = paddr & 0xF;
X    return (((short)get_byte(seg, offset)&0xFF)<<8) + (short)(get_byte(seg, offset+1)&0xFF);
X}
X
X/*
Xgetbyte(paddr)
Xphys_bytes paddr;
X{
X    vir_bytes	seg;
X    vir_bytes	offset;
X
X    seg = paddr >> 4;
X    offset = paddr & 0xf;
X    return get_byte(seg, offset);
X}
X
X
Xvp_copy(procnr, seg, vir_addr, phys_addr, count)
X    int		procnr;
X    int 	seg;
X    vir_bytes 	vir_addr;
X    phys_bytes 	phys_addr;
X    vir_bytes 	count;
X{
X    phys_bytes 	u_phys;
X    register struct proc *rp;
X    phys_bytes umap();
X
X    rp = proc_addr(procnr);
X    u_phys = umap(rp, seg, vir_addr, count);
X    assert(u_phys!=0L);
X    phys_copy(u_phys, phys_addr, (phys_bytes)count);
X}
X
Xpv_copy(phys_addr, procnr, seg, vir_addr, count)
X    phys_bytes 	phys_addr;
X    int		procnr;
X    int 	seg;
X    vir_bytes 	vir_addr;
X    vir_bytes 	count;
X{
X    phys_bytes 	vir_phys;
X    register struct proc *rp;
X    phys_bytes umap();
X
X    rp = proc_addr(procnr);
X    vir_phys = umap(rp, seg, vir_addr, count);
X    assert(vir_phys!=0L);
X    phys_copy(phys_addr, vir_phys, (phys_bytes)count);
X}
X*/
+ END-OF-FILE util.c
chmod 'u=rw,g=r,o=r' 'util.c'
set `wc -c 'util.c'`
count=$1
case $count in
2063)	:;;
*)	echo 'Bad character count in ''util.c' >&2
		echo 'Count should be 2063' >&2
esac
exit 0