[net.sources.mac] DDP Socket Listener

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