PDDREF:OS/2 Physical Device Driver Operations: Difference between revisions
No edit summary |
|||
Line 82: | Line 82: | ||
|[[DosWrite]]||Synchronous write to file | |[[DosWrite]]||Synchronous write to file | ||
|} | |} | ||
For a detailed description of each function, see the ''OS/2 Control Program Programming Reference''. | 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. | ;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. | 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 [[Device Helper (DevHlp) Services and Function Codes|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=== | ===Replacing Character Device Drivers=== |
Revision as of 04:16, 22 May 2025
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:
- 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.
- When started, the physical device driver interrupt handler tests the device to see if it generated the interrupt.
- 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).
- 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.