tim@cmu-cs-k.ARPA (Tim Maroney) (08/27/85)
Here is a DDP socket listener taken from the MacIP sources (which at 12,000
lines are a wee bit too big for polite posting in toto). It is in the
public domain and I hope it will help other Appletalk developers.
The interface with the rest of MacIP deserves a comment. The routines
arp_wake, ip_wake, IPEnable, and recv_failed are written in Pascal and
called from the socket listener. Because they use global variables, it is
necessary to restore A5 before calling them. (A5 cannot be assumed correct
anywhere at the interrupt level, not even from a vertical retrace task,
which is pretty dumb, but there it is.) arp_wake and ip_wake notify the
synchronous (non-interrupt) levels of the system that an ARP packet or an IP
packet has been received, so the packet can be processed later. IPEnable
gets another packet buffer, pre-allocated because the Memory Manager can't
be used at interrupt level, and sets it up for the next packet reception.
recv_failed notifies the synchronous level that a packet reception failed
for some reason, usually because no packet buffer was ready to be read into;
all this really does is increment a statistics variable.
.Title 'IP Socket Listener'
; code for Appletalk IP socket listener
; written by Tim Maroney, C-MU, July 1985
.nomacrolist
.PAGE
.INCLUDE -UPPER-TLASM-SYSEQU
.INCLUDE -UPPER-TLASM-ATALKEQU
.INCLUDE -UPPER-TLASM-TOOLEQU
.INCLUDE -UPPER-TLASM-TOOLMACS
AB_IP .EQU 22 ; DDP protocol number for IP
AB_ARP .EQU 23 ; DDP protocol number for ARP
IPsize .EQU 600 ; length of an IP packet buffer
ARPSize .EQU 24 ; length of an ARP buffer
AB_IP_Socket .EQU 72 ; static (sigh) IP socket number
nb_len .EQU 14 ; offset of the length field in a packet
; Here is the code for the socket listener. See the Appletalk Manager
; documentation to understand this strange routine.
.PROC ip_listen
.REF ARPbuffer,IPEnable,arp_wake,ip_wake,ARPAllowed
.REF IPrdpkt,IPrdbuf,recv_failed
; paranoia: make sure the packet is for our socket
cmpi.b #AB_IP_Socket,d0
beq.s @1
bra recvfail
@1
; First we have to find out the protocol, so we can figure out where
; to store the packet. This requires figuring out whether this DDP
; header is long or short.
lea toRHA(a2),a3 ; a3 := addr of top of RHA (LAP header)
cmpi.b #shortDDP,lapType(a3) ; short DDP header?
; the addq may be used here beause an addq to an address register
; leaves the condition codes untouched
addq.l #lapHdSz,a3 ; a3 := addr of DDP Header (RHA + 3)
bne.s @2 ; if not, it's long
move.b sDDPType(a3),d0 ; d0 := ddp protocol byte from short hdr
bra.s @3
@2
move.b ddpType(a3),d0 ; d0 := ddp protocol byte from long hdr
@3
; d0 now contains the protocol ID from the DDP Header.
cmpi.b #AB_IP,d0 ; is this a packet for IP?
bne.s @4 ; if not, maybe for ARP
; IP packet handling
lea IPrdbuf,a3
tst.l (a3) ; is there an ip buffer to read into?
bne.s @5 ; if there is, continue
clr.w d3 ; else signal ReadRest to throw away packet
jsr 2(a4) ; ReadRest
move.l a5,-(sp)
movea.l CurrentA5,a5
jsr IPEnable ; re-enable IP receive -- IPEnable will not work
; if called in a re-entrant way, so do this at the
; high interrupt priority
move.w vSCCEnable(a2),sr ; lower priority
jsr recv_failed
move.l (sp)+,a5
rts
@5
movea.l IPrdpkt,a5 ; get packet address to store packet length
move.w d1,nb_len(a5) ; IPrdpkt^.nb_len := d1 (pkt length from caller)
move.w #IPsize,d3 ; size of buffer for ReadRest
movea.l IPrdbuf,a3 ; get address of buffer arg to ReadRest
jsr 2(a4) ; ReadRest
; disable reception of further packets for now
lea IPrdbuf,a0
clr.l (a0)
; put the packet on the IP queue and wake up the IP demultiplexer
move.l a5,-(sp)
movea.l CurrentA5,a5
lea IPrdpkt,a0
move.l (a0),-(sp) ; push arg for later ip_wake call
jsr IPEnable ; can't be re-entrant
move.w vSCCEnable(a2),sr ; decrease priority
jsr ip_wake
move.l (sp)+,a5
rts
; The packet wasn't for IP. It had better be for ARP or I'll take my
; ball and go home.
@4
cmpi.b #AB_ARP,d0 ; is this a packet for ARP?
beq.s @9 ; if so, continue to process it
bra.s recvfail
@9
; ARP packet handling: it's simple because there's just one fixed buffer
lea ARPAllowed,a3
tst.b (a3) ; is ARP allowed right now?
bne.s @10 ; if not zero, continue
bra.s recvfail
@10
move.w #ARPsize,d3 ; size of ARP buffer for ReadRest
lea ARPbuffer,a3 ; address of ARP buffer for ReadRest
jsr 2(a4) ; call ReadRest
tst.l d3 ; check return status from ReadRest
beq.s @11 ; if not exactly ARPsize received, return
rts
@11
lea ARPAllowed,a0
clr.b (a0) ; disallow further ARP until processed
; by ARP task (which calls ARPEnable)
move.w vSCCEnable(a2),sr ; lower priority for the wakeup
move.l a5,-(sp)
movea.l CurrentA5,a5
jsr arp_wake ; wake up ARP handling task
move.l (sp)+,a5
rts
; demultiplex failure -- read the rest of the packet into nowhere
recvfail
clr.w d3 ; signals ReadRest to throw away packet
jsr 2(a4) ; ReadRest
move.w vSCCEnable(a2),sr ; lower priority
move.l a5,-(sp)
movea.l CurrentA5,a5
jsr recv_failed
move.l (sp)+,a5
rts
; ARPEnable: just set ARPAllowed to 1
.PROC ARPEnable
.DEF ARPAllowed
lea ARPAllowed,a0
move.b #1,(a0)
rts
ARPAllowed .BYTE ; 0 if ARP reception not allowed right now
; the next two procedures are used by the Pascal routine IPEnable
; each takes a pointer argument, and sticks it into the appropriate place
.PROC SET_BUF
.DEF IPrdbuf
move.l (sp)+,a1
lea IPrdbuf,a0
move.l (sp)+,(a0)
jmp (a1)
.align 2
IPrdbuf .LONG ; pointer to packet buffer for IP read
.PROC SET_PKT
.DEF IPrdpkt
move.l (sp)+,a1
lea IPrdpkt,a0
move.l (sp)+,(a0)
jmp (a1)
.align 2
IPrdpkt .LONG ; pointer to packet for IP read
; getarpbuf: returns the address of the ARP buffer, for use by Pascal code
; FUNCTION getarpbuf:ARP_PACKET; EXTERNAL;
.FUNC getarpbuf
.DEF ARPbuffer
lea ARPbuffer,a0
move.l a0,4(sp)
rts
.align 2
ARPbuffer .block ARPsize ; buffer used for receiving ARP packets
.END
-=-
Tim Maroney, Carnegie-Mellon University, Networking
ARPA: Tim.Maroney@CMU-CS-K uucp: seismo!cmu-cs-k!tim
CompuServe: 74176,1360 audio: shout "Hey, Tim!"