OS/2* operates within the confines of a four-ring structure, Ring 0 through Ring 3. As shown in the following figure, Ring 0 is the most-protected ring and contains the OS/2 kernel, which is the main engine that drives the operating system. Ring 3 is the least-protected ring and contains applications and system services. Ring 1 is not used by OS/2. Ring 2 is similar to Ring 0 in that it provides access to physical devices such as printers and displays. Ring 2 is similar to Ring 3 in that it is prohibited from modifying kernel data structures. The following figure shows the ring structure.
Throughout the ring structure, functions can be called from the same ring level or any numerically lower ring level. Conversely, data can be accessed from the same ring level or any numerically higher ring level.
For the sake of compatibility between 16-bit and 32-bit code, all 32-bit code is treated as "Ring-2-Conforming". This means that 32-bit code runs at the ring level of its caller. For example, if the code is called from Ring 3, it runs at Ring 3. If the code is called from Ring 2, it runs at Ring 2. This allows easier access to functions and data, and eliminates many of the costly ring transitions encountered in the previous 16-bit system. To be Ring-2-conforming (rather than strictly Ring 2 or Ring 3), a function cannot call any strictly Ring 3 code or directly access the hardware. In other words, it must abide by the restrictions of both ring levels.
Many of the system services that exist at Ring 3 in dynamic link libraries (DLLs) access another DLL called PMGRE.DLL, which is the graphics engine or kernel of the OS/2 graphics subsystem. The graphics engine is composed entirely of Ring-2-conforming code that interfaces with the presentation drivers, which interface directly with the physical device drivers (at Ring 0) and the hardware. These DLLs are identified as presentation drivers for hardcopy devices (hardcopy drivers) by the file-name extension .DRV, and as presentation drivers for display devices (display drivers) by the file-name extension .DLL. The following figure shows a conceptual view of presentation drivers.
Conceptual view of presentation drivers in the flow of control from an application program to the hardcopy device and the display screen.
The graphics engine loads and enables the presentation drivers, and then dispatches calls to them through dispatch tables as Ring 2, Ring 3, or Ring-2-conforming code. For optimal system performance, write all of the functions in 32-bit presentation drivers be written as Ring-2-conforming. This eliminates the need for ring transitions from system services at Ring 3 and from 16-bit presentation drivers at Ring 2.
By exporting a table named OS2_PM_DRV_RING_LEVELS in 32-bit presentation drivers, the ring level of each function in the dispatch table can be selected.
Note: If this table is not exported, all 32-bit functions will be dispatched as Ring-2-Conforming.
The presentation driver has certain responsibilities to the graphics engine. Specifically, a number of entry points exist within the graphics engine that the presentation driver is required to hook (a mechanism by which procedures are called) and support. Many of these functions (as they currently exist in the graphics engine) are not truly functional, and if calls were made to these entry points, nothing would happen. In many cases, they simply return when called.
There are other entry points in the graphics engine that can be optionally hooked by the presentation driver (for example, where only light processing is required, it might be preferable to use the presentation driver). These entry points can be called by the presentation driver for special processing.
The graphics engine calls the entry points within the presentation driver by means of a dispatch table. The dispatch table is a block of memory allocated by the graphics engine for the containment of entry points and is assigned for use by a presentation driver. Each presentation driver loaded by the system is given its own separate dispatch table by the graphics engine. When a presentation driver device context is enabled, the graphics engine allocates a dispatch table for that presentation driver and fills the dispatch table with pointers. Each entry in the table is a 32-bit pointer that points back to a specific routine existing in the graphics engine.
Because many of these routines must be hooked by the presentation driver, the graphics engine refers to the dispatch table to find the appropriate pointer any time that it calls a function in the presentation driver. At this point, however, all of the pointers in the dispatch table point back to routines in the graphics engine. The presentation driver must go into the dispatch table itself and replace some of the pointers with new pointers that point to corresponding routines within the presentation driver. This is mandatory for some routines, optional for others. The hooking of pointer entries in the dispatch table occurs the first time the presentation driver is called at its OS2_PM_DRV_ENABLE entry point for the first subfunction (see FillLogicalDeviceBlock). The following figure shows how the Graphics Function Dispatch Table works.
The exported entry point OS2_PM_DRV_ENABLE (see OS2_PM_DRV_ENABLE) has ten subfunctions. Four of these subfunctions are used in the enable process of a device context, three are used in the disable process, one is used to save the device context, one is used to restore the device context, and one is used to reset the device context.
When Enable Subfunction 01H - FillLogicalDeviceBlock is called, the graphics engine passes to it a pointer to the dispatch table that it allocated for the presentation driver. FillLogicalDeviceBlock then must substitute all of the mandatory pointers (and perhaps some optional routines) in the dispatch table with pointers to corresponding routines within the presentation driver itself.
The Developer's Toolkit for OS/2 provides support for writing source code in either C or assembler language. This support consists of a set of header files that define the functions, structures, and constants used in the internal interface to the presentation driver. Presentation drivers might also need to include OS2.H or OS2.INC, which define system functions, structures, and constants.
As with other components of OS/2, the presentation driver architecture ensures that:
Note that although many of the functions must be hooked by the presentation driver, some might also be passed back to the graphics engine for processing when called. Save the original pointer values stored in the dispatch table by the graphics engine before hooking them. Having access to the original pointers to the graphics engine simulation routines allows the presentation driver to optionally make calls back to the graphics engine (that is, to have the graphics engine do the processing instead). This technique has often proven to be a tremendous time saver in developing OS/2 presentation drivers, especially when calling back to the graphics engine to perform clipping routines.
When the Presentation Manager* interface is initialized, the presentation driver for the attached display is loaded and enabled. This driver has direct access to the video hardware. The function calls passed to the presentation driver are processed and then passed to the adapter interface.