PDRREF:Design Considerations for All Drivers

From EDM2
Jump to: navigation, search
Presentation Device Driver Reference for OS/2
  1. Introduction to OS/2 Presentation Drivers
  2. Design Considerations for All Drivers
  3. Graphics Engine/Presentation Driver Design Changes
  4. Design Considerations for Display Drivers
  5. Design Considerations for Hardcopy Drivers
  6. Display Drivers
  7. Distributed Console Access Facility (DCAF) Architecture
  8. Graphics Engine Hardcopy Drivers
  9. Queue Drivers
  10. Port Drivers
  11. Presentation Manager Function Categories
  12. Exported Driver Function Reference
  13. Mandatory and Simulated Graphics Engine Function Reference
  14. Device Support Function Reference
  15. DBIDI Command Structures and Command Flow

Appendixes

A - OS/2 Version Compatibility Considerations
B - Syntax Conventions
C - Format of the Journal File
D - Bit-Map Simulation for 16-Bit Hardcopy Drivers
E - Data Types
F - Notices

Miscellaneous

G - Glossary

Reprint Courtesy of International Business Machines Corporation, © International Business Machines Corporation

This chapter describes various design considerations involved in creating a presentation driver. The following list includes the topics covered in this chapter:

  • Angles
  • Bounds computations
  • Clipping
  • Closing figures in areas and paths
  • Coordinate values
  • Positions within text functions
  • Return codes
  • Transform matrix values
  • Allocating Memory
  • Error Strategy
  • Presentation Manager Error Codes
  • Exit List Processing
  • Per-Thread Exception Management
  • Using Macro Assembler Instructions
  • Protecting Objects or Device Contexts

Angles

Angles are passed as signed 32-bit numbers. Zero refers to the direction of the positive X-axis; 360 represents 360°. Positive values represent counterclockwise angles from the positive X-axis.

Bounds computations

All presentation drivers must accumulate bounds for unclipped primitives. Application bounds (COM_BOUND) are accumulated in model space. User bounds (COM_ALT_BOUND) are accumulated in device-coordinate space.

Clipping

The presentation driver must perform clipping for drawing and text functions, except for GreDrawLinesInPath and GrePolyShortLine. Clipping for these two functions is done by the graphics engine. The minimum requirement is to render each primitive clipped to a single rectangle and to clip each rectangle in turn. The rectangles can be enumerated as described in GreGetClipRects.

Note
Rectangles might not always be valid. See Drawing to Display Devices.

Closing figures in areas and paths

The graphics engine generates closure lines for figures within areas and paths unless the presentation driver has opted to hook all the path and area functions. In this case, the presentation driver is responsible for closing any figures. For details, see Area and Path Functions.


Coordinate values

All coordinates are passed to the presentation driver as 32-bit values. Unless stated otherwise, these values represent world coordinates. The graphics engine function, GreConvert, can be called to convert coordinates from one type to another. Coordinates must be converted back to world coordinates before returning to the presentation driver. Notice that screen coordinates are device coordinates to which the DC origin has been added.

Positions within text functions

When positions are used, a text function takes the position from the base line of the text box. Descenders, such as the tail of a lowercase character "y," are expressed as a negative value relative to the base line.

Return codes

The presentation driver must always return a full 32-bit (LONG) value. For example, BOOLEAN TRUE and FALSE are defined as:

#define TRUE   (1L);
#define FALSE  (0L);

Transform matrix values

Transform matrix elements are represented in fixed point notation, that is, as a 16-bit signed integer and a 16-bit fractional part. These precision limits apply during graphics engine matrix multiplication for all initial, intermediate, and final matrix element values.

Allocating Memory

Presentation drivers can allocate and manage memory by using:

  • A Dosxxx function such as DosAllocMem.
  • The SSxxx functions described in System Functions.

Display drivers, or presentation drivers that share objects such as bit maps and regions, must use the SSxxx functions to allocate memory for these objects. Memory allocated through calls to these functions is "shared memory", controlled by the memory allocator component of the graphics engine. Ownership of the memory can be transferred or (when the owning device context ceases to exist) marked as having no owner.

Error Strategy

Presentation drivers support the error strategy implemented by the Presentation Manager interface. When an error occurs, the driver calls WinSetErrorInfo (see WinSetErrorInfo) to log the appropriate error code and set the return code to show that an error was detected.

The component that implements a function must provide error checking for the environment, objects, and resources associated with it. The presentation driver requires the following error checking:

  • Fail-safe on routines that set attributes and transformation values. Any routine that changes attributes or transformation values must be able to restore the initial values if an error occurs during the change.
  • Full error checking on symbol sets, fonts, bit maps, and regions.
  • With segment drawing, drawing primitives, and primitive attributes in draw mode, unchecked parameters are passed directly to the graphics engine or the presentation driver. When one of these functions is hooked by the presentation driver, the handling routine must perform the necessary error checking and log any errors, or reset any invalid values to their defaults, as appropriate.
  • For any function with coordinates as parameters, the presentation driver must check that the values passed are valid. When an invalid coordinate is detected, the handling routine must log an error or use a default coordinate value, as appropriate.

For any defined error, the application sees the same error code regardless of whether the error was logged by the Graphics Programming Interface (GPI), graphics engine, or presentation driver.

Severity

Four ascending severity levels are defined for error messages:

  • Warning
  • Error
  • Severe error
  • Unrecoverable error
Warning
The function detected a problem, took corrective action, and was able to complete processing successfully.
Error
The function detected a problem for which no sensible corrective action is possible. The function is not executed and the system remains at the same state as when the function was requested.
Severe Error
The function detected a problem from which the system cannot reestablish its state. The function has partially executed and the application must now make some corrective action to restore the system to a known state.
Unrecoverable Error
The function detected an error from which it is impossible for the system to reestablish the state that it held at the time that the function was called. It is also impossible for the application to restore the system to a known state.

Presentation Manager Error Codes

Error codes are defined in the header file. These codes fall into general and specific groups. General error codes that are appropriate to many Grexxx functions include:

Error Code Must be logged by:
PMERR_COORDINATE_OVERFLOW Functions requiring matrix computation
PMERR_INSUFFICIENT_MEMORY Functions resulting in memory allocation
PMERR_INV_HBITMAP Functions with hbm as an explicit or implicit parameter
PMERR_INV_HRGN Functions with hrgn as an explicit or implicit parameter
PMERR_INV_COORDINATE Functions with coordinate parameters
PMERR_INV_IN_AREA Functions that are invalid inside an open area bracket
PMERR_BASE_ERROR Functions that directly or indirectly issue DOS routines
PMERR_DEV_FUNC_NOT_INSTALLED Functions not supported by the presentation driver

Specific error codes listed in the descriptions of each Grexxxfunction are found in Mandatory and Simulated Graphics Engine Function Reference.

To set an error code and the error's severity, the presentation driver must call WinSetErrorInfo (see WinSetErrorInfo). All error codes are listed and explained in the Presentation Manager Programming Reference.

Exit List Processing

An exit list is a list of routines that are given control when the current process ends, either normally or abnormally. The following is an example of exit list processing:

  • When the presentation driver's Enable Subfunction 01H - FillLogicalDeviceBlock is called, the driver can call function DosExitList:
 #define ROUTINE_ORDER 0x1000
 usResult = DosExitList (EXLST_ADD | ROUTINE_ORDER, (PFNEXITLIST)MyExitProc);

This adds the function MyExitProc to the list of functions that are called when this process terminates (either normally or because of some error such as a General Protection fault).

  • When MyExitProc is called, the presentation driver can perform any necessary cleanup such as releasing any semaphores. The last call in MyExitProc is another call to DosExitList:
usResult = DosExitList (EXLST_EXIT, (PFNEXITLIST)MyExitProc);

This allows the operating system to transfer control to the next function in the list of exit list processing functions for the process that has terminated. For more information, refer to DosExitList in the OS/2 Control Program Programming Reference and OS/2 Programming Guide.

At Enable time, the presentation driver must place an entry in the exit list for the application or process that opens the DC. This entry is a pointer identifying the routine in the presentation driver that releases all resources owned by the DC.

Note: When writing a presentation driver, consider what would happen if another thread of the process were to terminate.

Per-Thread Exception Management

Per-thread exception management is available to 32-bit EXEs, DLLs, and presentation drivers. The per-thread exception management model is a superset of DosExitList processing found in 16-bit OS/2 programming.

Under 16-bit OS/2 architecture, a process cannot handle access violations. In the event of an access violation, the system terminates the process. Given that the trapping process had registered an exit-list function, it had one final chance to do clean up just prior to termination. Sixteen-bit drivers and DLLs usually add an exitlist. At process termination, 16-bit drivers perform per-process clean up in exit-list functions.

Programmers of 32-bit drivers have a choice concerning per-process clean up. They may use the 32-bit version of DosExitList(), or they may add code for the DLL termination case in the driver function _DLL_InitTerm(). The _DLL_InitTerm() function, if present and exported from the driver, will be called by OS/2 with parameter 0 on DLL instantiation and parameter 1 on DLL termination or unload. The _DLL_InitTerm() function provides process-granular clean up.

Furthermore, 32-bit OS/2 has exception handlers that are thread-granular. OS/2 lets each thread in a process handle exceptions that arise on that thread. Some of these exceptions are:

  • Accessing invalid or uncommitted memory
  • Stack overruns
  • Thread terminations
  • Floating point errors
  • Asynchronous (external) signals
  • User-raised exceptions (synchronous signals)
  • Invalid opcodes
  • Bounds
  • Divide by zero errors
  • Overflow errors

The goals of a 32-bit driver that uses exception management are:

  • When passed bad parameters that cause exceptions, drivers can call WinSetErrorInfo and return suitable result codes for the application to inspect.
  • When threads are terminated while executing driver code, the driver can clean up before the thread exits.

The Exception Handler

A misconception about exception handlers is that they must be able to fix every bad situation and resume execution. This is not a realistic expectation.

Assume that the handler function will not resume every exception. Instead, it will perform clean up, report errors, and return an appropriate result code. In this case, one exception handling function can handle all exceptions.

At exception time, the handler's main activity is to jump out of the handler function back into the function that caused the trap. The handler does an inter-function jump using ANSI C setjmp() and longjmp().

After the inter-function jump, the thread is now executing at a known point in the function that had a problem, and doing clean up there is far easier than in the handler.

Sample Code

The code fragment below shows how to set a handler while the calling thread is executing driver code. In the event of an exception, OS/2 calls the handler function, which jumps back to the point of setjmp().

#define INCL_DOS
#include <os2.h>
#include <setjmp.h>

// this exception registration record extends the OS/2-defined one in BSEXCPT.H
// by adding a C jmp_buf at the end
struct _regrec {
  PULONG    pNext;
  PFN       pfnHandler;
  jmp_buf   jmp;
}
typedef struct _regrec REGREC, *PREGREC;

// -------------------------------------------------------------------------
// an exception handler function that jumps back to setjmp()

ULONG _System DriverHandler( PEXCEPTIONREPORTRECORD p1,
                             PREGREC p2,
                             PCONTEXTRECORD p3,
                             PVOID pv )
{
  // handle interesting exceptions
  switch( p1->ExceptionNum ) {
  case XCPT_ACCESS_VIOLATION:
  case XCPT_INTEGER_DIVIDE_BY_ZERO:
  case XCPT_INTEGER_OVERFLOW:
  case XCPT_PROCESS_TERMINATE:
  case XCPT_ASYNC_PROCESS_TERMINATE:
    // setjmp() will return p1->ExceptionNum
    longjmp(p2)->jmp, p1->ExceptionNum );
  }
  // not interested; system should continue search
  return XCPT_CONTINUE_SEARCH;
}

// -------------------------------------------------------------------------
// example of an exported driver function; return TRUE upon success
LONG _System ExportedDriverFunction( PVOID p )
{
  REGREC        regrec;
  ULONG         ulException;
  LONG          lResult;

  regrec.pfnHandler = (PFN)DriverHandler;
  DosSetExceptionHandler( (PEXCEPTIONREGISTRATIONRECORD)&regrec );

  // get thread state; normally rets 0, rets non-zero after longjmp()
  ulException = setjmp( regrec.jmp );

  if( ulException ) {
    // an exception occurred
    // clean up here: release memory, semaphores
    // must check for exiting thread case
    switch( ulException ) {
    case XCPT_PROCESS_TERMINATE:
    case XCPT_ASYNC_PROCESS_TERMINATE:
      // thread is ending or being terminated
      DosUnsetExceptionHandler( (PEXCEPTIONREGISTRATIONRECORD)&regrec );
      DosExit( EXIT_THREAD, 0 );
    }
    WinSetErrorInfo( ... );
    lResult = FALSE;
    goto depart;
  }

  // do the work of the exported function here

  lResult = TRUE;

depart:
  DosUnsetExceptionHandler( (PEXCEPTIONREGISTRATIONRECORD)&regrec );
  return lResult;
}

Using Macro Assembler Instructions

Presentation drivers never use the CLI and STI macro assembler instructions because these instructions can interfere with some of the base OS/2 system operations.

Protecting Objects or Device Contexts

A process that attempts to use a locked object will return an error such as PMERR_HDC_BUSY. Although a device context is owned by a single application or process, the owner can access the device context (DC) through multiple threads. The presentation driver must provide a mechanism whereby a DC can register that it is busy and block access from other threads. In its simplest form, this is performed by the EnterDriver and LeaveDriver routines, which are called at the start and end of each function-handling routine in the presentation driver.

An example of a typical EnterDriver routine for a display driver is as follows:

 /***************** Typical EnterDriver Routine ***********************/

enter_driver()
{
 do {                /* Lock DC for exclusive use of the current thread */
   SemEnter(Device); /* Some functions do not pass a Device Context (DC)*/
                     /* handle  */
   if (hdc == NULL)
       return(SUCCESS);  /* Check validity of the passed DC handle */

   if (hdc == ERROR) {
       WinSetErrorInfo (SEVERITY_ERROR, PMERR_INV_HDC);
       SemLeave(Device);
       return(ERROR);
                       }  /* DC region must be validated before */
                          /* driver draws into it.              */

   if (hdc_is_not_dirty)  /* Test the HDC_IS_DIRTY flag. */
                          /* If the flag is set, the DC         */
       return(SUCCESS);   /* must be recalculated by the system */

   SemLeave(Device);      /* Unlock DC.  Call back to engine    */
                          /* to force DC calculation.           */

   VisRegionNotify(hdc);  /* Loop back to reset lock and recheck*/
  }  while (TRUE);
 }

Changes to the Graphics Engine

In the Presentation Manager for OS/2 2.1 and previous versions, a presentation driver had to support more than 70 functions. In addition, it was also mandatory that the presentation driver keep device-contextual state information and, in the case of display presentation drivers, run some functions at interrupt time.

The design of the graphics engine in OS/2 Warp, Version 3 focuses on taking the complexity out of developing a presentation driver while keeping the expandability and flexibility it was originally designed to have.

This graphics engine has two major changes from OS/2 2.1 and previous versions:

  • Device-independent functions merged into the graphics engine
  • System rasterization support (referred to as SOFTDRAW)

This design is intended to do the following:

  • Make presentation drivers simple and easy to develop
  • Allow developers to add functionality in increments
  • Move contextual and state management out of the presentation drivers
  • Move common rasterization management out of the presentation drivers

The following table identifies the support for the Presentation Driver Model that is available in various versions of OS/2.

OS/2 Release 16BPD 32BPD NMPD
Runs on OS/2 1.X X
Runs on OS/2 2.0, 2.1 X X
Runs on OS/2 Warp, Version 3 X X X

Note:

16BPD 
Presentation Driver compiled as 16 bit
32BPD 
Presentation Driver compiled as 32 bit
NMPD 
New model of Presentation Driver