PDDREF:OS/2 Physical Device Driver Operations

Physical device driver operations consist of the activities and interactions between the strategy routine and the hardware interrupt handler in the processing of an I/O request.

The handling of an I/O request begins with OS/2 calling the strategy routine entry point with a pointer to the request packet. The strategy routine checks the validity of the I/O request. If the request is valid, the strategy routine might place the request on a work queue for the device.

If the device is currently idle, the strategy routine starts the request at the device. The strategy routine can then wait for the device interrupt by suspending its thread of execution with the DevHlp_Block.

When the device interrupt occurs, the hardware interrupt handler checks the request to see if it has been completed. If the request has not been completed, the hardware interrupt handler continues the request at the device. If the request has been completed, the hardware interrupt handler sets the return status in the request packet. The hardware interrupt handler can remove the completed request packet from the work queue and start the next request at the device. If the strategy routine is waiting for the device interrupt (which is blocked), the hardware interrupt handler can initiate the strategy routine with the DevHlp_Run.

The strategy routine queues the requests and initiates only the I/O, if the device has been inactive. The hardware interrupt handler starts requests as they reach the head of the work queue. The thread that is running when the physical device driver determines that a particular request is complete might not necessarily be the same thread in which the device driver had received the request. This is particularly true for the interrupt-time components of the physical device driver. For example, the address of a user buffer passed to the physical device driver when the request was issued belongs to a specific Local Descriptor Table (LDT), which might not be the current LDT when the request ends. The device driver can accommodate this by storing the buffer address as a 32-bit physical address.

The physical device driver strategy routine is called by the OS/2 operating system with a pointer to the request packet. Any addresses passed in the request packets for Read/Write requests are passed as 32-bit physical addresses. Therefore, the physical device driver does not need to lock or convert the addresses into physical addresses. The device driver needs to lock only these addresses that it receives from a source other than the OS/2 operating system, as in the case of a process passing an address by way of a generic IOCtl.

The multitasking environment dictates that the components of the device driver must be capable of handling requests simultaneously. This means that the components of the physical device driver must relinquish execution whenever possible. The physical device driver relinquishes control at task-time by blocking, yielding, or referencing a segment that had been swapped out. The OS/2 operating system does not preempt a thread in the physical device driver. However, once the physical device driver releases its execution, the operating system calls the physical device driver with a new request. Once the strategy routine blocks, yields, or references a swapped-out segment, its thread of execution is called with a new request under the context of a different thread.

While the strategy routine assumes that it will not be preempted by other task-time instances, it must protect itself against its own interrupt-time components. It must disable interrupts when checking if the device is active and when examining the device queue. The interrupt-time components are preempted only by other higher priority interrupts.

Physical Device Driver Initialization
Physical device driver initialization occurs during system initialization. During system initialization, base device drivers are preloaded with the operating system. Installable device drivers are loaded during CONFIG.SYS processing, by way of the DEVICE= configuration command. After a physical device driver has been loaded, it will be called at its strategy routine entry point with the request packet for the INIT command.

OS/2 device drivers have the following characteristics at INIT-time. They can: During CONFIG.SYS processing, each DEVICE= command is processed on a first-come, first-served basis: Installable OS/2 device drivers are initialized in protect mode. The device driver strategy routine will run under the thread of the System Initialization Process at Ring 3, with I/O privilege. Because of the special system process, the installable device driver is allowed to make dynamic link function calls only at INIT-time.
 * Initialize in protect mode
 * Have I/O Protect Level (IOPL) at all times or execute at Ring 3
 * Service hardware interrupts
 * Use timer services
 * Use some OS/2 dynamic link functions
 * Use ABIOS services
 * The DEVICE= device driver file image is loaded into memory.
 * A check is made to ensure that the previously initialized device driver has been removed.
 * If the previously installed device driver has been removed, the newly loaded device driver is initialized.

Device Driver INIT-Time Function Call Summary
A limited subset of the dynamic link API functions (Dosxxx functions) can be used by the physical device drivers at initialization time. The dynamic link functions available to a physical device driver at initialization time include: DosBeep        Generate sound from speaker DosCaseMap     Perform case mapping DosChgFilePtr  Change (move) file read/write pointer DosClose       Close file handle DosDelete      Delete file DosDevConfig   Get device configuration DosDevIOCtl    I/O control for devices DosFindClose   Close find handle DosFindFirst   Find first matching file DosFindNext    Find next matching file DosGetEnv      Get address of process environment string DosGetInfoSeg  Get address of system variables segment DosGetMessage  Get system message with variable text DosOpen        Open file DosPutMessage  Output message text to indicated handle DosQCurDir     Query current directory DosQCurDisk    Query current disk DosQFileInfo   Query file information DosQFileMode   Query file mode DosRead        Read from file DosSMRegisterDD Register session switch notification DosWrite       Synchronous write to file For a detailed description of each function, see the OS/2 Control Program Programming Reference.


 * Note: Because device driver initialization is started from the strategy routine, a physical device driver must not issue a DosExit function call. Instead, the physical device driver should return the INIT request packet by setting the return status of the packet and performing a far return to the kernel.

In addition to the set of dynamic link function calls, certain DevHlp services are available to device drivers at INIT-time. For a list of these DevHlp services, see DevHlp Services and Function Codes. For more information on device driver initialization, see the description of the device driver strategy command 0h.

Replacing Character Device Drivers
OS/2 character device drivers can be replaced. For system character device drivers, the appropriate bits in the attribute field of the device driver header and the name of the character device driver must match.

When the new device driver is loaded, the attribute field and name are used to determine if the new device driver is attempting to replace a driver already installed. If so, the previously installed device driver is requested to uninstall the indicated device. If this driver refuses the uninstall command, the new device driver is not allowed to initialize. If the previously installed device driver performs the uninstall operation, the new device driver is initialized.


 * Note: The uninstall command is needed to allow a physical device driver to relinquish its interrupt vectors and its allocated physical memory.

Compatibility with Previous-Level DOS Device Drivers
Not all previous-level (any level prior to DOS 5.0) DOS device drivers can be allowed to run in a DOS session. The supported set of previous-level device drivers has the following restrictions: 0 - INIT 3 - IOCtl input 4 - INPUT (read) 5 - NON-DESTRUCTIVE INPUT NO WAIT 6 - INPUT STATUS 7 - INPUT FLUSH 8 - OUTPUT 9 - OUTPUT with verify 10 - OUTPUT STATUS 11 - OUTPUT FLUSH 12 - IOCtl output 13 - DEVICE OPEN 14 - DEVICE CLOSE 16 - GENERIC IOCtl 31 - INIT Complete
 * Previous-level block device drivers are not permitted in a DOS session. Block device drivers must be written to the OS/2 interfaces.
 * Only a limited set of previous-level character device drivers can be supported by OS/2. In order to run in a DOS session, a previous-level character device driver must conform to the following rules:
 * The character device driver cannot have a hardware interrupt handler. The device must be a polled device rather than an interrupt-driven one.
 * The character device driver can be called by the OS/2 operating system in a DOS session, with all of the packets supported by character devices:

Initialization of Previous-Level DOS Device Drivers
A previous-level DOS character device driver can be initialized to run in a single DOS session or in all DOS sessions. A previous-level device driver can be installed for a single DOS session by using DOS settings. DOS device drivers can also be installed for all DOS sessions by specifying the configuration command DEVICE=.

Previous-level device drivers are loaded and initialized in a DOS session. The rules for replacing these drivers are the same. The replacement is guided by the name and attributes of the physical device driver. The functions that can be performed at initialization are more restrictive than for DOS 5.0. No INT 21h functions can be performed from the physical device driver initialization code.

Hardware Interrupt Management
The hardware interrupt handler is the component of the physical device driver that deals with a hardware interrupt. The handler is called by the OS/2 kernel when the hardware interrupt occurs, and must follow the far call/return model. By convention, the hardware interrupt handler does not need to save and restore any registers it uses because this is done by the kernel.

Before the handler can be started, its entry point must be registered for a specific hardware interrupt. This can be done during or after device driver initialization with the DevHlp_SetIRQ. Once the call to the DevHlp has been made, the hardware interrupt handler can be started.

Interrupt Level Sharing
A hardware interrupt level that is shared by two or more devices is referred to as a shared interrupt. Interrupt sharing is an extension of a device's design. A single interrupt level (IRQ) can be shared by two or more devices if the devices are specifically built for interrupt sharing. With cooperation from its device, a physical device driver can share only the hardware interrupt level. This is true for both edge-triggered and level-sensitive interrupt environments.

In an edge-triggered interrupt environment, an interrupt request is recognized by the 8259 Programmable Interrupt Controller (PIC) as a particular edge transition (such as low-to-high) on the hardware interrupt request line. The interrupt request line can remain level without generating another interrupt. The 8259 PIC requires an End-Of-Interrupt (EOI) and another of the same kind of edge transition to recognize an interrupt on that interrupt level.

In a level-sensitive interrupt environment, an interrupt request is recognized by the 8259 PIC as a particular level on the hardware interrupt request line. The interrupt condition must be removed before the EOI is issued, or else the 8259 continues to generate interrupts for that interrupt level.


 * Note: OS/2 supports interrupt sharing on the IBM PS/2 and the EISA machines, which provide a level-sensitive interrupt environment where multiple device drivers can share a particular hardware interrupt.

The basic model for managing a hardware interrupt follows:
 * 1) The physical device driver must register an interrupt handler for a hardware interrupt, specifying whether the physical device driver intends to share the interrupt level.
 * 2) When started, the physical device driver interrupt handler tests the device to see if it generated the interrupt.
 * 3) If the device has an interrupt pending or caused a spurious interrupt, the interrupt handler owns the processing of the interrupt. The handler services the device, resets the interrupting condition at the device, issues the End-Of-Interrupt (EOI) with the DevHlp_EOI, performs a far return (ret far) indicating that the interrupt handler owned the interrupt, and clears the carry flag (CF=0).
 * 4) If the device does not have an interrupt pending, the interrupt handler does not own processing of the interrupt. The handler must perform a ret far indicating that it does not own the interrupt, and set the carry flag (CF=1).

Rules for Sharing Interrupt Levels
All interrupt levels have the potential to be shared. There are some restrictions for permitting two or more device drivers to share an interrupt level. Each device driver must adhere to the rules on the following actions:

Interrupt Level Sharing

 * SYSTEM TIMER RULE: The system timer interrupt level (IRQ 0) cannot be shared.
 * ILL-BEHAVED DEVICE RULE: An interrupt handler for a device that is not well-behaved must not share an interrupt level. This is because a device that is not well-behaved generates interrupts before its interrupt handler is installed or cannot be told to stop generating interrupts.
 * Well-behaved devices do not power-up with interrupts pending and do not remain active after their handlers have terminated. Also, well-behaved devices do not usually generate spurious interrupts.


 * SET IRQ RULE: The physical device driver must use the DevHlp_SetIRQ to indicate whether it will share the interrupt level. The physical device driver should use the DevHlp_RegisterStackUsage for each Devhlp_Set IRQ call. See Limiting the Number of Nested Interrupts.
 * IRQ ENFORCEMENT RULE: If a physical device driver signs up for a hardware interrupt indicating that it will not share the hardware interrupt, then a subsequent SetIRQ request to share that hardware interrupt will be refused. Conversely, if a physical device driver signs up for a hardware interrupt indicating that it will share the hardware interrupt, then a subsequent SetIRQ request to exclusively own the hardware interrupt will be refused.
 * IRQ MASK RULE: The operating system owns the masking of the hardware interrupt at the 8259 interrupt controller, and enables the hardware interrupt when the first interrupt handler signs up for the hardware interrupt. This permits the handler to communicate with its device during initialization.

Processing the Interrupt

 * STI ENTRY RULE: Interrupt handlers that share interrupts are entered with processor interrupts enabled. This prevents the lockout of higher priority hardware interrupts because the search for the owner of the current interrupt level takes a variable amount of time. Interrupt handlers that do not share interrupts are entered with processor interrupts disabled.
 * IRQ OWNERSHIP RULE: The physical device driver interrupt handler, when started, must always interrogate its device to see if the device caused the interrupt. If the interrupt handler's device caused the interrupt, then the handler owns the processing of the interrupt and can briefly disable processor interrupts for critical operations. It must issue the EOI as soon as possible and must be aware that once it does so, it could be reentered at its interrupt handler's entry point. If the interrupt handler's device did not cause the interrupt, the handler must not issue an EOI.
 * INT RETURN RULE: The interrupt handler, after taking the appropriate action in processing the interrupt, must return an indication of whether it claimed the interrupt. If the handler owns the interrupt, then it must clear the carry flag (CF=0) and issue a far return (far ret) when processing is complete. If the handler does not own the interrupt, then it must set the carry flag (CF=1) and issue a far return.
 * SEARCH RULE: The operating system calls each interrupt handler registered for a particular interrupt level until one of the interrupt handlers claims the interrupt. If no interrupt handler claims the interrupt, then the IRQ is disabled.
 * EOI RULE: Management of the 8259 interrupt controllers is the responsibility of the operating system. However, the End-Of-Interrupt (EOI) is the responsibility of the interrupt handler. The handler must use the DevHlp_EOI service to issue the EOI as soon as possible in the processing of its interrupt. This permits the 8259 interrupt controller to process other interrupt requests at the current interrupt priority, as well as interrupt requests of lower priorities. In a level-sensitive interrupt environment, the EOI must not be issued to the 8259 interrupt controllers until the interrupt condition at the device is removed.
 * PhysToVirt RULE: Selectors used for PhysToVirt represent a critical resource. An interrupt handler that uses PhysToVirt must not issue the EOI until after it no longer needs the addresses generated by PhysToVirt. Otherwise, the interrupt handler must disable processor interrupts before issuing the EOI. This allows the interrupt handler to use the temporary selectors for its interrupt level without getting another interrupt on its level. However, this does not apply to the selectors obtained by the DevHlp_AllocGDT.
 * POSITION RULE: An interrupt handler that shares an interrupt level must not depend on its position in the list of handlers for that interrupt level.

Sharing Interrupts
An OS/2 device driver that supports more than one interrupting device on one interrupt request level (IRQ) must either: The reason for these requirements is a limitation in the DevHelp_UnSetIRQ system service. The service can only tell which interrupt handler to remove by the parameter passed into the DevHlp_IRQ. If more than one interrupt handler has been registered from one device driver for the same IRQ, the service removes one of the interrupt handlers belonging to the device driver. However, the interrupt handler will not be known by the device driver writer because the actual workings of the system service may change from one release of OS/2 to another.
 * Provide one interrupt handler that supports all of its devices on that level
 * Not unhook the interrupt vector using DevHelp_UnSetIRQ once it is hooked

It is possible for an interrupt handler that services multiple devices to service only the first device that it finds to be causing an interrupting condition. The level sensitive nature of the hardware will cause the interrupt handler to be invoked to service the other devices as needed. However, the overhead to do this type of servicing will quickly become excessive on a device that frequently causes interrupts. Therefore, it is important that the interrupt handler service all of the devices on the interrupt level it is responsible for on each interrupt.

OS/2 allows a maximum of four interrupt handlers to be registered to share one interrupt level at one time. These handlers can be in one device driver or different device drivers (except when they will be removed using DevHelp_UnSetIRQ as mentioned above). Each use of the DevHlp_SetIRQ increases by one the number of registered interrupt handlers for an interrupt level. Each use of the DevHlp_UnSetIRQ reduces by one the number of registered interrupt handlers for an interrupt level.

DevHelp Services
Some OS/2 services are limited by the context in which the device driver is running. See Device Helper (DevHlp) Services for a complete list of the DevHlp services, functional descriptions, and limitations.