ast@cs.vu.nl (Andy Tanenbaum) (07/23/88)
: This is a shar archive. Extract with sh, not csh. : This archive ends with exit, so do not worry about trailing junk. : --------------------------- cut here -------------------------- PATH=/bin:/usr/bin:/usr/ucb echo Extracting 'amoeba.c' sed 's/^X//' > 'amoeba.c' << '+ END-OF-FILE ''amoeba.c' X/* X** Minix /dev/amoeba driver X** X** This file contains two kernel servers: amint_task and amoeba_task. X** amoeba_task implements transactions for user tasks and amint_task X** handles asynchronous events such as timeouts, incoming ethernet X** packets and interrupts. X** X** An amoeba_task is permanently assigned to a process until a transaction X** is complete. If you do a getreq then the kernel task remains X** allocated until after the putrep or the server dies. X** There is a limit of one operation at a time except in the case that a X** getreq is followed by a trans. X** X** The value of curtask is only correct if there is non-preemptive X** scheduling of kernel tasks. It is reset by am_sleep when it returns X** which keeps it pointing to the correct place. X** X** Lines marked HACK are of doubtful portability but produce efficient X** code. X*/ X X#define NDEBUG X X#include "../h/const.h" X#include "../h/type.h" X#include "../h/com.h" X#include "../h/error.h" X#include "../h/callnr.h" X#include "../h/signal.h" X#include "const.h" X#include "type.h" X#include "proc.h" X#include "glo.h" /* need definition of cur_proc */ X X#include "../h/amoeba.h" X#undef umap X#include "../h/amparam.h" X#include "global.h" X#define MPX X#define TRANS X#include "task.h" X#include "assert.h" X#include "internet.h" X#include "etherformat.h" X#include "byteorder.h" X X/* amoeba task table - can't alloc memory in minix kernel */ XPUBLIC struct task am_task[AM_NTASKS]; X X#ifndef NONET X X#define ETH_HDRS (sizeof (Framehdr)) X#define HSZ (ETH_HDRS + HEADERSIZE) /* watchout for alignment */ X#define FAKESITENO 0xff /* to bluff trans.c */ X#define MAPENTRIES 127 X/* two hacks for speed */ X#define EANULL(a) NullPort((port *) (a)) /* HACK! */ X#define EACMP(a, b) PortCmp((port *) (a), (port *) (b)) /* HACK! */ X XPRIVATE Etherpacket Packet; /* the latest arrived amoeba ethernet packet */ XPRIVATE phys_bytes Bufaddr;/*physical address of Packet */ X XPRIVATE phys_bytes Inptr; /* used by pickoff() & getall() to copy data */ XPRIVATE unsigned Insiz; /* total size of received packet */ XPRIVATE phys_bytes Outptr; /* pointer to pos currently building packet */ XPRIVATE unsigned Outsiz; /* size of currently building packet */ XPRIVATE phys_bytes Xmtbuf; /* Pointer to current ethernet write buffer */ XPRIVATE Eth_addr Myaddr; /* ether address of this host */ XPRIVATE Eth_addr Gwaddr; /* ether address of pronet gateway */ X/* broadcast address for ethernet. see next comment. */ XPRIVATE Eth_addr Broadcastaddr = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; X/* the 128th map entry is to hold the broadcast address. can you say HACK? */ XPRIVATE Eth_addr Eamap[MAPENTRIES+1]; X X#endif X X X/*===========================================================================* X * amoeba_task * X *===========================================================================*/ XPUBLIC Xamoeba_task() X{ X void am_reply(); X message mess; X int mytask; X X mytask = AMOEBA_CLASS - cur_proc; X curtask = &am_task[mytask]; /* make me the current amoeba task */ X am_init(); X while (TRUE) X { X receive(ANY, &mess); X curtask = &am_task[mytask]; /* make me the current amoeba task */ X switch (mess.AM_OP) X { X case AM_TRANS: X do_trans(&mess); X break; X case AM_GETREQ: X do_getreq(&mess); X break; X case AM_PUTREP: X do_putrep(&mess); X break; X default: X if (mess.m_source >= 0) X am_reply(TASK_REPLY, mess.m_source, mess.AM_PROC_NR, 1, EINVAL); X break; X } /* end switch */ X } /* end while */ X} X X X/*===========================================================================* X * am_init * X *===========================================================================*/ XPRIVATE int Xam_init() X{ X/* non-pre-emptive scheduling is assumed here for initialisation! */ X static int Initialised; X X if (Initialised == 0) X { /* set up the ethernet driver and init the tables */ X Initialised++; X uppertask = &am_task[AM_NTASKS]; X ntask = AM_NTASKS; X#ifndef NONET X net_init(); X#endif X transinit(); X portinit(); X } X trinit(); X curtask->mx_flags = RUNNABLE; X} X X X/*===========================================================================* X * amint_task * X *===========================================================================*/ XPUBLIC Xamint_task() X{ X phys_bytes umap(); X message mess; X struct task * t; X X#ifndef NONET X Bufaddr = umap(proc_addr(AMINT_CLASS), D, (vir_bytes)&Packet, (vir_bytes)HSZ); X#endif X set_timer(); /* start the netsweep timer */ X while (TRUE) X { X receive(ANY, &mess); X switch (mess.AM_OP) X { X case ETHER_ARRIV: /* an ethernet packet arrived */ X do_arrive(&mess); X break; X case AM_TIMEOUT: /* run transaction sweepers every 0.1 secs */ X netsweep(); X portsweep(); X set_timer(); /* reset the timeout */ X break; X case AM_PUTSIG: /* user typed a del or a quit or a kill */ X sendsig(&am_task[mess.AM_COUNT], 1); X break; X case AM_TASK_DIED: /* a user task died while doing an operation */ X t = &am_task[mess.AM_COUNT]; X if (t->mx_active) /* if transaction record is still valid */ X { X destroy(t); /* then destroy it */ X t->mx_proc_nr = 0; X t->mx_active = 0; X t->mx_flags = 0; X } X break; X default: X break; X } X } X} X X X/*===========================================================================* X * set_timer * X *===========================================================================*/ XPRIVATE Xset_timer() X{ X message mess; X int am_runsweep(); X X mess.m_type = SET_ALARM; X mess.CLOCK_PROC_NR = AMINT_CLASS; X mess.DELTA_TICKS = HZ/10; /* every 0.1 seconds ! */ X mess.FUNC_TO_CALL = am_runsweep; X sendrec(CLOCK, &mess); X} X X X/*===========================================================================* X * am_runsweep * X *===========================================================================*/ XPRIVATE Xam_runsweep() X{ X message mess; X X mess.AM_OP = AM_TIMEOUT; X send(AMINT_CLASS, &mess); X} X X X/*===========================================================================* X * do_trans * X *===========================================================================*/ XPRIVATE Xdo_trans(m_ptr) Xmessage * m_ptr; X{ X header * am_starttask(); X void am_endtask(); X unshort trans(); X void am_reply(); X X unshort ret; X header * hdr; X Trpar param; /* parameter block for transaction */ X X#define req param.tp_par[0] X#define rep param.tp_par[1] X X/* copy in parameter block */ X if (get_param(m_ptr, ¶m) == 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, ¶m) == 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, ¶m) == 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