| 
 Q: I'm writing a driver for a USB network device.
         USB callbacks happen at secondary interrupt level. Many Open
         Transport calls can only be called from deferred task level.
         How do I get from one to the other? A: The answer to this question is simple, although
         the background is very complex and needs some explanation. I
         will start with the basics and then fill in the details for
         the relentlessly curious. Deferred Task to Secondary Interrupt To get from deferred task level to secondary
         interrupt level, use the DriverServicesLib call
         QueueSecondaryInterruptHandler. 
            
               | WARNING:Do not use
 CallSecondaryInterruptHandler2.
                  Depending on the system version this call will
                  either do the wrong thing or fail with an error
                  code if you call it from a deferred task. See the
                  detailed explanation later in this document. |  Secondary Interrupt to Deferred Task To get from secondary interrupt level to deferred
         task level, use the standard DTInstallsystem
         call. On some systems (see the explanation below), callingDTInstallfrom a secondary interrupt can result
         in the deferred task being called immediately (that is,
         beforeDTInstallreturns). If this is a problem
         for your software, you can force the Deferred Task Manager
         to always queue your deferred task using the code from
         Listing 1. 
            
               |  Listing 1. Forcing a deferred task
                  to always be queued. 
                     
                        | static OSStatus MySecondaryInterruptHandler(void *p1, void *p2)
{
    OSStatus junk;
    UInt16 oldInterruptMask;
    oldInterruptMask = SetInterruptMask(7);
    junk = DTInstall(&gDeferredTask);
    MoreAssertQ(junk == noErr);
    (void) SetInterruptMask(oldInterruptMask);
    return noErr;
} |  |  Listing 1 uses the routines from
         InterruptDisableLib, as described in DTS Technote 1137
         Disabling
         Interrupts on the Traditional Mac OS. The Full Story As explained in DTS Q&A DV 43 InterfaceLib
         and Native Drivers, DriverServicesLib defines a model
         that is somewhat out of sync with the reality of traditional
         Mac OS. This is not a big problem if your software works
         entirely within the DriverServicesLib model, but it can
         cause problems if you write code that operates in both
         worlds. An example of this is a USB network driver, which
         must use both deferred tasks (Open Transport) and secondary
         interrupts (USB). The biggest problem with calling DriverServicesLib
         routines from a deferred task is that many of its routines
         rely internally on the value returned by
         CurrentExecutionLevel. As explained in Technote
         1104 Interrupt-Safe
         Routines,CurrentExecutionLeveldoes not
         always yield accurate results when called from
         non-DriverServicesLib environments (such as deferred tasks).
         IfCurrentExecutionLevelreturns the wrong
         result, DriverServicesLib does the wrong thing. 
            
               | IMPORTANT:A specific example of this problem is
 CallSecondaryInterruptHandler2. This
                  routine callsCurrentExecutionLeveland uses the result to either: 
                     return an error (if the execution level is
                     kHardwareInterruptLevel), orcall the secondary interrupt handler
                     directly
                     (kSecondaryInterruptLevel), orenter secondary interrupt level and run all
                     secondary interrupts
                     (kTaskLevel). It has never been legal to call
                  CallSecondaryInterruptHandler2from a
                  deferred task, however the oldCurrentExecutionLevelalgorithm masked
                  this error. This mislead some developers into
                  thinking that it was OK to callCallSecondaryInterruptHandler2from a
                  deferred task. WhenCurrentExecutionLevelwas updated to
                  return more accurate results, these developers'
                  products broke. When calling
                  CallSecondaryInterruptHandler2from a
                  deferred task there are three cases to
                  consider. 
                     On older systems (those with the old
                     CurrentExecutionLevelalgorithm),CallSecondaryInterruptHandler2would treat deferred task level the same as
                     system task level. CallingCallSecondaryInterruptHandler2from
                     a deferred task would run the handler (and any
                     other queued secondary interrupt handlers) and
                     returnnoErr.On CPUs running Mac OS 9.0.4 with the new
                     CurrentExecutionLevelalgorithm
                     (Technote
                     1104 explains how to detect the presence of
                     the new algorithm),CallSecondaryInterruptHandler2would return an error if called from a deferred
                     task.Because this caused some compatibility
                     problems, Mac OS 9.1 was changed
                     [2529860] to include a special case
                     'hack' that restores the old behavior of
                     CallSecondaryInterruptHandler2without undoing the fix toCurrentExecutionLevel. In summary, your software should not call
                  CallSecondaryInterruptHandler2from a
                  deferred task because it defined to be illegal. It
                  might work, but that doesn't make it right! |  Likewise, calling the Deferred Task Manager from a
         secondary interrupt yields strange results. Because of its
         heritage, the Deferred Task Manager uses the 68K interrupt
         mask to detect whether to queue the deferred task or call it
         immediately. However, the 68K interrupts mask is zero while
         secondary interrupts execute (this is, after all, the
         purpose of secondary interrupts), so if you call
         DTInstallfrom a secondary interrupt the
         Deferred Task Manager gets confused and typically executes
         the deferred task immediately. This exact behavior is
         dependent on the system version and the presence of VM and
         is shown in Table 1. 
            
               |  Table 1. Behavior of
                  DTInstallwhen called from a secondary
                  interrupt. 
                     
                        | Mac OS Version | VM On | VM Off |  
                        | 9.0 and earlier | immediate • | immediate • |  
                        | 9.0.1 through 9.0.4 | queued | immediate • |  
                        | 9.1 and later | queued | queued |  
	The deferred task is only executed immediately if it isn't blocked in some other way, for example, if an another deferred task is already running, or paging is unsafe. |  The behavior of DTInstallexplains why
         the code in Listing 1 always forces the deferred task to be
         queued. By artificially (and temporarily) raising the
         interrupt mask to 7, it convinces the Deferred Task Manager
         that it is being called from a hardware interrupt, and hence
         must queue the deferred task. 
            
               | IMPORTANT:As you can see from this discussion, the 68K
                  interrupt mask is not a good indication of the
                  current execution level. In fact, there is no
                  general way to determine the current execution
                  level. Technote 1104 Interrupt-Safe
                  Routines has a section
                  describing the various common attempts to solve
                  this problem, and their various flaws.
 |  In summary, problems can arise when you mix native driver
         model execution levels with older execution levels. You can
         avoid these problems using the techniques described at the
         beginning of this Q&A. |