Jump to content

PDDREF:OS/2 Physical Device Driver Operations: Difference between revisions

From EDM2
Line 173: Line 173:


===DevHelp Services===
===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.
Some OS/2 services are limited by the context in which the device driver is running. See [[PDDREF:Device Helper (DevHlp) Services|Device Helper (DevHlp) Services]] for a complete list of the DevHlp services, functional descriptions, and limitations.

Revision as of 04:21, 22 May 2025

Physical Device Driver Reference
  1. Introduction
  2. Physical Device Driver Overview
  3. Physical Device Driver Architecture and Structure
  4. OS/2 Physical Device Driver Operations
  5. OS/2 Physical Device Driver Design Issues
  6. Character Device Monitors
  7. Installation of External Loadable Device Drivers
  8. Physical Device Driver Strategy Commands
  9. Device Helper (DevHlp) Services
  10. Resource Management
  11. Linking Resource Manager Services
  12. Generic IOCtl Commands
Appendixes
OS/2 Version Compatibility Considerations
Running OS/2 Version 1.3 16-Bit PDDs on OS/2
Using Advanced Bios
Notices
Glossary

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:

  • 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

During CONFIG.SYS processing, each DEVICE= command is processed on a first-come, first-served basis:

  • 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.

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.

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:

Function Description
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 Guide and 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:

  • 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:
 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

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:

  • 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

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.

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.