home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / C / Snippets / PReadDDP / PReadDDP.c next >
Encoding:
Text File  |  1993-09-08  |  15.3 KB  |  447 lines  |  [TEXT/KAHL]

  1. /********************************************************************************
  2.  *
  3.  * PReadDDP.c        Written by Jon Hueras [76174,3267]
  4.  *
  5.  *    This file provides a capability analogous to DDPRead in the "alternate"
  6.  *    AppleTalk interface for the "preferred" interface. You pass a pointer
  7.  *    to the PReadDDPListener entry point below to POpenSkt. Subsequent calls
  8.  *    to PReadDDP allow you to queue up one or more packet buffers to receive
  9.  *    incoming packets to that socket. Another entry point provides the
  10.  *    ability to flush all queued buffers for a particular socket.
  11.  *
  12.  *    The socket listener is designed so that you can assign it to multiple
  13.  *    sockets. A separate queue is maintained for each possible socket number.
  14.  *    This requires a 2K table containing 256 queue headers. If you cannot
  15.  *    afford that much memory (?), it is fairly straightforward to modify the
  16.  *    code to maintain a single queue, but then the resulting socket listener
  17.  *    could only be attached to a single socket at a time.
  18.  *
  19.  * Interfaces:
  20.  *
  21.  *   void PReadDDP(Byte SocketNum, DDPPacket *PacketBuff, Boolean Async);
  22.  *
  23.  *    PReadDDP takes the PacketBuff given and places it at the end of the
  24.  *    queue for the given SocketNum. The format of the PacketBuff is shown in
  25.  *    PReadDDP.h and, except for the PacketReceived flag at the beginning,
  26.  *    mirrors the packet format as received (and as described in Inside Mac
  27.  *    Volume II). When a valid packet arrives on the given socket, the socket
  28.  *    listener removes the PacketBuff from the socket's queue, places the
  29.  *    packet data into the PacketBuff, and sets the flag (which is initially
  30.  *    cleared by PReadDDP). If you pass FALSE for the Async parameter, PReadDDP
  31.  *    will wait in an internal loop for the flag to be set before returning.
  32.  *
  33.  *    Note that if the LAP protocol type of the packet is shortDDP, then the
  34.  *    CheckSum and the Net numbers will be cleared to zeros and the Node
  35.  *    numbers will be copied from the LAP header. To return a packet to the
  36.  *    sender, you need only copy the source Net, Node, and Socket fields to
  37.  *    the corresponding destination fields and pass a pointer to the
  38.  *    LAPDstNode field in your first WDS entry.
  39.  *
  40.  *    It is important to ensure that while a PacketBuff is queued it is not
  41.  *    purged or moved or altered in any way. Similarly, as long as the socket
  42.  *    listener is attached to any socket it must not be purged or moved. This
  43.  *    means that any code segment or resource that contains the socket
  44.  *    listener must be locked while it is in use.
  45.  *
  46.  *   void PReadDDPFlush(Byte SocketNum);
  47.  *
  48.  *    PReadDDPFlush will clear the socket listener's queue for the given
  49.  *    socket number. This is not quite the same as DDPReadCancel, but purging
  50.  *    individual queue elements doesn't make sense to me.
  51.  *
  52.  *    Because of the asynchronous nature of packet reception, one or more
  53.  *    buffers that were queued may no longer be in the queue by the time the
  54.  *    queue is flushed, i.e., they may have received packets. Buffers that are
  55.  *    actually flushed from the queue will have their PacketReceived flags
  56.  *    still clear, whereas buffers that have received packets will have their
  57.  *    flags set.
  58.  *
  59.  *    If you close a socket that uses PReadDDPListener, you should flush the
  60.  *    socket's queue unless you are certain it is already empty. This is in
  61.  *    case you open another socket that gets the same socket number.
  62.  *
  63.  * Implementation Details:
  64.  *
  65.  *    The socket table, which is defined using DC.L directives has the
  66.  *    following equivalent format in C:
  67.  *
  68.  *        struct {
  69.  *           DDPPacket *Head, *Tail;
  70.  *        } SocketTable[256];
  71.  *
  72.  *    The Head field points to the first buffer in the queue and the Tail
  73.  *    field points to the last buffer in the queue. If there is only one
  74.  *    buffer in the queue, then both the Head and the Tail point to it. If the
  75.  *    queue is empty, then both the Head and the Tail are NULL (zero).
  76.  *
  77.  *    While a packet buffer is in a queue, it is treated as though it had the
  78.  *    following format:
  79.  *
  80.  *        struct {
  81.  *           Boolean   PacketReceived;
  82.  *           DDPPacket *NextBuffer;
  83.  *        };
  84.  *
  85.  *    When the socket listener is called, it may not necessarily result in the
  86.  *    incoming packet being received into a buffer. A packet may be rejected
  87.  *    for one of three reasons:
  88.  *
  89.  *       1. There are no buffers queued for the socket.
  90.  *
  91.  *       2. There was a low-level error in the packet (such as a CRC
  92.  *          or framing error) reported by MPP.
  93.  *
  94.  *       3. The packet was of the longDDP form, had a non-zero checksum,
  95.  *          and the computed checksum didn't match the received one.
  96.  *
  97.  ********************************************************************************/
  98.  
  99.  
  100. /********************************************************************************
  101.  *    The #define immediately preceding the #include below inhibits the
  102.  *    prototype declarations in the #include. Alternate prototypes follow
  103.  *    the #include. This is all made necessary by the fact that all three
  104.  *    functions are actually entry points to a single assembly language
  105.  *    function. The subsequent #defines provide symbolic references to the
  106.  *    stack-based parameters of PReadDDP and PReadDDPFlush.
  107.  ********************************************************************************/
  108.  
  109. #define _No_PReadDDP_Prototypes_
  110. #include "PReadDDP.h"
  111.  
  112. void PReadDDP(void);
  113. void PReadDDPFlush(void);
  114. void PReadDDPListener(void);
  115.  
  116. #define SocketNum     4(a7)
  117. #define PacketBuff     6(a7)
  118. #define Async        10(a7)
  119.  
  120. /********************************************************************************
  121.  *    Since neither AppleTalk.h nor nAppleTalk.h provide any support for
  122.  *    socket listeners, the following #defines help to make things more
  123.  *    readable. See the AppleTalk Manager chapter in IM Vol. II for details.
  124.  ********************************************************************************/
  125.  
  126. #define ReadRest    2(a4)
  127.  
  128. #define toRHA        1
  129. #define RHA_LapProto    toRHA+2(a2)
  130. #define RHA_DDPLength    toRHA+3(a2)
  131. #define RHA_DDPChecksum    toRHA+5(a2)
  132. #define RHA_DDPDstNet    toRHA+7(a2)
  133.  
  134. #define shortDDP    1
  135.  
  136. /********************************************************************************
  137.  *    PktOffset is a macro that is merely easier to type and read than using
  138.  *    OFFSET(DDPPacket, ...). NextBuff is a macro that makes references to
  139.  *    a buffer's queue link field easier to read. Defining a union or another
  140.  *    struct would have accomplished this as well. The PacketReceived macro
  141.  *    is purely cosmetic.
  142.  ********************************************************************************/
  143.  
  144. #define PktOffset(field)    OFFSET(DDPPacket, field)
  145. #define NextBuff(reg)        2(reg)
  146. #define PacketReceived(reg)    (reg)
  147.  
  148.  
  149. void PReadDDP(void)
  150.   {
  151.     asm {
  152.     
  153.     ; Get the address of the socket table and the socket number. Use the socket
  154.     ; number to index into the socket table to the queue header for the socket.
  155.     ; Then get the packet buffer address.
  156.     
  157.         lea    @SocketTable, a1
  158.         moveq    #0, d0
  159.         move.b    SocketNum, d0
  160.         lsl.w    #3, d0                ;socket num * 8 bytes per table entry
  161.         add.w    d0, a1
  162.             move.l    PacketBuff, a0
  163.     
  164.     ; Disable interrupts to prevent the socket listener from manipulating the
  165.     ; queue while we are in the middle of changing it. Interrupts will only be
  166.     ; disabled for a few microseconds.
  167.         
  168.         move    sr, d1
  169.         ori    #0x0700, sr
  170.     
  171.     ; See if the queue is currently empty
  172.         
  173.         tst.l    (a1)+
  174.         beq.s    @QEmpty
  175.     
  176.     ; If the queue isn't empty, we make both the last queue entry and the
  177.     ; queue header Tail point to the new buffer.
  178.         
  179.         move.l    (a1), d0
  180.         move.l    a0, (a1)
  181.         move.l    d0, a1
  182.         move.l    a0, NextBuff(a1)
  183.         bra.s    @ClearFlag
  184.     
  185.     ; If the queue is empty, we just make both the Head and Tail of the queue
  186.     ; header point to it.
  187.         
  188. QEmpty:        move.l    a0, (a1)
  189.         move.l    a0, -(a1)
  190.     
  191.     ; Clear the PacketReceived flag and the queue link and reenable interrupts.
  192.         
  193. ClearFlag:    clr.b    PacketReceived(a0)
  194.             clr.l    NextBuff(a0)
  195.         move    d1, sr
  196.     
  197.     ; Check the Async parameter. If FALSE, wait for the PacketReceived flag to
  198.     ; be set by the socket listener before returning. Otherwise return
  199.     ; immediately.
  200.         
  201.         tst.b    Async
  202.         bne.s    @Asynch
  203.         
  204. Synch:        tst.b    PacketReceived(a0)
  205.         beq.s    @Synch
  206.         
  207. Asynch:        rts
  208.  
  209.  
  210. extern PReadDDPFlush:
  211.     
  212.     ; Get the address of the socket table and the socket number. Use the socket
  213.     ; number to index into the socket table to the queue header for the socket.
  214.     
  215.         lea    @SocketTable, a1
  216.         moveq    #0, d0
  217.         move.b    SocketNum, d0
  218.         lsl.w    #3, d0                ;socket num * 8 bytes per table entry
  219.         add.w    d0, a1
  220.     
  221.     ; Disable interrupts to prevent the socket listener from manipulating the
  222.     ; queue while we are in the middle of changing it. Interrupts will only be
  223.     ; disabled for a couple of microseconds.
  224.         
  225.         move    sr, d1
  226.         ori    #0x0700, sr
  227.     
  228.     ; Clear the Head and Tail of the queue header.
  229.     
  230.         clr.l    (a1)+
  231.         clr.l    (a1)
  232.     
  233.     ; Reenable interrupts and return
  234.     
  235.         move    d1, sr
  236.         rts
  237.  
  238.  
  239. extern PReadDDPListener:
  240.  
  241.     ; Upon entry to the socket listener, registers are set as follows:
  242.     ;
  243.     ;    A0-A1    Reserved for use by MPP
  244.     ;    A2    Pointer to MPP globals
  245.     ;    A3    Doesn't contain anything useful - needed to pass param to ReadRest
  246.     ;    A4    Points to ReadPacket and ReadRest jump vectors
  247.     ;    A5    Available for use until ReadRest is called, but we don't need it
  248.     ;
  249.     ;    D0    Destination socket number
  250.     ;    D1    Number of bytes remaining to be read in packet
  251.     ;    D2    Available for use
  252.     ;    D3    Available for use, but needed for parameter passing to ReadRest
  253.     ;
  254.     ; After ReadRest has been called A4 and A5 are no longer used or available.
  255.     ; A0-A3 are available for use, but A2 still contains a pointer to MPP globals
  256.     ; and we'll continue to use it to access MPP's Read Header Area (RHA), which
  257.     ; holds the packet's DDP header. D0-D3 are available for use.
  258.     
  259.     
  260.     ; Get the address of the socket table and the socket number. Use the socket
  261.     ; number to index into the socket table to the queue header for the socket.
  262.     
  263.         lea    @SocketTable, a3
  264.         and.w    #0x00FF, d0
  265.         lsl.w    #3, d0                ;socket num * 8 bytes per table entry
  266.         add.w    d0, a3
  267.         move.l    a3, d2                ;save address for later
  268.     
  269.     ; Use the Head of the queue header to get the next available buffer. If the
  270.     ; queue is empty, discard the packet.
  271.     
  272.         move.l    (a3), d0
  273.         move.l    d0, a3
  274.         bne.s    @GotBuffer
  275.  
  276.         moveq    #0, d3                ;flush packet and return
  277.         jmp    ReadRest
  278.     
  279.     ; Call ReadRest to pull the rest of the incoming packet into the Data portion
  280.     ; of the packet buffer. If ReadRest returns an error, discard the packet.
  281.  
  282. GotBuffer:    add.w    #PktOffset(Data), a3
  283.         move.w    d1, d3
  284.         jsr    ReadRest
  285.         bne.s    @ExitListener
  286.     
  287.     ; Retrieve the queue header and packet buffer addresses and check the LAP
  288.     ; protocol type in the RHA. We need to react differently depending on whether
  289.     ; the packet is shortDDP or longDDP.
  290.     
  291.         move.l    d2, a3
  292.         move.l    (a3), a1
  293.         cmp.b    #shortDDP, RHA_LapProto
  294.         beq.s    @IsShortDDP
  295.     
  296.     ; If the packet is longDDP and the Checksum (in the RHA) is non-zero, then
  297.     ; we need to compute the packet checksum and compare it with the received
  298.     ; value.
  299.     
  300.         tst.w    RHA_DDPChecksum            ;if packet checksum zero
  301.         beq.s    @NoChecksum            ;no need to check
  302.     
  303.     ; Checksum the header bytes in the RHA
  304.     
  305.         moveq    #0, d3
  306.         lea    RHA_DDPDstNet, a0
  307.         moveq    #9, d0
  308.         bsr.s    @DoChecksum
  309.     
  310.     ; Checksum the data bytes
  311.         
  312.         lea    PktOffset(Data)(a1), a0
  313.         move.w    RHA_DDPLength, d0        ;get packet length from RHA
  314.         and.w    #0x03FF, d0            ;mask out hop count &c.
  315.         sub.w    #13, d0                ;discount hdr length
  316.         bsr.s    @DoChecksum
  317.     
  318.     ; If the checksum happens to come out zero, we substitute -1 instead. We
  319.     ; then compare the computed and received checksum and if they don't match
  320.     ; we discard the packet.
  321.         
  322.         bne.s    @NonZChecksum
  323.         subq.w    #1, d3
  324. NonZChecksum:    cmp.w    RHA_DDPChecksum, d3
  325.         bne.s    @ExitListener
  326.     
  327.     ; Point the Head of the queue header to the next packet buffer. If there is
  328.     ; no next buffer, clear the Tail as well.
  329.  
  330. NoChecksum:    move.l    NextBuff(a1), (a3)+
  331.         bne.s    @QNonEmpty
  332.         clr.l    (a3)
  333.     
  334.     ; Copy the packet header bytes from the RHA to the packet buffer.
  335.     
  336. QNonEmpty:    move.l    a1, a3                ;save buffer addr
  337.         lea    toRHA(a2), a0
  338.         addq.w    #PktOffset(LAPDstNode), a1
  339.         moveq    #16, d0
  340.         bsr.s    @MoveBytes
  341.     
  342.     ; Set the PacketReceived flag and return
  343.     
  344.         move.b    #1, PacketReceived(a3)
  345.         rts
  346.     
  347.     ; For shortDDP packets there is no checksumming, but the header format is
  348.     ; different and we want to arrange things in the packet buffer for the
  349.     ; convenience of the caller. We first detach the packet buffer from the
  350.     ; queue.
  351.  
  352. IsShortDDP:    move.l    NextBuff(a1), (a3)+        ;put next buffer in queue head
  353.         bne.s    @QIsNotEmpty
  354.         clr.l    (a3)                ;clear queue tail if queue empty
  355.     
  356.     ; Copy the packet header from the RHA to the packet buffer up to and including
  357.     ; the packet length field.
  358.     
  359. QIsNotEmpty:    move.l    a1, a3                ;save buffer addr
  360.         lea    toRHA(a2), a0
  361.         addq.w    #PktOffset(LAPDstNode), a1
  362.         moveq    #5, d0
  363.         bsr.s    @MoveBytes
  364.     
  365.     ; Clear the Checksum and the DstNet and SrcNet fields in the packet buffer.
  366.         
  367.         clr.l    (a1)+
  368.         clr.w    (a1)+
  369.     
  370.     ; Copy the DstNode and SrcNode from the LAP header
  371.     
  372.         move.b    PktOffset(LAPDstNode)(a3), (a1)+
  373.         move.b    PktOffset(LAPSrcNode)(a3), (a1)+
  374.     
  375.     ; Copy the rest of the header (DstSocket, SrcSocket, DDPProtoType)
  376.     
  377.         moveq    #3, d0
  378.         bsr.s    @MoveBytes
  379.     
  380.     ; Set the PacketReceived flag and return
  381.     
  382.         move.b    #1, PacketReceived(a3)
  383. ExitListener:    rts
  384.  
  385.     
  386.     ; DoChecksum
  387.     ;
  388.     ;    Computes the DDP checksum for a sequence of bytes. D3 is used to hold
  389.     ;    the running checksum and should be initialized to zero before the
  390.     ;    first call. A0 is used to pass a pointer to the bytes to be
  391.     ;    checksummed and D0 is used to pass the number of bytes. D1 is
  392.     ;    used internally.
  393.     
  394. DoChecksum:    subq.w    #1, d0                ;decrement length for DBRA
  395.         bmi.s    @CSExit                ;exit if length was <= 0
  396.         moveq    #0, d1                ;clear upper bits of D1
  397. CSLoop:        move.b    (a0)+, d1            ;copy a byte into D1
  398.         add.w    d1, d3                ;add the *word* in D1 to the checksum
  399.         rol.w    #1, d3                ;rotate the checksum 1 bit left
  400.         dbra    d0, @CSLoop            ;loop if more
  401. CSExit:        rts
  402.  
  403.     
  404.     ; MoveBytes
  405.     ;
  406.     ;    SImilar to BlockMove, but we don't want to use BlockMove because it
  407.     ;    is less efficient for a small number of bytes and may furthermore
  408.     ;    be patched, making it even slower. Parameter passing is the same,
  409.     ;    but the registers aren't saved and restored. A0 is the source pointer,
  410.     ;    A1 is the destination pointer, and D0 is the number of bytes to move.
  411.  
  412. MoveBytes:    subq.w    #1, d0                ;decrement length for DBRA
  413.         bmi.s    @MBExit                ;exit if length was <= 0
  414. MBLoop:        move.b    (a0)+, (a1)+
  415.         dbra    d0, @MBLoop
  416. MBExit:        rts
  417.  
  418.     
  419.     ; Here, at last, is the socket table. It contains 256 queue headers. Each
  420.     ; queue header contains two pointers of 4 bytes each.
  421.     ;
  422.     ;     256*2*4 = 2048 bytes
  423.     ;    256*2   = 512 longs
  424.     ;
  425.     ; So you don't have to count, there are 32 longs per row below and 16 rows.
  426.     ;
  427.     ;    32*16   = 512 longs
  428.  
  429. SocketTable:
  430.     dc.l    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
  431.     dc.l    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
  432.     dc.l    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
  433.     dc.l    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
  434.     dc.l    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
  435.     dc.l    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
  436.     dc.l    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
  437.     dc.l    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
  438.     dc.l    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
  439.     dc.l    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
  440.     dc.l    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
  441.     dc.l    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
  442.     dc.l    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
  443.     dc.l    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
  444.     dc.l    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
  445.     dc.l    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
  446.     }
  447.   }