[comp.os.minix] V1.3 posting #39 - amoeba/kernel/*

ast@cs.vu.nl (Andy Tanenbaum) (07/18/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

ddl@husc6.harvard.edu (Dan Lanciani) (07/20/88)

In article <910@ast.cs.vu.nl|, ast@cs.vu.nl (Andy Tanenbaum) writes:
| 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						    */

	It seems unreasonable to disallow all use and disclosure of material
published to a public network such as USENET.  A literal interpretation
would appear to imply that letting someone read over my shoulder is
violating the copyright.  Any interpretation would indicate that I
can't *run* the software.

					Dan Lanciani
					ddl@harvard.*

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

In article <4989@husc6.harvard.edu> ddl@husc6.harvard.edu (Dan Lanciani) writes:
>	It seems unreasonable to disallow all use and disclosure of material
>published to a public network such as USENET.  


I agree.  The files for the MINIX networking were taken literally from the
Amoeba distribution, of which stuff I posted is only a small subset.  I didn't
even look at the copyright notice (which is appropriate for Amoeba, which is
licensed software)

I hereby grant permission for anyone to use the contents of the amoeba
directory that I posted for educational and research use.  It may also
be duplicated and redistributed to others for educational and research
use, but the copyright notice must remain intact.  For all commercial use
a licensing agreement is required.

Andy Tanenbaum (ast@cs.vu.nl)

oswald@cpe.UUCP (07/22/88)

I only received the first 100000 bytes of V1.3 posting #39 - amoeba/kernal/*.

Would some kind soul post or mail me the end of the file.  This is a tail of
what I received.

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 &&

Thanks,
Roy Oswald
Tandy Computer Product Engineering
convex!ctvax!trsvax!cpe!oswald
decvax!microsoft!trsvax!cpe!oswald