PDDREF:Character Device Monitors

A character device monitor is an application, or part of an application, that uses standard OS/2 function calls to interact with the physical device driver and its data streams.

Through the use of character device monitors, applications can directly access and intercept data flowing through data streams belonging to some character device drivers. The applications can then filter (remove, insert, or modify) data passing through a character device by registering one or more character device monitors with the physical device driver. The character device monitors require support by the character device driver, as well as by the application.

The following figure shows Data Interception by Device Monitors. Applications that monitor data passing through a character device can perform a variety of functions. They can serve as: The following figure shows Data Redirection Using Device Monitors. OS/2 character device drivers that provide monitor support include the keyboard, mouse, and parallel port device drivers.
 * User-extensions of a character device driver. Data passing through a data stream can be manipulated to modify the state of the device as viewed by another application.
 * Dialog managers. Command sequences or control keys can be activated on a given keystroke. These include pop-up facilities and notepad applications.
 * Language translators. Characters can be translated from one language to another.
 * Redirection mechanisms. Data can be redirected from one device to another by character device monitors, as shown in the following illustration. An application can register two monitors with two separate character devices. Data taken from the data stream belonging to the first device by the first monitor can be placed into the data stream belonging to the second device by the second monitor.

The OS/2 monitor mechanism includes the character device monitor process and the support required by a character device driver that enables device monitoring to be performed. Guidelines are given below for developing a character device monitor and for implementing monitor support in the character device driver.

Monitoring Character Device Data Streams
A physical device driver receives data from a device or receives requests from applications to send data to a device. The physical keyboard and mouse device drivers receive data from the keyboard and mouse devices, respectively. The physical parallel port device driver receives requests from applications to write data to the printer device.

The flow of data between a physical device driver and its device is called a data stream. The physical device driver defines the data streams for its device, and associates the data it receives from a device or application with a particular data stream. Physical device drivers can have more than one data-stream model within, or between, sessions. For example, the physical keyboard device driver supports three different data-stream models for DOS, Presentation Manager, and OS/2 sessions. Of these, only the OS/2-mode data stream supports keystroke monitors.


 * Note:OS/2 1.2, 1.3, 2.0, 2.1, and 3 do not allow an application to register a keyboard or mouse device monitor for a Presentation Manager session.

There are several ways in which a physical device driver can define its data streams. When the physical keyboard or mouse device driver receives data from its device, it associates that data with the current foreground OS/2 session (which can be the Presentation Manager session) and places the data into the data stream defined for that session. These physical device drivers define data streams for each session. When the physical parallel port device driver receives a request from an application to write data to the printer, it places the data into the data stream defined for that printer device. This physical device driver defines data streams for each physical device.

A single data stream can be filtered by one or more character device monitors that are linked together in a monitor chain. That is, the first character device monitor in a monitor chain receives data directly from the physical device driver. This character device monitor filters the data and passes it on to the next character device monitor in the chain. This character device monitor filters the filtered data and passes it on to the next character device monitor in the chain, and so forth. The last character device monitor in the chain passes the filtered data back to the physical device driver.

Just as physical device drivers define their data streams, they also determine how character monitors are chained. The physical keyboard and mouse device drivers, for example, support a monitor chain for each OS/2 session. The physical parallel port device driver supports two monitor chains for each physical printer device. Refer to the OS/2 Input/Output Device Driver Reference for more information about the physical keyboard device driver, the physical mouse device driver and the physical parallel port device driver for a description of how these device drivers support device monitors.

Device Monitor Support Limitations
Data streams for character devices can be monitored by a character-device monitor only when the character device driver provides device monitor support. Applications, including both OS/2-mode and Presentation Manager applications, can monitor keystrokes and mouse clicks for all OS/2 sessions. However, the applications cannot monitor keystrokes and mouse clicks for Presentation Manager or DOS session.

The keystroke and mouse click mechanism for Presentation Manager sessions differs from the mechanism for OS/2 sessions. The physical keyboard and mouse device drivers manage keystrokes and mouse clicks respectively for each OS/2 session. These physical device drivers create monitor chains for each OS/2 session. Applications can register keystroke and mouse device monitors for each OS/2 session.

The Presentation Manager manages both keystrokes and mouse clicks for each of its extended sessions, but does not create monitor chains for each of its extended sessions. Applications cannot register a keystroke or mouse device monitor for a Presentation Manager extended session. Notice that applications cannot intercept keystrokes or mouse clicks associated with the Presentation Manager session through keystroke or mouse monitors.

However, all applications (including OS/2 and Presentation Manager applications) can monitor printer data. The physical parallel port device driver bases the definition of its data streams on the physical device. In general, data streams for character devices that do not base the definition of their data streams on sessions can be monitored by the character device monitor when the character device driver provides device monitor support.

The following figure shows the OS/2 Monitors and Presentation Manager Applications. A Presentation Manager application can filter keyboard and mouse events (messages) through hook procedures. These procedures are registered by an application and are called when certain events occur. More than one procedure can be called when a single event occurs. In this case, procedures are chained together so that each event is passed first to one procedure and then to the next, and so on down the chain. This chain of procedures is called a hook chain.

There are two kinds of hook procedures, system hook and queue hook. System hook procedures are called when an event occurs in the Presentation Manager's system queue. Queue hook procedures are called when an event occurs in the Presentation Manager application's queue. There are several kinds of events that can be hooked. These include messages input to an application and messages sent by an application. For detailed descriptions of hook procedures, refer to the OS/2 Programming Guide, Volume 2, and the OS/2 Presentation Manager Programming Reference.

The OS/2 Monitor Mechanism
The OS/2 monitor mechanism consists of monitor buffers, monitor threads, and signals. This mechanism is supported by the OS/2 Monitor Dispatcher, which consists of:
 * OS/2 monitor dispatcher functions, a set of five dynamic link routines:
 * DosMonOpen
 * DosMonReg
 * DosMonClose
 * DosMonRead
 * DosMonWrite


 * Monitor Dispatcher Device Helper, a set of five OS/2 DevHlp services:
 * MonitorCreate
 * Register
 * DeRegister
 * MonWrite
 * MonFlush

The OS/2 monitor dispatcher functions are part of the MONCALLS dynamic link function library. These routines are loaded on demand at privilege level 2 and can be called from applications loaded at privilege level 2 or 3. Using these routines, applications can intercept and filter data passing through a device. The OS/2 monitor functions provide the interface for monitor applications to interact with the physical device driver (to request registration and termination of monitor activity) and the data stream.

The Monitor Dispatcher Device Helper (DevHlp) services are part of the OS/2 Device Helper, available to all character device drivers. Through these routines, a character device driver provides the support for applications that monitor its data streams. The Monitor Dispatcher Device Helper provides the interface for character device drivers to interact with the monitor dispatcher (for itself and for applications requesting registration and termination of monitors) and applications monitoring its data streams. In addition, the Monitor Dispatcher Device Helper provides the mechanism for passing data from one monitor to another.

Character device monitor applications are generally multithreaded, with child monitor threads that take data from the data stream and return filtered data to the data stream. Monitor threads use signals in two ways: Notice that signaling between monitor threads is managed by the monitor dispatcher and is transparent to the monitor application.
 * When no data is available for the monitor in the data stream, a monitor thread can wait for a signal from the monitor dispatcher when data is available for the monitor. When the data stream cannot accept data from the monitor (for example, there is a blockage downstream in the monitor chain or at the physical device driver), a monitor thread will wait for a signal from the monitor dispatcher when the data stream can accept data from the monitor.
 * When a monitor thread has finished taking data from, or returning data to, the data stream, it signals the monitor dispatcher that it has completed its work.

Character Device Monitor Process
A character device monitor can monitor only one data stream belonging to a character device driver. An application, however, can include one or more character device monitors that monitor one or more data streams belonging to one or more character device drivers. For example, an application can monitor keystrokes and mouse clicks for each OS/2 session. This application will include a character device monitor for each OS/2 session for each of the physical keyboard and mouse device drivers.

To monitor a data stream belonging to a character device driver, an application must first gain access to that data stream. An application gains access to a data stream by calling: After an application has gained access to a data stream, it can remove, insert, modify, or view all characters passing through the data stream. An application intercepts data flowing through a data stream by calling: When an application no longer needs to monitor data streams belonging to a single character device driver, it must relinquish access to all data streams belonging to that physical device driver, in addition to terminating its monitor threads. An application terminates a monitor by calling: Refer to the OS/2 Control Program Programming Reference for more information on the 16-bit Dosxxx functions. The following figure illustrates pseudocode for a simple character device monitor.  A Simple, Single-Threaded Character Device Monitor
 * DosMonOpen to establish a connection to the physical device driver. DosMonOpen returns a device handle for monitors to the application. The application will use this handle to direct subsequent DosMonReg and DosMonClose calls to the physical device driver.
 * DosMonReg to register a monitor with a particular data stream belonging to the physical device driver. Once the monitor is installed in the monitor chain, the monitor dispatcher automatically moves data between monitors (if there is more than one in the chain) and returns filtered data to the physical device driver.
 * DosMonRead to take data from the data stream and place it into a private data area where the application can freely access it for filtering.
 * DosMonWrite to take filtered data from the application's private data area and return it to the data stream.
 * DosMonClose to close the device handle (that is, terminate the connection to the physical device driver for the monitors).
 * DosExit to terminate its monitor threads that are directly accessing the data streams.

Get access to the device's data stream:

CALL DosMonOpen to open the device and get a monitor handle for the device CALL DosSetPrty to set priority of the monitor thread high CALL DosMonReg to register a monitor for the data stream of   the device

WHILE monitoring the data stream:

CALL DosMonRead to take a data record from the data stream Filter the data record CALL DosMonWrite to return the filtered data record to the data stream

END WHILE

After monitoring the data stream:

CALL DosMonClose to terminate the monitor and close the monitor handle to the device CALL DosExit to terminate this application 

Character Device Driver with Monitor Support
For an application to monitor data passing through a character device, the character device driver must provide monitor support. For each data stream that can be monitored by applications, the character device driver must first create a monitor chain. For each monitor chain, the character device driver must define a monitor chain buffer in its first data segment, where the monitor dispatcher will place filtered data that has passed through all monitors registered with the monitor chain. In addition, the character device driver must define a notification routine within its first code segment. This routine is called by the monitor dispatcher when filtered data has been placed into the monitor chain buffer. Note that a character device driver creates a monitor chain by calling the DevHlp_MonitorCreate.

When an application registers a monitor (that is, calls DosMonReg), the character device driver receives the IOCtl request MON_REGISTERMONITOR Register Monitor from the monitor dispatcher on behalf of the application. This registers the monitor with the monitor chain belonging to one of its data streams. A character device driver registers a monitor with the monitor chain belonging to one of its data streams by calling the DevHlp_Register.

When one or more monitors are registered with a monitor chain belonging to one of its data streams, a character device driver can send data to its monitors. A character device driver sends data to monitors registered with one of its monitor chains by calling the DevHlp_MonWrite. Under certain conditions, a character device driver can guarantee that all data sent to its monitors in a monitor chain has been filtered and returned to the data stream. A character device driver flushes all data from all monitors registered with a monitor chain by calling the DevHlp_MonFlush.

When a monitor application stops monitoring data streams belonging to a character device driver, or when a monitor application abnormally terminates (for example, the user presses the Ctrl+C key sequence), the character device driver receives a monitor close request from the file system. Because an application can have one or more monitors registered with one or more monitor chains belonging to the physical device driver, the character device driver must remove all monitors belonging to the application that are registered on any of its monitor chains. A character device driver removes monitors belonging to an application by calling the DevHlp_DeRegister for each monitor chain that belongs to each of its data streams.

Character Device Driver and Monitors
A character device driver and its monitors are interdependent. The implementation of the character device driver determines the ground rules for its monitors, including: Because a monitor directly interacts with a data stream, there is a danger of severely affecting the data stream if a monitor is not well-behaved (that is, if the monitor does not adhere to the rules of the character device driver). The interdependence of a character device driver and its monitors can be directly demonstrated by presenting a description of the sequence of events occurring in the character device driver and its monitors during:
 * The definition of its data streams and monitor chains
 * The format of the data flowing through its monitor chains
 * The rules on consuming, modifying, and returning data
 * Monitor registration and termination
 * Movement of data through a monitor chain

Registering and Terminating a Monitor
During monitor registration and termination, the monitor dispatcher communicates with the character device driver on behalf of the monitor application through the OS/2 file system and IOCtl interface. The application initiates OS/2 monitor functions, and the character device driver receives and responds to the corresponding requests. The following figure illustrates the monitor function calls made by the application and the corresponding responses by the character device driver.

The following figure shows an Application to the Device Driver Interface.  MONITOR APPLICATION        FILE SYSTEM          DEVICE DRIVER      DISPATCHER ┌─────┐              ┌─────┐               ┌─────┐            ┌─────┐ │  (1)│ DosMonOpen    │     │ Open request  │     MonitorCreate│     │ │   ────────────────►│    ────────────────►│    ─────────────►│     │ │  (3)│ DosMonClose   │     │ Close request │     │ DeRegister │     │ │   ────────────────►│    ────────────────►│    ─────────────►│     │ │     │               └─────┘               │     │            │     │ │     │           IOCtl INTERFACE           │     │            │     │ │    │               ┌─────┐               │     │            │     │ │  (2)│ DosMonReg     │     Register request│     │ Register   │     │ │   ────────────────►│    ────────────────►│    ─────────────►│     │ │     │               │     │               │     │            │     │ └─────┘               └─────┘               └─────┘            └─────┘  For example, Character Device Driver Z defines only one data stream for its device. Application X wishes to monitor Character Device Driver Z's data stream:
 * 1) Application X calls DosMonOpen to get a handle to Character Device Driver Z. Through the file system, the monitor dispatcher sends an Open request for monitors to Character Device Driver Z on behalf of Application X. It is recommended to wait until Step 2, when the device driver receives the monitor Register IOCtl.

If Character Device Driver Z has not already created a monitor chain for its data stream, Character Device Driver Z can call the DevHlp_MonitorCreate to create the monitor chain.
 * 1) Application X calls DosMonReg to register Monitor Y with the monitor chain for Character Device Driver Z's data stream. On behalf of Application X, the monitor dispatcher issues a monitor register IOCtl request to Character Device Driver Z.

If Character Device Driver Z has not already defined a monitor chain for its data stream, Character Device Driver Z must now call MonitorCreate to create the monitor chain.

On receiving the monitor register IOCtl request, Character Device Driver Z calls the DevHlp_Register so that the monitor dispatcher can install Application X in the monitor chain.
 * 1) When Application X stops monitoring Character Device Driver Z's data stream, Application X calls DosMonClose.

The monitor dispatcher sends a Close request for monitors through the file system to Character Device Driver Z on behalf of Application X.

On receiving the monitor close request, Character Device Driver Z must call the DevHlp_DeRegister so the monitor dispatcher can remove Application X from the monitor chain.

Data Passing Through a Monitor Chain
A character device can be an input device or an output device. A physical device driver receives data from an input device (for example, the keyboard or mouse) and makes it available to users through its API (Application Programming Interface) buffers. A physical device driver also receives requests from applications to send data to an output device. Using the example above, when Character Device Driver Z has created a monitor chain for its data stream, and Application X has registered Monitor Y with that monitor chain, data flows through the monitor chain as illustrated below:

The following figure shows Data Flow Through a Monitor Chain.  ┌─────────────────────┐                 │   Application X's   │ │   Address Space    │ │                    │                  │      Monitor Y      │ │  ┌─────────────┐   │                  │   │   Private   │   │ │  │  Data Area  │   │ │  └───▲─────│───┘   │                  └───────│─────│───────┘           (3) DosMonRead │     │ DosMonWrite (4) │    │ Privilege Level 2 or 3   │     │ --│-│ Privilege Level 0       │     │ │    │                  ┌───────│─────▼───────┐                  │  Monitor Dispatcher │ │    Device Helper   │ └───────▲─────│───────┘                         │     │             MonWrite (2) │     │ (5) │    │          ┌──────────────────────────────────┐  INT (1) │               │ |   │            │  INT (6) ──────────────►─────────┘ |  └───────────────────────►  WRITE   │    Character    |  Monitor Chain │  READ │ Device Driver Z |     Buffer    │ └──────────────────────────────────┘ 
 * 1) Character Device Driver Z receives data in one of the following ways:
 * 2) *If Character Device Z is an input device, the data received by its physical device driver comes directly from the device.
 * 3) *If Character Device Z is an output device, the data received by its physical device driver comes from an application requesting output to the device.
 * 4) Character Device Driver Z determines where to place the data (that is, into which data stream and monitor chain, if any). In this example, Character Device Driver Z has defined only one data stream for its device. Because a monitor chain has been created for the data stream, Character Device Driver Z places the data into the monitor chain by calling the DevHlp_MonWrite.
 * 5) Monitor Y calls DosMonRead to take data from the data stream and place it into a private data area where it can freely access and process the data.
 * 6) When Monitor Y has processed, or filtered, the data, it returns the filtered data to the data stream by calling DosMonWrite.
 * 7) Since Monitor Y is the only monitor registered with the monitor chain, the monitor dispatcher automatically returns the filtered data from the output buffer of Monitor Y to the monitor chain buffer of Character Device Driver Z, and calls Character Device Driver Z's notification routine.
 * 8) Character Device Driver Z processes the data received in its monitor chain buffer before returning to the monitor dispatcher:
 * 9) * If Character Device Z is an input device, Character Device Driver Z moves the filtered data from its monitor chain buffer into its API buffer, where it can be read from an application.
 * 10) * If Character Device Z is an output device, character Device Driver Z sends the filtered data to the device.

OS/2 Monitor Functions
An application process that monitors data passing through a character device uses the OS/2 monitor functions to: Monitor applications use the OS/2 monitor functions to communicate with a character device driver and to directly intercept data from a data stream belonging to the character device driver. The OS/2 monitor functions are part of the OS/2 monitor dispatcher. They are shipped with OS/2 2.1 (and later) as the MONCALLS dynamic link subroutine library. These routines are loaded at privilege level 2 and are callable from applications running at privilege level 2 or 3. Each OS/2 monitor function uses the OS/2 System Trace Facility, tracing entry, and exit parameters when tracing is enabled for monitors. This facility provides assistance for the monitor application developer during the debug phase of the application's development cycle.
 * Gain access to the data stream (DosMonOpen, DosMonReg)
 * Take data from the data stream (DosMonRead), and return filtered data to the data stream (DosMonWrite)
 * Relinquish access to the data stream (DosMonClose)

OS/2 API standards require that a General Protection (GP) fault occurs when a null selector is passed as an address parameter to an API call. To conform to this standard, the monitor functions generate a GP fault when a null selector is passed as an address parameter. A pop-up message notifies the user that the process is being terminated.

DosMonOpen
An application that monitors data passing through a character device must first obtain a handle to the device by calling DosMonOpen. The application will need this handle to communicate with the character device driver in subsequent monitor functions, DosMonReg (for monitor registration) and DosMonClose (for monitor termination).

The parameters for DosMonOpen are shown in the table below. The address parameters are selector:offset addresses. Refer to the OS/2 I/O Subsystems and Device Support for a detailed description of these parameters. DosMonOpen calls DosOpen to open a handle to a character device for monitors. DosMonOpen returns the device handle returned from DosOpen to the application. The character device driver differentiates a DosOpen request from a DosMonOpen request by the 08h value specified in the Status field of the request packet. On receiving the monitor open request, the character device driver can optionally issue a call to the DevHlp_MonitorCreate to create a monitor chain. A character device driver can create a monitor chain anytime prior to issuing a call to the DevHlp_Register, including at initialization time during device driver installation.


 * Note: An application needs to call DosMonOpen only once per character device. Repeated DosMonOpen calls by an application to open the same device for monitors will return the same handle. An application can register one or more monitors with data streams belonging to the same character device using the same handle. For example, an application can open the keyboard device for monitors (call DosMonOpen). Using the handle returned from this call, the application can register a monitor for each OS/2 session. Each OS/2 session has its own keystroke data stream and associated monitor chain.

DosMonReg
Before calling this function, an application previously must have called DosMonOpen to get a handle to the character device driver. The application needs this handle so a monitor register request can be sent to the character device driver.

The parameters for DosMonReg are shown in the table below. The address parameters are selector:offset addresses. Refer to the OS/2 I/O Subsystems and Device Support for a detailed description of the parameters to this function. The monitor's input and output buffers are allocated by the application from the same data segment. The first WORD of each buffer must contain the length of the private data area plus 20 bytes, length WORD inclusive. To maintain data movement through all monitor buffers in a monitor chain (more than one application can register monitors with the same monitor chain), the minimum size of these private data areas must be the length of the character device driver's monitor chain buffer. The monitor chain buffer is located at the end of a monitor chain, and receives filtered data that has passed through all monitors in the monitor chain. Because it is defined and owned by the character device driver, its length is documented in the descriptions of the individual character device drivers that support monitors. See the descriptions of the physical keyboard, mouse, and parallel port device drivers for this information.

The Index parameter indicates the data stream for the character device that the application is monitoring. The individual character device driver defines this parameter. For example, index indicates the OS/2 session number for the physical keyboard and mouse device drivers. See the descriptions of the physical keyboard, mouse, and parallel port device drivers for details.

The Positional Placement Flag is used to specify the placement (first, last, or default) of a monitor's buffers within the monitor chain. Location of a monitor within a monitor chain is relative to monitors that are already registered with the monitor chain. See Positioning of Monitors in a Monitor Chain for details.

DosMonReg issues a monitor registration Category 0AH MON_REGISTERMONITOR. Register Monitor. On receiving a monitor register request, a physical device driver must call the Monitor Dispatcher Device Helper to: Until there is a successful return from the call to DosMonReg, no character will enter the monitor's input buffer. That is, the monitor will not have access to the device driver's data stream. The application is responsible for synchronizing completion of the call to DosMonReg and the subsequent monitoring of the data stream with device input into the data stream. For an example and solution to this problem, see the Type-Ahead Characters.
 * 1) Create a monitor chain by calling the DevHlp_MonitorCreate routine, if no monitor chain was previously created.
 * 2) Establish the monitor placement within a monitor chain by calling the DevHlp_Register.

DosMonRead
After an application has registered as a monitor for a device, it can take data from the data stream for filtering by calling DosMonRead. DosMonRead has three primary actions: The parameters for DosMonRead are shown in the table below. The address parameters are selector:offset addresses of the following data areas within its address space: In addition, the application can specify whether to block (wait) if no data is available in the data stream For each DosMonRead call, a single data record is taken from the data stream and is placed into the monitor's private data area for filtering. A monitor data record is constructed by the physical device driver. It consists of a flag WORD, that can be followed by actual device data. Monitor data records are variable length (that is, some consist only of a flag WORD, while some consist of a flag WORD followed by actual device data of differing lengths). The maximum length of a monitor data record passing through a monitor chain is the length of the device driver's monitor chain buffer minus 2 bytes.
 * 1) Wait (optionally) for a signal from the monitor dispatcher that data is available to be read.
 * 2) Move that data from the data stream into a private data area where it can be accessed freely by the monitor for filtering.
 * 3) Signal the monitor dispatcher that data has been removed from the data stream.
 * The input buffer address previously registered with the monitor chain for the device
 * A private data area into which the data record read from the data stream is to be placed
 * A Byte Count variable that on entry indicates the size of the private data area, and on exit indicates the size of the data record read from the data stream

The physical device driver uses flags within the flag WORD to indicate the type of data that is part of this data record. The device driver also uses flags to indicate the action it expects its monitors to take when this data record is received. In addition, physical device drivers have specific requirements for the return of data records to the data stream. Because of this, character device monitors must not indiscriminately consume data records from the data stream. See the chapters on the individual character device drivers for descriptions of their monitor records and expected actions.

Flushing a data stream is an important operation. At certain times it is necessary to guarantee that all data is removed from the data stream. At these times, a specially marked record called a flush record is placed into the data stream by the physical device driver and must pass through all monitors in the chain. The flush data record consists of a single flag WORD, with the third bit of the first byte set. It is the only data record created by the monitor dispatcher, and it is placed into a monitor chain by the monitor dispatcher for the physical device driver. See MonFlush for more information. When a flush record is received by a monitor, the monitor must return the record to the data stream by calling DosMonWrite after taking the appropriate action. The action to be taken in response to the flush record varies with the type of device and type of support required. To guarantee that all data has been removed from all buffers in the monitor chain, the monitor dispatcher prevents the character device driver from placing additional data into the data stream until the flush record has passed through all monitors in the monitor chain.


 * Note: If the flush record is not returned to the data stream, the data stream will be severely and permanently affected.

Separate monitor threads from the same application can call DosMonRead to take data from the data stream. To protect the data stream during data movement, the monitor dispatcher guarantees that only one thread at a time from a single process can take data from the data stream. Therefore, the application does not have to synchronize calls to DosMonRead made by its separate threads.

If a monitor thread calls DosMonRead during monitor termination (for example, DosMonClose has been called from another thread belonging to the same application), it will receive an error return indicating that there is no data present in the data stream. If a monitor thread is blocked on a call to DosMonRead when monitor termination begins, it will be awakened and receive the same error return. In both cases, the monitor thread must handle the error return code. The application is responsible for the orderly termination of its individual monitor threads.

DosMonWrite
After an application has registered as a monitor for a device, it can return filtered data to the data stream by calling DosMonWrite. DosMonWrite has three primary actions: The parameters for DosMonWrite are shown in the table below. The address parameters are selector:offset addresses of the following data areas within its address space: In addition, the application must specify a Byte Count variable, indicating the size of the data record to be returned to its output buffer. For each call to DosMonWrite, a single filtered data record is placed into the data stream. See DosMonRead for a description of the monitor data record format, the flushing of monitor buffers, and the restrictions on data record consumption by a monitor.
 * Wait for a signal from the monitor dispatcher that data has been removed from the data stream if there is not enough room for the data.
 * Move filtered data from the monitor's private data area into the data stream.
 * Signal the monitor dispatcher that data has been placed into the data stream.
 * The output buffer address previously registered with the monitor chain for character device.
 * The private data area, which contains the filtered data record to be returned to the data stream.

Separate monitor threads from the same application can call DosMonWrite to return filtered data to the same data stream. To protect the data stream during data movement, the monitor dispatcher guarantees that only one thread at a time from a single process can return data to a data stream. Therefore, the application does not have to synchronize calls to DosMonWrite made by its separate threads.

If a monitor thread calls DosMonWrite during monitor termination (for example, DosMonClose has been called from another thread belonging to the same application), it will receive an error return indicating that it cannot return data to the data stream. If a monitor thread is blocked on a call to DosMonWrite when monitor termination begins, it is awakened and receives the same error return. In both cases, the monitor thread must handle the error return code. The application is responsible for the orderly termination of its individual monitor threads.

DosMonClose
When an application no longer needs to monitor data passing through a character device, it must call DosMonClose to remove all monitors that it has previously registered with the device. The parameters for DosMonClose are shown in the table below. DosMonClose calls on the OS/2 File System to close the application's device handle for monitors by calling DosClose. The file system, in turn, sends a monitor close request to the physical device driver.

The character device driver differentiates a DosClose request from a DosMonClose request by the 08h value specified in the Status field of the request packet. At this time, the physical device driver must call the DevHlp_DeRegister for each of its monitor chains to remove all monitors registered by the application. (The application might have registered monitors with more than one monitor chain for the same device.) On the return from each call to DeRegister, if the monitor chain is empty (that is, there are no more monitors in the monitor chain) and the physical device driver no longer needs to use it, the device driver can (optionally) issue a call to the DevHlp_MonitorCreate with the Delete option to remove the monitor chain.


 * Note: The physical device driver must receive notification that a monitor process is terminating so that the monitor dispatcher can be called to remove the monitors from the monitor chain. The monitor dispatcher has the responsibility to guarantee an orderly removal of the monitor from the monitor chain with minimal data loss, while maintaining the integrity of data movement through other monitors in the monitor chain.
 * The OS/2 File System is already aware that a monitor has opened a handle to the device during a previous call to DosMonOpen. When a monitor process terminates (normally or abnormally), all handles opened by that process are closed. The file system notifies the physical device driver that monitor termination is occurring by sending a monitor close request to the physical device driver.
 * If an application calls DosExit to terminate the process without calling DosMonClose, the file system sends a monitor close request to the physical device driver so that the monitor dispatcher can remove the monitors from all monitor chains.

Guidelines for a Character Device Monitor
The following is a summary of requirements and restrictions for character device monitors:
 * Monitor Buffers
 * Monitor Data Records
 * Positioning of Monitors in a Monitor Chain
 * Monitor Thread Priorities

Monitor Buffers
Applications allocate their own monitor input and output buffers. A monitor input/output buffer pair must be allocated from the same segment. Before registering the monitor with a monitor chain belonging to a character device driver, the application is required to provide the size of its private data area (plus 20 bytes) in the first WORD of each buffer.

If ERROR_MON_BUFFER_TOO_SMALL is returned from the call to DosMonReg, the second WORD will contain the size of the character monitor buffer of the physical device driver.

The registered input and output buffers must be at least two WORDs (4 bytes) in length. These buffers can be discarded after the call to DosMonReg. The monitor dispatcher does not use the registered input and output buffers after monitor registration, but their addresses must be passed as parameters to the DosMonRead and DosMonWrite API functions. The address is used as a handle to determine which monitor is calling the API.

More than one application can monitor the same data stream. Monitors belonging to separate applications can have private buffers of various sizes. To guarantee data movement through a monitor chain, the monitor dispatcher defines a minimum monitor buffer length. This length is defined as the length of the character device driver's monitor chain buffer. The length specified in a monitor's input and output buffers must be at least this length plus 20 bytes. This is the recommended length of the private data area specified on a call to DosMonRead.

Because a monitor chain buffer is defined and owned by a character device driver, its length is documented in the descriptions of the individual device drivers that support monitors. Refer to the OS/2 Input/Output Device Driver Reference for more information about the physical keyboard device driver, the physical mouse device driver, and the physical parallel port device driver for this information.

A character device driver will be called with a deinstall request packet if another device driver with the same name is loaded. Character device drivers are deinstalled by a loadable device driver when specified with a DEVICE= statement in CONFIG.SYS. The new loadable device drivers can implement the monitor buffer with a different size than the base physical device drivers. Character monitors must know the size of the character monitor buffer of the physical device driver in order to allocate their private data areas. The private data areas must be at least as large as the character monitor buffer of the physical device driver.

The character device monitor can issue DosMonReg first with a 4-byte buffer (the first two bytes containing the total length 4, and the second two bytes initialized to zero). On return from the call to DosMonReg, the second WORD of the 4-byte buffer will contain the size of the character monitor buffer of the physical device driver. The advantage of this approach is that the physical device driver can optimize the size of the character monitor buffer, and the character monitor can dynamically determine the buffer size. The character monitor then allocates the buffer size of the device driver, and re-issues the call to DosMonReg, replacing the first WORD of the input and output buffer with the length of the private data area plus 20 bytes.

Monitor Data Records
Only one monitor data record at a time can be taken from a data stream by a call to DosMonRead, or returned to the data stream by a call to DosMonWrite. A monitor data record is constructed by the physical device driver, and consists of a flag WORD, which can be followed by actual device data. Monitor data records are variable length. Some consist solely of a flag WORD, while some consist of a flag WORD followed by actual device data of differing lengths. To be consistent with the minimum buffer size defined for a monitor chain, the maximum length of a monitor data record passing through a monitor chain is the length of the device driver's monitor chain buffer minus 2 bytes. An application cannot return to the data stream a data record larger than this.

The physical device driver uses flags within the flag WORD to indicate the type of data that is part of this data record, and the action it expects its monitor to take on receiving this data record. Physical device drivers can have specific requirements for the return of data records to the data stream. Therefore, monitors should not indiscriminately consume data records from the data stream.

Positioning of Monitors in a Monitor Chain
Monitors are registered on a monitor chain with a positional preference parameter: Monitor buffers are placed in a monitor chain in a position relative to monitors already registered with the monitor chain. The first monitor in a chain registered as FIRST will be at the head of the monitor chain. The next monitor registered as FIRST will follow the first monitor registered as FIRST, and so forth.
 * 0 = DEFAULT
 * 1 = FIRST
 * 2 = LAST
 * 3 = DEFAULT
 * 4 = FIRST
 * 5 = LAST

This figure shows the Monitors Registered as FIRST.  Next FIRST monitor ┌─────────┐    ┌─────────┐     ┌─────────┐placed here┌─────────┐ │ MONITOR ├─────┤ MONITOR ├─────┤ MONITOR ├──────▼────┤ MONITOR ├── │ FIRST  │     │  FIRST  │     │  FIRST  │           │   LAST  │ └─────────┘    └─────────┘     └─────────┘           └─────────┘ First monitor   Second monitor  Last monitor        Last monitor registered as  registered as   registered as       registered as FIRST           FIRST           FIRST               LAST or DEFAULT 

Similarly, the first monitor registered as LAST will be at the very end of the monitor chain. The next monitor registered as LAST will precede the first monitor registered as LAST, and so forth.

This figure shows the Monitors Registered as LAST.  Next LAST monitor ┌─────────┐placed here┌─────────┐    ┌─────────┐     ┌─────────┐ ──┤ MONITOR ├─────▼─────┤ MONITOR ├─────┤ MONITOR ├─────┤ MONITOR │ │ FIRST  │           │   LAST  │     │   LAST  │     │   LAST  │ └─────────┘          └─────────┘     └─────────┘     └─────────┘  Last monitor          Last monitor    Second monitor  First monitor registered as        registered as   registered as   registered as  FIRST or DEFAULT      LAST            LAST            LAST  The first monitor registered as DEFAULT will precede the last monitor registered as LAST. The next monitor registered as DEFAULT will precede the first monitor registered as DEFAULT, and so forth.

This figure shows the Monitors Registered in a DEFAULT Position.  Next DEFAULT monitor placed here ┌─────────┐ │  ┌─────────┐   ┌─────────┐    ┌─────────┐   ┌─────────┐ ──┤ MONITOR ├──▼──┤ MONITOR ├───┤ MONITOR ├────┤ MONITOR ├───┤ MONITOR ├── │ FIRST  │     │ DEFAULT │   │ DEFAULT │    │ DEFAULT │   │   LAST  │ └─────────┘    └─────────┘   └─────────┘    └─────────┘   └─────────┘  Last monitor    Last monitor  Second monitor First monitor Last monitor registered as  registered as registered as  registered as registered as  FIRST           DEFAULT       DEFAULT        DEFAULT       LAST 
 * Note: It is possible for the last monitor registered as LAST or the last monitor registered as DEFAULT to be the first monitor in the chain.

Monitor Thread Priorities
To guarantee steady and reasonably fast data throughput in a monitor chain in this multithreaded, multitasking environment, the priority of monitor threads must be set high. Because character device monitors are part of a data stream, they must process data records rapidly so that they do not delay I/O. A monitor application must be written so the threads that actually read and write the monitor data run at a high priority. They must never perform operations such as I/O or semaphore waits that might cause excessive delay. The monitor application can have other threads running at normal priorities to handle such functions.

Threads responsible for moving keystroke data through a monitor chain must pay special attention to the thread priority. Keystroke monitor threads must execute within the time-critical priority class. More specifically, these threads must execute at a priority level greater than or equal to the lowest level in the time-critical priority class. The preferred level is Level 0. This applies to any threads that read (DosMonRead), process, or write (DosMonWrite) keystroke monitor data. In addition, the thread that makes the call to DosMonReg must also be executing in the time-critical priority class.

A monitor application can call DosCreateThread to create separate threads to intercept data from, and return filtered data to, a monitor chain. The application must also set the priority of these threads by calling DosSetPrty. See the OS/2 Control Program Programming Reference for descriptions of the DosCreateThread and DosSetPrty functions for more information concerning creating threads and priority classes.

Special Considerations for Character Device Monitors
The following section summarizes conditions to be considered when writing a character device monitor.

Performance
Several factors can affect the performance of a character device monitor (that is, its ability to intercept, filter, and return data to a data stream quickly and efficiently). These include:
 * Separate threads
 * Task synchronization
 * I/O requests
 * Data consumption
 * Expected responses
 * Error handling

Separate Threads
Because character device monitors are part of a data stream, they must process data records rapidly so that they do not delay I/O. A monitor application must be written so the threads that actually read and write the monitor data run at a high priority. They must never perform operations such as I/O or semaphore waits that might cause excessive delay. The monitor application can have other threads running at normal priorities to handle such functions.

Task Synchronization
Semaphores can be used to synchronize and serialize the separate tasks of taking data from the data stream, filtering the data, and returning the filtered data to the data stream. For example, a monitor thread that has completed a call to DosMonRead can signal another monitor thread to filter the data. This second thread, having filtered the data, can signal a third monitor thread that the filtered data can be returned to the data stream by calling DosMonWrite. The third thread, having called DosMonWrite, can signal the first thread that it can take more data from the data stream.

Note: To avoid a severe and permanent impact to the data stream, the application must take special care that its monitor threads do not wait on semaphores cleared by outside events (for example, waiting for I/O).

I/O Requests
Separate, nonmonitor threads within a monitor application can make I/O requests of other devices that will not delay movement of data through the monitor. However, a monitor thread intercepting, filtering, and returning data to the data stream should not make I/O requests of the device whose data stream it is monitoring. Such a request will result in a deadlock; data movement through the data stream will be permanently suspended. For example, a thread belonging to a keystroke monitor application calls DosMonRead, filters the data, and calls DosMonWrite. Before calling DosMonWrite, however, the thread makes a KbdCharIn keyboard subsystem call to take data from the keyboard API buffer. Because no data has reached the physical keyboard device driver's monitor chain buffer and, therefore, the keyboard API buffer, the monitor thread is blocked. The monitor thread cannot return the filtered data to the data stream and, therefore, it cannot return the filtered data to the physical device driver's monitor chain buffer.

Data Consumption
To prevent other deadlocks, a character device monitor must follow the rules on data record consumption set by the character device driver and monitor dispatcher. The monitor dispatcher requires flush records to be returned to the data stream. Physical device drivers can have specific requirements for the return of data records to the data stream. For example, a character device driver can place a data record into a monitor chain, and then block until that data record is returned from the monitors into its monitor chain buffer. If a monitor removes that data from the data stream by calling DosMonRead, and does not return that data to the data stream by calling DosMonWrite, it will never reach the device driver's monitor chain buffer. The physical device driver will never unblock its blocked condition. Because of this, monitors should not indiscriminately consume data records from the data stream.

Expected Responses
The physical device driver uses flags with the flag WORD of a data record to indicate the type of data that is part of this data record, and the action the device driver expects its monitor to take when it receives this data record. The monitor can also be expected to indicate a response to the action requested by the physical device driver by changing the flags before returning the data record to the data stream. In this way, flags in a monitor data record are used in a dialog between the physical device driver and its monitors. Failure on the part of a monitor to respond as expected to a request from the physical device driver can result in a deadlock.

Error Handling
The developer of a character device monitor must not disregard error returns from monitor functions and assume that good data is automatically received. A character device monitor must handle errors returned from all monitor function calls. Failure to do so can severely and permanently affect the data stream. For example, a monitor thread that ignores an error returned on a call to DosMonRead might attempt to call DosMonWrite with whatever data that is currently residing in the monitor's private data area. This data might be meaningful (that is, it might be the good data returned from the last call to DosMonRead). This data might not be meaningful but might be the result of some other internal processing by the application. In either case, data unexpected by the physical device driver might be returned to the data stream.

Monitor Termination
An application is responsible for terminating its own monitor threads. The monitor dispatcher is responsible for removing the monitors from the monitor chains belonging to a character device driver. The removal is initiated by the character device driver when it receives a monitor close request. This occurs when: When the character device driver receives a monitor close request, it calls the DevHlp_DeRegister routine for each of its monitor chains. Before returning to the physical device driver, the monitor dispatcher protects the integrity and order of data in the data stream, and guarantees minimal data loss, by: The successful removal of monitors from a monitor chain is dependent on the ability of the physical device driver, or other monitors downstream in the monitor chain, to receive and process data from the data stream. If the monitor being removed from the monitor chain is not the last in the monitor chain, the next monitor in the chain must not be blocked. Similarly, the physical device driver must not be blocked on a call to its notification routine; it must be able to process data it receives in its monitor chain buffer.
 * DosMonClose is called by the application
 * DosExit (terminate all threads in a process) is called by the application
 * The process is abnormally terminated by the user (for example, the Ctrl+C key sequence is pressed)
 * Locking out the application from subsequent calls to DosMonRead and DosMonWrite. The application is prevented from intercepting data from, and returning data to, the data stream.
 * Draining all data in an orderly manner from the data stream.

During monitor termination, the monitor dispatcher prevents the application from touching the data stream by returning errors on calls to DosMonRead or DosMonWrite. In addition, errors are returned to monitor threads that are blocked on a call to DosMonRead or DosMonWrite. The application can then clean up the appropriate monitor threads. In both cases, the application is responsible for checking and handling the error returns.

During monitor termination, there is always a risk of data loss. If the physical device driver or monitor dispatcher is blocked while waiting for critical data that is lost, the system can be permanently affected. To reduce this risk, a monitor application must be well-behaved. See Well-Behaved Monitor Applications. Separate threads monitoring the data stream must be terminated before DosMonClose is called. This guarantees that all data taken from the data stream is returned to the data stream by the application. The application must stop calling DosMonRead and DosMonWrite before it calls DosMonClose. This can mean synchronizing the calls with semaphores.

The closing of monitor threads can also be synchronized by using signal handler or exitlist routines. Signal handler routines are called when the system sends a signal to the process to indicate that certain events are occurring, (for example, the Ctrl+C key sequence has been pressed). Exitlist routines are a list of routines that are called when an application is terminating. In either case, these routines can be used to signal an application's separate monitor threads to prepare for termination. See the descriptions of the DosSetSigHandler, DosHoldSignal, and DosExitList functions in the OS/2 Control Program Programming Reference for details.

Well-Behaved Monitor Applications
A monitor application should be well-behaved so that data loss is minimal and system performance is not affected. The following characteristics are typical of well-behaved monitor applications:

A well-behaved, multi-threaded character device monitor is represented by the pseudocode in the following three figures:  Application's Data
 * Separate threads intercept data from the data stream (call DosMonRead), and return filtered data to the data stream (call DosMonWrite).
 * Device monitor rules are followed, as defined by the monitor dispatcher and the individual character device driver whose data stream is being monitored. This includes definition of data streams, format of data records passing through the monitor chain, flags and expected actions, and consumption of data records.
 * Error handling procedures exist for each function call.
 * Semaphores and shared memory are used carefully so that blockages do not occur because of the monitor application. If not used carefully, the entire data stream can be blocked.
 * Data monitoring is terminated before DosMonClose is called (that is, all DosMonRead and DosMonWrite threads issue no more calls before DosMonClose).
 * Signal handler or exitlist routines are included to coordinate the closing of monitor threads in an orderly manner.

Define device-specific data:

Device name string (8 ASCII characters, with NULL at end) Index (indicating data stream to be monitored) Length of monitor chain buffer (minimum buffer size for all   monitor buffers in monitor chain) Monitor input buffer (4 bytes) Monitor output buffer (4 bytes)

Define monitor data areas (the size of each of these areas is based on the size of the device driver's monitor chain  buffer for the data stream monitored):

Private data area

Define additional monitor data:

Device handle (returned from DosMonOpen call) Byte count variable (size of data record received by   DosMonRead and returned by DosMonWrite) Wait flag (wait for DosMonRead?)

Define a RAM semaphore to synchronize monitor threads:

Semaphore A

Define a variable to track error returns from DosMonRead and DosMonWrite:

SetError   An Application's Main Routine:

CALL DosMonOpen to open the device and get a monitor handle for the device. CALL DosSetPrty to set the priority of the monitor thread high. CALL DosMonReg to register a monitor for the data stream of   the device. CALL DosSemSet to set RAM Semaphore A, which is used to   synchronize termination of the application. CALL DosCreateThread to create a separate thread that executes the application's monitor routine. CALL DosSemWait to wait for RAM Semaphore A to be cleared by   the application's child thread executing the monitor routine. CALL DosMonClose to terminate the monitor and close the device handle. CALL DosExit to terminate this application.   An Application's Monitor Routine:

WHILE SetError = 0, the application's child thread will execute as long as there are no errors returned from calls to DosMonRead or DosMonWrite

CALL DosMonRead to take a data record from the data stream

SetError = error returned from DosMonRead

IF SetError = 0, THEN no error was returned from DosMonRead; continue.

Filter the data. CALL DosMonWrite to return the filtered data to the data stream SetError = error return from DosMonWrite

ENDIF

END WHILE

CALL DosSemClear to clear RAM Semaphore A to signal parent thread that monitoring of data stream has been completed CALL DosExit to terminate only this thread </PRE>

Monitor Problems and Solutions
The table shown below describes symptoms that can be experienced in a monitor environment and suggests the problems that cause them. The following section describes special monitor problems and suggests solutions.

Type-Ahead Characters
An application can require the monitoring of all characters passing through a character device. In addition, the application can require that type-ahead characters are intercepted. Type-ahead characters are those keystrokes entered by a user after a program is started but before prompts for information are given. Monitor applications must do some additional work to intercept these keystrokes.

There is a time window between the time when an application is started and the time when the registration of the application's monitor is completed. During this time window, the application does not have access to the data stream. Because the monitor is not yet registered, it cannot intercept keystrokes. The physical device driver processes type-ahead keystrokes, placing them into its API buffer.

Note: If other applications have monitors registered with the monitor chain associated with the same data stream, type-ahead keystrokes will be filtered by these applications and returned to the device driver for processing. Registration of other monitor applications with the same monitor chain is irrelevant.

To monitor type-ahead characters, an application must:  The Application
 * Register its monitor
 * Read and save type-ahead characters from the API buffer after monitor registration is completed and before a call to DosMonRead is made
 * Play back the saved type-ahead characters and return them to the data stream by calling DosMonWrite
 * Monitor the data stream as usual, intercepting data from the data stream by calling DosMonRead and returning filtered data to the data stream by calling DosMonWrite
 * Pseudocode for monitoring type-ahead characters is illustrated in the following figure:

Description: A single-threaded application that requires monitoring of all characters for a device, including type-ahead characters.

CALL DosMonOpen to open the device and get a monitor handle for the device CALL DosSetPrty to set the priority of the monitor thread high CALL DosMonReg to register a monitor for the data stream of the device

Before data is intercepted, get and save any type-ahead data that the physical device driver has processed before DosMonReg returned.

WHILE the physical device driver's API buffer is not empty

Get a character from the API buffer, using the appropriate device subsystem call Save the characters in a temporary buffer

END WHILE

Play back the type-ahead characters

WHILE the temporary buffer is not empty

Get a character from the temporary buffer Build a monitor record, according to the specifications of       the physical device driver Filter the data CALL DosMonWrite to return it to the data stream and, therefore, to the device driver. The physical device driver processes the data by placing it into its API buffer so that it can be received and processed by an application.

END WHILE

Monitor the data stream as usual.

WHILE monitoring the data stream

CALL DosMonRead to take a data record from the data stream Filter the data record CALL DosMonWrite to return the filtered data record to the data stream

END WHILE

CALL DosMonClose to remove the monitor buffers from the monitor chain and close the device handle Call DosExit to terminate this application. </PRE>

Redirecting Data to Another Device
Character device monitors can be used to redirect data from one character device to another, as long the corresponding physical device drivers provide monitor support. This is done by:
 * Opening both devices
 * Registering a monitor with the first device
 * Registering a monitor with the second device
 * Intercepting data from the data stream belonging to the first device by calling DosMonRead
 * Using the data taken from the data stream belonging to the first device and creating a monitor record in the format defined by the second physical device driver
 * Placing the new data record into the data stream belonging to the second device by calling DosMonWrite

The following pseudocode example shows how keystroke data can be redirected to the printer:  Redirecting Keystroke Data

CALL DosMonOpen to open keyboard device and obtain a monitor handle for the keyboard CALL DosMonOpen to open printer device and obtain a monitor handle for the printer CALL DosSetPrty to set priority of thread high CALL DosMonReg to register a keyboard monitor for the data stream associated with the current session CALL DosMonReg to register a printer monitor for the printer device's "data" monitor chain

WHILE continuing to redirect data

CALL DosMonRead to take a data record from the keyboard data stream CALL DosMonWrite to return the keyboard monitor data record to the keyboard device driver. Some data (for example, FLUSH records) must be returned to the keyboard data stream. Translate the keystroke monitor data record into a        printer monitor data record. CALL DosMonWrite to place the printer monitor data record into printer data stream

END WHILE

CALL DosMonClose to terminate the printer monitor and close the printer monitor handle CALL DosMonClose to terminate the keyboard monitor and close the keyboard monitor handle CALL DosExit to terminate this application </PRE> Note: Physical device drivers and the monitor dispatcher expect the return of certain monitor data records from its monitors. This requires that certain data not only be redirected to another data stream, but also be returned to its original data stream.

The advantage of this technique of redirecting data from one device to another is that it can be done without modifying or rewriting a character device driver. The disadvantage of using this technique is reduced performance. When performance is critical, data must be redirected between physical device drivers by using the inter-device-driver communication mechanism provided by OS/2 2.1 (and later).

Providing Monitor Support in a Character Device Driver
Character device drivers provide support for character device monitors by using the Monitor Dispatcher Device Helper services to: Character device drivers use the Monitor Dispatcher Device Helper services to manage the monitor chains associated with their data streams, and to move data between various monitors in their monitor chains. The Monitor Dispatcher Device Helper services are part of the OS/2 monitor dispatcher. The AX register is used to return errors. The Carry flag is clear if no errors are returned and is set if an error is returned. See Device Helper (DevHlp) Services for descriptions of the individual Device Helper services.
 * Create monitor chains for their data streams (MonitorCreate)
 * Register monitors with monitor chains (Register)
 * Send data to their monitors (MonWrite)
 * Flush all data from their monitors (MonFlush)
 * Remove monitors from monitor chains (DeRegister)

Each OS/2 Monitor Dispatcher Device Helper service uses the OS/2 System Trace Facility. Entry and exit parameters are traced when tracing is enabled for monitors. This facility provides assistance for the character device driver developer during the debugging phase of the development of a physical device driver. See the OS/2 Control Program Programming Reference for a description of the System Trace Facility.

MonitorCreate
As previously stated in the introduction to this chapter, individual physical device drivers determine how their data streams are defined. For example, the physical keyboard and mouse device drivers define data streams for each OS/2 session. The physical parallel port device drivers define data streams for each printer device. Each data stream can be monitored by a chain of one or more monitors. However, before monitor registration can occur, the physical device driver must create a monitor chain for its data stream by calling the DevHlp_MonitorCreate.

MonitorCreate can be called at task time or at INIT time. The physical device driver can call MonitorCreate anytime prior to registering a monitor when: On calling MonitorCreate, the physical device driver places the parameters to the call in the registers as illustrated in the table below. The parameters include the selector:offset addresses of the physical device driver's:
 * The physical device driver is installed. The physical device driver developer might prefer to create all monitor chains for each data stream during initialization of the physical device driver. For example, a character device driver can create monitor chains for each OS/2 session during initialization. Because each monitor chain requires additional system resources, creating them at install time can be a resource-expensive choice when monitors might not likely be registered on the monitor chains. In most cases, monitor chains should not be created at install time.
 * The physical device driver receives a monitor open request.
 * The physical device driver receives a monitor register request (when no monitor chain has been created, and before the DevHlp_Register is called to register the monitor). The physical device driver developer might prefer to create all monitor chains as they are needed. For example, a data stream can be defined when an OS/2 session is started. The monitor chains associated with that data stream can be created only when an application calls DosMonReg to register a monitor.
 * Monitor Chain Buffer.
 * This buffer is the last buffer in a monitor chain and must reside within the physical device driver's first data segment (that is, the device driver's header segment). The monitor dispatcher places filtered data (that is, data that has passed through all monitors in the monitor chain) into this buffer. The physical device driver processes this data by placing it into its API buffer, or by sending it to the device.
 * Notice that on calling MonitorCreate, the physical device driver must specify the length of the device driver's monitor chain buffer within the first WORD of the buffer, length WORD inclusive. Data records passing through a monitor chain will not exceed the length of the device driver's monitor chain buffer minus 2 bytes. The monitor dispatcher places filtered data into this buffer, starting at the second WORD of the buffer. The monitor dispatcher places the length of the data (in bytes) in the first WORD of the buffer, overwriting the original length WORD for the buffer.


 * Notification Routine.
 * This routine is called by the monitor dispatcher when filtered data is placed into the device driver's monitor chain buffer. This routine must reside in the device driver's first code segment.

In addition, the physical device driver specifies a monitor chain handle parameter. This parameter is used to indicate creation or deletion of a monitor chain. If the handle is zero when MonitorCreate is called, a new monitor chain is created and the monitor chain handle is returned to the physical device driver. If the handle is nonzero when MonitorCreate is called, the monitor chain with that handle is deleted. The monitor chain handle is known only to the physical device driver and to the monitor dispatcher. The physical device driver uses this handle to identify the monitor chain to the monitor dispatcher on subsequent calls to the Monitor Dispatcher Device Helper services. For example, when the physical device driver registers a monitor for an application with its monitor chain, it uses the monitor chain handle returned from a previous call to MonitorCreate to indicate on which monitor chain the monitor must be registered.

The physical device driver maintains the list of monitor chain handles for its monitor chains, as well as the number of monitors registered on each monitor chain. When there are no monitors registered with a monitor chain, the monitor chain is empty. Notice that monitor chains are initially created empty. An empty monitor chain can be deleted by the physical device driver by calling MonitorCreate with the handle parameter set to the handle of the empty monitor chain. Physical device drivers that create their monitor chains as they are needed generally delete empty monitor chains when they are no longer needed.

Register
When an application calls DosMonReg to register a monitor with a character device driver, the device driver receives an IOCtl (MON_REGISTERMONITOR) monitor register request. Based on the INDEX parameter to DosMonReg, the device driver determines: If no monitor chain has been defined or created for the data stream indicated, the physical device driver must first call MonitorCreate to create one. The physical device driver then calls Register to instruct the Monitor Dispatcher to add the monitors to the monitor chain.
 * To which data stream the application is requesting access
 * On which monitor chain the monitor must be registered

The Register Device Helper routine can be called at task time. On calling Register, the physical device driver places the parameters to the call in the registers as illustrated in the table below. The parameters include:
 * The selector:offset address of the monitor's input and output buffers
 * A flag indicating positional placement in the monitor chain (see Positioning of Monitors in a Monitor Chain)
 * The Process ID (PID) of the application requesting monitor registration
 * The handle of the monitor chain on which it is being registered

MonWrite
A character device driver places data into a monitor chain associated with one of its data streams by calling the DevHlp_MonWrite. If monitors are registered with the monitor chain (that is, the monitor chain is not empty), data placed into the monitor chain is automatically moved by the monitor dispatcher to the monitors in the chain. If monitors are not registered with the monitor chain (that is, the monitor chain is empty), data placed into the monitor chain is automatically moved by the monitor dispatcher into the device driver's monitor chain buffer. Notice that the physical device driver's notification routine is called when this occurs.

MonWrite can be called at task time or at interrupt time. On calling MonWrite, the physical device driver places the parameters to the call in the registers as illustrated in the table below. The parameters include:
 * The selector:offset address of the data record to be placed into the monitor chain. This address must be located within the device driver's first data segment (that is, the device driver's header segment).
 * The size of the data record (number of bytes).
 * The handle of the monitor chain associated with the data stream.

In addition, the physical device driver can specify whether the monitor dispatcher must block or wait until it can place data into the monitor chain.  Registers Parameter Description DS:SI    Address of Data Record to Send to Monitors CX       Size of Data Record (bytes) AX       Device Driver's Handle for Monitor Chain DH       Waitflag DI:BX    Timeout Value, if DH=2 </PRE> A physical device driver can call MonWrite at interrupt time. If the physical device driver specifies the option to block (wait until data is successfully placed into the monitor chain) at interrupt time, the device driver will receive an error from the monitor dispatcher if it is unable to place the data into the monitor chain. The physical device driver should re-issue the call so that no data is lost.

To prevent interruption while moving data during this call, the monitor dispatcher disables interrupts. This can be critical to the performance of a physical device driver that calls MonWrite to write data into an empty monitor chain. Under these circumstances, the monitor dispatcher: When the physical device driver receives data in its monitor chain buffer, it must quickly process that data by placing it into its API buffer or by sending it to its device. The time between the call to the notification routine and the return to the monitor dispatcher must be minimal so that interrupts are not disabled too long.
 * Disables interrupts
 * Places the data into the device driver's monitor chain buffer
 * Calls the device driver's notification routine
 * Enables interrupts when the physical device driver returns from its notification routine

MonFlush
A character device driver sometimes requires that all data placed into a monitor chain has passed through all monitors in the chain and has been returned to the physical device driver. A character device driver can call on the monitor dispatcher to flush all buffers in a monitor chain (that is, remove all data from all monitor buffers by calling the DevHlp_MonFlush. This service can be called at task time). On calling MonFlush, the physical device driver places the parameters to the call in the register as illustrated in the table below. The physical device driver specifies the handle of the monitor chain to be flushed.  Register      Parameter Description AX            Device Driver's Handle for Monitor Chain </PRE> The monitor dispatcher places a flush record (a single WORD data record with the third bit of the first byte set) into the monitor chain. To guarantee that all data has been removed from all buffers in the monitor chain, the monitor dispatcher prevents the physical device driver from placing additional data into the data stream until the flush record has passed through all monitors in the monitor chain.

Note: If the flush record is not returned to the data stream, the data stream will be severely and permanently affected. Because placement of data into the monitor chain is suspended while a flush record passes through a monitor chain, monitors should not consume flush records. Flush records obtained from the data stream from a call to DosMonRead must be returned to the data stream on a call to DosMonWrite.

The action to be taken by a monitor application in response to the flush record varies with the type of device and type of support required.

DeRegister
When a monitor application terminates normally (by calling DosMonClose and DosMonExit) or abnormally (for example, Ctrl+C has been pressed or the monitor buffers have been corrupted), the file system sends a monitor close request to the character device driver. When the physical device driver receives this request, it must call on the monitor dispatcher to remove all monitors registered by the terminating process from all monitor chains belonging to the physical device driver. It calls the DevHlp_DeRegister routine for each nonempty chain (that is, for each monitor chain for which there are monitors registered).

DeRegister is called at task time. On calling DeRegister, the physical device driver places the parameters to the call in the registers as illustrated in the table below. For each call to DeRegister, the physical device driver provides the monitor dispatcher with the PID (Process ID) of the terminating application and the handle of a nonempty monitor chain.  Registers     Parameter Description AX            Device Driver's Handle for Monitor Chain BX            PID of Monitor Process </PRE> For each call to DeRegister, the monitor dispatcher removes all monitors registered by the terminating process from the specified monitor chain, and returns to the physical device driver the number of monitors still registered with the chain. During the call to DeRegister, the monitor dispatcher reduces the risk for data loss by: The physical device driver or another monitor downstream in the monitor chain must not be blocked, so that all data can be drained from the monitor. The physical device driver must not issue the call to DeRegister in either of the following situations: Depending on timing and the well-behaved nature of the monitor, data records might be lost during monitor termination. A monitor can inadvertently consume data records if it does not stop taking data to the data stream before it terminates.
 * Suspending subsequent calls to MonWrite and MonFlush to place data into the monitor chain until the monitors belonging to the terminating application have been removed from the monitor chain.
 * Marking the monitor as closed to subsequent data movement through DosMonReads and DosMonWrites.
 * Draining all data from the monitor in an orderly manner.
 * It has previously blocked on a monitor chain notification routine call because it cannot process the data received into its monitor chain buffer.
 * The return of a critical data record is pending (for example, the physical device driver is waiting to receive a data record from the monitor chain before processing additional request packets).

Guidelines for a Character Device Driver
For an application to monitor data passing through a character device, the character device driver must provide monitor support. A summary of buffer and code requirements for character device drivers follows.

Buffer Requirements
The character device driver must include a set of buffers in its data segments. It must include in its device header segment or in its first data segment (the device driver can have multiple segments), a buffer in which to build the data record. It is then placed into a monitor chain and sent to its monitors on a call to MonWrite. For each data stream that can be monitored, the character device driver must also include in its first data segment a monitor chain buffer that receives filtered data from the monitor chain.

The first WORD of the monitor chain buffers must contain the length of the buffer, length WORD inclusive, each time MonitorCreate is called. The length of a monitor chain buffer defines:
 * The minimum size of all buffers that are part of the monitor chain. The length specified in each input and output buffer belonging to all monitors that register with the monitor chain must be greater than or equal to the length of the device driver's monitor chain buffer plus 20 bytes.
 * The maximum size of a data record placed into the chain of monitor buffers (that is, the length of the device driver's monitor chain buffer minus 2 bytes).

Code Requirements
The character device driver must include a set of routines and request handlers in the strategy routine in its code segment. Note that for each data stream that can be monitored, the character device driver must include a notification routine in its strategy routine. It is then called by the monitor dispatcher when it has placed a single filtered data record into the device driver's monitor chain buffer. The notification routine is called by the monitor dispatcher:


 * When the monitor dispatcher automatically moves filtered data from the last monitor in a monitor chain into the device driver's monitor chain buffer


 * When the physical device driver calls MonWrite to write data into an empty monitor chain

When the notification routine is called, the device driver must process the data in its monitor chain buffer before returning to the monitor dispatcher. A character device driver must manage its monitor chains by creating them, deleting them, writing data into them, and flushing them. For each data stream that can be monitored by a chain of monitors, a device driver must call MonitorCreate to define for the monitor dispatcher the location (addresses) of the notification routine and the monitor chain buffer.

The monitor dispatcher assigns a handle to the chain of monitors for the data stream. The physical device driver uses this handle when instructing the monitor dispatcher to perform device helper functions on the monitor chain (for example, Register, MonWrite, MonFlush, and DeRegister). The physical device driver can call MonitorCreate any time prior to issuing other monitor dispatcher device helper functions: A physical device driver sends data to its monitors by placing it into the monitor chain. When monitors are registered on a monitor chain associated with a device driver's data stream, the device driver must place data received from the device or an application into the monitor chain so it can be filtered. The physical device driver builds a data record in its data segment and calls MonWrite to write that record into the monitor chain.
 * At initialization time, when the physical device driver is installed
 * When it receives a monitor open request, if MonitorCreate has not been previously called to define the monitor chain
 * When it receives a monitor register IOCtl request, if MonitorCreate has not been previously called to define the monitor chain, and before the DevHlp_Register is called to register a monitor with the monitor chain

When a monitor chain associated with a device driver's data stream is empty (that is, a monitor chain has been defined during a call to MonitorCreate, but no monitors have been registered), the physical device driver can use its monitor support to place a data record directly into its monitor chain buffer by calling MonWrite. The monitor dispatcher automatically calls the device driver's notification routine to process the data.

A physical device driver can flush all data from the monitor chain associated with a data stream. When a device driver requires that all data be removed from the monitor chain, it issues a call to MonFlush to direct the monitor dispatcher to place a specially marked record called a flush record into the monitor chain. This record must pass through all monitors in the chain. All monitors in the chain that receive a flush record on a DosMonRead must return it to the monitor chain on a DosMonWrite. No new data records are placed into the monitor chain until the flush record reaches the device driver's monitor chain buffer.

A character device driver must handle monitor open, register, and close requests in its strategy routine. When an application calls DosMonOpen to get a handle to the device for monitors, a monitor open request is sent to the physical device driver. In response, a physical device driver can define a monitor chain for the data stream by calling MonitorCreate, if it has not previously done so.

When an application calls DosMonReg to register a monitor as part of the monitor chain for a specified data stream (see INDEX parameter) for a device, a monitor register request is sent to the physical device driver. In response, the device driver:
 * Calls MonitorCreate to define the monitor chain, if it has not previously done so
 * Uses the monitor chain handle returned from a previous call to MonitorCreate and calls the DevHlp_Register to instruct the monitor dispatcher to insert the monitor into the monitor chain for the specified data stream
 * Tracks the number of monitors it has registered with each of its monitor chains

When an application calls DosMonClose to terminate the monitoring of data passing to or from a device, a monitor close request is sent to the physical device driver. In response, the device driver:
 * Must call the DevHlp_DeRegister for each monitor chain with which there are monitors currently registered. The service also removes all monitors associated with the process that issued the DosMonClose from the monitor chain.
 * Notice that a single process can register monitors for more than one data stream for a device. For example, a process can register a keystroke monitor for each OS/2 session. When this process terminates, the physical keyboard device driver calls DeRegister for each monitor chain associated with an OS/2 session.


 * Calls MonitorCreate with the Delete option which deletes the monitor chain on return from each call to DeRegister. This call is made only if there are no monitors registered in the monitor chain.

Special Considerations for Character Device Drivers
This section summarizes conditions that must be considered when writing a character device driver with monitor support.

Device-Specific Monitor Information
The character device driver that provides device monitor support must carefully document the following information for its monitors:
 * The definition of its data streams and their monitor chains. The monitor application developer requires this information for the Index parameter when calling DosMonReg.
 * The length of its monitor chain buffers. The monitor application developer requires this information so that a large enough private data area can be allocated for the monitor.
 * The description of its monitor record format, including a detailed description of the flag usage, and expected actions and restrictions of data record consumption. The monitor application developer requires this information to understand the data flowing through the data stream and any actions or responses the physical device driver expects.
 * A description of conditions under which the physical device driver can block the data stream. The monitor application developer must ensure that the device monitor works with the physical device driver, not against it. The monitor application developer must understand expected blockages in the physical device driver (their causes, their effects) in order to handle them.

Performance
The performance of a character device driver with respect to its monitor chains can be affected by several factors:
 * Performance can be degraded when interrupts are disabled too long. When a monitor chain is empty, a character device driver can place data directly into its monitor chain buffer by calling MonWrite. The monitor dispatcher disables interrupts, writes the data into the monitor chain buffer, calls the device driver's notification routine, and re-enables interrupts when the physical device driver returns to the monitor dispatcher. If the physical device driver fails to quickly process the data placed into its monitor chain buffer quickly, interrupts can be disabled too long.
 * The monitor dispatcher must guarantee that the data placed into the device driver's monitor chain buffer during a call to MonWrite is processed before new data can be placed into the buffer on subsequent calls. Because there is no shared semaphore or other means of signalling between the monitor dispatcher and the physical device driver to protect this data area from being overwritten, the monitor dispatcher protects the data in the buffer by disabling interrupts until the device driver has processed the data.


 * Performance can be improved by increasing the size of the physical device driver's monitor chain buffer. Because the size of this buffer determines the size of all monitor buffers in a monitor chain, performance is improved during calls to MonWrite to place data into the monitor chain. Performance is also improved during DosMonRead and DosMonWrite monitor function calls that intercept and return data to the data stream. As the size of the buffers increase, fewer blockages occur.


 * Note: When increasing a published size of a device driver's monitor chain buffer, there is a danger of preventing existing monitors from successful monitor registration because the size of the monitor's private data area is based on the size of the device driver's monitor chain buffer.

Device Driver Problems
When character device monitors are registered and active, a character device driver can severely and permanently affect its data stream or the entire system due to its:
 * Failure to quickly process data received in its monitor chain buffer when the notification routine is called, especially when issuing MonWrite into an empty monitor chain. Blockages can occur in all monitor buffers in a monitor chain if the physical device driver is blocked too long.
 * Inability to notify its monitors that there is a problem with the device. When a blockage occurs in a physical device driver because the device is disabled, the device driver can notify its monitors of the problem by placing a special monitor record into the monitor chain. The monitor can inform the user that there is a problem by displaying a message on the screen so that the user can re-enable the device.
 * However, blockages in all monitor buffers in the monitor chain due to a blockage at the physical device driver will prevent the device driver from notifying its monitors of a problem through a special monitor data record.


 * Dependencies on the return of specific types of monitor data records from the monitors to the physical device driver. Documentation on individual device drivers must include requirements for returning special monitor data records to the monitor chain and to the physical device driver (for example, flush records). Even with well-behaved monitors, however, the physical device driver cannot guarantee that its monitors will return its critical data.


 * Note:The introduction of special monitor data records into a monitor chain that is currently being monitored by applications can cause the system to halt.