ast@cs.vu.nl (Andy Tanenbaum) (07/23/88)
/*
** Minix /dev/amoeba driver
**
** This file contains two kernel servers: amint_task and amoeba_task.
** amoeba_task implements transactions for user tasks and amint_task
** handles asynchronous events such as timeouts, incoming ethernet
** packets and interrupts.
**
** An amoeba_task is permanently assigned to a process until a transaction
** is complete. If you do a getreq then the kernel task remains
** allocated until after the putrep or the server dies.
** There is a limit of one operation at a time except in the case that a
** getreq is followed by a trans.
**
** The value of curtask is only correct if there is non-preemptive
** scheduling of kernel tasks. It is reset by am_sleep when it returns
** which keeps it pointing to the correct place.
**
** Lines marked HACK are of doubtful portability but produce efficient
** code.
*/
#define NDEBUG
#include "../h/const.h"
#include "../h/type.h"
#include "../h/com.h"
#include "../h/error.h"
#include "../h/callnr.h"
#include "../h/signal.h"
#include "const.h"
#include "type.h"
#include "proc.h"
#include "glo.h" /* need definition of cur_proc */
#include "../h/amoeba.h"
#undef umap
#include "../h/amparam.h"
#include "global.h"
#define MPX
#define TRANS
#include "task.h"
#include "assert.h"
#include "internet.h"
#include "etherformat.h"
#include "byteorder.h"
/* amoeba task table - can't alloc memory in minix kernel */
PUBLIC struct task am_task[AM_NTASKS];
#ifndef NONET
#define ETH_HDRS (sizeof (Framehdr))
#define HSZ (ETH_HDRS + HEADERSIZE) /* watchout for alignment */
#define FAKESITENO 0xff /* to bluff trans.c */
#define MAPENTRIES 127
/* two hacks for speed */
#define EANULL(a) NullPort((port *) (a)) /* HACK! */
#define EACMP(a, b) PortCmp((port *) (a), (port *) (b)) /* HACK! */
PRIVATE Etherpacket Packet; /* the latest arrived amoeba ethernet packet */
PRIVATE phys_bytes Bufaddr;/*physical address of Packet */
PRIVATE phys_bytes Inptr; /* used by pickoff() & getall() to copy data */
PRIVATE unsigned Insiz; /* total size of received packet */
PRIVATE phys_bytes Outptr; /* pointer to pos currently building packet */
PRIVATE unsigned Outsiz; /* size of currently building packet */
PRIVATE phys_bytes Xmtbuf; /* Pointer to current ethernet write buffer */
PRIVATE Eth_addr Myaddr; /* ether address of this host */
PRIVATE Eth_addr Gwaddr; /* ether address of pronet gateway */
/* broadcast address for ethernet. see next comment. */
PRIVATE Eth_addr Broadcastaddr = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
/* the 128th map entry is to hold the broadcast address. can you say HACK? */
PRIVATE Eth_addr Eamap[MAPENTRIES+1];
#endif
/*===========================================================================*
* amoeba_task *
*===========================================================================*/
PUBLIC
amoeba_task()
{
void am_reply();
message mess;
int mytask;
mytask = AMOEBA_CLASS - cur_proc;
curtask = &am_task[mytask]; /* make me the current amoeba task */
am_init();
while (TRUE)
{
receive(ANY, &mess);
curtask = &am_task[mytask]; /* make me the current amoeba task */
switch (mess.AM_OP)
{
case AM_TRANS:
do_trans(&mess);
break;
case AM_GETREQ:
do_getreq(&mess);
break;
case AM_PUTREP:
do_putrep(&mess);
break;
default:
if (mess.m_source >= 0)
am_reply(TASK_REPLY, mess.m_source, mess.AM_PROC_NR, 1, EINVAL);
break;
} /* end switch */
} /* end while */
}
/*===========================================================================*
* am_init *
*===========================================================================*/
PRIVATE int
am_init()
{
/* non-pre-emptive scheduling is assumed here for initialisation! */
static int Initialised;
if (Initialised == 0)
{ /* set up the ethernet driver and init the tables */
Initialised++;
uppertask = &am_task[AM_NTASKS];
ntask = AM_NTASKS;
#ifndef NONET
net_init();
#endif
transinit();
portinit();
}
trinit();
curtask->mx_flags = RUNNABLE;
}
/*===========================================================================*
* amint_task *
*===========================================================================*/
PUBLIC
amint_task()
{
phys_bytes umap();
message mess;
struct task * t;
#ifndef NONET
Bufaddr = umap(proc_addr(AMINT_CLASS), D, (vir_bytes)&Packet, (vir_bytes)HSZ);
#endif
set_timer(); /* start the netsweep timer */
while (TRUE)
{
receive(ANY, &mess);
switch (mess.AM_OP)
{
case ETHER_ARRIV: /* an ethernet packet arrived */
do_arrive(&mess);
break;
case AM_TIMEOUT: /* run transaction sweepers every 0.1 secs */
netsweep();
portsweep();
set_timer(); /* reset the timeout */
break;
case AM_PUTSIG: /* user typed a del or a quit or a kill */
sendsig(&am_task[mess.AM_COUNT], 1);
break;
case AM_TASK_DIED: /* a user task died while doing an operation */
t = &am_task[mess.AM_COUNT];
if (t->mx_active) /* if transaction record is still valid */
{
destroy(t); /* then destroy it */
t->mx_proc_nr = 0;
t->mx_active = 0;
t->mx_flags = 0;
}
break;
default:
break;
}
}
}
/*===========================================================================*
* set_timer *
*===========================================================================*/
PRIVATE
set_timer()
{
message mess;
int am_runsweep();
mess.m_type = SET_ALARM;
mess.CLOCK_PROC_NR = AMINT_CLASS;
mess.DELTA_TICKS = HZ/10; /* every 0.1 seconds ! */
mess.FUNC_TO_CALL = am_runsweep;
sendrec(CLOCK, &mess);
}
/*===========================================================================*
* am_runsweep *
*===========================================================================*/
PRIVATE
am_runsweep()
{
message mess;
mess.AM_OP = AM_TIMEOUT;
send(AMINT_CLASS, &mess);
}
/*===========================================================================*
* do_trans *
*===========================================================================*/
PRIVATE
do_trans(m_ptr)
message * m_ptr;
{
header * am_starttask();
void am_endtask();
unshort trans();
void am_reply();
unshort ret;
header * hdr;
Trpar param; /* parameter block for transaction */
#define req param.tp_par[0]
#define rep param.tp_par[1]
/* copy in parameter block */
if (get_param(m_ptr, ¶m) == 0)
{
am_reply(TASK_REPLY, m_ptr->m_source, m_ptr->AM_PROC_NR, 1, EFAULT);
return;
}
/* copy header in */
hdr = am_starttask(m_ptr);
if (get_header(m_ptr, req.p_hdr, hdr) == 0)
{
am_endtask();
am_reply(TASK_REPLY, m_ptr->m_source, m_ptr->AM_PROC_NR, 1, EFAULT);
return;
}
/* reply to FS to suspend luser task */
am_reply(TASK_REPLY, m_ptr->m_source, m_ptr->AM_PROC_NR, 0, SUSPEND);
/* start locate timer */
(void)am_timeout(param.tp_maxloc);
/* call trans */
ret = trans(hdr, req.p_buf, req.p_cnt, hdr, rep.p_buf, rep.p_cnt);
/* copy header to luser task (trans already copied the data) */
if ((short)ret >= 0 && put_header(m_ptr->AM_PROC_NR, hdr, rep.p_hdr) == 0)
{
am_endtask();
am_reply(AM_REVIVE, m_ptr->m_source, m_ptr->AM_PROC_NR, 1, EFAULT);
return;
}
am_endtask();
/* revive luser task */
am_reply(AM_REVIVE, m_ptr->m_source, m_ptr->AM_PROC_NR, 1, (int)ret);
}
/*===========================================================================*
* do_getreq *
*===========================================================================*/
PRIVATE
do_getreq(m_ptr)
message * m_ptr;
{
unshort getreq();
header * am_starttask();
void am_endtask();
void am_reply();
Trpar param;
unshort ret;
header * hdr;
int free;
/* copy parameter block for getreq */
if (get_param(m_ptr, ¶m) == 0)
{
am_reply(TASK_REPLY, m_ptr->m_source, m_ptr->AM_PROC_NR, 1, EFAULT);
return;
}
/* copy header in */
hdr = am_starttask(m_ptr);
if (get_header(m_ptr, param.tp_par[0].p_hdr, hdr) == 0)
{
am_endtask();
am_reply(TASK_REPLY, m_ptr->m_source, m_ptr->AM_PROC_NR, 1, EFAULT);
return;
}
/* reply to FS to suspend luser task */
am_reply(TASK_REPLY, m_ptr->m_source, m_ptr->AM_PROC_NR, 0, SUSPEND);
/* call getreq */
ret = getreq(hdr, param.tp_par[0].p_buf, param.tp_par[0].p_cnt);
free = 0;
if ((short)ret < 0) /* getreq failed */
{
free = 1;
am_endtask();
}
else /* copy header to luser task (getreq already copied the data) */
if (put_header(m_ptr->AM_PROC_NR, hdr, param.tp_par[0].p_hdr) == 0)
{
free = 1;
ret = EFAULT;
am_endtask();
}
/* restart luser task but don't free the kernel task! */
am_reply(AM_REVIVE, m_ptr->m_source, m_ptr->AM_PROC_NR, free, (int)ret);
}
/*===========================================================================*
* do_putrep *
*===========================================================================*/
PRIVATE
do_putrep(m_ptr)
message * m_ptr;
{
unshort putrep();
void am_reply();
Trpar param;
header * hdr;
unshort ret;
/* copy in parameter block */
if (get_param(m_ptr, ¶m) == 0)
{
am_reply(TASK_REPLY, m_ptr->m_source, m_ptr->AM_PROC_NR, 1, EFAULT);
return;
}
/* make sure that there was a getrequest */
if (!curtask->mx_active || curtask->mx_proc_nr != m_ptr->AM_PROC_NR)
{
am_reply(TASK_REPLY, m_ptr->m_source, m_ptr->AM_PROC_NR, 1, (int)FAIL);
return;
}
/* copy in header */
hdr = &curtask->mx_hdr;
if (get_header(m_ptr, param.tp_par[0].p_hdr, hdr) == 0)
{
am_endtask();
am_reply(TASK_REPLY, m_ptr->m_source, m_ptr->AM_PROC_NR, 1, EFAULT);
return;
}
/* tell FS to suspend luser task */
am_reply(TASK_REPLY, m_ptr->m_source, m_ptr->AM_PROC_NR, 0, SUSPEND);
/* send reply */
ret = putrep(hdr, param.tp_par[0].p_buf, param.tp_par[0].p_cnt);
am_endtask();
/* restart luser task */
am_reply(AM_REVIVE, m_ptr->m_source, m_ptr->AM_PROC_NR, 1, (int)ret);
}
/*===========================================================================*
* do_arrive *
*===========================================================================*/
PRIVATE
do_arrive(m_ptr)
message * m_ptr;
{
#ifndef NONET
/*
** we already know it is an amoeba packet, otherwise we wouldn't get here.
** copy in the ethernet header, the internet header and the amoeba header
** and then call packet handle. getall and pickoff do the rest!
*/
phys_copy(m_ptr->AM_PADDR, Bufaddr, (long)HSZ);
/* fix amoeba size field - NB. the following is a macro */
dec_s_le(&Packet.ep_fr.f_ah.ph_size);
/* if packethandle succeeds it calls netenable itself! */
if (check(&Packet)) /* then give it to the transaction layer */
{
Inptr = m_ptr->AM_PADDR + HSZ; /* pointer to the data after headers */
Insiz = Packet.ep_fr.f_ah.ph_size;
Packet.ep_fr.f_ah.ph_size -= ETH_HDRS;
Packet.ep_fr.f_ah.ph_dstnode = FAKESITENO;
if (!pkthandle(&Packet.ep_fr.f_ah, Packet.ep_data))
am_netenable();
}
else
am_netenable();
#endif
}
/*===========================================================================*
* am_reply *
*===========================================================================*/
PRIVATE void
am_reply(code, replyee, proc_nr, free_it, status)
int code; /* TASK_REPLY or revive */
int replyee; /* destination address for the reply */
int proc_nr; /* to whom the reply should go */
int free_it; /* if a getreq, then don't free this task */
int status; /* reply code */
{
/* send reply to process doing a trans, getreq or putrep or anything else */
message a_mess;
a_mess.m_type = AM_SYSCALL;
a_mess.AM_OP = code;
a_mess.AM_FREE_IT = (long)free_it;
a_mess.AM_PROC_NR = proc_nr;
a_mess.AM_STATUS = status;
send(replyee, &a_mess);
}
/*===========================================================================*
* am_starttask *
*===========================================================================*/
PRIVATE header *
am_starttask(m_ptr)
message * m_ptr;
{
/* check to see if already doing a transaction for this user */
if (curtask->mx_active && curtask->mx_proc_nr == m_ptr->AM_PROC_NR)
{
assert(curtask->mx_flags & RUNNABLE);
assert(!(curtask->mx_flags & NESTED));
curtask->mx_flags |= NESTED;
}
else
{
curtask->mx_proc_nr = m_ptr->AM_PROC_NR;
curtask->mx_active = 1;
curtask->mx_flags = RUNNABLE;
}
return &curtask->mx_hdr;
}
/*===========================================================================*
* am_endtask *
*===========================================================================*/
PRIVATE void
am_endtask()
{
if (curtask->mx_flags & NESTED)
curtask->mx_flags &= ~NESTED;
else
{
am_cleanup();
curtask->mx_flags = 0;
curtask->mx_active = 0;
}
}
/*===========================================================================*
* get_param *
*===========================================================================*/
PRIVATE
get_param(m_ptr, param)
message * m_ptr;
Trpar * param;
{
phys_bytes umap();
phys_bytes src; /* physical address of parameter block */
phys_bytes dst; /* kernel buffer */
/* copy parameter block for trans */
if (m_ptr->AM_COUNT != (int)sizeof (Trpar) ||
(src = umap(proc_addr(m_ptr->AM_PROC_NR), D,
(vir_bytes)m_ptr->AM_ADDRESS, (vir_bytes)sizeof (Trpar))) == 0)
return 0;
dst = umap(proc_addr(cur_proc), D, (vir_bytes)param,
(vir_bytes)sizeof (Trpar));
phys_copy(src, dst, (long)sizeof (Trpar));
return 1;
}
/*===========================================================================*
* get_header *
*===========================================================================*/
PRIVATE
get_header(m_ptr, h_src, h_dest)
message * m_ptr;
header * h_src;
header * h_dest;
{
/* get amoeba header from user space */
phys_bytes umap();
phys_bytes src; /* user's header */
phys_bytes dst; /* kernel buffer for header */
if ((src = umap(proc_addr(m_ptr->AM_PROC_NR), D, (vir_bytes)h_src,
(vir_bytes)sizeof (header))) == 0)
return 0;
dst = umap(proc_addr(cur_proc), D, (vir_bytes)h_dest,
(vir_bytes)sizeof (header));
phys_copy(src, dst, (long)sizeof (header));
return 1;
}
/*===========================================================================*
* put_header *
*===========================================================================*/
PRIVATE
put_header(proc_nr, h_src, h_dest)
int proc_nr;
header * h_src;
header * h_dest;
{
/* write an amoeba header into user space */
phys_bytes umap();
phys_bytes src;
phys_bytes dst;
if ((dst = umap(proc_addr(proc_nr), D, (vir_bytes)h_dest,
(vir_bytes)sizeof (header))) == 0)
return 0;
src = umap(proc_addr(cur_proc), D, (vir_bytes)h_src,
(vir_bytes)sizeof (header));
phys_copy(src, dst, (long)sizeof (header));
return 1;
}
/*
** routines which are needed by trans.c
*/
/*===========================================================================*
* am_umap *
*===========================================================================*/
PUBLIC phys_bytes
am_umap(a, b, c)
struct task * a;
vir_bytes b;
vir_bytes c;
{
/* the umap in trans.c needs to be converted to minix umap */
phys_bytes umap();
return umap(proc_addr(a->mx_proc_nr), D, b, c);
}
/*===========================================================================*
* am_psig *
*===========================================================================*/
PUBLIC
am_psig(t, sig)
struct task * t;
unshort sig;
{
/* should propagate if between a g & p and remember sig! */
sendsig(t, (char)sig); /* propagate signal to servers */
cause_sig(t->mx_proc_nr, SIGAMOEBA);
}
/*
** sleep and wakeup don't fit into the amoeba model too well.
** the following are hacks and don't give true sleep and wakeup
** semantics. they also do not take account of interrupts but seem to work.
*/
/*===========================================================================*
* am_sleep *
*===========================================================================*/
am_sleep(addr)
event_t addr;
{
message mess;
struct task * c;
c = curtask;
receive(ANY, &mess);
if (mess.AM_ADDRESS != addr)
printf("am_sleep: woken badly %x %x\n", mess.ADDRESS, addr);
curtask = c;
return 0;
}
/*===========================================================================*
* am_wakeup *
*===========================================================================*/
am_wakeup(addr)
event_t addr;
{
message mess;
int tasknr;
mess.AM_ADDRESS = addr;
tasknr = ((struct task *)addr - am_task); /* HACK */
if (am_task[tasknr].mx_active) /* don't wake it up if it is dead! */
send(AMOEBA_CLASS - tasknr, &mess);
}
#ifndef NDEBUG
#define PRINTABLE(c) (((c) >= ' ' && (c) <= '~') ? (c) : '?')
/*===========================================================================*
* prport *
*===========================================================================*/
PUBLIC
prport(p)
port * p;
{
int i;
for (i = 0; i < PORTSIZE; i++)
printf("%c", PRINTABLE(p->_portbytes[i]));
}
#endif
#ifndef NONET
/*===========================================================================*
* interinit *
*===========================================================================*/
PUBLIC address
interinit()
{
return 0xFF;
}
/*===========================================================================*
* check *
*===========================================================================*/
PRIVATE
check(p)
Etherpacket * p;
{
/* make sure that an ethernet packet is a valid amoeba packet */
if (p->ep_fr.f_ah.ph_srcnode == 0) /* from an ethernet host */
{
if ((p->ep_fr.f_ah.ph_srcnode = ealookup(&p->ep_fr.f_srcaddr)) == 0)
{
printf("ethernet mapping table overflow\n");
return 0;
}
}
else /* was the packet from ProNet? */
#ifdef PRONET
{
if (p->ep_fr.f_ah.ph_srcnode & ETHERBITS)
return 0;
/* a packet from the pronet gateway */
if (EANULL(&Gwaddr))
{
Gwaddr = p->ep_fr.f_srcaddr;
pr_addr("Gateway to pronet at", &Gwaddr);
}
else
if (!EACMP(&Gwaddr, &p->ep_fr.f_srcaddr))
pr_addr("Second gateway claims to be at", &p->ep_fr.f_srcaddr);
}
#else
return 0;
#endif PRONET
return 1;
}
/*===========================================================================*
* pr_addr *
*===========================================================================*/
PRIVATE
pr_addr(s, p)
char * s;
Eth_addr * p;
{
/* print an ethernet address */
printf("%s %x:%x:%x:%x:%x:%x\n", s,
p->e[0] & 0xff,
p->e[1] & 0xff,
p->e[2] & 0xff,
p->e[3] & 0xff,
p->e[4] & 0xff,
p->e[5] & 0xff);
}
/*===========================================================================*
* am_puthead *
*===========================================================================*/
PUBLIC
am_puthead(dst, src, ident, seq, type, size)
address dst;
address src;
char ident;
char seq;
char type;
unshort size;
{
phys_bytes umap();
phys_bytes eth_getbuf();
unshort totalsize;
char dstnode;
Framehdr fh;
phys_bytes phd;
totalsize = size + sizeof (Framehdr);
compare(totalsize, <=, 1514);
fh.f_ah.ph_dstnode = dstnode = lobyte(dst);
if ((dstnode & ETHERBITS) == 0)
{
assert(!EANULL(&Gwaddr));
fh.f_dstaddr = Gwaddr;
}
else /* broadcast is also handled here! */
fh.f_dstaddr = Eamap[dstnode & 0x7f];
fh.f_srcaddr = Myaddr;
fh.f_proto = AMOEBAPROTO;
enc_s_be(&fh.f_proto);
fh.f_ah.ph_srcnode = 0;
fh.f_ah.ph_dsttask = hibyte(dst);
fh.f_ah.ph_srctask = hibyte(src);
fh.f_ah.ph_ident = ident;
fh.f_ah.ph_seq = seq;
fh.f_ah.ph_type = type;
fh.f_ah.ph_flags = 0;
fh.f_ah.ph_size = totalsize;
enc_s_le(&fh.f_ah.ph_size);
if ((Xmtbuf = eth_getbuf()) != 0)
{
phd = umap(proc_addr(cur_proc), D, (vir_bytes)&fh, (vir_bytes)sizeof (Framehdr));
phys_copy(phd, Xmtbuf, (long)sizeof (Framehdr));
if (size == 0)
eth_write(Xmtbuf, 60);
else
{
Outsiz = sizeof (Framehdr);
Outptr = Xmtbuf + sizeof (Framehdr);
}
}
else
Outptr = 0;
}
/*===========================================================================*
* am_gall *
*===========================================================================*/
PUBLIC
am_gall() /* getall in trans.c */
{
/*
** copy in any bytes not already copied into packet! We've already copied
** the first HSZ bytes!
** Bufaddr points to the local buffer Packet and Inptr points to the current
** position in the buffer on the ethernet card.
*/
long size;
if ((size = (long)Insiz - HSZ) > 0)
phys_copy(Inptr, Bufaddr+HSZ, size);
}
/*===========================================================================*
* am_do_append *
*===========================================================================*/
PUBLIC
am_doappend(data, size, dosend)
phys_bytes data;
unshort size;
int dosend;
{
/* add more data to current ethernet output packet */
if (Outptr == 0) /* previous puthead failed */
return;
phys_copy(data, Outptr, (long)size);
Outptr += size;
Outsiz += size;
if (dosend)
{
if (Outsiz < 60)
Outsiz = 60;
eth_write(Xmtbuf, (int)Outsiz);
}
}
/*===========================================================================*
* am_pickoff *
*===========================================================================*/
PUBLIC
am_pickoff(data, size)
phys_bytes data;
unsigned size;
{
phys_copy(Inptr, data, (long)size);
Inptr += size;
}
/*===========================================================================*
* am_append *
*===========================================================================*/
PUBLIC
am_append(data, size, dosend)
phys_bytes data; /* not really a phys_bytes! really a vir_bytes */
unshort size;
int dosend;
{
phys_bytes paddr;
phys_bytes umap();
paddr = umap(proc_addr(cur_proc), D, (vir_bytes)data, (vir_bytes)size);
am_doappend(paddr, size, dosend);
}
/*===========================================================================*
* am_phys_copy *
*===========================================================================*/
PUBLIC
am_phys_copy(s, d, size)
vir_bytes s;
phys_bytes d;
phys_bytes size;
{
/*
** the phys_copy in trans.c needs a little help in places since kernel
** virtual address need to be umapped under minix
*/
phys_bytes umap();
phys_bytes ps;
ps = umap(proc_addr(cur_proc), D, (vir_bytes)s, (vir_bytes)size);
phys_copy(ps, d, size);
}
/*===========================================================================*
* ealookup *
*===========================================================================*/
PRIVATE
ealookup(addr)
Eth_addr * addr;
{
int index;
int i;
Eth_addr * mep;
index = addr->e[5] & 0x7f; /* hash it */
if (index >= MAPENTRIES)
index = 0;
i = index;
do
{
mep = &Eamap[i];
if (EACMP(addr, mep))
return i | ETHERBITS;
if (EANULL(mep))
{
*mep = *addr;
return i | ETHERBITS;
}
if (++i >= MAPENTRIES)
i = 0;
} while (i != index);
return 0;
}
/*
** the following routines provide the interface to the ethernet driver
*/
/*===========================================================================*
* net_init *
*===========================================================================*/
PUBLIC
net_init()
{
int pkt_arr(); /* called by ethernet driver when packet arrives */
int pkt_sent(); /* called by ethernet driver when packet was sent */
int eth_init(); /* initialise ethernet driver */
Eamap[MAPENTRIES] = Broadcastaddr;
epl_init();
etheraddr(&Myaddr);
pr_addr("Etheraddr:", &Myaddr);
eth_init(&Myaddr, pkt_arr, pkt_sent);
}
/*
** some special hack-defines because physical addresses are stored in a long
** and virtual addresses are not
*/
#define PROTO_OFFSET ((int) &((Etherpacket *) 0)->ep_fr.f_proto)
PRIVATE message arr_mess;
/*===========================================================================*
* pkt_arr *
*===========================================================================*/
PRIVATE
pkt_arr(addr, count)
phys_bytes addr;
int count;
{
/*
** This routine is called when an ethernet interrupt occurs.
** It must select appropriate amoeba task to give the packet to.
** NB: we are only interested in amoeba packets!
*/
short getbint();
short protocol;
protocol = getbint(addr + PROTO_OFFSET);
#ifdef ALTAMOEBAPROTO
if (protocol == AMOEBAPROTO || protocol == ALTAMOEBAPROTO)
#else
if (protocol == AMOEBAPROTO)
#endif
{
arr_mess.AM_OP = ETHER_ARRIV;
arr_mess.AM_PADDR = addr;
arr_mess.AM_COUNT = count;
interrupt(AMINT_CLASS, &arr_mess);
}
else /* not an amoeba packet, so give it back */
eth_release(addr);
}
/*===========================================================================*
* pkt_sent *
*===========================================================================*/
/*ARGSUSED*/
PRIVATE
pkt_sent (addr)
phys_bytes addr;
{
/*
** This is never called. The ethernet driver busy waits! It is here for
** compatibility with the ethernet driver
*/
}
/*===========================================================================*
* am_netenable *
*===========================================================================*/
PUBLIC
am_netenable() /* release the last received message */
{
eth_release((phys_bytes)arr_mess.AM_PADDR);
}
#endif NONET