Legacy Documentclose button

Important: The information in this document is obsolete and should not be used for new development.

Previous Book Contents Book Index Next

Inside Macintosh: Devices /
Chapter 5 - ADB Manager


Writing an ADB Device Handler

The previous section, "Using the ADB Manager," illustrates how you can use the ADB Manager to communicate with and get information about devices attached to the ADB. This section describes how to write a device handler for an ADB device. You should write a device handler for a device only if you are the manufacturer of that device.

A device handler is a low-level routine that communicates with a particular ADB device. The device handler gathers data from an ADB device through the ADB Manager and interprets the data; depending on the device, the device handler might then post an event into the event queue using the PostEvent function.

A single device handler can manage more than one device; for example, the standard device handler for the Apple Extended keyboard can manage multiple extended keyboards. Also, in some cases the same handler can be used to manage two or more device types. For example, a relative-position graphics tablet could emulate a mouse, using the same default ADB device address and device handler ID as used by the mouse, and providing the same information in response to Talk commands. In this case, when both the mouse and tablet are connected to the ADB at the same time, the ADB Manager calls the mouse handler when either device requires it.

Each ADB device has a default ADB device address and default device handler ID. Some ADB devices support more than one device handler ID. In this case, the device handler manages the device based on the current device handler ID; this allows an ADB device to add or modify its performance or feature set. For more information about ADB addresses and device handler IDs, see "Default ADB Device Address and Device Handler Identification" on page 5-11.

In addition to writing a device handler for your device, you need to write the code that installs the device handler. The next few sections explain how to write a device handler and code to install the handler.

IMPORTANT
You need the information in this section only if you are writing a device handler for a new ADB device. The Macintosh Operating System includes device handlers for all Apple keyboards and Apple mouse devices. You do not need to write a device handler to receive input from these standard Apple devices; instead, your application should get information about mouse movements and key presses by calling the Event Manager. See the chapter "Event Manager" in Inside Macintosh: Macintosh Toolbox Essentials for complete information about how the Event Manager interacts with applications.

Installing an ADB Device Handler

You install a device handler for an ADB device by placing the address of the device handler in the device's entry of the ADB device table. To do this, and to make your ADB device available to the user as soon as possible, Apple recommends that you provide users with a system extension that installs your device handler. Thus, your system extension should contain your device handler as well as the code that installs the device handler into the appropriate entry of the ADB device table. (See "ADB Device Table" on page 5-13 for a description of the structure of entries in the ADB device table.)

Your installation code should search the ADB device table for an entry whose default ADB device address and default device handler ID match the values assigned to your device. For example, if your ADB device has a default address of $7 and a default handler ID of 99, your installation code should search the ADB device table for entries matching these values. If your installation code finds any matching entries, it should install the address of your device handler into your device's entry in the ADB device table. The typical installation code for ADB devices other than a keyboard or mouse does this: calls the CountADBs function to determine the number of entries in the ADB device table; repeatedly calls the GetIndADB function to index through each device table entry and compares the default ADB device address and device handler ID with those of your device; for any matching entries, calls the SetADBInfo function to install the device handler for that device into the device's entry in the ADB device table. Note that before installing the address of your device handler into the ADB device table, your installation code must first allocate space in the system heap for your handler and copy your handler to this area; your installation code should also allocate space in the system heap for its optional data area.

If you provide a device handler for a mouse or keyboard you must consider whether your ADB device should use a standard device handler during initial startup (until your system extension has a chance to run and install the device's device handler) or whether your ADB device should use only its own device handler (which means your device will be unable to respond to the user until its handler is installed).

When the ADB Manager first builds the ADB device table, it associates with each device the device's default ADB address, the device's current ADB address, and the device's default device handler ID. In addition, for each device it initializes the field that contains the address of the device handler for the device and the field that contains a pointer to a data area used by the device handler. For Apple ADB devices, the ADB Manager installs the appropriate device handler provided by Apple. Thus, the device handler for an Apple keyboard or Apple mouse is available almost immediately after initial startup. For all other ADB devices, the device's device handler must be specifically installed by the device's installation code. For example, the ADB Manager does not install the Apple device handler for a keyboard with a default ADB device address of $2 and a default device handler ID of 99; instead, the device's system extension must install the device's device handler.

If your ADB device is a keyboard or mouse and you want it to function as soon as possible in the startup process and before system extensions are run, you can design your ADB device to emulate an Apple keyboard or mouse and use that device's device handler until its own device handler is installed. In this case, your ADB device's default ADB address and default device handler ID initially matches that of an Apple device. This causes the ADB Manager to install the address of an Apple device handler for your device's entry in the ADB device table. To install the actual device handler for your device, you can provide a system extension that

Your installation code should also store a pointer to its reinitialization code in the system global variable JADBProc and should preserve the existing value of JADBProc, as illustrated in Listing 5-6 and Listing 5-7.

The next three listings, Listing 5-6, Listing 5-7, and Listing 5-8, show code that installs a device handler, handles reinitialization by appropriate use of the system global variable JADBProc, and performs the actual actions of the device handler.

Listing 5-6 shows an example of code that installs an ADB device handler. The code first defines some constants. It also defines a stack frame which includes storage for a variable called myADBDB that is used later as a parameter block for both GetIndADB and SetADBInfo. The installation code then jumps to the code starting at the label MyInstallHandlers; this code uses CountADBs and then GetIndADB to search all entries in the ADB device table for a matching default ADB device address and device handler ID. If it finds such an entry, it uses the code at the label MySetDeviceInfo to set up information for that device in the device's entry in the ADB device table.

Specifically, for each occurrence of a matching entry, the code at the label MySetDeviceInfo allocates space in the system heap for the data area used by the device handler for the ADB device at that address. (It does not need to allocate space for the handler itself at this time. This is because a resource containing the code shown in Listing 5-6 is marked to be loaded into the system heap; thus the system software loads the resource into the system heap when it executes this system extension.) The code then uses the SetADBInfo function to install into the ADB device table the address of the device's device handler as well as a pointer to the global data area used by the device handler.

Finally, the installation code stores in the iNextProc field the current value of the system global variable JADBProc and then sets JADBProc to contain a pointer to myJADBProc.

Listing 5-6 Installing an ADB device handler

;For installation to work, the resource containing this resource must be 
;marked as sysHeap loaded. This way, you do not have to copy a version of it
;into the system heap prior to installing.
; MPW Build commands:
;   ASM 'ADBSample.a'
;   Link -t INIT -c WeSt -ra ADBSample=resSysHeap -rt INIT=128 -m MAIN -sg \x8F
;     ADBSample 'ADBSample.a'.o -o ADBSample
myAddr         EQU      $xx               ;default ADB device address 
myADBType      EQU      $xx               ;device handler ID definition

main           PROC     EXPORT

StackFrame     RECORD   {A6Link}, DECR    ;build a stack frame record
ParamBegin     EQU      *                 ;start parameters after this point
ParamSize      EQU      ParamBegin-*      ;size of all the passed parameters
RetAddr        DS.L     1                 ;place holder for return address
A6Link         DS.L     1                 ;place holder for A6 link
myADBDB        DS       ADBDataBlock      ;local handle to our ADB data block
LocalSize      EQU      *                 ;size of all the local variables
               ENDR

               WITH     StackFrame
               WITH     ADBDataBlock
               LINK     A6, #0            ;make a stack frame
               BSR      MyInstallHandlers ;install handlers for our devices
               TST.W    D0                ;D0 = number of old devices found
               BEQ.S    @exit             ;if none, exit

               LEA      main, A0       ;after installing, we need to
               _RecoverHandle, SYS     ; recover the handle and then
               MOVE.L   A0 -(SP)       ; detach this resource so it always
               _DetachResource         ; stays in memory

               LEA      iNextProc, A2  ;get pointer to old vector storage
               LEA      JADBProc, A3   ;make pointer to low memory vector
               MOVE.L   (A3), (A2)     ;save contents of vector for chaining

               LEA      myJADBProc, A2 ;get pointer to our jADBProc
               MOVE.L   A2, (A3)       ;install it in the low memory vector

@exit          UNLK     A6             ;dispose local variables
               RTS
;placeholder for MyADBHandler - see Listing 5-8 on page 5-37
;placeholder for myJADBProc - see Listing 5-7 on page 5-35

;MySetDeviceInfo routine (called by MyInstallHandlers)
; on entry: D0 = ADB address of our device
; does not preserve D4 or A1
MySetDeviceInfo
               LINK     A6, #LocalSize    ;make a stack frame
               LEA      myADBDB(A6), A1   ;pointer to stack-based param block
               LEA      MyADBHandler, A3  ;pointer to the handler routine
               MOVE.W   D0, D4            ;save the actual address
               MOVE.L   A3, (A1)          ;set up the handler address
               MOVE.L   #10, D0           ;data area for device is 10 bytes
               _NewPtr, SYS, CLEAR        ;allocate our data area
               TST.W    D0                ;test for error
               BNE.S    @SDIExit          ;exit if error
               MOVE.L   A0, 4(A1)         ;put pointer to parameter data
                                          ; in data area
               MOVE.W   D4, D0            ;put actual address to set in D0
               MOVE.L   A1, A0            ;put parameter block pointer in A0
               _SetADBInfo                ;set up info for this device
@SDIExit    
               UNLK     A6                ;dispose stack frame
               RTS                        ;exit this routine
               
iNextProc      DC.L     0                 ;store pointer to next jADBProc



;MyInstallHandlers routine (called by main)
; on exit: D0 = number of our device types found
; does not preserve D1, D2, D3, D4 or A1
MyInstallHandlers
               LINK     A6, #LocalSize    ;make a stack frame
               CLR.L    D3                ;clear device counter
               _CountADBs                 ;get number of ADB devices
               MOVE.W   D0, D2            ;save this number in D2
               BEQ.S    @return           ;exit if none
                                                   ;put handler ID and
               MOVE.W   #(myADBType<<8)+myAddr, D1 ; default address into D1
@cntLoop 
               MOVE.W   D2, D0            ;put device index in D0
               LEA      myADBDB(A6), A0   ;pointer to stack-based param block
               _GetINDADB                 ;get an ADB device table entry
               BMI.S    @nextRec          ;skip if invalid
               CMP.W    devType(A0), D1   ;is this one of our devices?
               BNE.S    @nextRec          ;skip if no match
               BSR.S    MySetDeviceInfo   ;set handler for this device
               ADDQ     #1, D3            ;found one of our devices, add to D3
@nextRec       SUBQ.W   #1, D2            ;try next index
               BNE.S    @cntLoop          ;loop if more
               MOVE.L   D3, D0            ;return number found in D0
@return        UNLK     A6                
               RTS
               ENDP
               END
Note
In the past, Apple recommended that you install an ADB device handler by placing the ADB device handler in an 'ADBS' resource in the System file. In this case, the 'ADBS' resource ID corresponds to the ADB device's default address. At system startup, the ADB Manager searches the System file for 'ADBS' resources for only those ADB devices that appear on the bus. The ADB Manager then loads these resources into memory and executes them. The ADB Manager also reads register 3 for each ADB device and places the device's default ADB device address and device handler ID into the ADB device table. This method, however, does not offer the same flexibility and scope as when you install a handler with an extension. For example, because 'ADBS' resource IDs are indexed only by their default addresses, you cannot install ADB resources for two different devices at the same address using 'ADBS' resources. Apple therefore recommends that you install all ADB device handlers using a system extension.
Your installation code should set up the value of JADBProc (by chaining) to point to a routine that you provide which appropriately handles the case when the ADB is reinitialized. When the ADB is reinitialized, the ADB Manager calls the routine pointed to by the system global variable JADBProc; it calls this routine twice: once before reinitializing the ADB, and once after reinitializing the ADB. When this routine is called, D0 contains the value 0 for preprocessing and 1 for postprocessing. Your routine must restore the value of D0 and branch to the original value of JADBProc on exit.

For preprocessing, your reinstallation routine should deallocate any storage. It must also take action for postprocessing. Because the ADB (and ADB device table) is reinitialized during postprocessing, the ADB Manager might need to perform address resolution. As a result, you cannot assume that your ADB device still resides at its default address after postprocessing occurs. Therefore, for postprocessing your reinstallation routine should search the ADB bus for a matching device (just as in its installation code) and install its entry into the ADB device table. Finally, the code jumps to the routine stored in iNextProc, and thus chains to the next routine that needs to perform postprocessing. Listing 5-7 shows an example of this entire process.

Listing 5-7 Installing a routine pointer into JADBProc

;main goes here, see Listing 5-6 on page 5-32
;handler code goes here, see Listing 5-8 on page 5-37 
;NOTE: This routine must be installed as part of the handler.
myJADBProc
               LINK     A6, #LocalSize    ;make a stack frame
               MOVEM.L  D0-D2/A1, -(SP)   ;save registers for next procedure
               TST.B    D0                ; D0 = 0 for pre-processing,
                                          ; D0 = 1 for post-processing
               BEQ.S    @preProc          ;if 0, pre-process data areas

@postProc      
               BSR.S    MyInstallHandlers ;install handlers (Listing 5-6)
               BRA.S    @JADBExit
@prePost 
               LEA      myADBDB(A6), A1   ;pointer to stack-based param block
               LEA      MyADBHandler, A2  ;address of handler for comparison
               _CountADBs                 ;get the number of ADB devices
               MOVE.W   D0, D2            ;save this value in D2
               BEQ.S    @JADBExit         ;exit if none
                                                   ;put handler ID and
               MOVE.W   #(myADBType<<8)+myAddr, D1 ; default address into D1
@preLoop
               MOVE.W   D2, D0            ;current index
               MOVE.L   A1, A0            ;address of data block
               _GetIndADB                 ;get ADB device table entry
               BMI.S    @nextRec          ;skip if invalid
               CMP.W    devType(A0), D1   ;is this one of our devices?
               BNE.S    @nextRec          ;skip if no match
               CMPA.L   dbServiceRtPtr(A0), A2  ;compare with our handler ID
               BNE.S    @nextRec          ;if no match, don't delete pointer
               MOVE.L   dbDataAreaAddr(A0), A0  ;get the pointer to dispose
               _DisposePtr                ;if matches, it's ours, so dispose
@nextRec
               SUBQ.W   #1, D2            ;get next index
               BNE.S    @preLoop          ;loop if more
@JADBExit
               MOVEM.L  (SP)+, D0-D2/A1   ;restore registers
               UNLK     A6                ;dispose stack frame
               LEA      iNextProc, A0     ;get pointer to next procedure
               MOVE.L   (A0), A0          
               JMP      (A0)              ;jump to next procedure

Creating an ADB Device Handler

A device handler communicates with a particular ADB device by gathering data about the device it manages from the ADB Manager, and then interpreting that data. For example, the device handler for a particular device might then post an event into the event queue using PostEvent.

Whenever an ADB device sends data (by responding to a Talk Register 0 command), the ADB Manager calls the associated device handler. The ADB Manager passes these parameters to the device handler:

Note
ADB device handlers are always called at interrupt time; they must follow all rules for interrupt-level processing as described in Inside Macintosh: Processes.
Listing 5-8 gives an example of a simple device handler that handles data from an ADB device. (Listing 5-6 on page 5-32 shows code that installs the address of this handler into the ADB device table.) This device handler simply saves the data sent by the ADB device into the device handler's global data area. Note that you must include with your device handler code that handles reinitialization of the ADB (see Listing 5-7 on page 5-35 for details of reinitialization).

Listing 5-8 A sample device handler

MyADBHandler
      ANDI.B   #$0F, D0          ;check command
      CMPI.B   #$0C, D0          ;was it a talk R0 command?
      BNE.S    @exit             ; no, exit (something is wrong)
      MOVE.B   (A0)+, D0         ;get the count
      CMPI.B   #2, D0            ;this device only sends 2 bytes
      BNE.S    @exit             ;bad count, exit
      MOVE. B  (A0)+, HndlrData(A2) ;grab the 1st byte, save in global area
      MOVE.B   (A0)+, MoreData(A2)  ;grab the 2nd byte, save in global area
@exit          RTS 
      ;code from Listing 5-7 goes here 

Previous Book Contents Book Index Next

© Apple Computer, Inc.
3 JUL 1996