/* enet.c Stanford 25 April 1983 */
* Ethernet packet filter layer,
* formerly: Ethernet interface driver
**********************************************************************
* 7 October 1985 Jeff Mogul Stanford
* Removed ENMAXOPENS limitation; available minors are now
* dynamically allocated to interfaces, out of pool of NENETFILTER
* Certain arrays formerly in the enState structure are now global.
* Depends on modified openi() function so that enetopen() need
* Remove support for "kernel access", it won't ever be used again.
* 17 October 1984 Jeff Mogul Stanford
* More performance improvements:
* Added ENF_CAND, ENF_COR, ENF_CNAND, and ENF_CNOR, short-circuit
* operators, to make filters run faster.
* All evaluate "(*sp++ == *sp++)":
* ENF_CAND: returns false immediately if result is false, otherwise
* ENF_COR: returns true immediately if result is true, otherwise
* ENF_CNAND: returns true immediately if result is false, otherwise
* ENF_CNOR: returns false immediately if result is true, otherwise
* Also added ENF_NEQ to complement ENF_EQ
* - Maintain count of received packets per filter, dynamically
* re-organize filter queue to keep highly active filters in
* front of queue (but maintaining priority order), if they are
* "high priority" filters.
* 2 October 1984 Jeff Mogul Stanford
* Made a few changes to enDoFilter() to speed it up, since profiling
* shows it to be rather popular:
* - precompute maximum word in packet and address of end of
* filters (thereby moving this code out of "inner loop").
* - minor re-arrangement to avoid re-evaluating a
* - changed #ifdef DEBUG in a few routines to #ifdef INNERDEBUG,
* so that code in inner loops isn't always testing the enDebug
* flag; this not only costs directly, but also breaks up some
* basic blocks that the optimizer could play with.
* - added enOneCopy flag; if true, then never deliver more than
* one copy of a packet. This is equivalent to giving everyone
* a "high priority" device, and cuts down the number of superfluous
* calls to enDoFilter(). [Temporary hack, will remove this!]
* 24 August 1984 Jeff Mogul Stanford
* YA bug with sleeping in enetwrite(); straightened out handling
* of counts in enKludgeSleep so that they indicate the number
* of sleeps in progress. Maybe I've got this right, now?
* Also, don't sleep forever (since the net might be down).
* 17 July 1984 Jeff Mogul Stanford
* Bug fix: in enetwrite(), several problems with sleeping on
* - don't do it for kernel mode writes.
* - count # of procs sleeping, to avoid lost wakeups. Old
* scheme would only wake up the first sleeper.
* - using sleeper-count, avoid using more than one timeout
* table entry per device; old scheme caused timeout table panics
* - trap interupted sleeps using setjmp, so that we can deallocate
* packet header and mbufs; otherwise we lost them and panicked.
* 5 July 1984 Jeff Mogul Stanford
* Bug fix: in enetwrite() make sure enP_RefCount is zero before
* deallocating "packet". Otherwise, "packets" get lost, and
* take mbufs (and ultimately, the system) with them.
* 8 December 1983 Jeffrey Mogul Stanford
* Fixed bug in enetwrite() that eventually caused allocator
* to run out of packets and panic. If enetwrite() returns
* an error it should first deallocate any packets it has allocated.
* 10 November 1983 Jeffrey Mogul Stanford
* Slight restructuring for support of 10mb ethers;
* - added the EIOCDEVP ioctl
* - removed the EIOCMTU ioctl (subsumed by EIOCDEVP)
* This requires an additional parameter to the enetattach
* call so that the device driver can specify things.
* Also, cleaned up the enDebug scheme by adding symbolic
* definitions for the bits.
* 25-Apr-83 Jeffrey Mogul Stanford
* Began conversion to 4.2BSD. This involves removing all
* references to the actual hardware.
* Changed read/write interface to use uio scheme.
* Changed ioctl interface to "new style"; this places a hard
* limit on the size of a filter (about 128 bytes).
* "Packets" now point to mbufs, not private buffers.
* Filter can only access data in first mbuf (about 50 words worst case);
* this is long enough for all Pup purposes.
* Added EIOCMTU ioctl to get MTU (max packet size).
* Added an enetselect() routine and other select() support.
* Other stuff is (more or less) left intact.
* Most previous history comments removed.
* Changed some names from enXXXX to enetXXXX to avoid confusion(?)
* 10-Aug-82 Mike Accetta (mja) at Carnegie-Mellon University
* Added new EIOCMBIS and EIOCMBIC ioctl calls to set and clear
* bits in mode word; added mode bit ENHOLDSIG which suppresses
* the resetting of an enabled signal after it is sent (to be
* used inconjunction with the SIGHOLD mechanism); changed
* EIOCGETP to zero pad word for future compatibility; changed enwrite()
* to enforce correct source host address on output packets (V3.05e).
* (Stanford already uses long timeout value and has no pad word - JCM)
* [Last change before 4.2BSD conversion starts.]
* 01-Dec-81 Mike Accetta (mja) at Carnegie-Mellon University
* Fixed bug in timeout handling caused by missing "break" in the
* "switch" state check within enetread(). This caused all reads
* to be preceeded by a bogus timeout. In addition, fixed another
* bug in signal processing by also recording process ID of
* process to signal when an input packet is available. This is
* necessary because it is possible for a process with an enabled
* signal to fork and exit with no guarantee that the child will
* reenable the signal. Thus under appropriately bizarre race
* conditions, an incoming packet to the child can cause a signal
* to be sent to the unsuspecting process which inherited the
* process slot of the parent. Of course, if the PID's wrap around
* AND the inheriting process has the same PID, well ... (V3.03d).
* 22-Feb-80 Rick Rashid (rfr) at Carnegie-Mellon University
* Rewritten to provide multiple user access via user settable
* 18-Jan-80 Mike Accetta (mja) at Carnegie-Mellon University
**********************************************************************
/* number of potential units */
#define NENET (NEC + NEN + NIL + NDE)
#include "../net/enetdefs.h"
#define NENETFILTER 256 /* maximum number of minor devices */
/* #define INNERDEBUG 1 */ /* define only when debugging enDoFilter()
#define enprintf(flags) if (enDebug&(flags)) printf
* Symbolic definitions for enDebug flag bits
* ENDBG_TRACE should be 1 because it is the most common
* use in the code, and the compiler generates faster code
* for testing the low bit in a word.
#define ENDBG_TRACE 1 /* trace most operations */
#define ENDBG_DESQ 2 /* trace descriptor queues */
#define ENDBG_INIT 4 /* initialization info */
#define ENDBG_SCAV 8 /* scavenger operation */
#define ENDBG_ABNORM 16 /* abnormal events */
#define min(a,b) ( ((a)<=(b)) ? (a) : (b) )
#define splenet splimp /* used to be spl6 but I'm paranoid */
#define PRINET 26 /* interruptible */
* 'enQueueElts' is the pool of packet headers used by the driver.
* 'enPackets' is the pool of packets used by the driver (these should
* be allocated dynamically when this becomes possible).
* 'enFreeq' is the queue of available packets
* 'enState' is the driver state table per logical unit number
* 'enUnit' is the physical unit number table per logical unit number;
* the first "attach"ed ethernet is logical unit 0, etc.
* 'enUnitMap' maps minor device numbers onto interface unit #s
* 'enAllocMap' indicates if minor device is allocated or free
* 'enAllDescriptors' stores OpenDescriptors, indexed by minor device #
* 'enFreeqMin' is the minimum number of packets ever in the free queue
* (for statistics purposes)
* 'enScavenges' is the number of scavenges of the active input queues
* (for statustics purposes)
* 'enDebug' is a collection of debugging bits which enable trace and/or
* diagnostic output as defined above (ENDBG_*)
* 'enUnits' is the number of attached units
* 'enOneCopy' if true, then no packet is delivered to more than one minor
struct enPacket enQueueElts
[ENPACKETS
];
struct enState enState
[NENET
];
char enUnitMap
[NENETFILTER
];
char enAllocMap
[NENETFILTER
];
enAllDescriptors
[NENETFILTER
];
int enFreeqMin
= ENPACKETS
;
int enDebug
= ENDBG_ABNORM
;
int enMaxMinors
= NENETFILTER
;
* Forward declarations for subroutines which return other
extern boolean
enDoFilter();
struct ifnet
*ifp
; /* which ifp for output */
struct sockaddr enetaf
= { AF_IMPLINK
};
/****************************************************************
* Various Macros & Routines *
****************************************************************/
* forAllOpenDescriptors(p) -- a macro for iterating
* over all currently open devices. Use it in place of
* "for ( ...; ... ; ... )"
* and supply your own loop body. The loop variable is the
* parameter p which is set to point to the descriptor for
* each open device in turn.
#define forAllOpenDescriptors(p) \
for ((p) = (struct enOpenDescriptor *)enDesq.enQ_F; \
(struct Queue *)(&enDesq) != &((p)->enOD_Link); \
(p) = (struct enOpenDescriptor *)(p)->enOD_Link.F)
* enEnqueue - add an element to a queue
#define enEnqueue(q, elt) \
enqueue((struct Queue *)(q), (struct Queue *)(elt)); \
* enFlushQueue - release all packets from queue, freeing any
* whose reference counts drop to 0. Assumes caller
* is at high IPL so that queue will not be modified while
register struct enQueue
*q
;
register struct enPacket
*qelt
;
while((qelt
=(struct enPacket
*)dequeue((struct Queue
*)q
)) != NULL
)
if (0 == --(qelt
->enP_RefCount
))
enEnqueue(&enFreeq
, qelt
);
* enInitWaitQueue - initialize an empty packet wait queue
register struct enWaitQueue
*wq
;
wq
->enWQ_MaxWaiting
= ENDEFWAITING
;
* enEnWaitQueue - add a packet to a wait queue
register struct enWaitQueue
*wq
;
wq
->enWQ_Packets
[wq
->enWQ_Tail
] = p
;
enNextWaitQueueIndex(wq
->enWQ_Tail
);
* enDeWaitQueue - remove a packet from a wait queue
register struct enWaitQueue
*wq
;
if (wq
->enWQ_NumQueued
< 0)
p
= wq
->enWQ_Packets
[wq
->enWQ_Head
];
enNextWaitQueueIndex(wq
->enWQ_Head
);
* enTrimWaitQueue - cut a wait queue back to size
enTrimWaitQueue(wq
, threshold
)
register struct enWaitQueue
*wq
;
register int Counter
= (wq
->enWQ_NumQueued
- threshold
);
register struct enPacket
*p
;
("enTrimWaitQueue(%x, %d): %d\n", wq
, threshold
, Counter
);
enPrevWaitQueueIndex(wq
->enWQ_Tail
);
p
= wq
->enWQ_Packets
[wq
->enWQ_Tail
];
if (0 == --(p
->enP_RefCount
))
* enFlushWaitQueue - remove all packets from wait queue
#define enFlushWaitQueue(wq) enTrimWaitQueue(wq, 0)
* index by number of active files; for N open files, each queue may retain
* up to 1/Nth of the packets not guaranteed to be freed on scavenge. The
* total number of available packets is computed less one for sending.
char enScavLevel
[NENETFILTER
+1];
* enInitScavenge -- set up ScavLevel table
register int PoolSize
= (ENPACKETS
-ENMINSCAVENGE
);
register int i
= sizeof(enScavLevel
);
PoolSize
--; /* leave one for transmitter */
enScavLevel
[i
] = (PoolSize
/ i
);
* enScavenge -- scan all OpenDescriptors for all ethernets, releasing
* any queued buffers beyond the prescribed limit and freeing any whose
* Assumes caller is at high IPL so that it is safe to modify the queues.
register struct enOpenDescriptor
*d
;
register int threshold
= 0;
register struct enState
*enStatep
;
for (enStatep
=enState
; enStatep
< &enState
[NENET
]; enStatep
++)
threshold
= enScavLevel
[threshold
];
/* recalculate thresholds based on current allocations */
enprintf(ENDBG_SCAV
)("enScavenge: %d\n", threshold
);
for (enStatep
=enState
; enStatep
< &enState
[NENET
]; enStatep
++)
continue; /* never initialized */
enTrimWaitQueue(&(d
->enOD_Waiting
), threshold
);
* enAllocatePacket - allocate the next packet from the free list
* Assumes IPL is at high priority so that it is safe to touch the
* packet queue. If the queue is currently empty, scavenge for
register struct enPacket
*p
;
if (0 == enFreeq
.enQ_NumQueued
)
p
= (struct enPacket
*)dequeue((struct Queue
*)&enFreeq
);
panic("enAllocatePacket");
if (enFreeqMin
> --enFreeq
.enQ_NumQueued
)
enFreeqMin
= enFreeq
.enQ_NumQueued
;
p
->enP_RefCount
= 0; /* just in case */
* enDeallocatePacket - place the packet back on the free packet queue
#define enDeallocatePacket(p) \
if (p->enP_RefCount) panic("enDeallocatePacket: refcount != 0");\
enqueue((struct Queue *)&enFreeq, (struct Queue *)(p)); \
enFreeq.enQ_NumQueued++; \
/****************************************************************
* Routines to move uio data to/from mbufs *
****************************************************************/
* These two routines were inspired by/stolen from ../sys/uipc_socket.c
* Both return error code (or 0 if success).
* read: return contents of mbufs to user. DO NOT free them, since
* there may be multiple claims on the packet!
register struct uio
*uio
;
count
= min(count
, uio
->uio_resid
); /* # of bytes to return */
while ((count
> 0) && m
&& (error
== 0)) {
len
= min(count
, m
->m_len
); /* length of this transfer */
error
= uiomove(mtod(m
, caddr_t
), (int)len
, UIO_READ
, uio
);
register struct uio
*uio
;
register struct mbuf
**mbufp
; /* top mbuf is returned by reference */
register struct mbuf
**mp
= &mtop
;
register struct iovec
*iov
;
while ((uio
->uio_resid
> 0) && (error
== 0)) {
panic("enwmove: uio_iovcnt < 0 while uio_resid > 0");
MGET(m
, M_WAIT
, MT_DATA
);
if (iov
->iov_len
>= CLBYTES
) { /* big enough to use a page */
m
->m_off
= (int)p
- (int)m
;
len
= MIN(MLEN
, iov
->iov_len
);
error
= uiomove(mtod(m
, caddr_t
), len
, UIO_WRITE
, uio
);
if (error
) { /* probably uiomove fouled up */
*mbufp
= mtop
; /* return ptr to top mbuf */
* enetopen - open ether net device
* Errors: ENXIO - illegal minor device number
* EBUSY - minor device already in use
enetopen(dev
, flag
, newmin
)
register int unit
= minor(dev
);
register struct enState
*enStatep
;
* Each open enet file has a different minor device number.
* When a user tries to open any of them, we actually open
* any available minor device and associate it with the
* This is not elegant, but UNIX will call
* open for each new open file using the same inode but calls
* close only when the last open file referring to the inode
* is released. This means that we cannot know inside the
* driver code when the resources associated with a particular
* open of the same inode should be deallocated. Thus, we have
* to make up a temporary inode to represent each simultaneous
* open of the ethernet. Each inode has a different minor device number.
enprintf(ENDBG_TRACE
)("enetopen(%o, %x):\n", unit
, flag
);
/* check for illegal minor dev */
if ( (unit
>= enUnits
) /* bad unit */
|| (enet_info
[unit
].ifp
== 0) /* ifp not known */
|| ((enet_info
[unit
].ifp
->if_flags
& IFF_UP
) == 0) )
enprintf(ENDBG_TRACE
)("enetopen: md = %d\n", md
);
error
= mkpseudo(makedev(major(dev
), md
)));
enStatep
= &enState
[unit
];
("enetopen: Desq: %x, %x\n", enDesq
.enQ_F
, enDesq
.enQ_B
);
enInitDescriptor(&enAllDescriptors
[md
], flag
);
enInsertDescriptor(&(enDesq
), &enAllDescriptors
[md
]);
* enFindMinor - find a free logical device on specified unit
for (md
= 0; md
< enMaxMinors
; md
++) {
if (enAllocMap
[md
] == FALSE
)
* enInit - intialize ethernet unit (called by enetattach)
register struct enState
*enStatep
;
enprintf(ENDBG_INIT
)("enInit(%x %d):\n", enStatep
, unit
);
/* initialize free queue if not already done */
initqueue((struct Queue
*)&enFreeq
);
for (i
=0; i
<ENPACKETS
; i
++)
register struct enPacket
*p
;
/* also a good time to init enAllocMap */
for (i
= 0; i
< enMaxMinors
; i
++)
initqueue((struct Queue
*)&enDesq
); /* init descriptor queue */
* enetclose - ether net device close routine
register int md
= ENINDEX(dev
);
register struct enState
*enStatep
= &enState
[ENUNIT(dev
)];
register struct enOpenDescriptor
*d
= &enAllDescriptors
[md
];
enprintf(ENDBG_TRACE
)("enetclose(%d, %x):\n", md
, flag
);
* insure that receiver doesn't try to queue something
* for the device as we are decommissioning it.
* (I don't think this is necessary, but I'm a coward.)
dequeue((struct Queue
*)d
->enOD_Link
.B
);
("enetclose: Desq: %x, %x\n", enDesq
.enQ_F
, enDesq
.enQ_B
);
enFlushWaitQueue(&(d
->enOD_Waiting
));
* enetread - read next packet from net
register struct uio
*uio
;
register struct enOpenDescriptor
*d
= &enAllDescriptors
[ENINDEX(dev
)];
register struct enPacket
*p
;
enprintf(ENDBG_TRACE
)("enetread(%x):", dev
);
* If nothing is on the queue of packets waiting for
* this open enet file, then set timer and sleep until
* either the timeout has occurred or a packet has
while (0 == d
->enOD_Waiting
.enWQ_NumQueued
)
* If there was a previous timeout pending for this file,
* cancel it before setting another. This is necessary since
* a cancel after the sleep might never happen if the read is
* interrupted by a signal.
if (d
->enOD_RecvState
== ENRECVTIMING
)
untimeout(enTimeout
, (caddr_t
)d
);
timeout(enTimeout
, (caddr_t
)d
, (int)(d
->enOD_Timeout
));
d
->enOD_RecvState
= ENRECVTIMING
;
d
->enOD_RecvState
= ENRECVIDLE
;
sleep((caddr_t
)d
, PRINET
);
switch (d
->enOD_RecvState
)
untimeout(enTimeout
, (caddr_t
)d
);
d
->enOD_RecvState
= ENRECVIDLE
;
p
= enDeWaitQueue(&(d
->enOD_Waiting
));
* Move data from packet into user space.
error
= enrmove(p
->enP_mbuf
, uio
, p
->enP_ByteCount
);
if (0 == --(p
->enP_RefCount
)) /* if no more claims on this packet */
m_freem(p
->enP_mbuf
); /* release mbuf */
enDeallocatePacket(p
); /* and packet */
* enTimeout - process ethernet read timeout
register struct enOpenDescriptor
* d
;
enprintf(ENDBG_TRACE
)("enTimeout(%x):\n", d
);
d
->enOD_RecvState
= ENRECVTIMEDOUT
;
* enetwrite - write next packet to net
int enKludgeSleep
[NENET
]; /* Are we sleeping on IF_QFULL? */
/* really, # of procs sleeping on IF_QFULL */
register struct uio
*uio
;
register int unit
= ENUNIT(dev
);
register struct enState
*enStatep
= &enState
[unit
];
register struct ifnet
*ifp
= enet_info
[unit
].ifp
;
enprintf(ENDBG_TRACE
)("enetwrite(%x):\n", dev
);
if (uio
->uio_resid
> ifp
->if_mtu
) /* too large */
* Copy user data into mbufs
if (error
= enwmove(uio
, &mp
)) {
* hang around until there's room or until process is interrupted
while (IF_QFULL(&(ifp
->if_snd
))) {
if (sleepcount
++ > 2) { /* don't sleep too long */
/* if nobody else has a timeout pending for this unit, set one */
if (enKludgeSleep
[unit
] == 0)
timeout(enKludgeTime
, (caddr_t
)unit
, 2 * hz
);
enKludgeSleep
[unit
]++; /* record that we are sleeping */
if (setjmp(&u
.u_qsave
)) {
/* sleep (following) was interrupted, clean up */
("enetwrite(%x): enet%d sleep %d interrupted\n", dev
,
unit
, enKludgeSleep
[unit
]);
enKludgeSleep
[unit
]--; /* we're no longer sleeping */
sleep((caddr_t
)&(enKludgeSleep
[unit
]), PRINET
);
enKludgeSleep
[unit
]--; /* we are no longer sleeping */
/* place mbuf chain on outgoing queue & start if necessary */
error
= (*ifp
->if_output
)(ifp
, mp
, &enetaf
);
/* this always frees the mbuf chain */
/* XXX perhaps we should always wakeup? */
if (enKludgeSleep
[unit
]) {
wakeup((caddr_t
)&(enKludgeSleep
[unit
]));
/* XXX should we restart transmitter? */
* enetioctl - ether net control
* EIOCGETP - get ethernet parameters
* EIOCSETP - set ethernet read timeout
* EIOCSETF - set ethernet read filter
* EIOCENBS - enable signal when read packet available
* EIOCINHS - inhibit signal when read packet available
* FIONREAD - check for read packet available
* EIOCSETW - set maximum read packet waiting queue length
* EIOCFLUSH - flush read packet waiting queue
* EIOCMBIS - set mode bits
* EIOCMBIC - clear mode bits
* EICODEVP - get device parameters
* EIOCMFREE - number of free minors
enetioctl(dev
, cmd
, addr
, flag
)
register struct enState
*enStatep
= &enState
[ENUNIT(dev
)];
register struct enOpenDescriptor
* d
= &enAllDescriptors
[ENINDEX(dev
)];
("enetioctl(%x, %x, %x, %x):\n", dev
, cmd
, addr
, flag
);
t
.en_maxwaiting
= ENMAXWAITING
;
t
.en_maxpriority
= ENMAXPRI
;
t
.en_rtout
= d
->enOD_Timeout
;
t
.en_maxfilters
= ENMAXFILTERS
;
bcopy((caddr_t
)&t
, addr
, sizeof t
);
bcopy(addr
, (caddr_t
)&t
, sizeof t
);
d
->enOD_Timeout
= t
.en_rtout
;
bcopy(addr
, (caddr_t
)&f
, sizeof f
);
if (f
.enf_FilterLen
> ENMAXFILTERS
)
/* insure that filter is installed indivisibly */
bcopy((caddr_t
)&f
, (caddr_t
)&(d
->enOD_OpenFilter
), sizeof f
);
/* pre-compute length of filter */
fp
= &(d
->enOD_OpenFilter
.enf_Filter
[0]);
d
->enOD_FiltEnd
= &(fp
[d
->enOD_OpenFilter
.enf_FilterLen
]);
d
->enOD_RecvCount
= 0; /* reset count when filter changes */
dequeue((struct Queue
*)d
->enOD_Link
.B
);
enInsertDescriptor(&(enDesq
), d
);
* Enable signal n on input packet
bcopy(addr
, (caddr_t
)&snum
, sizeof snum
);
d
->enOD_SigProc
= u
.u_procp
;
d
->enOD_SigPid
= u
.u_procp
->p_pid
;
d
->enOD_SigNumb
= snum
; /* This must be set last */
* Disable signal on input packet
* Check for packet waiting
register struct enWaitQueue
*wq
;
if ((wq
= &(d
->enOD_Waiting
))->enWQ_NumQueued
)
n
= wq
->enWQ_Packets
[wq
->enWQ_Head
]->enP_ByteCount
;
bcopy((caddr_t
)&n
, addr
, sizeof n
);
* Set maximum recv queue length for a device
bcopy(addr
, (caddr_t
)&un
, sizeof un
);
* ---------------- ------------
* MAXWAITING..-1 -> MAXWAITING
d
->enOD_Waiting
.enWQ_MaxWaiting
= (un
) ? min(un
, ENMAXWAITING
)
* Flush all packets queued for a device
enFlushWaitQueue(&(d
->enOD_Waiting
));
bcopy(addr
, (caddr_t
)&mode
, sizeof mode
);
bcopy(addr
, (caddr_t
)&mode
, sizeof mode
);
* Return hardware-specific device parameters.
bcopy((caddr_t
)&(enDevParams
), addr
, sizeof(struct endevp
));
* Return # of free minor devices.
for (md
= 0; md
< enMaxMinors
; md
++)
if (enAllocMap
[md
] == FALSE
)
/****************************************************************
* Support for select() system call *
****************************************************************/
* inspired by the code in tty.c for the same purpose.
* enetselect - returns true iff the specific operation
* will not block indefinitely. Otherwise, return
* false but make a note that a selwakeup() must be done.
register struct enOpenDescriptor
*d
;
register struct enWaitQueue
*wq
;
* an imitation of the FIONREAD ioctl code
d
= &(enAllDescriptors
[ENINDEX(dev
)]);
avail
= 1; /* at least one packet queued */
avail
= 0; /* sorry, nothing queued now */
* If there's already a select() waiting on this
* minor device then this is a collision.
* [This shouldn't happen because enet minors
* really should not be shared, but if a process
* forks while one of these is open, it is possible
* that both processes could select() us.]
&& d
->enOD_SelProc
->p_wchan
== (caddr_t
)&selwait
)
d
->enOD_SelProc
= u
.u_procp
;
* since the queueing for output is shared not just with
* the other enet devices but also with the IP system,
* we can't predict what would happen on a subsequent
* write. However, since we presume that all writes
* complete eventually, and probably fairly fast, we
* pretend that select() is true.
return(1); /* don't block in select() */
register struct enOpenDescriptor
*d
;
selwakeup(d
->enOD_SelProc
, d
->enOD_SelColl
);
* enetFilter - incoming linkage from ../vaxif/if_en.c
register struct enState
*enStatep
= &enState
[en
];
register struct enPacket
*p
;
register int pullcount
; /* bytes, not words */
enprintf(ENDBG_TRACE
)("enetFilter(%d):\n", en
);
p
= enAllocatePacket(); /* panics if not possible */
p
->enP_ByteCount
= count
;
pullcount
= min(MLEN
, count
); /* largest possible first mbuf */
if (m
->m_len
< pullcount
) {
/* first mbuf not as full as it could be - fix this */
if ((m
= m_pullup(m
, pullcount
)) == 0) {
/* evidently no resources; bloody m_pullup discarded mbuf */
p
->enP_Data
= mtod(m
, u_short
*);
enInputDone(enStatep
, p
);
* enInputDone - process correctly received packet
register struct enState
*enStatep
;
register struct enPacket
*p
;
register struct enOpenDescriptor
*d
;
register unsigned long rcount
;
register struct enOpenDescriptor
*prevd
;
enprintf(ENDBG_TRACE
)("enInputDone(%x): %x\n", enStatep
, p
);
/* precompute highest possible word offset */
/* can't address beyond end of packet or end of first mbuf */
maxword
= (min(p
->enP_ByteCount
, p
->enP_mbuf
->m_len
)>>1);
if (enDoFilter(p
, d
, maxword
))
if (d
->enOD_Waiting
.enWQ_NumQueued
< d
->enOD_Waiting
.enWQ_MaxWaiting
)
enEnWaitQueue(&(d
->enOD_Waiting
), p
);
enprintf(ENDBG_TRACE
)("enInputDone: queued\n");
/* send notification when input packet received */
if (d
->enOD_SigProc
->p_pid
== d
->enOD_SigPid
)
psignal(d
->enOD_SigProc
, d
->enOD_SigNumb
);
if ((d
->enOD_Flag
& ENHOLDSIG
) == 0)
d
->enOD_SigNumb
= 0; /* disable signal */
rcount
= ++(d
->enOD_RecvCount
);
/* see if ordering of filters is wrong */
if (d
->enOD_OpenFilter
.enf_Priority
>= ENHIPRI
) {
prevd
= (struct enOpenDescriptor
*)d
->enOD_Link
.B
;
* If d is not the first element on the queue, and
* the previous element is at equal priority but has
* a lower count, then promote d to be in front of prevd.
if (((struct Queue
*)prevd
!= &(enDesq
.enQ_Head
)) &&
(d
->enOD_OpenFilter
.enf_Priority
==
prevd
->enOD_OpenFilter
.enf_Priority
)) {
/* threshold difference to avoid thrashing */
if ((100 + prevd
->enOD_RecvCount
) < rcount
) {
enReorderQueue(&(prevd
->enOD_Link
), &(d
->enOD_Link
));
break; /* high-priority filter => no more deliveries */
if (queued
== 0) /* this buffer no longer in use */
m_freem(p
->enP_mbuf
); /* free mbuf */
enDeallocatePacket(p
); /* and packet */
#define opx(i) (i>>ENF_NBPA)
enDoFilter(p
, d
, maxword
)
struct enOpenDescriptor
*d
;
register unsigned short *sp
;
register unsigned short *fp
;
register unsigned short *fpe
;
unsigned short stack
[ENMAXFILTERS
+1];
struct fw
{unsigned arg
:ENF_NBPA
, op
:ENF_NBPO
;};
enprintf(ENDBG_TRACE
)("enDoFilter(%x,%x):\n", p
, d
);
sp
= &stack
[ENMAXFILTERS
];
fp
= &d
->enOD_OpenFilter
.enf_Filter
[0];
/* ^ is really: fpe = &fp[d->enOD_OpenFilter.enf_FilterLen]; */
op
= ((struct fw
*)fp
)->op
;
arg
= ((struct fw
*)fp
)->arg
;
* This next test is a little bogus; since arg
* is unsigned, it is always >= 0 (the compiler
* knows this and emits no code). If arg were
* less than ENF_PUSHWORD before the subtract,
* it is certaintly going to be more than maxword
* afterward, so the code does work "right"
if ((arg
>= 0) && (arg
< maxword
))
*--sp
= p
->enP_Data
[arg
];
enprintf(ENDBG_TRACE
)("=>0(len)\n");
if (sp
< &stack
[2]) /* check stack overflow: small yellow zone */
enprintf(ENDBG_TRACE
)("=>0(--sp)\n");
* all non-NOP operators binary, must have at least two operands
if (sp
> &stack
[ENMAXFILTERS
-2])
enprintf(ENDBG_TRACE
)("=>0(sp++)\n");
enprintf(ENDBG_TRACE
)("=>0(def)\n");
/* short-circuit operators */
enprintf(ENDBG_TRACE
)("=>COR %x\n", *sp
);
enprintf(ENDBG_TRACE
)("=>CAND %x\n", *sp
);
enprintf(ENDBG_TRACE
)("=>COR %x\n", *sp
);
enprintf(ENDBG_TRACE
)("=>CAND %x\n", *sp
);
enprintf(ENDBG_TRACE
)("=>%x\n", *sp
);
enInitDescriptor(d
, flag
)
register struct enOpenDescriptor
*d
;
enprintf(ENDBG_TRACE
)("enInitDescriptor(%x):\n", d
);
d
->enOD_RecvState
= ENRECVIDLE
;
d
->enOD_OpenFilter
.enf_FilterLen
= 0;
d
->enOD_OpenFilter
.enf_Priority
= 0;
d
->enOD_FiltEnd
= &(d
->enOD_OpenFilter
.enf_Filter
[0]);
d
->enOD_SelProc
= 0; /* probably unnecessary */
* Remember the PID that opened us, at least until some process
* sets a signal for this minor device
d
->enOD_SigPid
= u
.u_procp
->p_pid
;
enInitWaitQueue(&(d
->enOD_Waiting
));
enprintf(ENDBG_TRACE
)("=>eninitdescriptor\n");
* enInsertDescriptor - insert open descriptor in queue ordered by priority
register struct enQueue
*q
;
register struct enOpenDescriptor
*d
;
struct enOpenDescriptor
* nxt
;
nxt
= (struct enOpenDescriptor
*)q
->enQ_F
;
while ((struct Queue
*)q
!= &(nxt
->enOD_Link
))
if (d
->enOD_OpenFilter
.enf_Priority
> nxt
->enOD_OpenFilter
.enf_Priority
)
nxt
= (struct enOpenDescriptor
*)nxt
->enOD_Link
.F
;
enqueue((struct Queue
*)&(nxt
->enOD_Link
),(struct Queue
*)&(d
->enOD_Link
));
enprintf(ENDBG_DESQ
)("enID: Desq: %x, %x\n", q
->enQ_F
, q
->enQ_B
);
int enReorderCount
= 0; /* for external monitoring */
* enReorderQueue - swap order of two elements in queue
* assumed to be called at splenet
enReorderQueue(first
, last
)
register struct Queue
*first
;
register struct Queue
*last
;
register struct Queue
*prev
;
register struct Queue
*next
;
enprintf(ENDBG_DESQ
)("enReorderQ: %x, %x\n", first
, last
);
/* get pointers to other queue elements */
* no more reading from queue elements; this ensures that
* the code works even if there are fewer than 4 elements
register struct enState
*enStatep
= &enState
[enUnits
];
enprintf(ENDBG_INIT
) ("enetattach: type %d, addr ", devp
->end_dev_type
);
if (enDebug
&ENDBG_INIT
) {
for (i
= 0; i
< devp
->end_addr_len
; i
++)
printf("%o ", devp
->end_addr
[i
]);
enet_info
[enUnits
].ifp
= ifp
;
bcopy((caddr_t
)devp
, (caddr_t
)&(enDevParams
), sizeof(struct endevp
));
enInit(enStatep
, enUnits
);