Entry-Point Functions

Following is an example of a DriverProc function. Regardless of how many types of audio devices a driver supports, the driver always has a single DriverProc function. The following example is the DriverProc function from the DRVPROC.C file, which supports four types of audio devices. While this example focuses heavily on ACM examples, the concepts are similar to those used for other types of drivers as well.


//  DWORD DriverProc
//  
//  Description:
//      The entry point for an installable driver.
//  
//  Parameters:
//      DWORD dwDriverID
//
//      For most messages, dwDriverId is the DWORD
//      value that the driver returns in response to a DRV_OPEN message.
//      Each time that the driver is opened, through the DrvOpen API,
//      the driver receives a DRV_OPEN message and can return an
//      arbitrary, non-zero value. The installable driver interface
//      saves this value and returns a unique driver handle to the
//      application. Whenever the application sends a message to the
//      driver using the driver handle, the interface routes the message
//      to this entry point and passes the corresponding dwDriverId.
//      This mechanism allows the driver to use the same or different
//      identifiers for multiple opens but ensures that driver handles
//      are unique at the application interface layer.
//
//      The following messages are not related to a particular open
//      instance of the driver. For these messages, the dwDriverId
//      will always be zero.
//
//         DRV_LOAD, DRV_FREE, DRV_ENABLE, DRV_DISABLE, DRV_OPEN
//  
//      HANDLE hDriver
//
//      This is the handle returned to the application by the driver
//      interface.
//
//      WORD wMessage
//
//      The requested action to be performed. Message values 
//      below DRV_RESERVED are used for globally defined messages.
//      Message values from DRV_RESERVED to DRV_USER are used for
//      defined driver protocols. Messages above DRV_USER are used
//      for driver specific messages.
//  
//      DWORD dwParam1
//
//      Data for this message.  Defined separately for each message
//
//      DWORD dwParam2
//
//      Data for this message.  Defined separately for each message
//  
//  Return (DWORD):
//      Defined separately for each message.

DWORD FAR PASCAL _loadds DriverProc
(
    DWORD           dwDriverID,
    HANDLE          hDriver,
    WORD            wMessage,
    DWORD           dwParam1,
    DWORD           dwParam2
)
{
   switch (wMessage)
   {
      case DRV_LOAD:

         // Sent to the driver when it is loaded. Always the first
         // message received by a driver.
         //
         // dwDriverID is 0L.
         // lParam1 is 0L.
         // lParam2 is 0L.
         //
         // Return 0L to fail the load.
         //
         // DefDriverProc will return NON-ZERO so we don't have to
         // handle DRV_LOAD
         //

         DPF( 1, "DRV_LOAD" ) ;

#if 0
         // Make sure we're running on Windows 95...

         if (GetWindowsVersionCorrectly() < 0x0400)
         {
            AlertBox( NULL, SR_ALERT_31 ) ;
            return 0L ;
         }
#endif

         return 1L ;

      case DRV_FREE:

         // 
         // Sent to the driver when it is about to be discarded. This
         // will always be the last message received by a driver before
         // it is freed.
         // 
         // dwDriverID is 0L.
         // lParam1 is 0L.
         // lParam2 is 0L.
         // 
         // Return value is ignored.
         // 

         DPF( 1, "DRV_FREE" ) ;

         return 1L ;

      case DRV_OPEN:

         // 
         // Sent to the driver when it is opened.
         // 
         // dwDriverID is 0L.
         // 
         // lParam1 is a far pointer to a zero-terminated string
         // containing the name used to open the driver.
         // 
         // lParam2 is passed through from the drvOpen call.
         // 
         // Return 0L to fail the open.
         // 
         // DefDriverProc will return ZERO so we do have to
         // handle the DRV_OPEN message.
         // 

         DPF( 1, "DRV_OPEN" ) ;

         // No longer in initialization

#pragma message( REMIND( "bJustStart used???" ) )

         // bJustStart = 0 ;
         return 1L ;

      case DRV_CLOSE:

         // 
         // Sent to the driver when it is closed. Drivers are unloaded
         // when the close count reaches zero.
         // 
         // dwDriverID is the driver identifier returned from the
         // corresponding DRV_OPEN.
         // 
         // lParam1 is passed through from the drvOpen call.
         // 
         // lParam2 is passed through from the drvOpen call.
         // 
         // Return 0L to fail the close.
         // 
         // DefDriverProc will return ZERO so we do have to
         // handle the DRV_CLOSE message.
         // 

         DPF( 1, "DRV_CLOSE" ) ;

         return 1L ;

      case DRV_ENABLE:

         // 
         // Sent to the driver when the driver is loaded or reloaded
         // and whenever Windows is enabled. Drivers should only
         // hook interrupts or expect any part of the driver to be in
         // memory between enable and disable messages
         // 
         // dwDriverID is 0L.
         // lParam1 is 0L.
         // lParam2 is 0L.
         // 
         // Return value is ignored.
         // 

         DPF( 1, "DRV_ENABLE" ) ;

         if (DrvInit())
            return 0L ; // error

         // Load the volume settings, etc.

#pragma message( REMIND( "Need to load settings from registry" ) )

//         DrvLoadVolumeFromIni();

         // No longer in initialization

#pragma message( REMIND( "bJustStart used???" ) )

         // bJustStart = 0 ;
         return 1L ;

      case DRV_DISABLE:

         // 
         // Sent to the driver before the driver is freed.
         // and whenever Windows is disabled
         // 
         // dwDriverID is 0L.
         // lParam1 is 0L.
         // lParam2 is 0L.
         // 
         // Return value is ignored.
         // 

         DPF( 1, "DRV_DISABLE" ) ;

#pragma message( REMIND( "Need to save settings to registry" ) )

//         DrvSaveVolumeToIni () ;

         DrvEnd() ;

         return 1L ;

      case DRV_QUERYCONFIGURE:

         //
         // Sent to the driver so that applications can
         // determine whether the driver supports custom
         // configuration. The driver should return a
         // non-zero value to indicate that configuration
         // is supported.
         // 
         // dwDriverID is the value returned from the DRV_OPEN
         // call that must have succeeded before this message
         // was sent.
         // 
         // lParam1 is passed from the app and is undefined.
         // lParam2 is passed from the app and is undefined.
         // 
         // Return 0L to indicate configuration NOT supported.
         // 

         DPF( 1, "DRV_QUERYCONFIGURE" ) ;

#pragma message ( REMIND( "configure if not operational???" ) )

#if 0
         return (fEnabled != 0) ;        // we do configuration
#endif
         return TRUE ;

      case DRV_CONFIGURE:

         //
         // Sent to the driver so that it can display a custom
         // configuration dialog box.
         //
         // lParam1 is passed from the app. and should contain
         // the parent window handle in the loword.
         // lParam2 is passed from the app and is undefined.
         // 
         // Return value is REBOOT, OK, RESTART.
         // 

         DPF( 1, "DRV_CONFIGURE" ) ;

#if 0
         if (fEnabled)
            return DrvConfig( LOWORD( dwParam1 ), ghModule ) ;
#endif
         // else, default */
            break;

      case DRV_INSTALL:
         DPF( 1, "DRV_INSTALL" ) ;
         return DRV_OK ;

      case DRV_REMOVE:
         DPF( 1, "DRV_REMOVE" ) ;
         return 0 ;
               

      case DRV_POWER:
         DPF( 1, "DRV_POWER" ) ;

         switch (dwParam1)
         {
            case PWR_SUSPENDREQUEST:

               // If we decided we wanted to try to cause the machine to
               // not suspend, we would return PWR_FAIL. However, the 
               // machine may still be suspended - (the APM BIOS may 
               // override the driver's wishes).

               break ;

            case PWR_SUSPENDRESUME:

               // This notification is just for information.  PWR_OK 
               // should be returned regardless of what the driver does 
               // to resume normal hardware operations.

               break ;

            case PWR_CRITICALRESUME:

               // This notification is just for information.  This 
               // message indicates that the APM BIOS suspended the 
               // system without prior notice.  PWR_OK should be 
               // returned regardless of what the driver does to resume 
               // normal hardware operations.
               break ;
         }
         return PWR_OK ;
   }

   return DefDriverProc( dwDriverID, hDriver, wMessage, 
                         dwParam1, dwParam2 ) ;

} // DriverProc()

Enabling and Disabling Drivers

The DriverProc function calls two functions, Enable and Disable, to enable and disable the driver. These functions are part of the driver. Their implementation is dependent upon the target hardware and the structure of the driver.

Generally, when a driver is enabled, you initialize the hardware, hook interrupts, allocate any memory you need, and set a flag to indicate that the driver is enabled. The exact sequence your device driver follows is determined by the requirements and structure of your device driver. For example, the Bravado device driver uses interrupts only for streaming data. When enabled, it will hook its interrupts only if it was disabled while streaming data.

If your driver has not been enabled by MMSYSTEM, or if it failed the enable process, the driver should return MMSYSERR_NOTENABLED for any messages it receives from client applications. When a driver is disabled, you free any memory you allocated, unhook interrupts, reset the hardware, and set a flag to indicate the driver is not enabled.

It's possible for a driver to receive a DRV_DISABLE message while it is sending or receiving audio data, resulting in a DRV_DISABLE/DRV_ENABLE message pair. For example, this can happen when the user switches to an MS-DOS application while Windows is running in standard mode.

The following table indicates how audio and video device drivers should respond to a DRV_DISABLE/DRV_ENABLE message pair.

Driver type

Responds as if

Waveform output

The driver is paused with a WODM_PAUSE message, and then restarted with a WODM_RESTART message.

Waveform input

The driver is stopped with a WIDM_STOP message, and then restarted with a WIDM_START message.

MIDI output

The driver is disabled, and then re-enabled. If the driver is asynchronous, output stops when the driver is disabled and continues when the driver is re-enabled.

MIDI input

The driver is stopped with a MIDM_STOP message, and then restarted with a MIDM_START message.

Video capture

The driver is stopped with a DVM_STREAM_STOP message, and then restarted with a DVM_STREAM_START message.


Combining Device Drivers

Audio hardware that has waveform, MIDI, and mixer capabilities typically uses one driver to access its hardware functions. While the driver has separate entry-points for each audio function, the driver can use a single DriverProc entry point to handle the system messages.

If the same hardware supports video capture as well as video compression, you can combine both of these functions into one DLL and use a single DriverProc entry point to service them. The shared entry point simplifies the coordination of the different functions.

Additional Entry-Point Functions for Audio Device Drivers

In addition to the installable-driver entry-point function, DriverProc, an audio device driver must have at least one of the following entry-point functions:

Drivers that service more than one type of audio device must have one of these entry-point functions for each type of device serviced.

Note

ACM compression and decompression drivers use only the DriverProc entry point.

These functions process messages sent by MMSYSTEM to the driver as the result of an application call to a low-level audio function. For example, when an application opens a waveform output device with the waveOutOpen function, MMSYSTEM sends the specified waveform-output device driver a WODM_OPEN message. The driver's wodMessage function receives and processes this message. For the exact syntax of each of these entry-point functions, see the sections about writing drivers for specific types of audio devices, later in this main topic.

Reentrancy Considerations with Entry-Point Functions

The auxMessage, widMessage, wodMessage, and midMessage entry-point functions are not called at interrupt time and therefore do not have to be written to be reentrant. However, the entry-point function for MIDI output devices, modMessage, can be called at interrupt time.