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!"