Important: The information in this document is obsolete and should not be used for new development.
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 theGetIndADB
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 theSetADBInfo
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
- uses the
CountADBs
function to count the number of entries in the ADB device table.- repeatedly uses the
GetIndADB
function to examine each entry in the ADB device table for an entry with a default ADB device address and default device handler ID that matches that of a standard device.- upon finding a matching entry, uses the
ADBOp
function to send a Talk Register 3 command to the selected ADB device so that it sends its contents across the bus; uses theADBOp
function to send a Listen Register 3 command to the device to change its device handler ID from its default device handler ID to its actual device handler ID; and uses theADBOp
function to send another Talk Register 3 command to the device and examine the register contents to see whether the device returns the new device handler ID. If so, your extension has found the index entry for your device and can use theSetADBInfo
function to install the appropriate device handler for your device. Note that when you request an ADB device to change to another device handler ID, the ADB Manager does not update the ADB device table entry to reflect the new device handler ID. You can find out the new handler ID for that device only by sending it a Talk Register 3 command.
JADBProc
and should preserve the existing value ofJADBProc
, 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
andSetADBInfo
. The installation code then jumps to the code starting at the labelMyInstallHandlers
; this code usesCountADBs
and thenGetIndADB
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 labelMySetDeviceInfo
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 theSetADBInfo
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 variableJADBProc
and then setsJADBProc
to contain a pointer tomyJADBProc
.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 ENDYour installation code should set up the value of
- 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.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 variableJADBProc
; 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 ofJADBProc
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 procedureCreating 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 usingPostEvent
.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:
- in register A0, a pointer to the ADB data sent by the ADB device
- in register A1, a pointer to the device handler routine
- in register A2, a pointer to the data area (if any) associated with the device handler
- in register D0, the ADB command that resulted in the handler being called
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).
- 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 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