ADC Membership Technical Business Join ADC
Search Advanced Search
NOTE: This Technical Note has been retired. Please see the Technical Notes page for current documentation.

Technical Note NW13
AppleTalk: The Rest of the Story

CONTENTS

This Technical Note discusses the updates, and modifications to a number of facets of the lower levels of AppleTalk Phase 2 since the release of Inside Macintosh Volume VI. Topics range from discussion of the new Datagram Delivery Protocol (DDP) layer calls to the AppleTalk Multiple Node Architecture to a discussion at the driver level of the new Multivendor ADEV Architecture. Most of the information presented here concerns AppleTalk version 56 through 58; however, additional material is presented to clarify and correct material presented in other AppleTalk documentation relating to all versions of AppleTalk.

Reordered subjects according to the order of the AppleTalk version in which the feature was first implemented. Added a table of contents to make it easier to find material. Added a discussion on Multivendor ADEV Architecture, the .TOKN driver interface, plus information on making AppleTalk drivers compatible with virtual memory under system software version 7.0.x. Added a description of the change to the .ENET interface presented by the Apple SONIC based Ethernet drivers. Added socket listener sample code plus corrections to the original sample socket listener. Added AppleTalk version list.

[Feb 01 1994]






Overview

This technote is broken down into the following topics

  • Where Can I Get the Latest Version of AppleTalk?
  • Going, Going, Gone...
  • The 'atkv' Gestalt Selector
  • Sample Socket Listener
  • Socket Listener Review
  • Timing Considerations for LocalTalk
  • Register Usage
  • Socket Listener Overview
  • Socket Listener Assembler Code
  • Initializing the Socket Listener
  • Using the Socket Listener
  • The AppleTalk Transition Queue
  • Bug with LAPAddATQ and LAPRmvATQ Glue Code
  • Calling the AppleTalk Transition Queue
  • Standard AppleTalk Transition Constants
  • The Flagship Naming Service
  • The ATTransNameChangeAskTask Transition
  • The ATTransNameChangeTellTask Transition
  • The ATTransCancelNameChange Transition
  • System 7.0 Sharing Setup cdev / Flagship Naming Service Interaction
  • AppleTalk Remote Access Network Transition Event
  • The ATTransNetworkTransition Transition
  • Network Transition Event for AppleTalk Remote Access
  • Cable Range Change Transition Event
  • The ATTransCableChange Transition
  • The Speed Change Transition Event
  • The ATTransSpeedChange Transition
  • Sample Pascal Source to LAPMgrExists Function
  • Sample AppleTalk Transition Queue Function
  • Sample AppleTalk Transition Queue Function in C
  • Sample AppleTalk Transition Queue Function in Pascal
  • Multivendor ADEV Architecture
  • Original Limitations
  • .TOKN Driver Shell
  • .TOKN Driver Basics
  • Driver Considerations for Virtual Memory Limiting DeferUserFn Calls Implementing DeferUserFn
  • .TOKN Application Programming Interface (API)
  • Using L802Attach on Token Ring
  • Writing the Protocol Handler for the LAP Manager
  • .FDDI Application Programming Interface (API)
  • SONIC-Based Ethernet Driver Software Interface Change
  • EGetInfo Changes
  • Distinguishing Apple's SONIC-Based Ethernet Systems
  • Correction to the ENET.h Header File
  • AppleTalk Multiple Node Architecture
  • What Is It?
  • Glue Code For Multinode Control Calls
  • Things You Need to Know When Writing a Multinode Application
  • AddNode (csCode=262)
  • Receiving Packets
  • Sending Datagrams Through Multinodes
  • NetWrite (csCode=261)
  • AppleTalk Remote Access Network Number Remapping
  • Is There a Router on the Network?
  • New for AppleTalk ADEVs
  • AGetInfo (D0=3)
  • ADelNode (D0=10)
  • AGetNodeRef (D0=11)
  • AOpen (D0=7)
  • AClose (D0=8)
  • AInstall (D0=1)
  • AShutDown (D0=2)
  • Receiving Packets
  • Defending Multinode Addresses
  • Corrections/Clarifications to the LAP Manager
  • LRdDispatch (D0 = 1)
  • LWrtInsert (D0 = 2)
  • AppleTalk Version Information
  • Contacting Apple Software Licensing

Back to top

Introduction

This Technote differs from previous revisions in that the subjects have been reordered. The topics are presented according to the order of the AppleTalk version in which the feature was first implemented. You can find new topics and modifications to this Technote by looking for material set off by change bars in the margins.

The first section in this Note presents a list of the original AppleTalk Manager calls which are *NOT* being ported to the PowerPC platform. The list includes the Preferred AppleTalk version of the function call, or the preferred method for implementing this functionality. It is important that you review this list and check whether your program is using any of these original calls. If so, and you want your program code to be compatible with the Power PC platform, it is advised that you modify your program to use the Preferred AppleTalk interface calls.

The next section in this Note, "The 'atkv' Gestalt Selector," discusses the new Gestalt selector 'atkv', which provides version information when AppleTalk is available.

The next section, "Sample Socket Listener," presents a sample socket listener, including initialization code to assist high-level language programmers. The socket listener comments describe in detail the basic functions of packet handling at the data link layer. Information is included on how to make this socket listener compatible with Virtual Memory.

The next section, "The AppleTalk Transition Queue," discusses the AppleTalk Transition Queue, including its support for the Flagship Naming Service, AppleTalk Remote Access, and changes to processor speed that can affect LocalTalk and other processes dependent on processor speed. Included is Pascal source code for checking whether the Phase 2 LAP Manager exists to support the Transition Queue mechanism, plus sample Transition Queue handlers written in both C and Pascal. This section includes a description of an important bug which exists in the glue code, implemented for the LAPAddATQ and the LAPRmvATQ functions in MPW v3.2.x. Under MPW v3.3, the glue routines for these functions has been corrected. The glue code is from the MPW Interface.o file. Replacement glue routines are provided to work around the problem for both MPW v3.2.x and Think programmers, v5.x and earlier.

The section "Multivendor ADEV Architecture" presents the Multivendor ADEV Architecture, which allows for Ethernet and token ring cards from multiple vendors to be installed on the same system. Included is a description of the functionality of the new driver shells for Ethernet and token ring, plus a description of the .TOKN interface required for compatibility with the new ADEV Architecture.

The section, "Driver Considerations for Virtual Memory," shows how to modify driver code for compatibility with system software version 7.0 virtual memory.

The section "Ethernet NB and Macintosh Quadra Built-in Software Interface Change" presents a change to the .ENET interface that resulted from the implementation of the SONIC Network Interface Controller on the Ethernet NB Card and in the Macintosh Quadra computer's built-in Ethernet. The change concerns the EGetInfo function, which now returns additional network information for Apple Ethernet products based on the SONIC chip.

The section "Correction to the ENET.h Header File" presents a correction to the header file for programs which will make a parameter block Control call to the .ENET, .TOKN, or .FDDI driver, to add or delete multicast addresses. This problem only applies to C programs which are written to include the ENET.h file supplied with MPW v3.2.x and with Think C v5.0 to v5.0.4.

The section "AppleTalk Multiple Node Architecture" discusses the new program interfaces to the AppleTalk Multiple Node Architecture. The new architecture was developed to support multiple node capability on the Macintosh computer, which allows the Macintosh to present itself as separate entities, or unique nodes on the network. The AppleTalk Remote Access program uses Multinode capability to implement Remote Access functionality. This section presents the Datagram Delivery Protocol (DDP) interface for Multinode AppleTalk for applications to take advantage of this new functionality. This Note, however, does not discuss the Remote Access program.

The section, "New for AppleTalk ADEVs," presents the changes required of an ADEV's 'atlk' code resource for compatibility with the AppleTalk Multinode Architecture. While we recommend that developers of Ethernet and token ring network hardware conform to the specifications of the Multivendor ADEV Architecture, this information is presented for those developers of network products for which Apple does not supply an ADEV.

The final section, "AppleTalk Version Information," lists the various versions of AppleTalk, and the new products that require the support of the AppleTalk version.

Back to top

Where Can I Get the Latest Version of AppleTalk?

For testing purposes, the latest version of AppleTalk and related software is available on the latest Developer CD Series disc, on AppleLink on the Developer Services Bulletin Board, and on the Internet through anonymous FTP to ftp.apple.com (130.43.2.3). It can be installed by using the Network Software Installer.

Back to top

Going, Going, Gone...

Five years ago in Technote #132, "AppleTalk Interface Update", we alerted developers that the use of the old alternate AppleTalk interface functions "may cause future compatibility problems." For PowerPC, the old alternate AppleTalk interface functions are not being ported. These calls make Memory Manager calls and are of limited use in contrast to the Preferred Style calls presented in "Inside Macintosh" Volume V. The following is a list of the AppleTalk calls which are going away and along with their Preferred counterpart.

Inside Macintosh Volume II, presents additional interface functions which are available through MPW glue in the Interface.o library, and are of dubious value. They are:

  • MPPOpen - You should instead use OpenDriver('.MPP', refNum) to open the .MPP driver which also makes available the driver refNum.
  • ATPLoad - opening the .MPP driver automatically loads the .ATP driver.
  • ATPUnload - one should not be unloading the .ATP driver. In addition, this function applies only to the Macintosh 128K
  • GetNodeAddress - It is preferable to use the PGetAppleTalkInfo function to obtain the network and node number. GetNodeAddress accesses low memory globals. PGetAppleTalkInfo will be easier to support in the future.
  • IsMPPOpen - accesses a low memory global. Instead, use the OpenDriver function to open the .MPP driver. You can also use the Gestalt function with the gestaltAppleTalkVersion selector to find out if the .MPP driver is open. Refer to the AppleTalkVersion sample function listed below.
  • IsATPOpen - the .ATP driver is open if the .MPP driver is open.

The only function which is still useful among the glue routines from the AB package is the NBPExtract function.

Back to top

The 'atkv' Gestalt Selector

The 'atkv' Gestalt selector is available beginning with AppleTalk version 56 to provide more complete version information regarding AppleTalk, and as an alternative to the existing 'atlk' Gestalt selector. Beginning with AppleTalk version 54, the 'atlk' Gestalt selector was available to provide basic version information. The 'atlk' selector is not available when AppleTalk is turned off in the Chooser. It is important to note that the information between the two resources is provided in a different manner. Calling Gestalt with the 'atlk' selector provides the major revision version information in the low-order byte of the function result. Calling Gestalt with the 'atkv' selector provides the version information in a manner similar to the 'vers' resource. The format of the LONGINT result is as follows:

    byte;                        /* Major revision */
    byte;                        /* Minor revision */
    byte        development = 0x20,    /* Release stage  */
            alpha = 0x40,
            beta = 0x60,
            final = 0x80, /* or */ release = 0x80;
    byte;                        /* Nonfinal release # */

For example, passing the 'atkv' selector in a Gestalt call under AppleTalk version 57 gives the following LONGINT result: 0x39008000.


Note:
With the release of the System 7 Tuner product, AppleTalk will not be loaded at startup, if prior to the previous shutdown AppleTalk was turned off in the Chooser. Under this circumstance, the 'atkv' selector is not available. If the 'atkv' selector is not available under System 7, this is an indicator that AppleTalk cannot be turned on without doing so in the Chooser and rebooting the system.


Back to top

Sample Socket Listener

The preferred AppleTalk calls presented in Inside Macintosh Volume V, page 513, do not include a preferred style call for DDPRead. As a result developers are faced with the prospect of writing their socket listeners and using the POpenSkt function when upgrading their programs. Inside Macintosh Volume II, page 324, presents an overview of how socket listeners should function. Inside Macintosh states that socket listeners, as well as protocol handlers, need to be written in assembly code, since parameters are passed in registers. To assist high-level programmers with implementing a socket listener, the generic listener code is provided. The following code demonstrates how to do the following:

  • buffer multiple packets return DDP/LAP header information that has already been read into the Read Header Area (RHA) by DDP calculate and compare the packet checksum when a packet uses a long DDP header, and includes the checksum value

Some of the things that the listener sample does not do, which you might wish to implement, are the following:

  • Check the DDP type and ignore any packets that don't match the desired type(s) that you're interested in.
  • Check the source node ID and ignore any packets that don't come from the desired node(s).
  • If the socket listener is used by more than one socket, it could route the packets differently based on the socket number found in D0.
  • The socket listener does not handle the implementation of a completion routine to be executed when the packet is processed.

The example listener code includes an initialization routine which the listener client uses to notify the listener code of the "available" and "used" buffer queues. The sample initialization routine also provides information about the listener code which will enable the client to protect the listener code from being paged out when Virtual Memory is present. A high-level procedure is provided to demonstrate the initialization of the listener, the use of the socket listener and a technique for protecting the listener code in the presence of Virtual Memory.

For users concerned with the compatibility between this sample socket listener and PowerPC, the PowerPC mixed mode magic will handle 68000 Assembler instructions transparently

Back to top

Socket Listener Review

The reader is advised to refer to Inside Macintosh Volume II, pages 324 to 330, for a description of protocol handlers, socket listeners, and data reception in the AppleTalk Manager over LocalTalk. The same architecture applies to AppleTalk on Ethernet and token ring. With the advent of AppleTalk Phase 2, the size of the Read Header Area (RHA) has been expanded to accommodate the long DDP header.

After every ReadPacket or ReadRest call, the listener code must check the Z (Zero) condition code for errors. If an error is detected from ReadPacket , the code must not call ReadRest.

It is the responsibility of the socket listener code to check for the existence of the DDP checksum. In contrast with the Frame Check Sequence which the hardware uses to verify frame, the DDP checksum is implemented in extended DDP headers to verify that the packet data is not corrupted by memory or data bus errors within routers on the Internet. If the checksum has been entered, then the socket listener code must calculate the checksum after the packet has been read in, and compare the computed value with the passed checksum value. The sample listener code demonstrates this check and calculation of the checksum. The listener code sets a flag which the program can check to determine whether the checksum matched or not.

The record structure presented in this sample returns the DDP type, destination node ID, source address in AddrBlock format, the hop count, the size of the packet, a flag to indicate whether a checksum error occurred, followed by the actual datagram. The record structure can be extended to return additional information, such as the tick count at the time the socket handler was called.

Back to top

Timing Considerations for LocalTalk

If LocalTalk is being used, your socket listener has less than 95 microseconds (best case) to read more data with a ReadPacket or ReadRest call. If you need more time, you might consider reading another three bytes into the RHA to buy another 95 microseconds. Remember that the RHA may only have eight bytes still available.

Back to top

Register Usage

When the socket listener is called, the registers will be set up as follows:

Register(s)       Contents
     A0-A1          SCC addresses used by MPP
     A2                Pointer to MPP's local variables
     A3                Pointer to next free byte in RHA
     A4                Pointer to ReadPacket {JSR (A4)} and ReadRest {JSR
2(A4)} jump table
     D0               This packet's destination socket number
     D1                Number of bytes left to read in packet

Registers D0, D2, and D3 can be used freely throughout the socket listener. A6, and D4 to D7 must be preserved.

From entry to socket listener until ReadRest is called:

The A5 register can be used.

Registers A0-A2, A4, and D1 must be preserved.

From ReadRest until exit from socket listener:

The A5 register must be preserved.
Registers A0-A3 and D0-D3 are free to use.

You should assume only eight bytes are still available in the RHA for your use. The RHA will contain one of the following:

Back to top

Socket Listener Overview

The sample socket listener utilizes two standard Operating System (OS) queues (see Inside Macintosh II-372), a free queue of available buffers which the listener uses to fill with incoming datagrams. The second linked list is a used queue of buffers which the listener has processed, but have not been processed by the listener client. The SL_InitSktListener routine is called to pass the listener the pointers to the two OS queues. In addition, the SL_InitSktListener routine returns the address and length of the socket listener code. A client process can then check for the presence of Virtual Memory, and call HoldMemory to protect the socket listener code from being paged out.

When the listener is called to process a packet, the listener checks whether there is an available buffer record in the free queue by checking that qHead element of the free queue is not nil. If so, then the listener sets register A3 to point to the buffer_data element of the record and calls the ReadRest routine. If there is no available buffer record, the packet is ignored by calling ReadRest with a buffer size value of 0. Perhaps the next time a packet is handled, a buffer will be available. If an error occurs during the ReadRest function, then the listener simply returns to the caller.

If the packet is successfully read, the listener processes the header information. The header information has been stored by the hardware driver in the MPP local variable space pointed to in register A2. The listener code fills in the hop count field of the packet buffer record and determines the packet length. The listener then figures out whether it is dealing with a short or extended DDP header and fills in the remaining fields of the packet buffer. A check is made to determine whether the checksum field of the DDP header is non-zero. If the field is non-zero, the value is passed to the SL_DoChkSum function to verify that the resulting checksum is zero. If the resulting checksum is non-zero, the buffer_CheckSum field is set to ckSumErr, -3103, otherwise the field is set to noErr. Finally, the listener Enqueues the packet buffer into the used queue and Dequeues it from the free queue before returning to the caller.

The calling program periodically checks the QHead element of the used queue. When QHead is no longer nil, a packet is available for processing. The program processes the packet buffer. When finished, the packet buffer is Enqueued into the free queue and Dequeued from the used queue. The program might then check for additional packets in the used queue and process them in the same manner.

The program needs to define a sufficient number of packet buffers so that the listener has buffers available in the free queue between times when the program checks the used queue and processes incoming packets.

Back to top

Socket Listener Assembler Code

;_________________________________________________________________________
; Socket Listener Sample
;
; 3/92 Jim Luther,  Apple DTS
;
; (c)1992 Apple Computer, Inc.
;_________________________________________________________________________

    INCLUDE    'QuickEqu.a'
    INCLUDE    'ToolEqu.a'
    INCLUDE    'SysEqu.a'
    INCLUDE    'Traps.a'
    INCLUDE    'ATalkEqu.a'
    INCLUDE    'SysErr.a'

;
;
; Record Types
;
;_________________________________________________________________________

;QHdr                  RECORD    0
;qFlags                DS.W      1
;qHead                 DS.L      1
;qTail                 DS.L      1
;                      ENDR

PacketBuffer          RECORD    0
qLink                 DS.L      1
qType                 DS.W      1
buffer_Type           DS.W      1            ; DDP Type
buffer_NodeID         DS.W      1            ; Destination node
buffer_Address        DS.L      1            ; Source address in AddrBlock format
buffer_Hops           DS.W      1            ; Hop count
buffer_ActCount       DS.W      1            ; length of DDP datagram
buffer_CheckSum       DS.W      1            ; Chksum error returned here
                                             ;(cksumErr or noErr)
buffer_Ticks          DS.L      1            ; TickCount when handler called
buffer_Data           DS.B      ddpMaxData   ; the DDP datagram
                      ENDR

;_________________________________________________________________________
;
; Local Variables
;
;_________________________________________________________________________

SL_Locals    PROC
        ENTRY free_queue,used_queue,current_qelem

free_queue        DC.L    0      ; pointer to freeQ QHdr - init'd by InitSktListener
used_queue        DC.L    0      ; pointer to usedQ QHdr - init'd by InitSktListener
current_qelem     DC.L    0      ; pointer to current PacketBuffer record
                                 ; initialized by InitSktListener, then
                                 ; set by socket listener after every packet.
                                 ; NIL if no buffer is available.
    ENDP

;_________________________________________________________________________
;
; SL_DoChksum - accumulate ongoing checksum (from Inside Macintosh)
;
;    Input:
;     D1 (word) = number of bytes to checksum
;     D3 (word) = current checksum
;     A1 points to the bytes to checksum
;
;    Return:
;     D0 is modified
;     D3 (word) = accumulated checksum
;_________________________________________________________________________

SL_DoChksum    PROC
    CLR.W      D0                    ; Clear high byte
    SUBQ.W     #1,D1                 ; Decrement count for DBRA
ChksumLoop:
    MOVE.B     (A1)+,D0              ; read a byte into D0
    ADD.W      D0,D3                 ; accumulate checksum
    ROL.W      #1,D3                 ; rotate left one bit
    DBRA       D1,ChksumLoop         ; loop if more bytes
    RTS
    ENDP

;_________________________________________________________________________
;
; SL_TheListener
;
;_________________________________________________________________________

;_________________________________________________________________________
;
; SL_TheListner - process packets received at the designated socket
;
;    Input:
;     D0 (byte) = packet's destination socket number
;     D1 (word) = number of bytes left to read in packet
;     A0 points to the bytes to checksum
;     A1 points to the bytes to checksum
;     A2 points to MPP's local variables
;     A3 points to next free byte in Read Header Area
;     A4 points to ReadPacket and ReadRest jump table
;
;    Return:
;     D0 is modified
;     D3 (word) = accumulated checksum
;_________________________________________________________________________
SL_TheListener  PROC    EXPORT

    EXPORT  SL_InitSktListener

    WITH    PacketBuffer

; get pointer to current PacketBuffer

GetBuffer:
    LEA       current_qelem,A3          ; get the pointer to the PacketBuffer to use
    MOVE.L    (A3),A3
    MOVE.L    A3,D0                     ; if no PacketBuffer
    BEQ.S     NoBuffer                  ; then ignore packet

; read rest of packet into PacketBuffer.datagramData

    MOVE.L    D1,D3                     ; read rest of packet
    LEA       buffer_data(A3),A3        ; A3 = ^bufferData
    JSR       2(A4)                     ; ReadRest
    BEQ.S     ProcessPacket             ; If no error, continue
    BRA       RcvRTS                    ; there was an error, so ignore packet

; No buffer; ignore the packet

NoBuffer      CLR D3                    ; Set to ignore packet (buffer size = 0)
    JSR       2(A4)                     ; ReadRest
    BRA       GetNextBuffer             ; We missed this packet, but maybe there
                                        ; will be a buffer for the next packet...

; Process the packet you just read in.
; ReadRest has been called so registers A0-A3 and D0-D3 are free to use.
; We'll use registers this way:
PktBuff         EQU    A0        ; the current PacketBuffer
MPPLocals       EQU    A2        ; pointer to MPP's local variables (still set up
                                 ;  from entry to socket listener)
HopCount        EQU    D0        ; used to get the hop count
DatagramLength  EQU    D1        ; used to determine the datagram length
SourceNetAddr   EQU    D2        ; used to build the source network address

ProcessPacket:
    LEA        current_qelem,PktBuff          ; PktBuff = current_qelem
    MOVE.L     (PktBuff),PktBuff

; do everything that's common to both long and short DDP headers

; save tickCount in buffer_Ticks field

    MOVE.L     Ticks,buffer_Ticks(PktBuff)

; first, clear buffer_Type and buffer_NodeID to ensure their high bytes are 0

    CLR.W      buffer_Type(PktBuff)           ; clear buffer_Type
    CLR.W      buffer_NodeID(PktBuff)         ; clear buffer_NodeID

; clear SourceNetAddr to prepare to build network address

    MOVEQ      #0,SourceNetAddr               ; build the network address in
                                              ; SourceNetAddr

; get the hop count
    MOVE.W     toRHA+lapHdSz+ddpLength(MPPLocals),HopCount ; Get hop/length field
    ANDI.W     #DDPHopsMask,HopCount          ; Mask off the hop count bits
    LSR.W      #2,HopCount                    ; shift hop count into low bits of
                                              ; high byte
    LSR.W      #8,HopCount                    ; shift hop count into low byte
    MOVE.W     HopCount,buffer_Hops(PktBuff)  ; and move it into the PacketBuffer

; get the packet length (including the DDP header)
    MOVE.W     toRHA+lapHdSz+ddpLength(MPPLocals),DatagramLength ; Get length field
    ANDI.W     #ddpLenMask,DatagramLength     ; Mask off the hop count bits

; now, find out if the DDP header is long or short

    MOVE.B     toRHA+lapType(MPPLocals),D3    ; Get LAP type
    CMPI.B     #shortDDP,D3                   ; is this a long or short DDP header?
    BEQ.S      IsShortHdr                     ; skip if short DDP header

; it's a long DDP header

    MOVE.B     toRHA+lapHdSz+ddpType(MPPLocals),buffer_Type+1(PktBuff)
; get DDP type

    MOVE.B     toRHA+lapHdSz+ddpDstNode(MPPLocals),buffer_NodeID+1(PktBuff)
                                              ; get destination node from LAP header

    MOVE.L     toRHA+lapHdSz+ddpSrcNet(MPPLocals),SourceNetAddr
                                              ; source network in hi word
                                              ; source node in lo byte
    LSL.W      #8,SourceNetAddr    ; shift source node up to high byte of low word
                                              ; get source socket from DDP header
    MOVE.B     toRHA+lapHdSz+ddpSrcSkt(MPPLocals),SourceNetAddr

    SUB.W      #ddpType+1,DatagramLength      ; DatagramLength = number of bytes in
                                              ; datagram
; checksum time...
    TST.W      toRHA+lapHdSz+ddpChecksum(MPPLocals) ;Does packet have checksum?
    BEQ.S      noChecksum

; Calculate checksum over DDP header
    MOVE.W     DatagramLength,-(SP)           ; save DatagramLength (D1)

    CLR        D3                             ; set checksum to zero
    MOVEQ      #ddphSzLong-ddpDstNet,D1       ; D1 = length of header part to
                                              ; checksum pointer to dest network
                                              ; number in DDP header
    LEA        toRHA+lapHdSz+ddpDstNet(MPPLocals),A1
    JSR        SL_DoChksum                    ; checksum of DDP header part
                                              ; (D3 holds accumulated checksum)

; Calculate checksum over data portion (if any)

    LEA        buffer_Data(PktBuff),A1        ; pointer to datagram
    MOVE.W     (SP)+,DatagramLength           ; restore DatagramLength (D1)
    MOVE.W     DatagramLength,-(SP)           ; save DatagramLength (D1)
                                              ;  before calling SL_DoChksum
    BEQ.S      TestChecksum                   ; don't checksum datagram if its
                                              ; length = 0
    JSR        SL_DoChksum                    ; checksum of DDP datagram part
                                              ; (D3 holds accumulated checksum)

TestChecksum:
    MOVE.W     (SP)+,DatagramLength           ; restore DatagramLength (D1)

; Now make sure the checksum is OK.
    TST.W      D3                             ; is the calculated value zero?
    BNE.S      NotZero                        ; no -- go and use it
    SUBQ.W     #1,D3                          ; it is 0; make it -1

NotZero:
    CMP.W      toRHA+lapHdSz+ddpChecksum(MPPLocals),D3
    BNE.S      ChecksumErr                    ; Bad checksum
    MOVE.W     #0,buffer_CheckSum(A0)         ; no errors
    BRA.S      noChecksum
ChecksumErr:
    MOVE.W     #ckSumErr,buffer_CheckSum(PktBuff) ; checksum error

noChecksum:
    BRA.S      MoveToBuffer


; it's a short DDP header

IsShortHdr:
                                              ; get DDP type
    MOVE.B     toRHA+lapHdSz+sddpType(MPPLocals),buffer_Type+1(PktBuff)
                                              ; get destination node from LAP header
    MOVE.B     toRHA+lapDstAdr(MPPLocals),buffer_NodeID+1(PktBuff)
                                              ; get source node from LAP header
    MOVE.B     toRHA+lapSrcAdr(MPPLocals),SourceNetAddr
    LSL.W      #8,SourceNetAddr               ; shift src node up to high byte of
                                              ; low word

                                              ; get source socket from short DDP
                                              ;  header
    MOVE.B     toRHA+lapHdSz+sddpSrcSkt(MPPLocals),SourceNetAddr

                                              ; DatagramLength = number of bytes in
                                              ; datagram
    SUB.W      #sddpType+1,DatagramLength

MoveToBuffer:
                                      ;move source network address into PacketBuffer
    MOVE.L     SourceNetAddr,buffer_Address(PktBuff)
                                      ; move datagram length into PacketBuffer
    MOVE.W     DatagramLength,buffer_ActCount(PktBuff)

; Now that we're done with the PacketBuffer, enqueue it into the usedQ and get
; another buffer from the freeQ for the next packet.

    LEA        used_queue,A1                   ; A1 = ^used_queue
    MOVE.L     (A1),A1                         ; A1 = used_queue (pointer to usedQ)
    _Enqueue                                    ; put the PacketBuffer in the usedQ

GetNextBuffer:
    LEA        free_queue,A1                   ; A1 = ^free_queue
    MOVE.L     (A1),A1                         ; A1 = free_queue (pointer to freeQ)
    LEA        current_qelem, A0               ; copy freeQ.qHead into current_qelem
    MOVE.L     qHead(A1),(A0)
    MOVEA.L    qHead(A1),A0                    ; A0 = freeQ.qHead
    _Dequeue

RcvRTS:
    RTS                                        ; return to caller

listenerSize   EQU     *-SL_TheListener        ; calculate the size of the listener

ENDWITH

;_________________________________________________________________________
; Function SL_InitSktListener(freeQ, usedQ: QHdrPtr;
;                             VAR listenerAddress: ProcPtr;
;                             VAR listenerLength: LongInt): OSErr
;

SL_InitSktListener:

StackFrame     RECORD    {A6Link},DECR     ; build a stack frame record
Result1        DS.W      1                 ; function's result returned to caller
ParamBegin     EQU       *                 ; start parameters after this point
freeQ          DS.L      1                 ; freeQ parameter
usedQ          DS.L      1                 ; usedQ parameter
listenerAddress DS.L     1                 ; listenerAddress parameter
listenerLength DS.L      1                 ; listenerLength parameter
ParamSize      EQU       ParamBegin-*      ; size of all the passed parameters
RetAddr        DS.L      1                 ; placeholder for return address
A6Link         DS.L      1                 ; placeholder for A6 link
LocalSize      EQU       *                 ; size of all the local variables
               ENDR

    WITH       StackFrame,QHdr             ; use these record types

    LINK       A6,#LocalSize               ; allocate our local stack frame

    MOVEA.L    listenerLength(A6),A2       ; address of listenerLength in A2
    MOVE.L     #listenerSize,(A2)          ; return the listener length

    LEA        SL_TheListener,A0           ; get address of SL_TheListener
    MOVE.L     A0,D0                       ; setup for StripAddress

    _StripAddress
    MOVEA.L    listenerAddress(A6),A2      ; address of listenerAddresss in A2
    MOVE.L     D0,(A2)                     ; return the listener address

; copy queue header pointers into our local storage for use in the listener

    LEA        used_queue,A0               ; copy usedQ into used_queue
    MOVE.L     usedQ(A6),(A0)

    LEA        free_queue,A0               ; copy freeQ into free_queue
    MOVE.L     freeQ(A6),(A0)

; dequeue the first buffer record from freeQ and set current_qelem to it

    MOVEA.L    freeQ(A6),A1                ; A1 = ^freeQ
    LEA        current_qelem, A0           ; copy freeQ.qHead into
                                           ; current_qelem
    MOVE.L     qHead(A1),(A0)
    MOVEA.L    qHead(A1),A0                ; A0 = freeQ.qHead
    _Dequeue
    MOVE.W     D0,Result1(A6)              ; Return status

@1  UNLK       A6                          ; destroy the link
    MOVEA.L    (SP)+,A0                    ; pull off the return address
    ADDA.L     #ParamSize,SP               ; strip all of the caller's
                                           ; parameters
    JMP        (A0)                        ; return to the caller

    ENDWITH
    ENDP

    END                                    ; end of this source file

Back to top

Initializing the Socket Listener

To initialize the socket listener, define the free and used queue QHdr variables, plus a ProcPtr and length variables for use in getting the address and size of the socket listener code. You'll need to define a PacketBuffer structure to match the record structure defined in the socket listener code. If you add any new fields, then you need to modify the PacketBuffer structure defined in the listener code. In the sample below, an array of 10 PacketBuffers is declared. Initialize the buffer packets, then queue them into the free queue using the _Enqueue trap. Call SL_InitSktListener and pass the addresses of the QHdr variable for the free and used queues, plus pointers to ProcPtr Address and length variables.

The SL_InitSktListener routine returns the address and size of the socket listener code so that steps can be taken to protect against a double page fault should Virtual Memory be active. In addition, it is also important to protect the PacketBuffer memory. The simplest method to protect the socket listener code, buffers, and QHdr variables is to detect whether Virtual Memory is on somewhere during program initialization. If Virtual Memory is enabled, then call the HoldMemory function, passing each of the memory locations and structure sizes as arguments. Note that the HoldMemory option is preferable to allocating the PacketBuffer memory or loading the socket listener code resource in the System heap. A future release of the operating system may not allow an application to allocate memory from the system heap. Remember to use the UnholdMemory function on each memory location passed to HoldMemory. The following Pascal code demonstrates a sample initialization technique:

USES MEMTYPES, QUICKDRAW, OSINTF, APPLETALK;

CONST
  ddpMaxData = 586;
TYPE
    PacketBuffer = RECORD
        qLink: QElemPtr;
        qType: Integer;
        buffer_Type: Integer;
        buffer_NodeID: Integer;
        buffer_Address: AddrBlock;
        buffer_Hops: Integer;
        buffer_ActCount: Integer;
        buffer_CheckSum: OSErr;
        buffer_Data: ARRAY[1..ddpMaxData] OF SignedByte;
    END;

    PacketBufPtr = ^PacketBuffer;

VAR
    Buffers: ARRAY[1..10] OF PacketBuffer;
    freeQ, usedQ: QHdr;
    SocketListenerAddr: ProcPtr;
    SktListenerLen: LONGINT;
    VMEnabled: Boolean;        { determine if VM is enabled using Gestalt }
                               { and gestaltVMAttr selector }

PROCEDURE SL_TheListener;
External;

Function SL_InitSktListener(freeQ, usedQ: QHdrPtr;
                             VAR listenerAddress: ProcPtr;
                             VAR listenerLength: LongInt): OSErr
External;

FUNCTION SetUpSocketListener: OSErr;
    VAR
        err: OSErr;
        i: Integer;

    BEGIN
        freeQ.QHead := NIL;    { initialize to nil to indicate empty queue }
        freeQ.QTail := NIL;    { initialize to nil to indicate end of queue }

        usedQ.QHead := NIL;    { initialize to nil to indicate empty queue }
        usedQ.QTail := NIL;    { initialize to nil to indicate end of queue }

        IF VMEnabled THEN      { VMEnabled is user defined - is true if VM }
            BEGIN              {  on }
                err := HoldMemory(@Buffers, sizeof(Buffers));  { protect buffer }
                IF err = noErr THEN
                    err := HoldMemory(@freeQ, sizeof(QHdr));   { protect freeQ }
                IF err = noErr THEN
                    err := HoldMemory(@usedQ, sizeof(QHdr));   { protect usedQ}
            END;

        IF err = noErr THEN
            BEGIN
                FOR i := 1 TO 10 DO
                    Enqueue(QElemPtr(Buffers[i]), @freeQ);

                               { call socket listener init code }
                err := SL_InitSktListener(@freeQ, @usedQ,
                                          SocketListenerAddr, SktListenerLen);
                IF (err = noErr) AND VMEnabled  THEN
                    err = HoldMemory(SocketListenerAddr, SktListenerLen);
            END;
        SetUpSocketListener := err;
    END;

For C Programmer's the initialization code is as follows:

#include    <types.h>
#include    <appletalk.h>
#include    <OSUtils.h>
#include    <stdio.h>
#include    <errors.h>

#define ddpMaxData   586

typedef struct {
    QElemPtr    qLink;
    short       qType;
    short       buffer_type;        /* DDP Type */
    short       buffer_NodeID;      /* Destination Node */
    AddrBlock   buffer_Address;     /* Source Address in AddrBlock format */
    short       buffer_Hops;        /* Hop count */
    short       buffer_ActCount;    /* length of DDP datagram */
    OSErr       buffer_CheckSum;    /* Checksum returned here */
    char        buffer_Data[ddpMaxData]; /* the DDP datagram */
} PacketBuffer;

typedef struct PacketBuffer *PacketBufPtr;

PacketBufPtr  buffers[10];
QHdr          freeQ, usedQ;
ProcPtr       SocketListenerAddr;   /* address of socket listener */
unsigned longint SktListenerLen;    /* length of listener code */
Boolean       VMEnabled;     /* determine if VM is enabled using Gestalt */
                             /* and gestaltVMAttr selector */

extern void SL_THELISTENER(void);

extern pascal OSErr SL_INITSKTLISTENER (QHdrPtr freeQ, QHdrPtr usedQ,
                                        ProcPtr *SocketListenerAddr,
                                        unsigned longint *SktListenerLen);

OSErr SetUpSocketListener()
{
    OSErr  err = noErr;
    short  i;

    freeQ.qHead = nil;       /* initialize to nil to indicate empty queue */
    freeQ.qTail = nil;       /* initialize to nil to indicate end of queue */

    usedQ.qHead = nil;       /* initialize to nil to indicate empty queue */
    usedQ.qTail = nil;       /* initialize to nil to indicate end of queue */

    if (VMEnabled) {         /* VMEnabled is user defined - is true if VM */
                             /*  is on */
        err = HoldMemory(&buffers, sizeof(buffers));   /* protect buffers */
        if (err == noErr)
            err = HoldMemory(&freeQ, sizeof(QHdr));    /* protect freeQ */
        if (err == noErr)
            err = HoldMemory(&usedQ, sizeof(QHdr));    /* protect usedQ */
        if (err)
            return (err);
    }

    for (i = 0; i < 10; i++) {
        Enqueue((QElemPtr)&buffers[i], &freeQ);
    }

                             /* call socket listener init code */
    err = SL_INITSKTLISTENER (&freeQ, &usedQ, &SocketListenerAddr, &SktListenerLen);
        if ((err == noErr) && VMEnabled)
            err = HoldMemory(SocketListenerAddr, SktListenerLen);

    return(err);
}

Back to top

Using the Socket Listener

The socket listener is set in use with the POpenSkt function, or with the more specific POpenATPSkt function. The program then periodically checks the usedQ.QHead value to determine whether the socket listener has processed a packet. If so, the packet is processed, Dequeued from the used queue, and Enqueued into the free queue. It's also possible for the same socket listener to be used by separate processes in the program. If so, then program must scan the list for the desired packet(s). Note that if multiple packets are expected, it is possible that the program may process the first packet before the listener processes the second packet. The program needs to be designed to check the usedQ.QHead value later for the additional packets.

TYPE
    PacketBuffer = RECORD
        qLink: QElemPtr;
        qType: Integer;
        buffer_Type: Integer;
        buffer_NodeID: Integer;
        buffer_Address: AddrBlock;
        buffer_Hops: Integer;
        buffer_ActCount: Integer;
        buffer_CheckSum: OSErr;
        buffer_Data: ARRAY[1..ddpMaxData] OF SignedByte;
    END;

    PacketPtr = ^PacketBuffer;

VAR
    freeQ, usedQ: QHdr;
    bufPtr : PacketPtr;
    .
    .
    .

    WHILE (usedQ.QHead <> nil) DO     { is packet available for processing? }
        BEGIN
            bufPtr := PacketPtr(usedQ.QHead);  { get the packet ptr }
            IF (Dequeue(QElemPtr(bufPtr), @usedQ) <> noErr) THEN
                BEGIN

                    { Process the packet information }

                    { requeue the packet buffer for use. }
                    Enqueue(QElemPtr(bufPtr), @freeQ);
                END
            ELSE
                BEGIN
                    { error occurred dequeueing packet }
                    { - perform error processing here }
                END;
        END;

For C Programmers, the socket listener code is used as follows:

typedef struct {
    QElemPtr    qLink;
    short       qType;
    short       buffer_type;        /* DDP Type */
    short       buffer_NodeID;      /* Destination Node */
    AddrBlock   buffer_Address;     /* Source Address in AddrBlock format */
    short       buffer_Hops;        /* Hop count */
    short       buffer_ActCount;    /* length of DDP datagram */
    OSErr       buffer_CheckSum;    /* Checksum returned here */
    char        buffer_Data[ddpMaxData]; /* the DDP datagram */
} PacketBuffer;

typedef PacketBuffer *PacketPtr;

QHdr       freeQ, usedQ;
PacketPtr  bufPtr;
    .
    .
    .

    while (usedQ.QHead != nil) {     /* is packet available for processing */
        bufPtr = (PacketPtr)usedQ.QHead;       /* get the packet ptr */
        if (Dequeue(QElemPtr(bufPtr), &usedQ) == noErr {

            /* Process the packet information */

            /* requeue the packet buffer for use. */
            Enqueue(QElemPtr(bufPtr), &freeQ);
        }
        else {

            /* error occurred dequeueing packet */
            /* - perform error processing here */
        }
    }

Back to top

The AppleTalk Transition Queue

The AppleTalk Transition Queue keeps applications and other resident processes on the Macintosh informed of AppleTalk events, such as the opening and closing of AppleTalk drivers, or changes to the Flagship name (to be discussed later in this Note). A comprehensive discussion of the AppleTalk Transition Queue is presented in Inside Macintosh Volume VI, Chapter 32. New to the AppleTalk Transition Queue are messages regarding the Flagship Naming Service, the AppleTalk Multiple Node Architecture (also to be discussed later in this Note), changes to processor speed that may affect LocalTalk timers, and a transition to indicate change of the network cable range.

Later in this section is a sample Transition Queue procedure in both C and Pascal which includes the known transition selectors. There is also a sample Pascal source for determining whether the LAP Manager version 53 or later exists. Calling LAPAddATQ for AppleTalk versions 52 and earlier will result in a system crash since the LAP Manager is not implemented prior to AppleTalk version 53. The Boolean function, LAPMgrExists, should be used instead of checking the low-memory global LAPMgrPtr, $0B18.

Back to top

Bug with LAPAddATQ and LAPRmvATQ Glue Code

A bug exists in the glue code for the LAPAddATQ and for the LAPRmvATQ routines in the Interface.o file of MPW v3.2.x. The same glue code is used with the Think C v5.0.x product and will affect those users as well. The glue code for these routines are correct under MPW v3.3 and for Think C v6.0. The following section remains in this Technote for developers who have not upgraded to these recent development platforms.

In the glue code, these calls use the Pascal stack calling convention by allocating memory on the stack for the OSErr result. The ATQEntryPtr is then pushed onto the stack and the LAP Manager is called. Upon return from a JSR instruction to the LAP Manager code, the return address is placed in register A0 and the stack incremented. The glue code should then move the two byte result onto the stack into the location reserved for the result. Instead, the glue code decrements the stack pointer by two bytes before moving the result onto the stack. The glue code jumps to the return address in register A0. Upon return, the stack is off by two bytes. If local variables are used and are referenced from the stack pointer in register A7, following the return from these LAP Manager calls, access to the local variables may not be correct.

The following Assembler glue code is supplied for MPW users to Assemble and link with their programs. For the remainder of this Technote, the call LAPAddATQFix and LAPRmvATQFix will be used instead and refer to the following code. For Think C programmers, the same functions are presented using inline Assembler. For Think Pascal programmers, one solution is to compile the Think C code and to link with the resulting Library file.

;_________________________________________________________________________
;
; ATQFix.a
;_________________________________________________________________________
;
; DTS Code Sample
;
; (c)1992 Apple Computer, Inc.
;
; Replacement code for LAPAddATQ and LAPRmvATQ in which
; the glue code in Interface.o for MPW v3.2.x, does not restore the stack to
; it's original condition.  Use the following code as opposed
; to that in the Interface.o file.
; MWP v3.3 users, disregard
;
;_________________________________________________________________________

;
; interface
; pascal OSErr LAPAddATQFix(ATQEntryPtr theATQEntry);
; pascal OSErr LAPRmvATQFix(ATQEntryPtr theATQEntry);

LAPAddATQFix   PROC   EXPORT

           MOVE.W     #$0017,D0       ; D0 selector $0017 = LAPAddATQ
           MOVEA.L    $0004(A7),A0    ; A0 -> ATQ Proc
           MOVEA.L    $0B18,A1        ; Set up to call LAP Manager
           MOVE.L     (A7)+,(A7)      ; Move return address up 4 bytes
           JSR        $0002(A1)       ; call LAP Manager
           MOVEA.L    (A7)+,A0        ; Move return address into A0
           MOVE.W     D0,(A7)         ; Move result into space reserved on
stack
           JMP        (A0)            ; Return

           ENDP

LAPRmvATQFix   PROC   EXPORT

           MOVE.W     #$0018,D0       ; D0 selector $0018 = LAPRmvATQ
           MOVEA.L    $0004(A7),A0    ; A0 -> ATQ Proc
           MOVEA.L    $0B18,A1        ; Set up to call LAP Manager
           MOVE.L     (A7)+,(A7)      ; Move return address up 4 bytes
           JSR        $0002(A1)       ; call LAP Manager
           MOVEA.L    (A7)+,A0        ; Move return address into A0
           MOVE.W     D0,(A7)         ; Move result into space reserved on
stack
           JMP        (A0)            ; Return

           ENDP

           END

;_________________________________________________________________________
; End File: ATQFix.a
;_________________________________________________________________________

For Think C, v5.x Programmers, the following code sample can be used. Think Pascal programmers can link with the library file produced by compiling this code with the Think C compiler. For Think C v6.0 Programmers, the glue code supplied with the development environment is correct, and the following sample is not necessary.

/*
 *_________________________________________________________________________
 * File: ATQFix.c
 *_________________________________________________________________________
 *
 * DTS Code Sample
 *
 * (c)1992 Apple Computer, Inc.
 *
 * Replacement code for LAPAddATQ and LAPRmvATQ for Think C, v5.x programmers

 * to fix the glue code to fix a bug in the Think C library supplied via
 * the MPW Interface.o file.  The glue code does not restore the stack to
 * it's original condition.  Use the following code as opposed
 * to that in the Think C library instead.  Think C 6.0 programmers do not
 * need to implement this solution.
 *
 *_________________________________________________________________________
 */

#ifndef __TYPES__
#include <Types.h>
#endif

#define LAPAddATQCall    0x17
#define LAPRmvATQCall    0x18
#define LAPMgrPtr        0xB18
#define LAPMgrCall       2

/**********  Prototypes ****************************/
pascal OSErr LAPAddATQFix(ATQEntryPtr theATQEntry);
pascal OSErr LAPRmvATQFix(ATQEntryPtr theATQEntry);


pascal OSErr LAPAddATQFix(ATQEntryPtr theATQEntry)
{
        asm {
            MOVE.W     #LAPAddATQCall,D0          /* D0 selector $0017 =
LAPAddATQ */
            MOVEA.L    theATQEntry,A0             /* A0 -> ATQ Proc */
            MOVEA.L    LAPMgrPtr,A1               /* Set up to call LAP Manager
*/
            JSR        LAPMgrCall(A1)             /* call LAP Manager */
            MOVE.W     D0,12(A6)                  /* move result in D0 onto the
stack */
        }
}

pascal OSErr LAPRmvATQFix(ATQEntryPtr theATQEntry)
{
        asm {
            MOVE.W     #LAPRmvATQCall,D0          /* D0 selector $0018 =
LAPRmvATQ */
            MOVEA.L    theATQEntry,A0             /* A0 -> ATQ Proc */
            MOVEA.L    LAPMgrPtr,A1               /* Set up to call LAP Manager
*/
            JSR        LAPMgrCall(A1)             /* call LAP Manager */
            MOVE.W     D0,12(A6)                  /* move result in D0 onto the
stack */
        }
}

/*
 *_________________________________________________________________________
 * End file: ATQFix.c
 *_________________________________________________________________________
 */

Back to top

Calling the AppleTalk Transition Queue

System software version 7.0 requires the use of the MPW version 3.2 interface files and libraries. The AppleTalk interface presents two new routines for calling all processes in the AppleTalk Transition Queue. Rather than use parameter block control calls as described in M.NW.AppleTalk2Mac, use the ATEvent procedure or the ATPreFlightEvent function to send transition notification to all queue elements. These procedures are discussed in Inside Macintosh Volume VI, Chapter 32.


Note:
You can call the ATEvent and ATPreFlightEvent routines only at virtual memory safe time. See the Memory Management chapter of Inside Macintosh Volume VI, Chapter 28, for information on virtual memory.


Back to top

Standard AppleTalk Transition Constants

Use the following constants for the standard AppleTalk transitions:

CONST    ATTransOpen    = 0;  {open transition }
    ATTransClose    = 2;  {prepare-to-close transition }
    ATTransClosePrep    = 3;  {permission-to-close transition }
    ATTransCancelClose    = 4;  {cancel-close transition }
    ATTransNetworkTransition    = 5;  {.MPP Network ADEV Transition }
    ATTransNameChangeTellTask    = 6;  {change-Flagship-name transition }
    ATTransNameChangeAskTask    = 7;  {permission-to-change-Flagship-name trans }
    ATTransCancelNameChange    = 8;  {cancel-change-Flagship-name transition }
    ATTransCableChange    = 'rnge' {cable range change transition }
    ATTransSpeedChange    = 'sped' {change in cpu speed }

The following information concerns the new transitions from ATTransNetworkTransition through ATTransSpeedChange.

Back to top

The Flagship Naming Service

System software version 7.0 allows the user to enter a personalized name by which her system will be published when connected to an AppleTalk network. The System 'STR ' resource ID -16413 is used to hold this name. The name (listed as Macintosh Name) can be up to 31 characters in length and can be set using the Sharing Setup Control Panel Device (cdev). This resource is different from the Chooser name, System 'STR ' resource ID -16096. When providing network services for a workstation, the Flagship name should be used so that the user can personalize his workstation name while maintaining the use of the Chooser name for server connection identification. It's important to note that the Flagship name resource is available only from system software version 7.0. DTS recommends that applications not change either of these 'STR ' resources.

Applications taking advantage of this feature should place an entry in the AppleTalk Transition Queue to stay informed as to changes to this name. Three new transitions have been defined to communicate Flagship name changes between applications and other resident processes. Support for the Flagship Naming Service Transitions is provided starting from AppleTalk version 56. Note that AppleTalk version 56 can be installed on pre-7.0 systems; however, the Flagship Naming Service is available only from System 7.0 and later.

Back to top

The ATTransNameChangeAskTask Transition

From Assembly language, the stack upon calling looks as follows:

ATQEvent            RECORD   0
ReturnAddr          DS.L     1      ; address of caller
theEvent            DS.L     1      ; = 7; ID of ATTransNameChangeAskTask
transaction
aqe                 DS.L     1      ; pointer to task record
infoPtr             DS.L     1      ; pointer to NameChangeInfo parameter
block
                    ENDR

The NameChangeInfo record block is as follows:

NameChangeInfoPtr: ^NameChangeInfo;
NameChangeInfo  = RECORD
                    newObjStr:     Str32;       {new Flagship name to change to
}
                    name:          StringPtr;   {ptr to location to place ptr
to process }
                                                {name }
                  END;

The ATTransChangeNameAskTask is issued under system software version 7.0 to inform Flagship clients that a process wants to change the Flagship name. Each AppleTalk Transition Queue element that processes the ATTransChangeNameAskTask can inspect the NameChangeInfoPtr^.newObjStr to determine the new Flagship name. If you deny the request, you must set the NameChangeInfoPtr^.name pointer with a pointer to a Pascal string buffer containing the name of your application or to the nil pointer. The AppleTalk Transition Queue process returns this pointer. The requesting application can display a dialog notifying the user of the name of the application that refused the change request.

While processing this event, you can make synchronous calls to the Name-Binding Protocol (NBP) to attempt to register your entity under the new name. It is recommended that you register an entity using the new Flagship name while handling the ATTransChangeNameAskTask event. You should not deregister an older entity at this point. Your routine must return a function result of 0 in the D0 register, indicating that it accepts the request to change the Flagship name, or a non zero value, indicating that it denies the request.


Warning:
DTS does not recommend that you change the Flagship name. The Sharing Setup cdev does not handle this event and the Macintosh name will not be updated to reflect this change if the cdev is open.


Back to top

The ATTransNameChangeTellTask Transition

From Assembly language, the stack upon calling looks as follows:

ATQEvent       RECORD  0
ReturnAddr     DS.L   1   ; address of caller
theEvent       DS.L   1   ; = 6 ID of ATTransNameChangeTellTask transaction
aqe            DS.L   1   ; pointer to task record
infoPtr        DS.L   1   ; pointer to the new Flagship name
               ENDR

A process uses ATEvent to send the ATTransNameChangeTellTaskto notify AppleTalk Transition Queue clients that the Flagship name is being changed. The LAP Manager then calls every routine in the AppleTalk Transition Queue that the Flagship name is being changed.

When the AppleTalk Manager calls your routine with a ATTransNameChangeTellTask transition, the third item on the stack is a pointer to a Pascal string of the new Flagship name to be registered. Your process should deregister any entities under the old Flagship name at this time. You can make synchronous calls to NBP to deregister an entity. Return a result of 0 in the D0 register.


Note:
When the AppleTalk Manager calls your process with a TellTask transition (that is, with a routine selector of ATTransNameChangeTellTask), you cannot prevent the Flagship name from being changed.


To send notification that your process intends to change the Flagship name, use the ATEvent function described above. Pass ATTransNameChangeTellTask as the event parameter and a pointer to the new Flagship name (Pascal string) as the infoPtr parameter.

Back to top

The ATTransCancelNameChange Transition

From Assembly language, the stack upon calling looks as follows:

ATQEvent    RECORD    0
ReturnAddr    DS.L    1    ; address of caller
theEvent    DS.L    1    ; = 8; ID of ATTransCancelNameChange transaction
aqe    DS.L    1    ; pointer to task record
    ENDR

The ATTransCancelNameChange transition complements the ATTransNameChangeAskTask transition. Processes that acknowledged an ATTransNameChangeAskTask transition will be sent the ATTransCancelNameChange transition if a later process disallows the change of Flagship name. Your process should deregister any NBP entities registered during the ATTransNameChangeAskTask transition. You can make synchronous calls to NBP to deregister an entity. Return a result of 0 in the D0 register.

Back to top

System 7.0 Sharing Setup cdev / Flagship Naming Service Interaction:

The Flagship Naming Service is a new system service built into System 7. It is used to publish the workstation using the Flagship name. The Flagship Naming Service implements an AppleTalk Transition Queue element to respond to changes in the Flagship name. For example, the Sharing Setup cdev can be used to reset the Flagship name. When a new Macintosh (Flagship) name is entered in Sharing Setup, Sharing Setup sends an ATTransNameChangeAskTask message to the AppleTalk Transition Queue to request permission to change the Flagship name. The Flagship Naming Service receives the ATTransNameChangeAskTask transition and registers the new name under the type "Workstation" on the local network. Sharing Setup follows with the ATTransNameChangeTellTask to notify AppleTalk Transition Queue clients that a change in Flagship name will occur. The Flagship Naming Service responds by deregistering the workstation under the old Flagship name.

If an error occurs from the NBPRegister call, Flagship Naming Service returns a non-zero error (the error returned from NBPRegister) and a pointer to its name in the NameChangeInfoPtr^.Name field. Note that the Workstation name is still registered under the previous Flagship name at this point.

Back to top

AppleTalk Remote Access Network Transition Event

AppleTalk Remote Access allows you to establish an AppleTalk connection between two Macintosh computers over standard telephone lines. If the Macintosh you dial-in to is on an AppleTalk network, such as LocalTalk or Ethernet, your Macintosh becomes, effectively, a node on that network. You are then able to use all the services on the new network. Given this new capability, it is important that services running on your Macintosh be notified when new AppleTalk connections are established and broken. For this reason, the ATTransNetworkTransition event has been added to AppleTalk version 57. With version 57 present, this event can be expected in system software version 6.0.5 or later.

Internally, both the AppleTalk Session Protocol (ASP) and the AppleTalk Data Stream Protocol (ADSP) have been modified to respond to this transition event. When a disconnect transition event is detected, these drivers close down sessions on the remote side of the connection.

Back to top

The ATTransNetworkTransition Transition

From Assembly language, the stack upon calling looks as follows:

ATQEvent    RECORD    0
ReturnAddr    DS.L    1    ; address of caller
theEvent    DS.L    1    ; = 5; ID of ATTransNetworkTransition
aqe    DS.L    1    ; pointer to task record
infoPtr    DS.L    1    ; pointer to the TNetworkTransition record
    ENDR

The TNetworkTransition record block is passed as follows:

TNetworkTransition    RECORD    0
private    DS.L    1    ; pointer used internally by AppleTalk Remote Access
netValidProc    DS.L    1    ; pointer to the network validate procedure
newConnectivity    DS.B    1    ; true = new connectivity, false = loss of
connectivity
    ENDR

Back to top

Network Transition Event for AppleTalk Remote Access

Network Transition events are generated by AppleTalk Remote Access to inform AppleTalk Transition Queue applications and resident processes that network connectivity has changed. The type of change is indicated by the NetTransPtr^.newConnectivity flag. If this flag is true, a connection to a new internet has taken place. In this case, all network addresses will be returned as reachable. If the newConnectivity flag is false, certain networks are no longer reachable. Since AppleTalk Remote Access is connection based, it has knowledge of where a specific network exists. AppleTalk Remote Access can take advantage of that knowledge during a disconnect to inform AppleTalk Transition Queue clients that a network is no longer reachable. This information can be used by clients to age out connections immediately rather than waiting a potentially long period of time before discovering that the other end is no longer responding.

When AppleTalk Remote Access is disconnecting, it passes a network validation hook in the TNetworkTransition record, NetTransPtr^.netValidProc. A client can use the validation hook to ask AppleTalk Remote Access whether a specific network is still reachable. If the network is still reachable, the validate function will return true. A client can then continue to check other networks of interest until the status of each one has been determined. After a client has finished checking networks, control returns to AppleTalk Remote Access where the next AppleTalk Transition Queue client is called.

The information the network validation hook returns is valid only if a client has just been called as a result of a transition. A client can validate networks only when she has been called to handle a Network Transition event. Note that the Network Transition event can be called as the result of an interrupt, so a client should obey all of the normal conventions involved with being called at this time (for example, don't make calls that move memory and don't make synchronous Preferred AppleTalk calls).

To check a network number for validity the client uses the network validate procedure to call AppleTalk Remote Access. This call is defined using C calling conventions as follows:

pascal long netValidProc(TNetworkTransition *thetrans, unsigned long
theAddress);

    thetrans    -->    Pass in the TNetworkTransition record given to you when your
               transition handler was called.

    theAddress    -->    This is the network address you want checked. The format
of
            theAddress is the same as AddrBlock as defined in Inside
            Macintosh II, page 281:

            Bytes 2 & 3 (High Word) - Network Number
            Byte 1 - Node Number
            Byte 0 (Low Byte) - Socket Number

Result codes
true network is still reachable
false network is no longer reachable

AppleTalk Transition Queue handlers written in Pascal must implement glue code to use the netValidProc.

Back to top

Cable Range Change Transition Event

The Cable Range Transition, ATTransCableChange, event informs AppleTalk Transition Queue processes that the cable range for the current network has changed. This can occur when a router is first seen, when the last router ages out, or when an RTMP broadcast packet is first received with a cable range that is different from the current range. The ATTransCableChange event is implemented beginning with AppleTalk version 57. This transition is useful when an application must stay informed as to the presence of zones. With version 57 present, this event can be expected in system software version 6.0.5 and later.

Back to top

The ATTransCableChange Transition

From Assembly language, the stack upon calling looks as follows:

ATQEvent    RECORD    0
ReturnAddr    DS.L    1    ; address of caller
theEvent    DS.L    1    ; = 'rnge'; ID of ATTransCableChange
aqe    DS.L    1    ; pointer to task record
infoPtr    DS.L    1    ; pointer to the TNetworkTransition record
    ENDR

The TNewCRTrans record block is passed as follows:

TNewCRTrans    RECORD    0
newCableLo    DS.W    1    ; the new Cable Lo received from RTMP
newCableHi    DS.W    1    ; the new Cable Hi received from RTMP
    ENDR

The cable range is a range of network numbers starting from the lowest network number through the highest network number defined by a seed router for a network. All node addresses acquired on a network must have a network number within the defined cable range. For non extended networks, the lowest and the highest network numbers are the same. If the cable range on the network changes, for example, if the router on the network goes down, the Cable Range Change event will be issued with the parameters described earlier in this Technical Note.

After receiving the event, a multinode application should use the new cable range to check if all the multinodes it obtained prior to the event are still valid. For the invalid multinodes, the application should issue the .MPP RemoveNode control call to get rid of invalid nodes. The .MPP AddNode control call can be issued immediately after removing invalid nodes to obtain new valid multinodes in the new cable range. This Cable Range Change Transition event will be issued only during system task time.

Back to top

The Speed Change Transition Event

The ATTransSpeedChange Transition event is defined for applications that change CPU speed without rebooting, to notify time-dependent processes that such change has taken place. Such speed change occurs when altering the cache states on the 68030 or 68040 CPUs, or with third-party accelerator cards that allow speed changes on the fly via a cdev. Any process that alters the effective CPU speed should use the ATEvent to notify processes of this change. Issue the ATTransSpeedChange event only at SystemTask time! Any process that depends on changes to the CPU speed should watch for this event. The Speed Change Transition event is issued only during system task time.

One time-dependent code module is LocalTalk, whose low-level timer values must be recalculated when the CPU speed changes. Note that altering the cache state on the 68030 does not affect LocalTalk; however, doing so on the 68040 does affect LocalTalk timers. This event must be sent by any application that toggles caching on the 68040 processor on the fly. If the cache is toggled and LocalTalk is not notified, a loss of network connection will result if LocalTalk is the current network connection. Note that only LocalTalk implemented in AppleTalk version 57 or later recognizes the Speed Change Transition event. Contact Apple Software Licensing for licensing AppleTalk version 57.

Regarding LocalTalk on the Macintosh Plus, the timing values are hard-coded in ROM regardless of the CPU speed. Vendors of accelerators for Macintosh Plus computers should contact DTS for information on how to make LocalTalk work for you.

Back to top

The ATTransSpeedChange Transition

From Assembly language, the stack upon calling looks as follows:

ATQEvent    RECORD    0
ReturnAddr    DS.L    1    ; address of caller
theEvent    DS.L    1    ; = 'sped'; ID of ATTransSpeedChange
aqe    DS.L    1    ; pointer to task record
    ENDR

To notify LocalTalk that a change in processor speed has taken place, use the ATEvent procedure. Pass ATTransSpeedChange as the event parameter and a nil pointer as the infoPtr parameter. This event must be issued only at system task time. The ATEvent procedure is implemented as a glue routine in MPW 3.2 or greater. The following line of code demonstrates notification of the ATTransSpeedChange event.

ATEvent (ATTransSpeedChange, nil);

Back to top

Sample Pascal Source to LAPMgrExists Function

It is important to check whether the LAP Manager exists before making LAP Manager calls like LAPAddATQ. The LAP Manager is implemented beginning with AppleTalk version 53. Rather than check the low-memory global LAPMgrPtr, it is preferable to check for its existence from a higher level. The following Pascal source demonstrates this technique:

FUNCTION GestaltAvailable: Boolean;
CONST
    _Gestalt = $A1AD;
BEGIN
    GestaltAvailable := TrapAvailable(_Gestalt);
    { TrapAvailable is documented in Inside Macintosh Volume VI, page 3-8 }
END;


FUNCTION AppleTalkVersion: Integer;
CONST
    versionRequested = 1; { version of SysEnvRec }
VAR
    refNum: INTEGER;
    world: SysEnvRec;
    attrib: LONGINT;
BEGIN
    AppleTalkVersion := 0;    { default to no AppleTalk }
    IF OpenDriver('.MPP', refNum) = noErr THEN    { open the AppleTalk driver }
        IF GestaltAvailable THEN
        BEGIN
            IF (Gestalt(gestaltAppleTalkVersion, attrib) = noErr) THEN
                AppleTalkVersion := BAND(attrib, $000000FF);
        END
        ELSE    { Gestalt or gestaltAppleTalkVersion selector isn't available. }
            IF SysEnvirons(versionRequested, world) = noErr THEN
                AppleTalkVersion := world.atDrvrVersNum;
END;


FUNCTION LAPMgrExists: Boolean;
BEGIN
    { AppleTalk phase 2 is in AppleTalk version 53 and later }
    LAPMgrExists := (AppleTalkVersion >= 53);
END;

Back to top

Sample AppleTalk Transition Queue Function

A sample AppleTalk Transition Queue function has been implemented in both C and Pascal. These samples have been submitted as snippet code to appear on the Developer CD Series disc. Since Transition Queue handlers are called with a C-style stack frame, the Pascal sample includes the necessary C glue.

Back to top

Sample AppleTalk Transition Queue Function in C

The following is a sample AppleTalk Transition Queue handler for C programmers. To place the handler in the AppleTalk Transition Queue, define a structure of type myATQEntry in the main body of the application. Assign the SampleTransQueue function to the myATQEntry.CallAddr field. Use the LAPAddATQFixed function to add the handler to the AppleTalk Transition Queue. Remember to remove the handler with the LAPRmvATQFixed function before quitting the application.


Warning:
The System 7 Tuner extension will not load AppleTalk resources if it detects that AppleTalk is off at boot time. Remember to check the result from the LAPAddATQFixed function to determine whether the handler was installed successfully.


The following code was written with MPW C v3.2:


/*----------------------------------------------------------------------
  file: TransQueue.h
----------------------------------------------------------------------*/

#include <AppleTalk.h>

/*
 *  Transition Queue routines are designed with C calling conventions in
mind.
 *  They are passed parameters with a C-style stack and return values are
expected
 *  to be in register D0.
 */

#define ATTransOpen                    0        /* .MPP just opened */
#define ATTransClose                    2        /* .MPP is closing */
#define ATTransClosePrep                3        /* OK for .MPP to close? */
#define ATTransCancelClose            4        /* .MPP close was canceled */
#define ATTransNetworkTransition    5        /* .MPP Network ADEV transition */
#define ATTransNameChangeTellTask    6        /* Flagship name is changing */
#define ATTransNameChangeAskTask    7        /* OK to change Flagship name */
#define ATTransCancelNameChange        8        /* Flagship name change was canceled */
#define ATTransCableChange            'rnge'    /* Cable Range Change has occurred */
#define ATTransSpeedChange            'sped'    /* Change in processor speed has occurred
*/

/*----------------------------------------------------------------------
        NBP Name Change Info record
----------------------------------------------------------------------*/
typedef struct NameChangeInfo {
    Str32     newObjStr;    /* new NBP name */
    Ptr    name;            /* Ptr to location to place a pointer to Pascal string of */
                            /* name of process that NAK'd the event */
}
    NameChangeInfo, *NameChangePtr, **NameChangeHdl;

/*----------------------------------------------------------------------
        Network Transition Info Record
----------------------------------------------------------------------*/

typedef struct TNetworkTransition {
    Ptr        private;            /* pointer to private structure */
    ProcPtr    netValidProc;        /* pointer to network validation procedure */
    Boolean     newConnectivity;    /* true = new connection */
                                     /* false = loss of connection */

}
    TNetworkTransition , *TNetworkTransitionPtr, **TNetworkTransitionHdl;

typedef    pascal long    (*NetworkTransitionProcPtr)(TNetworkTransitionPtr netTrans,
\
                      unsigned long theNet);

/*----------------------------------------------------------------------
        Cable Range Transition Info Record
----------------------------------------------------------------------*/
typedef struct TNewCRTrans {
    short    newCableLo;    /* the new Cable Lo received from RTMP */
    short    newCableHi;    /* the new Cable Hi received from RTMP */
}
    TNewCRTrans , *TNewCRTransPtr, **TNewCRTransHdl;

/*----------------------------------------------------------------------
        AppleTalk Transition Queue Element
----------------------------------------------------------------------*/
typedef struct    myATQEntry {
    Ptr        qLink;        /* -> next queue element */
    short        qType;        /* unused */
    ProcPtr    CallAddr;    /* -> transition procedure */
    Ptr        globs;        /* -> to user defined globals */
}
    myATQEntry, *myATQEntryPtr, **myATQEntryHdl;


/*----------------------------------------------------------------------
  file: TransQueue.c
----------------------------------------------------------------------*/

#include <Memory.h>
#include <AppleTalk.h>
#include "TransQueue.h"

long SampleTransQueue(long selector, myATQEntry *q, void *p)
{
    long                        returnVal = 0; /* return 0 for unrecognized events */
    NameChangePtr                myNameChangePtr;
    TNewCRTransPtr            myTNewCRTransPtr;
      TNetworkTransitionPtr    myTNetworkTransitionPtr;
    NetworkTransitionProcPtr    myNTProcPtr;
    StringPtr                    newNamePtr;
    long                        checkThisNet;
    char                         **t;
    short                        myCableLo, myCableHi;


    /*
     * This is the dispatch part of the routine. We'll check the selector passed into
     * the task; its location is 4 bytes off the stack (selector).
     */
    switch(selector) {
        case ATTransOpen:
            /*
             *  Someone has opened the .MPP driver. This is where one would reset the
             *  application to its usual network state (that is, you could register your
             *  NBP name here). Always return 0.
             */
            break;

        case ATTransClose:
            /*
             *  When this routine is called, .MPP is going to shut down no matter what we
             *  do. Handle that type of situation here (that is, one could remove an NBP
             *  name and close down all sessions); 'p' will be nil. Return 0
             *  to indicate no error.
             */
            break;

        case ATTransClosePrep:
            /*
             *  This event gives us the chance to deny the closing of AppleTalk if we
             *  want. Returning    a value of 0 means it's OK to close; nonzero
             *  indicates we'd rather not close at this time.
             *
             *  With this event, the parameter 'p' actually means something. 'p' in
             *  this event is a pointer to an address that can hold a pointer to a
             *  string of our choosing. This string indicates to the user which task
             *  would rather not close. If you don't want AppleTalk to close, but you
             *  don't have a name to stick in there, you MUST place a nil value in
             *  there instead.
             *
             *  (We're doing this all locally to this case because it's C and we can, so

             *  there.)
             */
            newNamePtr = (StringPtr)NewPtr(sizeof(Str32));

            /*
             *  Assume Ptr allocation successful.
             */

            newNamePtr = "\pBanana Mail";    /* This will either be an Ax reference or PC
             *  relative depending on compiler and options. */

            /*
             *  Get a new reference to the address we were passed (in a form we can use).
             */
            t = (char **) p;
            /*
             *  Place the address of our string into the address we were passed.
             */
            *t = (char *)newNamePtr;

            /*
             *  Return a nonzero value so that AppleTalk knows we'd rather not close.
             */
            returnVal = 1;
            break;

        case ATTransCancelClose:
            /*
             *  Just kidding, we didn't really want to cancel that AppleTalk closing
             *  after all. Reset all your network activities that you have disabled
*  here (if any). In our case, we'll just fall through. 'p' will be nil.
             */
            break;

        case ATTransNetworkTransition:
            /*
             *  A Remote AppleTalk connection has been made or broken.
             *  'p' is a pointer to a TNetworkTransition record.
             *   Always return 0.
             */
            myTNetworkTransitionPtr = (TNetworkTransitionPtr)p;
            /*
             *  Check newConnectivity element to determine whether
             *  Remote Access is coming up or going down.
             */
            if (myTNetworkTransitionPtr->newConnectivity) {
                /*
                 * Have a new connection
                 */
            }
            else {
                /*
                 * Determine which network addresses need to be validated
                 * and assign the value to checkThisNet.
                 */
                checkThisNet = 0x1234FD80;  /* network 0x1234, node 0xFD, socket 0x80 */
                myNTProcPtr = (NetworkTransitionProcPtr)myTNetworkTransitionPtr->netValidProc;
                if ((*myNTProcPtr)(myTNetworkTransitionPtr, checkThisNet)) {
                    /*
                     * Network is still valid.
                     */
                }
                else {
                    /*
                     * Network is no longer valid.
                     */
                }
            }
            break;

        case ATTransNameChangeTellTask:
            /*
             *  Someone is changing the Flagship name and there is nothing we can do.
             *  The parameter 'p' is a pointer to a Pascal-style string that holds the new
             *  Flagship name.
               */
            newNamePtr = (StringPtr) p;

            /*
             *  You should deregister any previously registered NBP entries under the
             *  'old' Flagship name. Always return 0.
             */
            break;

        case ATTransNameChangeAskTask:
            /*
             *  Someone is messing with the Flagship name.
             *  With this event, the parameter 'p' actually means something. 'p' is
             *  a pointer to a NameChangeInfo record. The newObjStr field contains the

             *  new Flagship name. Try to register a new entity using the new Flagship name.
             *  Returning a value of 0 means it's OK to change the Flagship name.
             */
            myNameChangePtr = (NameChangePtr)p;

            /*
             *  If the NBPRegister is unsuccessful, return the error. You must also set
             *  p->name pointer with a pointer to a Pascal-style string of the process
             *  name.
             */
            break;

        case ATTransCancelNameChange:
            /*
             *  Just kidding, we didn't really want to change that name after
             *  all. Remove new NBP entry registered under the ATTransNameChangeAskTask
             *  Transition. In our case,  we'll just fall through. 'p' will be nil. Remember
             *  to return 0.
             */
            break;

        case ATTransCableChange:
            /*
             *  The cable range for the network has changed. The pointer 'p' points
             *  to a structure with the new network range. (TNewCRTransPtr)p->newCableLo
             *  is the lowest value of the new network range. (TNewCRTransPtr)p->newCableHi
             *  is the highest value of the new network range. After handling this event,
             *  always return 0.
             */
            myTNewCRTransPtr = (TNewCRTransPtr)p;
            myCableLo = myTNewCRTransPtr->newCableLo;
            myCableHi = myTNewCRTransPtr->newCableHi;
            break;

        case ATTransSpeedChange:
            /*
             *  The processor speed has changed. Only LocalTalk responds to this event.
             *  We demonstrate this event for completeness only.
             *  Always return 0.
             */
            break;

        default:
            /*
             *  For future Transition Queue events (and yes, Virginia, there will be more).
             */
            break;
    } /* end of switch */

    /*
     *    return value in register D0
     */
    return returnVal;
}

Back to top

Sample AppleTalk Transition Queue Function in Pascal

The following is a sample AppleTalk Transition Queue handler for Pascal programmers. AppleTalk Transition Queue handlers are passed parameters using the C parameter passing convention. In addition, the 4-byte function result must be returned in register D0. To meet this requirement, a C procedure is used to call the handler, then to place the 4-byte result into register D0. The stub procedure listing follows the handler.

To place the handler in the AppleTalk Transition Queue, define a structure of type myATQEntry in the main body of the application. Assign the CallTransQueue C procedure to the myATQEntry.CallAddr field. Use the LAPAddATQFixed function to add the handler to the AppleTalk Transition Queue. Remember to remove the handler with the LAPRmvATQFixed function before quitting the application.


Note:
The System 7 Tuner extension will not load AppleTalk resources if it detects that AppleTalk is off at boot time. Remember to check the result from the LAPAddATQFixed function to determine whether the handler was installed successfully.


The following code was written with MPW Pascal and Assembler v3.2:

{*******************************************************************************
  file: TransQueueGlue.a
********************************************************************************

;FUNCTION CallTransQueue(selector : LONGINT; q : ATQEntryPtr; p : Ptr) : LONGINT; ;EXTERNAL;
;{
; *  CallTransQueue is the routine passed to the LAP Manager using the LAPAddATQ
; *  function.  This glue routine is necessary since the LAP Manager sends stack
; *  parameters in the C stack passing convention.  Before calling the LAPAddATQ
; *  function, the users set the PATQProcPtr to the address of the PAscal style
; *  ATQ handler.  The glue routine takes the parameters from the stack and sets up
; *  a Pascal stack, then calls the function pointed to by the PATQProcPtr field.
; *  On return, the result is pulled from the stack and put into the D0 register
; *  where the LAP Manager expects the result.  This routine must be written in
; *  Assembler or C
; *
; }
CallTransQueue    PROC    EXPORT
           LINK       A6,#$0000       ; set up a local stack frame }
           CLR.L      -(A7)           ; set space for return result }
           MOVE.L     $0008(A6),-(A7) ; move selector to stack }
           MOVE.L     $000C(A6),-(A7) ; move ATQPtr to stack }
           MOVEA.L    $000E(A7),A0    ; move Ptr to pascal ATQ func to stack }
           MOVE.L     $0010(A6),-(A7) ; move last parameter - Ptr to stack }
           JSR        (A0)            ; call the pascal ATQ func }
           MOVE.L     (A7)+,D0        ; move result into D0 }
           UNLK       A6              ; tear down local stack frame }
           RTS                        ; return
           ENDP
           END

{*******************************************************************************
  file: TransQueue.p
********************************************************************************

UNIT TransQueue;

INTERFACE

USES MemTypes, QuickDraw, OSIntF, AppleTalk;

CONST
(*  Comment the following 4 constants since they are already defined in the AppleTalk unit.
    ATTransOpen                 =   0;  { .MPP is opening }
    ATTransClose                =   2;  { .MPP is closing }
    ATTransClosePrep            =   3;  { OK for .MPP to close? }
    ATTransCancelClose          =   4;  { .MPP close was canceled }
*)
    ATTransNetworkTransition    =   5;  { .MPP Network ADEV transition }
    ATTransNameChangeTellTask   =   6;  { Flagship name is changing }
    ATTransNameChangeAskTask    =   7;  { OK to change Flagship name }
    ATTransCancelNameChange     =   8;  { Flagship name change was canceled.
}
    ATTransCableChange          =   'rnge'; { Cable Range Change has occurred.
}
    ATTransSpeedChange          =   'sped'; { Change in processor speed has
occurred. }

{----------------------------------------------------------------------
        NBP Name Change Info record
----------------------------------------------------------------------}

TYPE

NameChangeInfo = RECORD
    newObjStr    : Str32;        { new NBP name }
    name        : Ptr;            { Ptr to location to place a pointer to Pascal string of }
                                { name of process that NAK'd the event }
    END;
NameChangePtr = ^NameChangeInfo;
NameChangeHdl = ^NameChangePtr;

{----------------------------------------------------------------------
        Network Transition Info Record
----------------------------------------------------------------------}

TNetworkTransition = RECORD
    private        : Ptr;            { pointer to private structure }
    netValidProc    : ProcPtr;    { pointer to network validation procedure }
    newConnectivity : Boolean;    { true = new connection, }
                                     { false = loss of connection }
    END;

TNetworkTransitionPtr = ^TNetworkTransition;
TNetworkTransitionHdl = ^TNetworkTransitionPtr;

{ The netValidProc procedure has the following C interface. Note the }
{ CallNetValidProc C function, which follows. The C Glue routine allows the
Pascal }
{ handler to make the call to the netValidProc function. }

{
typedef pascal long    (*NetworkTransitionProcPtr)(TNetworkTransitionPtr netTrans,
\
                      unsigned long theNet);
}
{----------------------------------------------------------------------
        Cable Range Transition Info Record
----------------------------------------------------------------------}
TNewCRTrans = RECORD
    newCableLo    : INTEGER;    { the new Cable Lo received from RTMP }
    newCableHi    : INTEGER;    { the new Cable Hi received from RTMP }
    END;
TNewCRTransPtr = ^TNewCRTrans;
TNewCRTransHdl = ^TNewCRTransPtr;

{----------------------------------------------------------------------
        AppleTalk Transition Queue Element
----------------------------------------------------------------------}
myATQEntry = RECORD
    qlink        : Ptr;            { -> next queue element }
    qType        : INTEGER;    { unused }
    CallAddr    : ProcPtr;    { -> transition procedure }
    globs        : Ptr;            { -> to user defined globals }
    PATQProcPtr    : ProcPtr;    {.-> to pascal ATQ routine }
    END;
myATQEntryPtr = ^myATQEntry;
myATQEntryHdl = ^myATQEntryPtr;


{---------------- Prototypes --------------------}

FUNCTION SampleTransQueue (selector :LONGINT; q :myATQEntryPtr;  p :Ptr) :
LONGINT;
{
 *  Transition Queue routines are designed with C calling conventions in mind.
 *  They are passed parameters with a C-style stack and return values are expected
 *  to be in register D0. Note that the CallTransQueue C glue routine is used
 *  to reverse the C-style stack to Pascal style before calling the handler. The
 *  procedure CallTransQueue follows this listing. To install this Trans Queue
 *  handler, assign CallTransQueue to the CallAddr field, NOT SampleTransQueue.
 }

FUNCTION CallNetValidProc(netTrans : TNetworkTransitionPtr; theNet : LONGINT;
                          p : ProcPtr) : LONGINT;
{
 *  CallNetValidProc is used to call the netValidProc passed in the
TNetworkTransition
 *  record. Since Pascal cannot call the ProcPtr directly, a glue routine is
 *  used.
 }
    INLINE
        $205F,  { MOVEA.L  (SP)+,A0 ; get ProcPtr into A0, and make stack right for call }
        $4E90;  { JSR      (A0)     ; call ProcPtr, and return to caller }

IMPLEMENTATION

FUNCTION SampleTransQueue (selector :LONGINT; q :myATQEntryPtr;  p :Ptr) :
LONGINT;VAR
    returnVal                    : LONGINT;
    myNameChgPtr                : NameChangePtr;
    myTNewCRTransPtr            : TNewCRTransPtr;
    myTNetworkTransitionPtr    : TNetworkTransitionPtr;
    newNamePtr                : StringPtr;
    processNameHdl            : StringHandle;
    myCableLo, myCableHi        : INTEGER;
    shortSelector                : INTEGER;
    checkThisNet                : LONGINT;


BEGIN
    returnVal := 0;                     { return 0 for unrecognized events )
    {
     *  This is the dispatch part of the routine. We'll check the selector passed into
     *  the task; its location is 4 bytes off the stack (selector).
     }
    IF ((selector <= ATTransCancelNameChange) AND (selector >= ATTransOpen)) THEN
    {
     *  Check whether a numeric selector is being used whose known values are between
     *  8 and 0 so that we can implement a CASE statement with an INTEGER var.
     }
    BEGIN
        shortSelector := selector;
        CASE shortSelector OF
            ATTransOpen:
            BEGIN
                {
                 *  Someone has opened the .MPP driver. This is where one would reset the

                 *  application to its usual network state (that is, you could register your
                 *  NBP name here). Always return 0.
                 }
            END;

            ATTransClose:
            BEGIN
                {
                 *  When this routine is called, .MPP is going to shut down no matter what we
                 *  do. Handle that type of situation here (that is, one could remove an NBP
                 *  name and close down all sessions). 'p' will be nil. Return 0 to
                 *  indicate no error.
                 }
            END;

            ATTransClosePrep:
            BEGIN
                {
                 *  This event gives us the chance to deny the closing of AppleTalk IF we
                 *  want. Returning a value of 0 means it's OK to close; nonzero
                 *  indicates we'd rather not close at this time.
                 *
                 *  With this event, the parameter 'p' actually means something. 'p' in
                 *  this event is a pointer to an address that can hold a pointer to a
                 *  string of our choosing. This string indicates to the user which task
                 *  would rather not close. If you don't want AppleTalk to close, but you
                 *  don't have a name to stick in there,  you MUST place a nil value in
                 *  there instead.
                 }

                {
                 *  Get a new reference to the address we were passed (in a form we can use).
                 *  (We're doing this all locally to this case because we can, so
                 *  there.)
                 }
                processNameHdl := StringHandle(NewHandle(sizeof(Str32)));

                {
                 *  Place the address of our string into the address we were passed.
                 }
                 := 'Banana Mail';
                Ptr(p) := Ptr(processNameHdl);

                {
                 *  Return a nonzero value so that AppleTalk knows we'd rather not close.
                 }
                returnVal := 1;
            END;

            ATTransCancelClose:
            BEGIN
                {
                 *  Just kidding, we didn't really want to cancel that AppleTalk closing
                 *  after all. Reset all your network activities that you have disabled here
                 *  (IF any). In our case, we'll just fall through. 'p' will be nil.
                 }
            END;

            ATTransNetworkTransition:
            BEGIN
                {
                 *  A Remote AppleTalk connection has been made or broken.
                 *  'p' is a pointer to a TNetworkTransition record.
                 *   Always return 0.
                 }
                myTNetworkTransitionPtr := TNetworkTransitionPtr(p);
                {
                 *  Check newConnectivity element to determine whether
                 *  Remote Access is coming up or going down.
                 }
                if (myTNetworkTransitionPtr^.newConnectivity) THEN
                BEGIN
                    {
                     * Have a new connection.
                     }
                END
                ELSE
                BEGIN
                    {
                     * Determine which network addresses need to be validated
                     * and assign the value to checkThisNet.
                     }
                    checkThisNet = $1234FD80;  /* network $1234, node $FD, socket $80 */
                    if (CallNetValidProc(myTNetworkTransitionPtr, checkThisNet,
                        myTNetworkTransitionPtr^.netValidProc) <> 0) THEN
                    BEGIN
                        {
                         * Network is still valid.
                         }
                    END
                    ELSE
                    BEGIN
                        {
                         * Network is no longer valid.
                         }
                    END;
                END;
            END;

            ATTransNameChangeTellTask:
            BEGIN
                {
                 *  Someone is changing the Flagship name and there is nothing we can do.
                 *  The parameter 'p' is a pointer to a Pascal-style string that holds the new
                 *  Flagship name.
                }
                newNamePtr := StringPtr (p);

                {
                 *  You should deregister any previously registered NBP entries under the
                 *  'old' Flagship name. Always return 0.
                 }
            END;

            ATTransNameChangeAskTask:
            BEGIN
                {
                 *  Someone is messing with the Flagship name.
                 *  With this event, the parameter 'p' actually means something. 'p' is
                 *  a pointer to a NameChangeInfo record. The newObjStr field contains the
                 *  new Flagship name. Try to register a new entity using the new Flagship
                 *  name. Returning a value of 0 means it's OK to change the Flagship name.
                 }
                myNameChgPtr := NameChangePtr (p);

                {
                 *  If the NBPRegister is unsuccessful, return the error. You must also set
                 *  p->name pointer with a pointer to a string of the process name.
                 }
            END;

            ATTransCancelNameChange:
            BEGIN
                {
                 *  Just kidding, we didn't really want to cancel that name change after
                 *  all. Remove new NBP entry registered under the
                 *  ATTransNameChangeAskTask Transition. 'p' will be nil.
                 *  Remember to return 0.
                 }
            END;

            OTHERWISE
                returnVal := 0;
                {
                 *  Just in case some other numeric selector is implemented.
                 }
        END; { CASE }
    END
    ELSE IF (ResType(selector) = ATTransCableChange) THEN
    BEGIN
        {
         *  The cable range for the network has changed. The pointer 'p' points
         *  to a structure with the new network range.  (TNewCRTransPtr)p->newCableLo
         *  is the lowest value of the new network range. (TNewCRTransPtr)p->newCableHi
         *  is the highest value of the new network range. After handling this event,
         *  always return 0.
         }
        myTNewCRTransPtr := TNewCRTransPtr(p);
        myCableLo := myTNewCRTransPtr^.newCableLo;
        myCableHi := myTNewCRTransPtr^.newCableHi;
        returnVal := 0;
    END
    ELSE IF (ResType(selector) = ATTransSpeedChange) THEN
    BEGIN
        {
         *  The processor speed has changed. Only LocalTalk responds to this event.
         *  We demonstrate this event for completeness only.
         *  Always return 0.
         }
         returnVal := 0;
    END; { IF }

    SampleTransQueue := returnVal;
END;

END. { of UNIT }

Back to top

Multivendor ADEV Architecture

With the release of AppleTalk version 56, Apple implemented the Multivendor ADEV Architecture. Under the original architecture with versions of AppleTalk prior to 56, using EtherTalk or TokenTalk on Macintosh II class machines permitted only one brand of NuBus card where multiple Ethernet or token ring connections were desired. Furthermore, there was no support for a configuration of a NuBus slot device and a "slotless" device, such as a SCSI Ethernet connection.

As Ethernet comes built in on next-generation CPUs, this clearly presents a problem for customers wishing to mix Ethernet controller brands on the same CPU. The Multivendor Architecture presents a common interface through which basic AppleTalk services are provided. The new architecture simplifies software development whereas AppleTalk engineering provides the ADEV file, and the developer provides the hardware level driver software for Ethernet and token ring. By following the new architecture, Ethernet and token ring hardware cards will be compatible as new services are provided by AppleTalk (for example, AppleTalk Remote Access and MacTCP).

AppleTalk version 56 and later is compatible with system software version 6.0.5 unless specifically stated otherwise in the release notes.

Back to top

Original Limitations

The original product allowed only one type of NuBus Ethernet or token ring controller or one "slotless" controller. This Multivendor ADEV Architecture deals only with the restriction of differing NuBus controllers. It does not address the mutual exclusion of slot and slotless devices, nor does it address the singularity of slotless devices.

NuBus slot Ethernet or token ring controller hardware is recognized by the original product through a series of Slot Manager SNextTypesRsrc calls. Any NuBus device that is in the network category and has a type classification of Ethernet /token ring is considered a NuBus slot controller device. Whenever such a device is found in a NuBus slot, the user can select it as the current AppleTalk network connection, or it can be used as a port in a Internet Router configuration.

When the AppleTalk network system uses this connection, an _Open, IMMED trap call is made with an ioNamePtr -> ".ENET/.TOKN", and the ioSlot field set to the slot containing the card. Since only one driver resource can be installed in the system with this name, only one type of Ethernet or token ring card was supported under the original architecture.

Back to top

.ENET Driver Shell

System software version 7.0 and later (and Network Software Installers (NSI) system software version 1.1 and later) is packaged with the .ENET driver shell that will support multiple NuBus Ethernet controllers. The sole function of this driver is to locate the appropriate driver resource for the particular device selected, and transfer control to the open routine for that driver. It accomplishes this in the following fashion:

* Obtains the Board ID from the board sResource information for the given slot

* For the driver shell installed using NSI v1.2.4 and greater, the shell searches for resources of type 'enet' with the ID equal to the Board ID in the System file and in the System ROM, and for a driver in the slot resources in the ROM of the slot device. It uses the word (2 bytes) immediately following the DRVR name to determine which driver is the most recent; higher values are newer versions. If no driver is found, an open error is returned.

For the driver shell installed prior to NSI v1.2.4, the shell uses _GetResource to obtain a resource of type 'enet' with the id equal to the Board ID from the system file. If the resource is present, proceeds to use it as the driver code resource as defined below, otherwise attempts to load the driver from the slot resources in the ROM of the slot device. If neither code resource is found, returns an open error.

  • Detaches the newly loaded resource
  • Modifies the device control entry for the current _Open call with information from the loaded driver code (address to the driver)
  • Obtains the address of the open routine from the driver header information
  • JSRs to the open routine of the loaded driver
  • If the open is successful, returns, otherwise recovers the handle for the loaded driver and disposes of it

Note:
The 'enet' resource must have it's locked resource bit set, as the Driver Shell will NOT perform this function. This is also true for the 'tokn' resource.


This very simple technique allows developers to quickly repackage driver resources by simply changing the resource type and ID.

Built-in Ethernet on newer CPUs makes use of the board sResource list for slot zero, which should be present on all CPUs. These systems also have the Ethernet device sResource lists, and also have the .ENET driver in the sResources as well.

The Easy Install process supplied on the Network Software Installer version 1.1 and later, and on the system software installers for 7.0 and later, install the driver shell when it recognizes that an Apple EtherTalk NB or Ethernet NB (or other Ethernet board with Board ID 8) is installed in the system.

Back to top

.TOKN Driver Shell

The .TOKN driver shell is currently available from Apple Software Licensing (SW.LICENSE) for licensing. The driver and Multivendor TokenTalk ADEV are being packaged beginning with system software version 7.0.1 and AppleTalk products that require AppleTalk version 57 or later. The operation of the .TOKN driver shell is similar to the .ENET driver shell. In place of searching for and loading the 'enet' resource, a 'tokn' resource will be used instead. The new driver will affect all developers whose .TOKN drivers get replaced by the driver shell. For those who are interested in programming to the .TOKN API, skip the section ".TOKN Application Programming Interface (API)".

Back to top

.TOKN Driver Basics

The following guidelines describe the minimum requirements for developers of token ring products for the Macintosh to be compatible with the TokenTalk Phase 2 driver software. MacDTS strongly recommends that all developers of token ring products implement the basic functionality described below. By following these guidelines, the product will be compatible with AppleTalk Remote Access, MacTCP, and future releases of AppleTalk and related products. For applications developers, the API for the token ring driver is described below.

The .TOKN driver interface is similar to the interface of the .ENET driver as described in Appendix B, "Macintosh Ethernet Driver Details" in the Macintosh AppleTalk Connections Programmer's Guide (Final Draft 2, November 11, 1989) and more recently in Inside Macintosh Volume VI, page 32-88. These documents describe the expected functionality of the .ENET driver. The .TOKN driver interface that you design can be a superset of the functionality discussed here. The following are some additional guidelines and exceptions to consider:

  • The driver can obtain the slot number from the DCE entry dCtlSlot.[]
  • The driver need only support EAttach protocol type 0. Return an error on other protocol types.
  • Implement the add and delete functional address in place of the EAddMulti and EDelMulti commands.
  • Implement the ESetGeneral call to return a result of noErr.
  • Implement source routing support if the driver is to support the source routing bridges.

On EWrite call:

The first buffer in the WDS contains a 802.3 MAC header (6-byte destination address + 6-byte source address + 2-byte length field). The 6-byte destination address is the only important field to the driver. The source address and the length fields are not used for token ring media. The header is 14 bytes in length.

The second buffer in the WDS contains the LLC header and the SNAP header. This buffer is 8 bytes in length. The remainder of the WDS is the user data. Note that there may not be a second or third buffer in the WDS. The complete frame may get passed in the first buffer of the WDS.

The packet which gets put out on the "wire" will not include the 2-byte length field. The packet header will have the 6-byte destination address, followed by the 6-byte source address, followed by the LLC and SNAP header, and the user data.

On Receive:

Define a Read Header Area (RHA) into which to create the 14 byte 802.3 MAC header. Read the header into the RHA and set A3 to point to the end of the RHA; place the 6-byte destination and source addresses into the first 12 byte of the RHA. From the hardware, get the length of the packet and place it into the last 2 bytes of the RHA. This step is necessary as the LAP Manager is designed to handle and Ethernet style packet.

Calculate the packet length (LLC header + SNAP header + data). Place the length in register D1.W. Also place the computed length into the length field of the 802.3 MAC header (this length does not include the source routing and 802.5 fields). Place the address of the ReadPacket routine into register A4. Disable the interrupt. Call the protocol handler or use DeferUserFn as described below. Enable the interrupt.

As A0 is reserved for use by the driver, it could be used to point to the next byte to be read by the driver, for the ReadPacket and ReadRest functions.

Other Notes:

  1. If the protocol handler calls your ReadRest routine with a buffer too small to hold the entire remaining packet, set the Z-bit in the CCR before returning. Clearing the Z-bit indicates that the ReadRest routine handled the packet successfully.
  2. In general, the 'tokn' driver should expect an Ethernet style frame to be passed down from the upper layers when processing a write function. On receive, the 'tokn' driver needs to return an Ethernet style packet to the upper layer.
  3. The System Heap and the Locked resource flags must be set for the 'tokn' resource. The driver shell does not automatically lock the resource once it is loaded.
  4. There is no equivalent to the Ethernet 'eadr' resource used to define the hardware address to be registered by the token ring card on startup.

Back to top

Driver Considerations for Virtual Memory

With the release of system software version 7.0 and the virtual memory option, it is critical for driver software to protect against the possibility of a double page fault. Since driver software runs at interrupt time, a non-virtual memory compatible packet processing routine could cause a page fault while the Macintosh is already processing a page fault. To protect against this possibility, the DeferUserFn is provided to allow interrupt service routines to defer code, which might cause a page fault, until a safe time. The following guidelines will help make your driver code compatible with virtual memory.

  • In the Open routine, use Gestalt to test for the presence of virtual memory, and whether it's on. If so, set a flag in your dctlstorage that you can reference later.
  • If virtual memory is enabled, always use DeferUserFn to defer the delivery of your packet data to your clients. This is necessary to protect against page faults at interrupt time when your client reads data into her own (probably unlocked) memory. In addition, do not touch any memory that is not locked down (in the virtual memory sense, not the Memory Manager sense) while processing your interrupts.
  • Set the VMImmuneBit to keep the system from locking down memory (bit 0 at offset dCtlFlags + 1). If the VMImmuneBit isn't set, the system locks the user's parameter block. In contrast, the user's buffers remain unlocked unless locked by the application. As a result, it is necessary to assume that the buffers are unlocked, and to use DeferUserFn accordingly. Having the system lock the parameter block results in a noticeable performance hit. The solution to this problem is to set the VMImmuneBit, and to be careful to "touch" the parameter block only when it is "safe" to do so. One time when it might be "unsafe" is in a completion routine. Therefore, use DeferUserFn.

The VMImmune bit is not currently found in the MPW headers. Add the following line somewhere at the beginning of your driver code:

VMImmuneBit EQU 0

Somewhere in the beginning of your code, assuming that the driver is now virtual memory-aware, add the following line:

BSET #VMImmuneBit,dCtlFlags+1(A1) ; set the bit


Note:
Do not assume that DeferUserFn will always successfully queue your packet- handling routine. Check the return result. Under specific situations, the Defer-Function Queue can become full. If the return result is cannotDeferErr exit the slot interrupt routine with a result of zero to indicate that the interrupt could not be serviced.


Back to top

Limiting DeferUserFn Calls

Your interrupt service routine can reduce the number of calls to DeferUserFn depending on the Network Interface Controller (NIC) being used. With the SONIC and other NICs, incoming packets are queued. An ISR for such a NIC can be designed to process not only the packet that generated the interrupt, but also successive packets. As a result, the ISR can be designed to set a "deferred function" flag to indicate that the service routine has been queued, then process all packets that it finds in the card's queue. When the service routine has completed, it can then reset the deferred function flag. If the ISR is reentered, it can check whether the deferred function flag is set. If so, simply exit with a non zero result in register D0 to indicate that the packet was processed.

Using this algorithm, it is important to reset the NIC's interrupt service register each time the ISR determines that a packet will be processed by a previously deferred function. If the register is not cleared, the card will remain in a constant state of interrupt, and the deferred function will never get a chance to execute.

Back to top

Implementing DeferUserFn

The question may arise as to where to implement the DeferUserFn The following approach is one possible suggestion for devices that are not able to empty the NIC's packet RAM all at once, and which implement a circular buffer or linked buffer list. Define an entry point where the ISR begins processing the packet from the card. At this entry point, there may be some code to check whether a packet transmission is under way and to perform a cleanup. There may also be code here to check whether the buffer has become overrun and to reset the NIC according to manufacturer's guidelines. The driver code would copy the header into the RHA (Read Header Area), identify the protocol handler and set up register A4 with a pointer to the ReadPacket routine, then call the handler. Upon completion, the ISR might check whether additional packets have been received, if applicable. A flowchart of the deferred function process is as follows:

myDeferedFunction()
{
    If transmit complete
        do final cleanup of packet transmission

    If buffer overrun
        Reset the NIC according to manufacturer's guidelines

    while(received packets are waiting in adapter ram)
    {
        process packet
        call protocol handler
    }
}

On entry to the ISR, see whether virtual memory is active by checking the flag set by the open routine. Perform whatever processing is necessary, then pass DeferUserFn, the entry point described above, if virtual memory is active. If virtual memory is inactive, branch to the entry point and process the packet.

Back to top

.TOKN Application Programming Interface (API)

The Apple specification for the .TOKN interface specifies that the .TOKN driver process token ring packets to the upper protocol layers in the same manner as for the .ENET specification. An important difference from Ethernet is that the specification calls for the handling of Logical Link Control (LLC) token ring frames only. There is no default protocol handler specification, and applications must implement a protocol handler and use the LAP Manager to attach the handler. The .TOKN API for sending and receiving 802.2 (LLC) packets over token ring uses many of the same Control calls implemented for Ethernet. Use the same parameter block style of Control calls and the same csCode values. Set the ioRefNum field to the reference number of the .TOKN driver acquired when opening the driver. The programming sequence is quite similar except for the specifics listed here. The general programming algorithm follows.

As described in Inside Macintosh Volume VI, Chapter 32, a suggested programming sequence is presented here.

  1. Use the Slot Manager to identify installed token ring cards. In calling the SGetTypeSRsrc function, set the spCategory field to catNetwork (4), and the spCType field to typeTokenRing (2).
  2. Use the OpenSlot function to open the driver, setting the ioNamePtr field to ".TOKN", instead of ".ENET". If there are no NuBus token ring cards found, the application should also try opening the ".TOKN0" driver using the OpenDriver function call.
  3. If a process requires the use of functional addresses (the token ring equivalent to Ethernet multicast addresses) use the EAddMulti function to register a functional address.
  4. Instead of using the EAttachPH call to install a protocol handler, use the LAP Manager equivalent. A discussion of the reasons for using the LAP Manager follows. Note also that the .TOKN interface does not define a default protocol handler. Calls to ERead will return a ControlErr (-17).
  5. Use the EWrite function in the same manner as under Ethernet to send packets across the token ring network. Similar to Ethernet, the complete data packet can be pointed to by the first and only entry in the Write Data Structure (WDS). At a minimum, the first WDS entry must consist of 14 bytes, with the destination address in the first 6 bytes, and the packet length occupying the 13th and 14th bytes. The .TOKN driver fills in the source address into bytes 7 - 12.
  6. The ERdCancel function is not defined under the Apple .TOKN specification and should not be used. A call to this function will return a ControlErr, -17.
  7. The ESetGeneral function does not apply to token ring and should simply return noErr.
  8. The EDetachPH is implemented under the Apple .TOKN specification but should not be used. See the discussion which follows.
  9. The EGetInfo function does not apply to token ring. Instead network processes should get the Simple Network Management Protocol Programmer's guide available from APDA to learn how to obtain communications statistics relating to token ring.
  10. Use the EDelMulti function to remove a registered functional address, when finished.

Back to top

Using L802Attach on Token Ring

The Apple token ring driver specification specifies that the driver need support only the default protocol type 0, which equates to IEEE 802.2 Type 1, a connectionless or datagram service. Furthermore, the Apple .TOKN interface defines support for only the Sub-Network Access Protocol (SNAP), a protocol which includes only 802.2 packets with a destination Service Access Point of $AA. Since the .TOKN driver interface allows the attachment of only a single protocol handler, the LAP Manager has been designed to install a generic protocol handler and which maintains an index of registered protocol IDs and pointers to their associated protocol handler. When the L802Attach call is made, a network process specifies the driver reference number, the protocol specification, and the pointer to the protocol handler. It is assumed that the network process will open the token ring driver. The LAP Manager determines whether the generic protocol handler has already been installed to that driver. If not, then an EAttachPH call is made, and the protocol specification and handler are stored in a lookup table. If the generic handler has been installed, then the protocol specification and handler pointer are simply entered into the lookup table.

The advantage of using the LAP Manager is best described by example. Suppose that the user has specified that the default AppleTalk connection is through some installed Ethernet hardware. Imagine that the user launches a MacTCP application which communicates across an installed token ring card on the same workstation. MacTCP will install a protocol handler into the LAP Manager, which in turn installs it's generic handler into the token ring driver. The LAP Manager must now process both token ring and Ethernet packets, sending the Ethernet packets up to DDP and the token ring packet to MacTCP. Imagine now, that the user decides to switch the AppleTalk connection on the fly to the token ring hardware. The token ring ADEV will make the OpenSlot call to obtain the reference number, make the EAddMulti call to register the AppleTalk multicast address, then make the L802Attach call to the LAP Manager to install the AppleTalk protocol handler. The LAP Manager finds that the generic handler has already been installed and simply registers the AppleTalk protocol type.

If one imagines the case were the MacTCP driver to find that the .TOKN driver wasn't open, and made the EAttachPH call directly to the driver. When the AppleTalk connection switch is attempted, the EAttachPH call would fail, since the driver will report that a protocol handler for type 0 already exists. For this reason, it is strongly recommended that token ring applications do NOT make the EAttachPH call to the .TOKN driver.

Back to top

Writing the Protocol Handler for the LAP Manager

Now that you're going to do the right thing and register the protocol handler with the LAP Manager, you need to know how to write the protocol handler. First, some overview. When the .TOKN driver calls the LAP Manager, it will already have read in the 14-byte frame header otherwise known as the 802.3 header. Since the frame length field will likely be some value of 1500 or less, the .TOKN driver will call the LAP Manager to process the packet. The LAP Manager will read the 3-byte 802.2 header (which includes the source and destination SAPs - $AA for both), and the 5 byte SNAP header. The LAP Manager will then scan it's lookup table searching for a matching protocol specification. If one is found, the associated protocol handler is called.

On entry to the protocol handler, the following register conventions apply.

Register     Description/Contents

  A0           preserve until ReadRest called
  A1           preserve until ReadRest called
  A3           Pointer to next free byte in RHA
                -5(A3) is the beginning of the SNAP address
                -8(A3) is the beginning of the 802.2 address
                -24(A3) is the beginning of the 6-byte destination address
  A4           Pointer to ReadPacket and ReadRest routines.
  A5           Free for use until ReadRest call made
  D1           Number of bytes remaining to read in the packet
  D2.          driver reference number
  

The standard protocol handler requirements specified in Inside Macintosh Volume II, pages 326/327 apply here. The sample socket listener code described above could easily be modified for use as a .token ring protocol handler.

Back to top

.FDDI Application Programming Interface (API)

The Apple specification for the .FDDI interface specifies that the .FDDI driver process FDDI packets to the upper protocol layers in the same manner as for the .ENET specification. An important difference from Ethernet is that the specification calls for the handling of Logical Link Control (LLC) token ring frames only. There is no default protocol handler specification, and applications must implement a protocol handler and use the LAP Manager to attach the handler. The .FDDI API for sending and receiving 802.2 (LLC) packets over token ring uses many of the same Control calls implemented for Ethernet. Use the same parameter block style of Control calls and the same csCode values. Set the ioRefNum field to the reference number of the .FDDI driver acquired when opening the driver. The programming sequence is quite similar except for the specifics listed here. The general programming algorithm follows.

As described in Inside Macintosh Volume VI, Chapter 32, a suggested programming sequence is presented here.

  • Use the Slot Manager to identify installed FDDI cards. In calling the SGetTypeSRsrc function, set the spCategory field to catNetwork (4), and the spCType field to typeFDDI (0x11).
  • Use the OpenSlot function to open the driver, setting the ioNamePtr field to ".FDDI", instead of ".ENET". If there are no NuBus token ring cards found, the application should also try opening the ".FDDI0" driver using the OpenDriver function call.
  • If a process requires the use of multicast addresses, use the EAddMulti function to register a functional address. The valid AppleTalk multicast addresses under FDDI are the same as for Ethernet.
  • Instead of using the EAttachPH call to install a protocol handler, use the LAP Manager equivalent. The reasons for using the LAP Manager to install a protocol handler are the same as for token ring, described above. As with token ring, the .FDDI interface does not define a default protocol handler. Calls to ERead will return a ControlErr (-17).
  • Use the EWrite function in the same manner as under Ethernet to send packets across the token ring network. Similar to Ethernet, the complete data packet can be pointed to by the first and only entry in the Write Data Structure (WDS). At a minimum, the first WDS entry must consist of 14 bytes, with the destination address in the first 6 bytes, and the packet length occupying the 13th and 14th bytes. The .FDDI driver fills in the source address into bytes 7 - 12.
  • The ERdCancel function is not defined under the Apple .FDDI specification and should not be used. A call to this function will return a ControlErr, -17.
  • The ESetGeneral function does not apply to token ring and should simply return noErr.
  • The EDetachPH is implemented under the Apple .FDDI specification but should not be used. See the previous discussion regarding token ring and the LAP Manager.
  • The EGetInfo function does not apply to FDDI. Instead, obtain the Simple Network Management Protocol Programmer's guide available from APDA to learn how to obtain communications statistics relating to token ring.
  • Use the EDelMulti function to remove a registered functional address, when finished.

It is important to note that the Apple FDDI driver spec calls for the driver to handle 802.2 packets to Service Access Points (SAP) other than to only SAP $AA.

In writing a protocol handler for the FDDI application, follow the guidelines listed above for writing a protocol handler for the LAP Manager.

Back to top

SONIC-Based Ethernet Driver Software Interface Change

With the introduction of SONIC based Ethernet controllers, a modification was implemented into the Ethernet driver software to return additional information available from the SONIC chip network statistics counters. This section describes the format of the information returned by an EGetInfo call when the current network connection is through an Ethernet NB Card, an Ethernet LC Card, or through the built-in Ethernet available on the Macintosh Quadra and Centris systems.

Back to top

EGetInfo Changes

The EGetInfo call can return up to 60 additional bytes of new information making the maximum number of bytes returned 78. As with the Apple EtherTalk NB Card (Apple/3Com card), the first 6 bytes returned contain the card's Ethernet address. The remaining bytes that are returned contain information different from that returned with the Apple EtherTalk NB Card.

The next 12 bytes (offset 6-17) returned contain NO information but are always returned zero filled for compatibility. The remaining 60 bytes returned contain SONIC chip network statistic counters. The counters are listed below in the following table with the decimal and hexadecimal offsets given from the start of the return buffer. Note that the offset of the first item in the return buffer, the Ethernet address, is at offset 0.

With the release of AppleTalk version 58, a minor change was implemented into the EGetInfo call such that the number of bytes filled in by the call, is returned in the eDataSize field. The revised parameter block description for the .ENET driver supplied with AppleTalk v58 is:

Back to top

Distinguishing Apple's SONIC-Based Ethernet Systems

When making the EGetInfo call, it is important to pass the correct size buffer. The control call will only fill in the buffer with the number of bytes specified in the eBuffSize field. Unless it is already known that the active Ethernet card is the Apple (3Com) EtherTalk NB Card, it is recommended that you pass a buffer large enough to accommodate the additional information returned by the driver for the SONIC chip. One method to distinguish the Apple (3Com) EtherTalk NB Card from Apple's SONIC-based systems, is to fill the 78-byte buffer with a byte pattern like 0xFF. For the Apple EtherTalk NB Card, the last 60 bytes of the buffer will still be filled with the byte pattern. For Apple's SONIC-based systems, the last 60 bytes of the buffer will NOT all contain the byte pattern.

With the Ethernet driver released under AppleTalk version 58, an alternate method to distinguishing the two Apple Ethernet hardware cards is to pass in a pointer to a 78-byte buffer in the ePointer field, then to check the EDataSize value for the number of bytes filled into the buffer. For the original EtherTalk NB card, the number of returned bytes is 18; for the Ethernet NB card, the number of returned bytes will be 78.

Back to top

Correction to the ENET.h Header File

Programs written for compilation with MPW C, which use the ENET.h header file supplied with MPW v3.2.x and greater, are alerted to the fact the following declaration is incorrect. Users of Think C should check their ENET.h header file for the same error, as the v5.0.x and 6.0 products was shipped with the header files supplied by the MPW team. This problem does not affect the corresponding Pascal interface file.

typedef struct {
      EParamHeader
      EParamMisc1 EParms1;
      char eMultiAddr[5];                 /*Multicast Address*/
}EParamMisc2;

The correct declaration has theMultiAddr field immediately following the EParamHeader structure and the character array allocated 6 instead of 5 bytes as follows:

typedef struct {
      EParamHeader
      char eMultiAddr[6];                 /*Multicast Address*/
}EParamMisc2;

The EParamMisc2 structure applies only to the EAddMulti and EDelMulti control calls to the Ethernet, Token Ring and FDDI drivers. If you are using these calls, you might include a revised structure declaration in your source code file so that you need not worry about overwriting the corrected header file when supplied with a new version of MPW. The declaration would be as follows:

#include <ENET.h>
.
.
/* The following structure declaration replaces the incorrect EParamMisc2
 * structure declaration presented in the ENET.h files supplied with MPW
 * to v3.2.4 and possibly greater.
 */
typedef struct {
      EParamHeader
      char eMultiAddr[6];                 /*Multicast Address*/
}EParamMultiCast;

Back to top

AppleTalk Multiple Node Architecture

Supporting multiple node addresses on a single machine connected to AppleTalk is a feature that has been created to support software applications such as AppleTalk Remote Access. Its implementation is general enough to be used by other applications as well.


Note:
AppleTalk version 57 or later is required to support the AppleTalk Multiple Node Architecture. Version 57 is compatible with system software version 6.0.5 and greater. If you implement multinode functionality into your program you should also plan to include AppleTalk version 57 with your product. Contact Apple's Software Licensing department (see end of this Note) for information on licensing AppleTalk.


Back to top

What Is It?

Multiple Node AppleTalk provides network node addresses that are in addition to the normal (user node) DDP address assigned when AppleTalk is opened. These additional addresses have different characteristics from those of the user node address. The new architecture provides a means for a multinode process to process AppleTalk packets in a custom manner. For example, a multinode process will receive all NBP Lookup requests. It is up to the multinode process to decide how to respond to the Lookup requests, and to all other AppleTalk packets which it receives. There are no AppleTalk hooks for the multinode process to pass packets back to the AppleTalk stack for processing.

Multinode processes are not connected to the protocol stack above the data link layer. When a process acquires a multinode, the process has to supply a receive routine through which AppleTalk will deliver broadcasts and packets directed to that multinode address. The multinode process becomes responsible for processing all AppleTalk packets which it receives. This includes Echo Protocol packets.

The number of multinode addresses that can be supported on one single machine is determined by a static limit imposed by the AppleTalk ADEV itself (for example, EtherTalk). The limit is currently 253 nodes for Apple's implementation of EtherTalk ($0, $FF, and $FE being invalid node addresses) and 254 for LocalTalk ($0 and $FF being invalid node addresses). The number of receive routines that .MPP supports is determined by the static limit of 256. If all of the multiple nodes acquired need to have unique receive routines, then only a maximum of 256 nodes can be acquired, even if the ADEV provides support for more than 256 nodes. .MPP will support the lesser of either the maximum of 256 receive routines, or the node limit imposed by the ADEV.

Outbound DDP packets can be created with a user-specified source network, node, and socket (normally equal to a multinode address) with the new Network Write call. With this capability and the packet reception rules described above, a single machine can effectively become several nodes on a network. The user node continues to function as it always has.

Back to top

Glue Code For Multinode Control Calls

The following files are provided for C and Pascal programmers to implement the new multinode calls presented in this Technote. First, for C programmers:

/*----------------------------------------------------------------------
  file: MultiNode.c
----------------------------------------------------------------------*/

#include <Types.h>
#include <Devices.h>
#include <OSUtils.h>
#include <AppleTalk.h>

enum {

/*  MultiNode .MPP csCodes */

    netWrite = 261,                 /* Send packet through multinode */
    addNode = 262,                  /* Request a multinode */
    removeNode = 263               /* Remove multinode */
};

enum {
  tryAddNodeAgainErr = -1021,       /* .MPP was not able to add node, try again
*/
  mnNotSupported =     -1022,       /* Multinode is not supported by the
current ADEV */
  noMoreMultiNodes =   -1023        /* no node address is available on the
network */
};

typedef struct {
    MPPATPHeader
       char filler1;
       unsigned char checkSumFlag;     /* perform checksum on datagram */
       Ptr wdsPointer;                 /* Ptr to write-data structure */
       char filler2[2];
       union {
           AddrBlock reqNodeAddr;      /* preferred address requested */
           AddrBlock nodeAddr;         /* node address to be deleted */
              } MNaddrs;
       AddrBlock actNodeAddr;          /* actual node address acquired */
       Ptr recvRoutine;                /* address of packet receive routine
*/
       short reqCableLo;               /* preferred network range for the */
       short reqCableHi;               /*  node being acquired */
       char reserved[70];
} MNParamBlock;

typedef MNParamBlock*MNParmBlkPtr;

#ifdef __cplusplus
extern "C" {
#endif
pascal OSErr MNAddNode(MNParmBlkPtr thePBptr);
pascal OSErr MNRemoveNode(MNParmBlkPtr thePBptr,Boolean async);
pascal OSErr MNNetWrite(MNParmBlkPtr thePBptr,Boolean async);
#ifdef __cplusplus
}
#endif

#pragma parameter __D0 PBControlImmedSync(__A0)
pascal OSErr PBControlImmedSync(ParmBlkPtr paramBlock)
       = 0xA204;

pascal OSErr MNAddNode(MNParmBlkPtr thePBptr)
{
       OSErr  err;

       thePBptr->csCode = addNode;
       thePBptr->ioRefNum = mppUnitNum;

           /* AddNode must be made synchronously and as an Immediate Control
call*/
           /* Make Control call continuously if the Immed call returns the */
           /*   tryAddNodeAgainErr  */
       do {
            err = PBControlImmedSync((ParmBlkPtr)thePBptr);
       }
       while (err == tryAddNodeAgainErr );
       return (err);
}

pascal OSErr MNRemoveNode(MNParmBlkPtr thePBptr,Boolean async)
{
       thePBptr->csCode = removeNode;
       thePBptr->ioRefNum = mppUnitNum;
       return (PBControl((ParmBlkPtr)thePBptr, async));
}

pascal OSErr MNNetWrite(MNParmBlkPtr thePBptr,Boolean async)
{
       thePBptr->csCode = netWrite;
       thePBptr->ioRefNum = mppUnitNum;
       return (PBControl((ParmBlkPtr)thePBptr, async));
}

Now for Pascal Programmers:

UNIT MultiNode;
INTERFACE

USES
    MemTypes, QuickDraw, OSIntf, AppleTalk;

CONST

{    MultiNode .MPP csCodes }

    netWrite = 261;                 { Send packet through multinode }
    addNode = 262;                  { Request a multinode }
    removeNode = 263;               { Remove multinode }

{    MultiNode errors  }

    tryAddNodeAgainErr = -1021;     { .MPP was not able to add node, try again
}
    mnNotSupported =     -1022;     { Multinode is not supported by the current
ADEV }
    noMoreMultiNodes =   -1023;     { no node address is available on the
network }

TYPE
MNParmType = (AddNodeParm,RemoveNodeParm);

MNParamBlock = PACKED RECORD
    qLink: QElemPtr;                  {next queue entry}
    qType: INTEGER;                   {queue type}
    ioTrap: INTEGER;                  {routine trap}
    ioCmdAddr: Ptr;                   {routine address}
    ioCompletion: ProcPtr;            {completion routine}
    ioResult: OSErr;                  {result code}
    ioNamePtr: StringPtr;             {->filename}
    ioVRefNum: INTEGER;               {volume reference or drive number}
    ioRefNum: INTEGER;                {driver reference number}
    csCode: INTEGER;                  {call command code AUTOMATICALLY set}
       filler1: Byte;
       checkSumFlag: Byte;            { perform checksum on datagram }
       wdsPointer: Ptr;               { Ptr to write-data structure }
       filler2: INTEGER;
       CASE MNParmType of
         AddNodeParm:
          (reqNodeAddr: AddrBlock;    { preferred address requested }
           actNodeAddr: AddrBlock;    { actual node address acquired }
           recvRoutine: ProcPtr;      { address of packet receive routine }
           reqCableLo: INTEGER;       { preferred network range for the }
           reqCableHi: INTEGER;       {  node being acquired }
           reserved: PACKED ARRAY [1..70] of Byte);
         RemoveNodeParm:
          (nodeAddr: AddrBlock);  { node address to be deleted }
    END;

MNParmBlkPtr = ^MNParamBlock;

FUNCTION MNAddNode(thePBptr: MNParmBlkPtr): OSErr;
FUNCTION MNRemoveNode(thePBptr: MNParmBlkPtr; async: BOOLEAN): OSErr;
FUNCTION MNNetWrite(thePBptr: MNParmBlkPtr; async: BOOLEAN): OSErr;

IMPLEMENTATION

FUNCTION PBControlImmedSync(paramBlock: ParmBlkPtr): OSErr;
    INLINE $205F,$A204,$3E80;

FUNCTION MNAddNode(thePBptr: MNParmBlkPtr): OSErr;

VAR
       err : OSErr;

BEGIN
       thePBptr^.csCode := addNode;
       thePBptr^.ioRefNum := mppUnitNum;

           { AddNode must be made synchronously and as an Immediate Control
call }
           { Make Control call continuously if the Immed call returns the }
           {   tryAddNodeAgainErr  }
       REPEAT
           err := PBControlImmedSync(ParmBlkPtr(thePBptr));
       UNTIL (err <> tryAddNodeAgainErr);
       MNAddNode := err;
END;

FUNCTION MNRemoveNode(thePBptr: MNParmBlkPtr; async: BOOLEAN): OSErr;
BEGIN
       thePBptr^.csCode := removeNode;
       thePBptr^.ioRefNum := mppUnitNum;
       MNRemoveNode := PBControl(ParmBlkPtr(thePBptr), async);
END;

FUNCTION MNNetWrite(thePBptr: MNParmBlkPtr; async: BOOLEAN): OSErr;
BEGIN
       thePBptr^.csCode := netWrite;
       thePBptr^.ioRefNum := mppUnitNum;
       MNNetWrite := PBControl(ParmBlkPtr(thePBptr), async);
END;

END.

Back to top

Things You Need to Know When Writing a Multinode Application

Two new .MPP driver control calls have been added to allow multinode applications to add and remove multinodes.

Back to top

AddNode (csCode=262)

A user can request an extra node using a control call to the .MPP driver after it has opened. Only one node is acquired through each call.

Parameter Block:

      -->    24    ioRefNum      short        ; driver ref. number
      -->    26    csCode        short        ; always = AddNode (262)
      -->    36    reqNodeAddr   AddrBlock    ; the preferred address
requested
                                              ; by the user.
      <--    40    actNodeAddr   AddrBlock    ; actual node address
acquired.
      -->    44    recvRoutine   long         ; address of the receive
routine for MPP
                                              ; to call during packet
delivery.
      -->    48    reqCableLo    short        ; the preferred range for the

      -->    50    reqCableHi    short        ; node being acquired.
      -->    52    reserved[70]  char         ; 70 reserved bytes

AddrBlock:

      aNet         short                      ; network #
      aNode        unsigned char              ; node #
      aSocket      unsigned char              ; should be zero for this
call.

The AddNode call must be made as an IMMEDIATE control call at system task time. The glue code above, implements this requirement. The .MPP driver will try to acquire the requested node address. The result code will be returned in the ioResult field in the parameter block. The result code -1021 indicates that the .MPP driver was unable to continue with the AddNode call because of the current state of .MPP. The caller should retry the AddNode call (the retry can be issued immediately after the AddNode call failed with -1021) until a node address is successfully attained or another error is returned. The glue code above demonstrates one technique for implementing the immediate AddNode call. Because the immediate Control call is made repeatedly if error -1021 is returned, the call cannot be made asynchronously.

If the requested node address is zero, invalid, or already taken by another machine on the network, a random node address will be generated by the .MPP driver. The parameters reqCableLo and reqCableHi will be used only if there is no router on the network and all the node addresses in the network number specified in NetHint (the last used network number stored in parameter RAM) are taken up.

In this case, the .MPP driver tries to acquire a node address from the network range as specified by reqCableLo and reqCableHi. The network range is defined by the seed router on a network. If a specific cable range is not important to the application, set the reqCableLo and reqCableHi fields to zero. The recvRoutine is an address of a routine in the application to receive broadcasts and directed packets for the corresponding multinode.

Possible Error Codes:


    noErr            0        ; success
    tryAddNodeAgainErr     -1021        ; .MPP was not able to add node, try again.
    MNNotSupported    -1022        ; Multinode is not supported by
                ; the current ADEV.
    noMoreMultiNodes    -1023        ; no node address is available on
                          ; the network.

Back to top

RemoveNode (csCode=263)

This call removes a multinode address and must be made at system task time. Removal of the user node is not allowed.

Parameter Block:
-->    24    ioRefNum    word        ; driver ref. number
    -->    26    csCode        word        ; always = RemoveNode (263)
    -->    36    NodeAddr    AddrBlock    ; node address to be deleted.

Possible Error Codes:

noErr        0        ; success
    paramErr    -50        ; bad parameter passed

Back to top

Receiving Packets

Broadcast packets are delivered to both the user's node and the multinodes on every machine. If several multinodes are acquired with the same recvRoutine address, the recvRoutine , listening for these multinodes, will be called only once in the case of a broadcast packet.

Multinode receive handlers should determine the number of bytes already read into the Read Header Area (RHA) by subtracting the beginning address of the RHA from the value in A3 (see Inside Macintosh Volume II, page 326, for a description of the Read Header Area). A3 points past the last byte read in the RHA. The offset of RHA from the top of the .MPP variables is defined by the equate ToRHA in the MPW include file ATalk.Equ.a. The receive handler is expected to call ReadRest to read in the rest of the packet. In the case of LocalTalk, ReadRest should be done as soon as possible to avoid loss of the packet. Register A4 contains the pointer to the ReadPacket and ReadRest routines in the ADEV.

To read in the rest of the packet:

              JSR    2(A4)

On entry:
              A3            pointer to a buffer to hold the bytes
              D3            size of the buffer (word), which can be zero to throw away packet

On exit:
              D0            modified
              D1            modified
              D2            preserved
              D3            Equals zero if requested number of bytes was read; is
                            less than zero if packet was -D3 bytes too large to fit
                            in buffer and was truncated; is greater than zero if D3
                            bytes were not read (packet is smaller than buffer)
              A0            preserved
              A1            preserved
              A2            preserved
              A3            pointer to 1 byte after the last byte read

For more information about ReadPacket and ReadRest, refer to the Inside Macintosh Volume II, page 327.

A user can determine if a link is extended by using the GetAppleTalkInfo control call. The Configuration field returned by this call is a 32-bit word that describes the AppleTalk configuration. Bit number 15 (0 is LSB) is on if the link in use is extended. Refer to Inside Macintosh Volume VI, page 32-15.

Back to top

Sending Datagrams Through Multinodes

To send packets through multinodes, use the new .MPP control call, NetWrite. NetWrite allows the owner of the multinode to specify a source network, node, and socket from which to send a datagram.

Back to top

NetWrite (csCode=261)

Parameter Block:

-->    26    csCode        word        ; always NetWrite (261)
    -->    29    checkSumFlag    byte        ; checksum flag
    -->    30    wdsPointer    pointer    ; write data structure

Possible Error Codes:

noErr        0        ; success
    ddpLenErr    -92        ; datagram length too big
    noBridgeErr    -93        ; no router found
    excessCollsns    -95        ; excessive collisions on write

This call is very similar to the WriteDDP call. The key differences are as follows:

  • The source socket is not specified in the parameter block. Instead it is specified along with the source network number and source node address in the DDP header pointed to by the write-data structure (WDS). Furthermore, the socket need not be opened. Refer to Inside Macintosh Volume II, page 310, for a description of the write-data structure. It is important to note that the caller needs to fill in the WDS with the source network, source node, and source socket values. .MPP does not set these values for the NetWrite call.
  • The checkSumFlag field has a slightly different meaning. If true (non zero), then the checksum for the datagram will be calculated prior to transmission and placed into the DDP header of the packet. If false (zero), then the checksum field is left alone in the DDP header portion of the packet. Thus if a checksum is already present, it is passed along unmodified. For example, the AppleTalk Remote Access program sets this field to zero, since remote packets that it passes to the .MPP driver already have valid checksum fields. Finally, if the application desires no checksum, the checksum field in the DDP header in the WDS header must be set to zero.

Datagrams sent with this call are always sent using a long DDP header. Refer to Inside AppleTalk, second edition, page 4-16, for a description of the DDP header. Even if the destination node is on the same LocalTalk network, a long DDP datagram is used so that the source information can be specified. The LAP header source node field will always be equal to the user node address (sysLapAddr), regardless of the source node address in the DDP header.

Back to top

AppleTalk Remote Access Network Number Remapping

Network applications should be careful not to pass network numbers as data in a network transaction. AppleTalk Remote Access performs limited network number remapping. If network numbers are passed as data, they will not get remapped. AppleTalk Remote Access recognizes network numbers in the DDP header and among the various standard protocol packets, NBP, ZIP, RTMP, and so on.

Back to top

Is There a Router on the Network?

Do not assume that there are no routers on the network if your network number is zero. With AppleTalk Remote Access, you can be on network zero and be connected to a remote network. Network applications should use the GetZoneList or the GetBridgeAddress calls to determine if there is a router on the network.

Back to top

New for AppleTalk ADEVs

First, a word from our sponsors: The information in this section is provided to assist ADEV developers in updating their products for compatibility with AppleTalk Remote Access. If you are a Ethernet or token ring developer, MacDTS strongly urges that you consider following the Multivendor ADEV Architecture described earlier. For developers of Fiber Data Distribution Interface (FDDI) network interface cards, please contact Apple Software Licensing for information on licensing the new FDDI Phase 2 ADEV and Driver shell.

Several new calls have been implemented into the .MPP driver for AppleTalk version 57. Two calls, AOpen and AClose, were built into AppleTalk version 54 and later, and are also documented here. These calls notified the ADEV of changes in the status of the .MPP driver. For AppleTalk version 57, three new calls, AAddNode, ADelNode, and AGetNodeRef, plus a change to the AGetInfo call, were implemented to support the Multiple Node Architecture.

With the release of AppleTalk version 58, support is provided for the Simple Network Management Protocol (SNMP), and for the new Router Manager product. Programmer's Guides for each of these products are available from APDA. For all ADEV's, an important change is required for the Router Manager product to provide basic SNMP support. An ADEV must be modified to support a minor change in the AGetInfo call. The ADEV must respond to the AGetInfo call by returning the slot number of the network device being supported, in the second reserved byte. This change is required so that when queried by a console, the Router Manager can report the slot number of the connection being supported. For those network devices running via the LocalTalk ports, the second reserved byte should continue to return zero. There is no support for such ADEV's at this time.

EtherTalk Phase 2, version 2.3, and TokenTalk phase 2, version 2.4, drivers support the new Multiple Node Architecture. Both drivers and AppleTalk version 57 are available through the Network Software Installer, version 1.1. As mentioned previously, AppleTalk version 57 and these drivers are compatible with system software version 6.0.5 and later. Note that the AppleTalk Remote Access product includes the EtherTalk Phase 2, version 2.3 driver, but not the multinode-compatible TokenTalk Phase 2, version 2.4, driver. Token ring developers, who license TokenTalk Phase 2, version 2.2 and earlier, should contact Apple's Software Licensing department.

The following information describes changes to the ADEV that are required for multinode compatibility. This information is of specific importance to developers of custom ADEVs. The ADEV can be expected to function under version 6.0.5 and later. A version 3 ADEV must be used with AppleTalk version 57 or later. Developers of custom ADEVs will want to contact Software Licensing to license AppleTalk version 57.

For compatibility with Multinode AppleTalk, the 'atlk' resource of an ADEV must be modified to respond to these calls as described below. To determine whether an ADEV is multinode compatible, the .MPP driver makes an AGetInfo call to determine whether the ADEV version is 3 or later. Any ADEVs responding with a version of 3 or later must be prepared to respond to the new calls: AAddNode, ADelNode, and AGetNodeRef. See the Macintosh AppleTalk Connections Programmer's Guide for more information about writing an AppleTalk ADEV.

The desired architecture for a multinode-compatible ADEV is such that it delivers incoming packets to the LAP Manager along with an address reference number, AddrRefNum. The LAP Manager uses the AddrRefNum to locate the correct receive routine to process the packet. For broadcast packets, the LAP Manager handles multiple deliveries of the packet to each multinode receive routine.

The .MPP driver for AppleTalk version 57 supports the new control call to add and remove multinodes, along with the network write call that allows the specification of the source address. .MPP includes a modification in its write function to check for one multinode sending to another. .MPP supports inter-multinode transmission within the same machine. For example, the user node may want to send a packet to a multinode within the same system.

Back to top

AGetInfo (D0=3)

The AGetInfo call should be modified to return the maximum number of AppleTalk nodes that can be provided by the atlk. This limit will be used by .MPP to control the number of multinodes that can be added on a single machine. The new interface is as follows:

Call:        D1 (word)    length (in bytes) of reply buffer
        A1 ->         Pointer to GetInfo record buffer
Return:    A1 ->        Pointer to GetInfo record
        D0        non zero if error (buffer is too small)

    AGetInfoRec = RECORD
<--    version:        INTEGER;    { version of ADEV, set to three (3) }
<--    length:        INTEGER;    { length of this record in bytes }
<--    speed:            LongInt;    { speed of link in bits/sec }
<--    BandWidth:        Byte;        { link speed weight factor }
<--    reserved:        Byte;        { set to zero }
<--    slot:                   Byte;        { device slot number or zero for LocalTalk
ports }
<--    reserved:        Byte;        { set to zero }
<--    flags:            Byte;        { see below }
<--    linkAddrSize:        Byte;        { of link addr in bytes }
<--    linkAddress:        ARRAY[0..5] OF Byte;
<--    maxnodes:         INTEGER;
    END;

    flags:    bit 7 = 1 if this is an extended AppleTalk, else 0
        bit 6 = 1 if the link is used for a router-only connection (reserved
            for half-routing)
        bit 5 through 0 reserved, = 0

maxnodes is the total number of nodes (user node and multinodes) the ADEV supports. If a version 3 ADEV does not support multinodes, it must return 0 or 1 in the maxnodes field in AGetInfoRec and the ADEV will not be called to acquire multinodes. The version 3 ADEV will be called by .MPP in one of the following two ways to acquire the user node:

  • If the ADEV returns a value of 0 in maxnodes, .MPP will issue Lap Write calls to the ADEV with D0 set to $FF indicating that ENQs should be sent to acquire the user node. .MPP is responsible for retries of ENQs to make sure no other nodes on the network already have this address. This was the method .MPP used to acquire the user node before multinodes were introduced. This method of sending ENQs must be available, even though the new AAddNode call is provided, to allow older versions of AppleTalk to function properly with a version 3 ADEV.
  • If the ADEV returns a value of 1 in maxnodes, the new AAddNode function will be called by .MPP to acquire the user node.

For values of maxnode greater than 1, the new AAddNode function will be called by .MPP to acquire the additional multinodes.

Back to top

AAddNode (D0=9)

This is a new call which is used to request the acquisition of an AppleTalk node address. It is called by the .MPP driver during the execution of the AddNode control call mentioned earlier. The ADEV is responsible for retrying enough ENQs to make sure no other nodes on the network already have the address. .MPP makes this call only during system task time.

Call:         A0->        Pointer to parameter block

Return:     D0         = zero if address was acquired successfully



                != zero if no more addresses can be acquired

    atlkPBRec        Record    csParam
-->    NetAddr        DS.L    1    ; offset 0x1C  24-bit node address to acquire
-->    NumTrys        DS.W    1    ; offset 0x20  # of tries for address
-->    DRVRPtr        DS.L    1    ; offset 0x22  ptr to .MPP vars
-->    PortUsePtr        DS.L    1    ; offset 0x26  ptr to port use byte
-->    AddrRefNum        DS.W    1    ; offset 0x2A  address ref number used by .MPP
                EndR

The offset values describe the location of the fields from the beginning of the parameter block pointed to by A0. atlkPBRec is the standard parameter block record header for a _Control call. The field NetAddr is the 24-bit AppleTalk node address that should be acquired. The node number is in the least significant byte 0 of NetAddr. The network number is in bytes 1 and 2 of NetAddr ; byte 3 is unused. NumTrys is the number of tries the atlk should send AARP probes on non-LocalTalk networks to verify that the address is not in use by another entity. On LocalTalk networks, NumTrys x 32 number of ENQs will be sent to verify an address.

DRVRPtr and PortUsePtr are normally passed when the ATLK is called to perform a write function. For ADEVs that support multinodes, AppleTalk calls the new AAddNode function rather than the write function in the ADEV to send ENQs to acquire nodes. However, the values DRVRPtr and PortUsePtr are still required for the ADEV to function properly and are passed to the AAddNode call. AddrRefNum is a reference number passed in by .MPP. The ADEV must store each reference number with its corresponding multinode address. The use of the reference number is described in the following two sections.

For multinode-compatible ADEVs, .MPP issues the first AAddNode call to acquire the user node. The AddrRefNum associated with the user node must be 0xFFFF. It is important to assign 0xFFFF as the AddrRefNum of the user node, and to disregard the AddrRefNum passed by .MPP for the user node. See the discussion at the end of the ADelNode description.

Back to top

ADelNode (D0=10)

This is a new call which is used to remove an AppleTalk node address. It can be called by the .MPP driver to process the RemoveNode control call mentioned earlier.

Call:     A0->       Pointer to parameter block
                     NetAddr contains the node address to be deleted
Return:   D0         = zero if address is removed successfully
                     != zero if address does not exist
                     atlkPBRec.AddrRefNum = AddrRefNum to be used by .MPP if the
                     operation is successful

       atlkPBRec     Record       csParam
   --> NetAddr       DS.L  1      ; offset 0x1C  24-bit node address to remove
   <-- AddrRefNum    DS.W  1      ; offset 0x2A  AddrRefNum passed in by AAddNode
                                  ;    on return
                     EndR

The field NetAddr is the 24-bit AppleTalk node address that should be removed. As with the AAddNode selector, the node number is in the least significant byte 0 of NetAddr . The network number is in bytes 1 and 2 of NetAddr ; byte 3 is unused. The address reference number, AddrRefNum, associated with the NetAddr , must be returned to .MPP in order for .MPP to clean up its data structures for the removed node address.

As mentioned above, a value of 0xFFFF must be returned to .MPP after deleting the user node. When the AppleTalk connection is started up for the first time on an extended network, the ADEV can expect to process an AAddNode request followed shortly by an ADelNode request. This results from the implementation of the provisional node address for the purpose of talking with the router to determine the valid network number range to which the node is connected. After obtaining the network range, .MPP issues the ADelNode call to delete the provisional node. The next ADelNode call will be to acquire the unique node ID for the user node. As mentioned previously, .MPP can pass a value different from 0xFFFF for the user node. The user node is acquired before any multinode. The ADEV needs to keep track of the number of AAddNode and ADelNode calls issued to determine whether the user node is being acquired. Refer to Inside AppleTalk, second edition, page 4-8, for additional information.

Back to top

AGetNodeRef (D0=11)

This is a new call which is used by .MPP to find out if a multinode address exists on the current ADEV. This call is currently used by .MPP to check if a write should be looped back to one of the other nodes on the machine (the packet does not actually need to be sent through the network) or should be sent to the ADEV for transmission.

Call:     A0->       Pointer to parameter block
Return:   D0         = zero if address does not exist on this machine
                     != zero if address exists on this machine
                     atlkPBRec.AddrRefNum = AddrRefNum (corresponding to
                     the node address) if the operation is successful

       atlkPBRec     Record        csParam
   --> NetAddr       DS.L  1       ; offset 0x1C  24-bit node address to remove
   <-- AddrRefNum    DS.W  1       ; offset 0x2A  AddrRefNum passed in by AAddNode
                                   ;   on return
                     EndR

The field NetAddr is the 24-bit AppleTalk node address whose AddrRefNum is requested. The node number is in the least significant byte 0 of NetAddr. The network number is in bytes 1 and 2 of NetAddr ; byte 3 is unused. The address reference number, AddrRefNum , associated with the NetAddr , must be returned to .MPP. Remember to return 0xFFFF as the AddrRefNum for the user node.

Back to top

AOpen (D0=7)

Call:
    -->    D4.B        current port number

ADEVs should expect the AOpen call whenever the .MPP driver is being opened. This is a good time for the ADEV to register multicast addresses with the link layer or register a Transition Queue handler. After this call is completed, .MPP is ready to receive packets. If the ADEV does not process this message, simply return, RTN with a ControlErr.

Note that AOpen is not specific to the Multinode Architecture.

Back to top

AClose (D0=8)

AClose is called only when .MPP is being closed (for example, .MPP is closed when the "inactive" option is selected in the Chooser or when the user switches links in network cdev). The ADEV can use this event to deregister any multicast addresses with the link layer or remove an existing Transition Queue handler. After this AClose call is completed, the ADEV should not defend for any node addresses until .MPP reopens and acquires new node addresses. If the ADEV does not process this message, simply return, RTN with a ControlErr.

Note that AClose is not specific to the Multinode Architecture.

For comparison, descriptions of AInstall and AShutDown are documented as follows:

Back to top

AInstall (D0=1)

Call:
    -->    D1.L =    value from PRAM (slot, ID, unused, atlk resource ID)
    <--    D1.L =    high 3 bytes for parameter RAM returned by the ADEV, if no error
    <--    D0.W =    error code

The AInstall call is made before .MPP is opened either during boot time or when the user switches links in network cdev. This call is made during system task time so that the ADEV is allowed to allocate memory, make file system calls, or load resources and so on.


Note:
AOpen call will be made during .MPP opens.


Back to top

AShutDown (D0=2)

ADEVs should expect the AShutDown call to be made when the user switches links in the Network cdev. The network cdev closes .MPP, which causes the AClose call to be made before the cdev issues the AShutDown call.


Note:
The AShutDown call is always made during system task time; therefore, deleting memory, unloading resources, and file system calls can be done at this time.


Back to top

Receiving Packets

The address reference number (AddrRefNum) associated with each node address must be passed to .MPP when delivering packets upward. When making the LAP Manager call LReadDispatch to deliver packets to AppleTalk, the ADEV must fill the high word of D2 in with the address reference number, corresponding to the packet's destination address (LAP node address in the LocalTalk case and DDP address in the non-LocalTalk case). There are a few special cases:

  • In the case of broadcasts and packets directed to the user node, $FFFF (word) should be used as the address reference number.
  • On non-LocalTalk networks, packets with DDP destination addresses matching neither the user node address nor any of the multinode addresses should still be delivered to the LAP Manager so that the router can forward the packet on to the appropriate network. In this case, the high word of D2 should be filled in with the address reference number, $FFFE, to indicate to MPP that this packet is not for any of the nodes on the machine in the case of a router running on a machine on an extended network.
  • On LocalTalk networks, the ADEV looks only at the LAP address; therefore, if the LAP address is not the user node, one of the multinodes, or a broadcast, the packet should be thrown away.

Back to top

Defending Multinode Addresses

Both LocalTalk (RTS and CTS) and non-LocalTalk (AARP) ADEVs have to be modified to defend not only for the user node address but also for any active multinode addresses.

Back to top

Corrections/Clarifications to the LAP Manager

The interface for the Link Access Protocol (LAP) Manager is presented in the document Macintosh AppleTalk Connections Programmer's Guide, available from ADPA, p/n M7056/A. This section provides additional descriptions to the LAP Manager functions described in the Programmer's Guide.

Back to top

LRdDispatch (D0 = 1)

The LRdDispatch routine is called by an ADEV's 'atlk' packet handling routine to notify the LAP Manager that a packet has been received, and to pass the packet to the LAP Manager for processing by the appropriate protocol handler. The documentation indicates that this routine "even though it is called with a JSR, does not return to the caller, but jumps to the protocol handler that is attached to the protocol indicated in (register) D2."

When called to handle LRdDispatch, the LAP Manager searches it's protocol table for a match to the protocol specified in D2. If found, the return address placed on the stack by the JSR instruction is removed. For this reason, the caller will not be returned to if a protocol handler is found. This also means that the packet handling routine which calls LRdDispatch, must restore the stack to the state the stack was in, when the packet handler was entered.

Back to top

LWrtInsert (D0 = 2)

The LWrtInsert description indicates that setting bit 6 of the flag byte in register D1, "does not disable the port B serial communications controller (SCC)...". This bit no longer has any meaning under AppleTalk.

Back to top

AppleTalk Version Information

The following table is presented to assist AppleTalk developers in understanding which versions of AppleTalk are required by the various AppleTalk products. This list does not identify the individual bug fixes associated with each release of AppleTalk.

AppleTalk     .MPP Version  .ATP Version   Apple Products Using That Version
Version

19            19            19            Macintosh Plus ROM
48            48            46            Macintosh SE ROM
                                          Macintosh Classic ROM
48.1          48            46            AppleShare File Server v1.0
49            49            49            Macintosh II ROM
                                          Macintosh IIcx ROM
                                          Macintosh SE/30
50            50            49            AppleShare File Server v1.1
51            51            51            AppleShare Print Server v2.0
52            52            52            AppleShare File Server v2.0
                                          Macintosh IIx ROM
                                          Macintosh IIci ROM
                                          Macintosh Portable ROM
                                          PowerBook 100 ROM

Back to top

Phase 1 drivers

AppleTalk     .MPP Version  .ATP Version   Apple Products Using That Version
Version
53            53            53            AppleTalk Phase 2 products
                                          AppleTalk Internet Router v2.0
                                          Apple EtherTalk NB Card 2.0
                                          Apple TokenTalk NB Card 2.0
54            54            54            Macintosh IIfx
                                          Macintosh LC, LC II, & IIsi
55            55            55            Apple Ethernet LC Card (for Macintosh LC)
56            56            56            System 7.0, 7.0.1
                                          Macintosh Classic II
                                          PowerBook 100, 140, 170
                                          Macintosh Quadra 700, 900, 950
57.0.1        57.0.1        57.0.1        AppleTalk Remote Access
                                          Apple Token ring 4/16 NB Card
                                          Apple Ethernet NB Card
57.0.3        57.0.3        57.0.3        Apple Ethernet LC Card (for Macintosh LC
                                          II)
57.0.4        57.0.4        57.0.4        MacTCP Token Ring Extension v1.0
                                          System 7.1, MacTCP 1.1.1
                                          Macintosh Duo 210, 230
                                          Macintosh IIvi, IIvx
                                          Macintosh Color Classic
                                          Macintosh LC II
                                          PowerBook 145, 145b, 160, 165c, 180, 180c
                                          Macintosh Quadra 800
                                          Macintosh Centris 610, 650
58            58            58            Apple Internet Router v3.0
                                          Mac SNMP
58.0.2        58.0.2        58.0.2        AppleShare File Server 4.0
58.0.3        58.0.3        58.0.3        Macintosh Quadra 840AV, Centris 660AV
58.1          58.1          58.1          PowerTalk
58.1.1        58.1.1        58.1.1        Ethernet NB Twisted Pair

Back to top

Phase 2 drivers

Some interesting notes:

  • .MPP and .ATP driver versions weren't always the same in versions of AppleTalk before version 51. The .MPP driver version is the AppleTalk version number.
  • The Phase 1 RAM-based drivers (versions 49 through 52) were supplied as file that could be drag-installed (that is, the installation consisted of dropping them into the System Folder).
  • The Phase 2 RAM-based drivers (versions 53 through 58) must be installed by an Installer script that will install the various system pieces (files, resources, and so on) needed to load and support these new drivers; these versions cannot be drag-installed.
  • AppleTalk version 56 or greater includes the .DSP driver (ADSP). Starting with version 56, the .DSP driver version is the same as the AppleTalk version.

Apple software products that require the Phase 2 RAM-based drivers must be installed using the Installer program. Apple supplies an Installer script that will copy all files and system resources needed. Apple licenses the source to an Installer script that you can use if you license AppleTalk to ship with your products.

Back to top

Contacting Apple Software Licensing

Software Licensing can be reached as follows:

Software Licensing
Apple Computer, Inc.
20525 Mariani Avenue, M/S 38-I
Cupertino, CA 95014
MCI: 312-5360
AppleLink: SW.LICENSE
Internet: SW.LICENSE@AppleLink.Apple.com
(408)974-4667

References

Inside AppleTalk, Second Edition, Addison-Wesley

Inside Macintosh, Volume II, The AppleTalk Manager, Addison-Wesley

Inside Macintosh, Volume V, The AppleTalk Manager, Addison-Wesley

Inside Macintosh, Volume VI, The AppleTalk Manager, Addison-Wesley

Macintosh AppleTalk Connections Programmer's Guide, Final Draft 2, Apple Computer, Inc. (M7056/A)

AppleTalk Phase 2 Protocol Specification, Apple Computer, Inc. (C0144LL/A)

AppleTalk Remote Access Developer's Toolkit, Apple Computer, Inc. (R0128LL/A)

M.NW.AppleTalk2Mac

NuBus is a trademark of Texas Instruments.

Back to top

Change History

01-Feb-1994

Provide list of original AppleTalk Manager routines which will are NOT being ported to the PowerPC platform. Corrected the Multinode Protocol glue to reflect that the AddNode call must be made as a synchronous immediate Control call to the .MPP driver. Included information on making the sample socket listener compatible with Virtual Memory. Added note that the LAPAddATQ and LAPRmvATQ routines have been corrected in the Interface.o library supplied with MPW v3.3. Added information on using the .TOKN and .FDDI API. Made corrections to socket listener code so that the Checksumming feature worked correctly.

01-Jan-1993

Corrected the .TOKN interface to remove reference to the A1 register on packet reception. Describe a bug with the LAPAddATQ the LAPRmvATQ glue code which exists in the MPW Interface.o library file, and publish an Assembler glue code file to work around the problem. Describe change to the .ENET EGetInfo call interface. Describe change to the 'atlk' AGetInfo call required of ADEV's to support SNMP (Simple Network Management Protocol). Discuss other changes required for .TOKN and .ENET support of SNMP. Provide additional clarification to the LAP Manager calls, LRdDispatch, and LWrtInsert. Present correction to the ENET.h header file supplied with MPW 3.2.x.


Back to top

Downloadables

Acrobat gif

Acrobat version of this Note (424K)

Download


Back to top


Technical Notes by Date | Number | Technology | Title
Developer Documentation | Technical Q&As | Development Kits | Sample Code




Gray line

Contact ADC |  ADC Site Map |  ADC Advanced Search
For information about Apple Products, please visit Apple.com.
Contact Apple | Privacy Notice
Copyright © 2002 Apple Computer, Inc. All rights reserved.
1-800-MY-APPLE