(This part is from C++ 1.52 Win31 help file: Overviews -> Installable Drivers)


Installable Drivers

This topic describes installable drivers and the installable-driver interface for the Microsoft Windows operating system. Topics discussed in this topic include: the common entry point for installable drivers, messages used by the common entry point, actions that an installable driver should take in response to these messages, and functions available for the installable driver interface.

About Installable Drivers

An installable driver is a Windows dynamic-link library (DLL) that a Windows application (or another Windows DLL) can open, enable, query, disable, and close. An application can perform these operations by calling the following functions:

Function

Description

CloseDriver

Closes an installable driver.

GetDriverInfo

Retrieves installable-driver data.

GetDriverModuleHandle

Retrieves an installable driver's module handle.

GetNextDriver

Enumerates installed drivers.

OpenDriver

Opens an installable driver.

SendDriverMessage

Sends a message to an installable driver.

When an application calls the OpenDriver, SendDriverMessage, or CloseDriver function, Windows processes the call and issues one or more of the following driver messages:

Message

Description

DRV_CLOSE

Notifies an installable driver that Windows will decrement the use count for the driver and send a DRV_FREE message if the use count reaches zero.

DRV_CONFIGURE

Notifies an installable driver that it should display a custom-configuration dialog box. (This message should only be sent if the driver returns a nonzero value when the DRV_QUERYCONFIGURE message is processed.)

DRV_DISABLE

Notifies an installable driver that the memory that it has allocated is about to be freed.

DRV_ENABLE

Notifies an installable driver that it has been loaded or reloaded or that Windows has been enabled.

DRV_FREE

Notifies an installable driver that it will be discarded.

DRV_INSTALL

Notifies an installable driver that it has been successfully installed.

DRV_LOAD

Notifies an installable driver that it has been successfully loaded.

DRV_OPEN

Notifies an installable driver that it is about to be opened.

DRV_POWER

Notifies an installable driver that the power source for the device is about to be turned off or on.

DRV_QUERYCONFIGURE

Queries an installable driver about whether it supports the DRV_CONFIGURE message and can display a private configuration dialog box.

DRV_REMOVE

Notifies an installable driver that it is about to be removed from the system.

These messages, which are defined in the Windows header file (WINDOWS.H), are processed by the main routine in an installable driver. This routine is called the DriverProc function. Some of the preceding messages should be sent by Windows only when one of the installable driver functions is called by an application.

The circumstances under which these messages are sent are described in the following list:

Message

Description

DRV_CLOSE

Issued by Windows when an application calls the CloseDriver function.

DRV_DISABLE

Issued prior to exiting Windows and returning to MS-DOS or when the driver is freed.

DRV_ENABLE

Issued when returning to Windows from MS-DOS or the first time the installable driver is loaded.

DRV_FREE

Issued by Windows after an application calls the CloseDriver function and the use count is decremented to zero.

DRV_LOAD

Issued by Windows after the first OpenDriver call is made for a particular installable driver.

The remaining messages can be sent by an application to an installable driver by calling the SendDriverMessage function.

 

Creating an Installable Driver

An installable driver is a Windows dynamic-link library (DLL) that supports a special entry point, the DriverProc function. This function processes the driver messages described in the previous section. This function may also process private driver messages. These messages can be assigned values ranging from DRV_RESERVED to DRV_USER (two constants that appear in WINDOWS.H).

The following example shows the basic structure of the DriverProc function:

 

LRESULT CALLBACK* DriverProc (DWORD dwDriverIdentifier, HDRVR hDriver, UINT wMessage, LPARAM lParam1, LPARAM lParam2)

{

DWORD dwRes = 0L;

switch (wMessage)

{

case DRV_LOAD:

/* Sent when the driver is loaded. This is always */

/* the first message received by a driver. */

dwRes = 1L; /* Return 0L to fail. */

break;

case DRV_FREE:

/* Sent when the driver is about to be discarded. */

/* This is the last message a driver receives */

/* before it is freed. */

dwRes = 1L; /* Return value is ignored. */

break;

case DRV_OPEN:

/* Sent when the driver is opened. */

dwRes = 1L; /* Return 0L to fail. */

/* This value is subsequently used */

/* for dwDriverIdentifier. */

break;

case DRV_CLOSE:

/* Sent when the driver is closed. Drivers are */

/* unloaded when the open count reaches zero. */

dwRes = 1L; /* Return 0L to fail. */

break;

case DRV_ENABLE:

/* Sent when the driver is loaded or reloaded and */

/* when Windows is enabled. Hook or rehook */

/* interrupts and initialize hardware. Expect the */

/* driver to be in memory only between the enable */

/* and disable messages. */

dwRes = 1L; /* Return value is ignored. */

break;

case DRV_DISABLE:

/* Sent before the driver is freed or when Windows */

/* is disabled. Unhook interrupts and place */

/* peripherals in an inactive state. */

dwRes = 1L; /* Return value is ignored. */

break;

case DRV_INSTALL:

/* Sent when the driver is installed. */

dwRes = DRV_OK; /* Can also return DRV_CANCEL */

/* and DRV_RESTART. */

break;

case DRV_REMOVE:

/* Sent when the driver is removed. */

dwRes = 1L; /* Return value is ignored. */

break;

case DRV_QUERYCONFIGURE:

/* Sent to determine if the driver can be */

/* configured. */

dwRes = 0L; /* Zero indicates configuration */

/* NOT supported. */

break;

case DRV_CONFIGURE:

/* Sent to display the custom-configuration */

/* dialog box for the driver. */

dwRes = DRV_OK; /* Can also return DRV_CANCEL */

/* and DRV_RESTART. */

break;

default:

/* Process any messages not explicitly trapped. */

return DefDriverProc (dwDriverIdentifier, hDriver,

wMessage, lParam1, lParam2);

}

return dwRes;

}

 

Opening an Installable Driver

An application opens an installable driver by calling the OpenDriver function. When an application calls this function, Windows adds the driver name to an internal list of installed drivers. (When the application calls the CloseDriver function, Windows deletes the corresponding driver name from this list.)

When an application calls the OpenDriver function to open the first instance of a driver, Windows issues the DRV_LOAD, DRV_ENABLE, and DRV_OPEN messages, in that order. (Subsequent calls to OpenDriver cause only DRV_OPEN to be sent.)

When the driver processes the DRV_LOAD message, it reads the configuration settings (if any exist) from the corresponding entry in the SYSTEM.INI file and configures the driver and any associated hardware. In addition to configuring the driver and associated hardware, the driver also allocates required memory.

After processing the DRV_LOAD message, the driver returns a nonzero value if it loads successfully. If it returns zero, Windows immediately unloads the driver (without issuing a DRV_FREE message).

When the driver processes the DRV_ENABLE message, it hooks or chains required interrupts and prepares associated peripherals.

When the driver processes the DRV_OPEN message, it allocates memory required by a single instance of the driver.

 

Closing an Installable Driver

An application closes an installable driver by calling the CloseDriver function. When the application calls this function, Windows deletes the corresponding driver name from an internal list.

When an application calls the CloseDriver function to close the last instance of a driver, Windows issues the DRV_CLOSE, DRV_DISABLE, and DRV_FREE messages, in that order. (When the application is not closing the last instance of the driver, only DRV_CLOSE is sent.)

When the driver processes the DRV_CLOSE message, it frees any resources that were allocated when the driver was opened and returns a nonzero value. If the driver returns a value of zero, closing fails.

When the driver processes the DRV_DISABLE message, it places any associated peripherals in an inactive state and unhooks all interrupts.

When the driver processes the DRV_FREE message, it frees any resources that are still allocated.

 

Configuring an Installable Driver

Many installable drivers support a private configuration dialog box that lets the user configure the driver and associated hardware. To determine whether a driver supports such a dialog box, an application calls the SendDriverMessage function and issues the DRV_QUERYCONFIGURE message. If the driver is configurable, this function returns a nonzero value. If it is not configurable, this function returns zero. If the SendDriverMessage function returns a nonzero value, the application displays the configuration dialog box by calling the SendDriverMessage function a second time and sending the DRV_CONFIGURE message.

If the driver supports a private configuration dialog box, it should display the dialog box and process user input when it receives the DRV_CONFIGURE message. Typically, any configuration data specified by the user is maintained in the [drivers] section of the Windows SYSTEM.INI file.

 

Enumerating Instances of an Installable Driver

An application can retrieve a handle identifying either the first instance of an installable driver or each instance of the driver by calling the GetNextDriver function.

 

Updating the SYSTEM.INI File

Upon installation, the [drivers] section of the SYSTEM.INI file contains an entry for each installable driver. This entry has the following form:

entry=driver_filename optional_information

An application can open a driver by using its filename or its entry. If a fully qualified path is not specified with the filename, the driver file must exist on the standard Windows search path. The driver interface searches for the driver as follows:

If an application specifies a section name, that section of SYSTEM.INI is searched instead of the [drivers] section.

If an application specifies an entry in the search section, the driver with a filename corresponding to the entry is opened.

If the string specified by the application does not match an entry in the search section, the system assumes the string is a driver filename.

The optional information (optional_information) following the driver name (driver_filename) lists information a driver needs after installation. A driver maintains configuration information here if the information is limited or if it needs to be associated with the entry. For example, two prototype drivers could be installed in the system. The first driver could be associated with serial port one, and the second driver could be associated with serial port two. The [drivers] section of the SYSTEM.INI might show this association in the following way:

[drivers]

prototype1=proto.drv com1

prototype2=proto.drv com2

If your driver uses more extensive configuration information, it can create a section in the SYSTEM.INI file reserved for its parameters. For example, the installable driver PROTO.DRV might create the following [proto.drv] section:

[proto.drv]

port=230

int=3

When reserving a section for your driver, use the filename of your driver to identify the section. A driver usually configures and maintains this section of information when it displays the configuration dialog box used for the DRV_CONFIGURE message.

If you want your installable driver loaded when Windows starts, place its filename or an alias from the [drivers] section of the SYSTEM.INI file on the drivers= line of the [boot] section found in the SYSTEM.INI file. Windows loads these drivers at startup and sends DRV_LOAD and DRV_ENABLE messages to them but does not open them. This makes it possible for you to install drivers that remain resident while Windows is enabled.

 

Contents of the OEMSETUP.INF Files

The OEMSETUP.INF file uses the same format as the Windows 3.0 SETUP.INF file with the exception of a new [Installable.Drivers] section. This section identifies the names and characteristics of each driver on the disk. Each driver entry has the following form:

entry=disk:filename, type(s), description, VxD(s), default_params

Note that the elements that compose a driver entry are separated by commas. Comments are delimited by semicolons; all characters following a semicolon are considered part of the comment string.

Following are the elements that compose a driver entry:

Element

Description

entry

Identifies the driver. This string must be unique.

disk

Specifies the disk number for the disk that contains the driver. This entry corresponds to an entry in the [disks] section of SETUP.INF.

filename

Specifies the name of the file that contains the driver.

type(s)

Specifies the driver type.

description

Describes the driver. This string appears in the dialog box displayed by the Drivers Control Panel application.

VxDs

Identifies any VxDs required by the driver. (For a description of the manner in which multiple VxD names are parsed, see the Microsoft Windows Virtual Device Adaptation Guide.)

default_params

Specifies default parameters for the driver. Additional options are appended to the driver entry in the [drivers] section of SYSTEM.INI.

If you create an OEMSETUP.INF file to distribute with your driver, it must include the [disks] and [Installable.Drivers] sections. For example, the following entries could be used in an OEMSETUP.INF file for a prototype installable driver:

[disks]

; Numeric mappings for disk titles

1 = ., "Sample Distribution Disk 1"

[Installable.Drivers]

; The installable drivers section is unique to the drivers application.

; It is parsed with comma-separated fields.

prototype=1:proto.drv,"ampl,freq","Sample scope driver","1:VXDA.386"

The Drivers Control Panel application may need to copy files that support your driver. If any of these files are not VxDs, include a section in the SYSTEM.INI file listing them. Use the entry (that is, prototype) as the name of this new section. For example, if the prototype driver has an additional file called POWERSRC.DLL, include the following section:

[prototype]

; Keyname sections can be created for dependent files. All

; dependent files will be copied directly to the system directory.

1:POWERSRC.DLL

 

Drivers Control Panel Application

The Drivers Control Panel application installs, configures, and removes drivers. When started, the Drivers Control Panel application displays the following dialog box.

The Installed Drivers list box displays the description strings of the installed drivers. The installed drivers are determined by examining the [drivers] and [mci] sections of the SYSTEM.INI file. The description strings are cached in the [drivers.description] section of the CONTROL.INI file to reduce delays in finding and loading them. If a description string does not match an installed driver, the application searches the MMSETUP.INF file and then the header of the driver file to obtain the description string. A scroll bar appears in the list box if there are more drivers than can be displayed.

The following buttons are found in the Control Panel dialog box:

Button

Result when chosen

   

OK

Exits the dialog box and makes any changes permanent.

Cancel

Exits the dialog box. The application ignores any requests to install or remove drivers made during the session. Any configuration changes made during the session are retained because they are done by the driver.

Remove

Removes the information about the selected driver from the SYSTEM.INI file. When removing drivers, the Control Panel application sends the DRV_REMOVE message to the driver if there is only one entry in the SYSTEM.INI file for it.

Setup

Applies only to configurable drivers. When the user selects a driver in the list box, the application opens the driver and sends it the DRV_QUERYCONFIGURE message. If a driver responds that it can be configured--that is, it supports a configuration dialog box to set such parameters as the COM port, the interrupt number, or input and output (I/O) port address--then the application enables the Setup button. If the user chooses the Setup button, the application sends a DRV_CONFIGURE message to the driver.

   

Add Drivers

Installs a new driver.

Default

Redisplays the list of files from the MMSETUP.INF file. Note that the Default button is active when the OEM drivers are displayed.

 

Installing a Driver

When the user selects a driver from the Installed Drivers list box, the Add Driver dialog box closes. The new driver becomes selected in the list box when the user chooses the OK button. The Drivers Control Panel application sends the DRV_INSTALL message to the driver if there is only one entry in the SYSTEM.INI file for it. (A driver receives the DRV_INSTALL message for its initial installation.) The Drivers Control Panel application can install up to four wave devices, four musical instrument digital interface (MIDI) devices, and ten media control interface (MCI) devices of the same type. If the selected driver is not an installable driver, the Driver Control Panel applications displays a "Cannot Install" message. If the user chooses the Cancel button, the dialog box closes with no changes made.

 

Using Drivers with the Drivers Control Panel Application

During installation, the Drivers Control Panel application opens the driver and obtains the description line, originally defined in the module-definition (.DEF) file, from the driver header. The application uses the description line to construct the settings for the [drivers] section. The description line in the .DEF file should have the following form:

DESCRIPTION type(s):text

Following are the parameters in the description line:

Parameter

Meaning

   

type(s)

Type of driver used for the entry in the SYSTEM.INI file. Multiple entries are separated by commas.

text

Text that describes the driver. This will be displayed in the Drivers Control Panel application.

 

For example, the header file for an oscilloscope driver (OSCI.DRV) can use the following description line:

DESCRIPTION 'FREQ,AMPL:Oscilloscope frequency and amplitude drivers.'

Based on this definition, if both drivers are installed (that is, if the Drivers Control Panel application displays a selection for both FREQ and AMPL), the Drivers Control Panel application creates the following settings in the SYSTEM.INI file:

[drivers]

FREQ = osci.drv

AMPL = osci.drv

If you want your driver added to a named section of the SYSTEM.INI file, you can add the section name to the type of driver. For example, the following description line specifies that a voltmeter driver be added to the [RCC] section:

DESCRIPTION 'VOLTMETER[RCC]:RCC voltmeter driver.'

 

Creating a Custom Configuration Application

The Drivers Control Panel application provides a convenient interface for installing drivers. You should use this interface for configuring features that are hardware- or driver-dependent. If your driver configures system features--those features that are hardware- and device-independent--you should create a custom Control Panel application.


(The following are excerpts from Windows DDK 2600 help)


Writing a MIDI Output Driver

Internal MIDI synthesizer devices and MIDI output ports are both MIDI output devices. They use the same entry-point function and receive the same messages. However, instead of sending MIDI data to an output port, an internal synthesizer device converts the MIDI data into music or sound.

To write a MIDI output driver, first create the initialization part of the driver-the part that includes the LibEntry, WEP, and DriverProc entry-point functions. Next, create the modMessage entry-point function and the rest of the support code required to implement the driver. Be sure to process all of the required messages for MIDI output device drivers. For each message you process, use the information provided by the reference at the end of this section to ensure you did everything required to correctly process the message.

Note For information on the content of MIDI data, see the MIDI specification published by the International MIDI Association.

 

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.

 

Reference

This section lists the functions, messages, and structures provided by Windows for use by audio device drivers.

Windows communicates with audio device drivers through messages sent to the driver. The driver processes these messages with its wodMessage, widMessage, modMessage, midMessage, or auxMessage entry-point function. The function used depends on the type of device the driver supports. Drivers can also send messages to client applications with the DriverCallback function.

This section contains an alphabetical list of all messages that can be received and sent by audio device drivers. Each message name contains a prefix, identifying the type of the message. The following list identifies the prefixes used for audio device driver messages:

Prefix

Description

AUXDM

Sent by MMSYSTEM to an auxiliary audio device driver's auxMessage function.

MIDM

Sent by MMSYSTEM to a MIDI input driver's midMessage function.

MIM

Sent by MIDI input drivers to notify client applications.

MODM

Sent by MMSYSTEM to a MIDI output driver's modMessage function.

MOM

Sent by MIDI output drivers to notify client applications.

MXDM

Sent by MMSYSTEM to a mixer driver's mxdMessage function

WIDM

Sent by MMSYSTEM to a waveform input driver's widMessage function.

WIM

Sent by waveform input drivers to notify client applications.

WODM

Sent by MMSYSTEM to a waveform output driver's wodMessage function.

WOM

Sent by waveform output drivers to notify client applications.

A message consists of three parts: a message number and two DWORD parameters. Message numbers are identified by predefined message names. The two DWORD parameters contain message-dependent values.

The stream buffer enhancement to the MIDI device architecture not only affects the MIDI SDK functions, data structures, and messages, but also reaches to the DDK level as driver messages. Many of the new MIDI driver messages documented in this section support the block data transfer of the stream buffer.


Module-Definition File

To build a device-driver DLL, you must have a module-definition (.DEF) file. In the module-definition file, you must export at least two of the following entry-point functions:

Functions are exported by ordinal, as shown in the following example MSSB16.DEF file:

LIBRARY     MSSB16

DESCRIPTION 'wave,aux,mixer:Creative Labs Sound Blaster 16 Driver'

EXETYPE     WINDOWS
PROTMODE

CODE        MOVEABLE DISCARDABLE LOADONCALL
DATA        FIXED SINGLE PRELOAD

SEGMENTS
    _TEXT       FIXED                          PRELOAD
    WEP_TEXT    FIXED                          PRELOAD
    INIT        MOVEABLE    DISCARDABLE        PRELOAD
    COMMON      MOVEABLE    DISCARDABLE        PRELOAD
    MIXER       MOVEABLE    DISCARDABLE        PRELOAD
    WAVE        MOVEABLE    DISCARDABLE        PRELOAD

HEAPSIZE    2048

EXPORTS
            WEP               @1       RESIDENTNAME
            DriverProc        @2       RESIDENTNAME
            wodMessage        @3       RESIDENTNAME
            widMessage        @4       RESIDENTNAME
            auxMessage        @5       RESIDENTNAME
            mxdMessage        @6       RESIDENTNAME
 

The actual ordinal values you assign to each exported function are not significant, though each must be unique within the DLL.

 

Handling Errors

For a description of the error return values from DriverProc, see the discussion of installable drivers in the documentation for the Microsoft Windows Software Development Kit. The following table lists the general error return values from auxMessage, mxdMessage, wodMessage, widMessage, midMessage, and midMessage.

In addition to the general error return values, there are error return values unique to each type of audio device. The following list identifies these device-specific error return values.

Category

Error

Meaning

General

MMSYSERR_NOERROR

No error.

 

MMSYSERR_ERROR

Unspecified error.

 

MMSYSERR_NOTENABLED

Driver is not enabled.

 

MMSYSERR_ALLOCATED

Requested device is already allocated.

 

MMSYSERR_NOMEM

Memory allocation error.

 

MMSYSERR_NOTSUPPORTED

Driver does not support specified operation.

Device-specific

MIDIERR_UNPREPARED

MIDI data block has not been prepared

 

MIDIERR_NOTREADY

MIDI output hardware is busy.

 

MIDIERR_STILLPLAYING

Driver is still playing MIDI data.

 

WAVERR_BADFORMAT

Unsupported waveform data format.

 

WAVERR_STILLPLAYING

Driver is still playing waveform data.

 

WAVERR_UNPREPARED

Specified waveform data block has not been prepared.

 

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, nonzero 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 nonzero 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
         // nonzero 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()
 

 

Multimedia Subsystem Driver Notification

After the VxD has successfully processed the CONFIG_START message, the multimedia device loader notifies the multimedia subsystem (MMSYSTEM.DLL) that a device node has successfully started. MMSYSTEM loads each of the associated ring-3 drivers and sends a device arrival notification. The ring-3 drivers query the supporting VxD for configuration information and complete the initialization necessary to support the device. The multimedia subsystem is designed to accommodate device nodes that have multiple function traits, such as music synthesis, digital sound support, hardware compression and/or decompression, and mixer support. When a device node is started, MMSYSTEM locates the registry entries for device node and loads the drivers for each supported class.

Note The changes and additions to the Window 3.1 multimedia device driver model as described below are specific to Windows 95 Plug and Play device drivers. Multimedia drivers loaded using the Windows 3.1 SYSTEM.INI entries will not experience these changes.

A device node might support four driver classes: wave, midi, aux and mixer. Each class may have multiple drivers. MMSYSTEM loads all drivers for all active classes when the device successfully starts. MMSYSTEM loads each module only once and increments reference counts as necessary (standard LoadLibrary() behavior); therefore, a multimedia ring-3 device driver's DriverProc() receives only one DRV_LOAD message when it is first loaded and only one DRV_FREE message when the last reference to the device is released.

A ring-3 device driver should maintain separate data structures for each device node and if the device driver supports multiple classes it should maintain reference counts for each active class. To assist in this task, Windows 95 introduces new parameters for the xxxx_INIT, xxxx_GETNUMDEVS, xxxx_GETDEVCAPS and xxxx_OPEN messages, and introduces new messages: DRVM_ENABLE, DRVM_DISABLE and DRVM_EXIT.

MMSYSTEM sends the xxxx_INIT, DRVM_ENABLE, DRVM_DISABLE and DRVM_EXIT message to each message procedure of each class of device when the corresponding activity has taken place for the device node.

Below is a description of these messages and the appropriate driver response:

When the driver vendor follows the appropriate handling of these messages it is possible to accomplish any or all of the following:

The code example below is the output wave device message handler from the Windows Sound System ring-3 driver. It demonstrates the appropriate handling of these new and modified messages.

DWORD FAR PASCAL _loadds wodMessage
(
    UINT            uDevId,
    WORD            msg,
    DWORD           dwUser,
    DWORD           dwParam1,
    DWORD           dwParam2
)
{
   NPWAVEALLOC         pClient ;
   PHARDWAREINSTANCE   phwi ;

   // take care of init time messages...

   switch (msg)
   {
      case WODM_INIT:
      {
         DPF( 3, "WODM_INIT" ) ;

         if (gwGlobalStatus)
         {
            DisplayConfigErrors() ;
            return 0L ;
         }
         else
         {
            // dwParam2 == PnP DevNode

            return (AddDevNode( dwParam2 )) ;
         }
      }
      break ;

      case DRVM_ENABLE:
      {
         DPF( 3, "WODM_ENABLE" ) ;

         // dwParam2 == PnP DevNode

         return (EnableDevNode( dwParam2 )) ;

      }
      break ;

      case DRVM_DISABLE:
      {
         DPF( 3, "WODM_DISABLE" ) ;

         // dwParam2 == PnP DevNode

         return (DisableDevNode( dwParam2 )) ;

      }
      break ;

      case DRVM_EXIT:
      {
         DPF( 3, "WODM_EXIT" ) ;

         // dwParam2 == PnP DevNode

         return (RemoveDevNode( dwParam2 )) ;

      }
      break ;

      case WODM_GETNUMDEVS:
      {
         DPF( 3, "WODM_GETNUMDEVS" ) ;

         if (NULL == (phwi = DevNodeToHardwareInstance( dwParam1 )))
            return MAKELONG( 0, MMSYSERR_INVALPARAM ) ;

         if (dwParam1)
         {
            if (phwi -> fEnabled)
               return 1L ;
            else
               return 0L ;
         }
         else
            return 0L ;
      }
      break ;

      case WODM_OPEN:
      {
         DWORD  dn ;

         DPF( 3, "WODM_OPEN" ) ;

         if (uDevId > 1)
            return MMSYSERR_BADDEVICEID ;

         dn = ((LPWAVEOPENDESC) dwParam1) -> dnDevNode ;

         if (NULL ==
               (phwi = DevNodeToHardwareInstance(dn)))
         {
            DPF( 1, "devnode not associated with hardware instance???" ) ;
            return MMSYSERR_BADDEVICEID ;
         }

         if (!phwi -> fEnabled)
            return MMSYSERR_NOTENABLED ;

         return (wodOpen(phwi,
                         dwUser,
                         (LPWAVEOPENDESC) dwParam1,
                         dwParam2 )) ;
      }
      break ;

      case WODM_GETDEVCAPS:
      {
         DPF( 3, "WODM_GETDEVCAPS" ) ;

         if (uDevId > 1)
            return MMSYSERR_BADDEVICEID ;

         if (NULL ==
               (phwi = DevNodeToHardwareInstance( dwParam2 )))
         {
            DPF( 1, "devnode not associated with hardware instance???" ) ;
            return MMSYSERR_BADDEVICEID ;
         }

         if (!phwi -> fEnabled)
            return MMSYSERR_NOTENABLED ;

         wodGetDevCaps( phwi, (MDEVICECAPSEX FAR *) dwParam1 ) ;
         return MMSYSERR_NOERROR ;
      }
      break ;
   }
}
   // Note that the remaining cases for this procedure have
   // been removed from this code example. Please see the 
   // MSSNDSYS.DRV example in the DDK for a complete sample.
 

The procedure of unloading the drivers for a device node is the inverse of the load procedure. That is, the ring-3 drivers are notified and unloaded and then MMDEVLDR issues the CONFIG_STOP message to the virtual device driver.