PDDR/2 - Printer Bidirectional Communications

From EDM2
Jump to: navigation, search
Printer Device Driver Reference

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

Printer Bidirectional Communications

This chapter discusses OS/2 communications for bidirectional printers.

Introduction

The OS/2 print spooler and print object take advantage of the features of bidirectional printing. The IBM Developer Connection Device Driver Kit for OS/2 (DDK) now provides the following in support of bidirectional printers:

  1. Spooler Prt API interface that enables bidirectional communications with printers - this is bidirectional-software protocol independent.
  2. Presentation Manager (PM) port-driver interface that enables bidirectional communications with printers - this is also bidirectional-software protocol independent.
  3. Bidirectional protocol router layer that routes generic printer queries/control commands to the correct bidirectional protocol converter.
  4. Print object that takes advantage of the features of bidirectional printers.
  5. Network-independent API interface that allows remote printer queries and effective printer-alert handling.

All OS/2 printer drivers developed with a prior DDK version should work without modification when printing to a bidirectional printer.

PM port drivers developed with a prior DDK version should work without modification, but they will not provide bidirectional communication.

The changes to the OS/2 print subsystem:

  • provide precise printer warnings/errors to users
  • keep print jobs until they appear in an output bin
  • provide remote control panel functions for certain printers

Overview

The OS/2 print subsystem has a set of APIs that applications and OS/2 printer drivers can use to communicate bidirectionally with printers. The application interface consists of PrtXXXX APIs supported by the spooler. These APIs support generic query and set commands that allow OS/2 applications to get information about any type of supported printer without having to know the printer's specific command language.

From the set of PrtXXXX APIs, the spooler calls PM port drivers written to support bidirectional printers. The port driver is then responsible for sending data across the physical medium to the printer. A port driver can be written to support a parallel-port attached printer, a network-wire attached printer, or a printer attached in any other way.

Many printers use the same command language and structure for query and response information. In order to limit the amount of code required by new port-driver interfaces, the OS/2 print subsystem allows port drivers to use bidirectional protocol converters to formulate the commands necessary to query and set information in the printer. These protocol converters assist port drivers by interpreting data received by the printer and converting generic query as well as set commands into commands that the printer understands.

                 +----------------+
                 |     OS/2       |
                 | Printer Driver |
                 +----------------+
                         |
                  +--------------+          +---------------+
                  | OS/2 Spooler |--------- | Bidirectional |
                  \--------------/          |   Protocol    |
                                            |   Converter   |
                     |         |            | (PJL or NPAP) |
                     |         |            +---------------+
                     |     +---+---------------+   |
                           |                       |
         +--------------+  | +---------------+     |
         | Network Port | -+ | Parallel Port |     |
         |    Driver    |    |    Driver     | ----+
         +--------------+    +---------------+
                 |                  |
            +---------+       +-------------+      +---------------+
            | Network |       |     OS/2    |      | OS/2 Parallel |
            +---------+       | File System | ---- |  Port Driver  |
                              +-------------+      |    Driver     |
                 |                                 +---------------+
                 |
                 |                                         |

            +---------+                               +---------+
            | Printer |                               | Printer |
            +---------+                               +---------+

Spooler Print APIs

See the OS/2 Presentation Device Driver Reference for detailed information on these APIs, their syntax, and their parameters.

PrtOpen Opens a device for output. Routed to the SplPdOpen() routine in the port driver.
PrtClose Routed to port-driver SplPdClose to close a device connection.
PrtAbort Routed to port driver SplPdAbortDoc to abort operations to the device connection.
PrtAbortDoc Routed to SplPdAbortDoc for document abort processing.
PrtResetAbort Routed to SplPdResetAbort to reset an aborted print job to allow a print driver to send multiple reset sequences to the printer.
PrtWrite Routed to SplPdWrite to write print job data to the device.
PrtDevIOCtl Passes device-specific commands to the print device.
PrtNewPage Routed to port-driver SplPdNewPage to signal the beginning of a new output page.
PrtQuery Routed to port-driver SplPdQuery or protocol-converter SplProtSendCmd to get information about the print device.
PrtSet Routed to port-driver SplPdSet or protocol-converter SplProtSendCmd to set printer device information.

Presentation Manager Port-Driver Interface

A port driver is responsible for transmitting data to a printer and receiving data from a printer. The port driver does not need to know the format of the data - a protocol converter handles the command language for bidirectional printers.

The following API definitions are exported by bidirectional PM port drivers:

  • SplPdOpen
  • SplPdClose
  • SplPdAbortDoc
  • SplPdResetAbort
  • SplPdWrite
  • SplPdNewPage
  • SplPdQuery
  • SplPdSet
  • SplPdEnumPort
  • SplPdGetPortIcon
  • SplPdInstallPort
  • SplPdRemovePort
  • SplPdSetPort

as well as (optionally):

  • SplPdRemoteSetPort
  • SplPdSendCmd

If SplPdOpen is exported by a port driver, then the spooler no longer calls SplPdInitPort or SplPdTermPort for port initialization/termination.

If SplPdSendCmd is exported, the spooler may call the bidirectional-software protocol converter directly, instead of calling SplPdQuery/SplPdSet first. The protocol converter then calls SplPdSendCmd to pass the command data to the printer.

The spooler handles all inter-process communication for the port driver. All calls for sending print jobs, querying, or setting printer configurations are done from the single spooler process.

All structures returned by PrtQuery or passed into PrtSet must use offsets from the beginning of the buffer instead of imbedded pointers. This is done to allow the spooler to perform more efficient inter-process communications.

The following is the flow when a port driver exports SplPdSendCmd:

               |
                 PrtSet
          +---------+
          | Spooler |-----------+
          +---------+           |
                                  SplProtSendCmd
                           +---------------+
                           | Bidirectional |
                           |    Protocol   |
                           |   Converter   |
                           +---------------+
                  +-------------+
                  |
                    SplPdSendCmd
          +-------------+
          | Port Driver |
          +-------------+

The following is the flow when a port driver does not export SplPdSendCmd:

       |
         PrtSet
  +---------+
  | Spooler |            Send set command to port driver
  +---------+
       |
         SplPdSet
  +-------------+
  | Port Driver |        Gives port driver's SendCmd function address to converter
  +-------------+
       |
         SplProtSendCmd
  +---------+
  | Spooler |            Routes to correct bidirectional protocol converter
  +---------+
       |
         SplProtSendCmd
  +--------------------+
  | Protocol Converter | Generates command string, and sends it to port driver
  +--------------------+
       |
         SendCmd
  +-------------+
  | Port Driver |        Sends protocol-specific commands to printer
  +-------------+
       |
       /
       /
       |
   +---------+
   |         |
   | Printer |
   |         |
   +---------+

See the OS/2 Presentation Device Driver Reference for detailed information on these APIs, their syntax, and their parameters.

SplPdOpen Called by PrtOpen to open a device for output.
SplPdClose Called by PrtClose to close a device connection and free the handle.
SplPdAbortDoc Called by PrtAbort and PrtAbortDoc for document-abort processing.
SplPdResetAbort Called by PrtResetAbort to reset an aborted device handle to allow writes again.
SplPdWrite Called by PrtWrite and SplProtWrite to write data to the device.
SplPdNewPage Called by PrtNewPage to signal the beginning of a new output page.
SplPdQuery Called by PrtQuery to get information about the print device.
SplPdSet Called by PrtSet to set printer device information.
SplPdSendCmd Called by SplProtSendCmd to send protocol-specific commands to the printer.
SplPdEnumPort Called by the print object to enumerate ports supported by this port driver when the port driver does not export the default install-port extended attribute.
SplPdGetPortIcon Returns the resource ID of the icon that represents the ports supported by this port driver.
SplPdInstallPort Called by the print object when the user elects to install a new printer port.
SplPdRemovePort Called by the print object when the user elects to delete an existing printer port.
SplPdSetPort Called by the print object when the user elects to open the settings on a printer port.
SplPdRemoteSetPort Called by the network print object when an administrator elects to open the settings on a server's printer port from a client machine.

Port-Driver Extended Attributes

Port-driver files (with a .PDR extension) can have the following extended attributes to assist the print object in installing the port driver:

DEFAULT_PORT (Optional)
Name of default output port to install from this port driver.
The format is default-port name followed by a semi-colon and the default-port name's description.
For example, "MyPort;A default port called MyPort".
This makes enumerating ports to install from the print object more efficient. Without this EA, the print object has to load and call the port driver's SplPdEnumPort API to display the list of ports available to install.
VENDORNAME (Optional)
Vendor name of the port driver.
This is used to specify the subdirectory from /OS2/DLL in which to install the port driver. If not given, the port driver is installed in the /OS2/DLL directory.
REQUIREDVENDORFILES (Optional unless other files are required)
Comma-separated list of extra files required for correct operation of the driver.
These files are copied into the VENDORNAME directory. If this EA is not given, only the port-driver file (.PDR) is copied into the /OS2/DLL directory when installing the port driver.
For example, REQUIREDVENDORFILES=MYHELPER.DLL,MYNET.HLP.
OPTIONALDRIVERFILES (Optional)
Names of extra files that are optional - that is, files not required for correct operation, such as OPTIONALDRIVERFILES=LASERJET.SYM, LASERJET.MAP.
These files are copied into the driver directory, which is a subdirectory under /OS2/DLL.
REQUIREDDRIVERFILES (Required for multi-file drivers)
Names of extra files required by the driver for correct operation, such as REQUIREDDRIVERFILES=LASERJET.DRV.
These files are copied into the driver directory, which is a subdirectory under /OS2/DLL.
CLASSNAME (Optional)
Name of the output stream created by the hardcopy driver, such as CLASSNAME=PCL.
REQUIREDCLASSFILES (Optional unless the driver requires other files)
Names of extra files required for correct operation by the driver files in the CLASSNAME directory, such as REQUIREDCLASSFILES=GENERIC.DLL.
These files are copied into the CLASSNAME directory.
OPTIONALCLASSFILES (Optional)
Names of extra files that are optional, that is, files not required for correct operation, such as OPTIONALCLASSFILES=*.FNT, GENERIC.SYM, GENERIC.MAP.
These files are copied into the CLASSNAME directory.
OPTIONALVENDORFILES (Optional)
Comma-separated list of extra files that are not required for correct driver operation.
These files are copied into the VENDORNAME directory.
For example, OPTIONALVENDORFILES=MYNET.SYM.
.ICON (Optional)
Gives the icon definition used by the workplace shell to display the port objects for this port driver.
This extended attribute is automatically created during the build process if the ICON keyword is used with a resource identifier of 1 or if the DEFAULTICON keyword is used in the RC file for the port driver.
For example, .ICON 1 PORT.ICO.
.VERSION (Optional)
Can be used to indicate the version number of a port driver.

For example, .VERSION=30.001.

Port-Installation Steps

The user opens an existing print object's settings notebook's "Output Port" settings page to install a new port or port driver or to update an existing port driver. This settings page has two buttons-one for installing a new port, and the other for updating an existing port driver.

To update an existing port driver, the following steps are taken:

  1. If the "Update Port Driver" button is selected, the print object checks for the spooler being enabled.
  2. If the spooler is enabled, the user
is told that port drivers cannot be updated while the spooler is enabled
is asked if they want to disable the spooler at this time.
3.If the spooler is disabled, the user:
is asked for the path to an updated port driver
must select a port driver from the list of port drivers that exist on that path.

To install a new printer port, the following steps are taken:

1.If the user selects the "Install New Port" pushbutton or selects the "Install" menu item from an existing port object, a dialog box appears listing all ports available to be installed by the existing port drivers. Each port driver can export the extended attribute DEFAULT_PORT - whose contents are the port name followed by a semi-colon and the port description. This is the name and description of a default new port to install for this port driver.
If the port driver does not have this extended attribute, the print object calls SplPdEnumPort to list ports available to be installed for this port driver.
The port driver can also export the extended attribute ".ICON"-which contains the port driver's icon for the new port to install. This avoids loading and calling a port driver just to display the ports available to be installed by the port driver.
There are two radio buttons in this install dialog box. The default is selected to install a new port from an existing port driver, the other button is selected to install a new port driver. If the "Install New Portdriver" is selected, the user is prompted for the path to the new port driver. The print object uses the above method to determine the installable ports.
2.When the user selects a port object to install, the print object installs the port driver-if it is a new port driver, by copying the port driver and its files and then adding the port driver entry to the system initialization file.
3.The print object calls the port driver's SplPdInstallPort API with the default port name selected by the user.
The port driver can display a configuration dialog to the user at this time. This configuration dialog can be similar to the one displayed by SplPdSetPort. The print object does not call SplPdSetPort during port installation; it is up to each port driver to decide whether or not a configuration dialog is needed when installing a new port.
If the port driver decides to display a configuration dialog during SplPdInstallPort, the port driver may need to add a "virtual" port name to the system. This is needed to allow PrtQuery and PrtSet commands to be sent to the port-driver code running under the spooler process. It is suggested that the port driver start a background thread to issue the PrtQuery and PrtSet commands, since the port driver might not even be initialized under the spooler process yet.

Once the configuration dialog is complete, the port name configured by the user can be installed and the virtual port can be removed by the port driver.

At this point, the port selected by the user is installed and ready to connect to a print object.

Virtual-Printer Ports

The spooler allows applications and port drivers to communicate with printers that are not connected to the print subsystem in OS/2. This is done by allowing the port driver to use a virtual port to connect to non-OS /2 printers. This allows query and set commands to be sent to these printers, but the OS/2 print subsystem is not allowed to select these printer ports for output.

An application can add a virtual-printer port by calling SplCreatePort. The application must know extra information about the port driver to be allowed to create a virtual port-such as the network adapter address-and this information is passed into SplCreatePort.

When SplCreatePort is called with a unique port name, the spooler calls the port driver with command BIDI_ADD_VIRTUAL_PORT, passing the data buffer supplied by the application. If this is the first port installed by the port driver, the port driver is first called with BIDI_INIT_PORTDRV to allow the port driver to initialize. If this is successful, the port driver is called to add the virtual port.

Once the virtual port is created, the application can issue queries to the printer using the virtual-port name. When the application no longer needs a virtual port, the application should remove the port by calling SplDeletePort. This causes the spooler to call the port driver with command BIDI_DEL_VIRTUAL_PORT.

Virtual ports allow use of the existing port-driver and protocol-converter interfaces while not allowing the user to select the virtual port as an output port. These virtual ports do not get stored in the INI file, and they are deleted when the spooler is disabled or when the system is rebooted.

See the OS/2 Presentation Device Driver Reference for detailed information on these APIs, their syntax, and their parameters.

SplCreatePort Called to create a printer port.
SplDeletePort Called to remove a printer port.

Sample Bidirectional Infrared Port Driver

A port driver is only responsible for transmitting data to and from the printer; it is not responsible for interpreting the information being sent to or returned by the printer. A PM port driver is a DLL that is used to communicate with a printer. The PM port-driver interface now passes all print job data to the port driver and allows the port driver to return information sent by the printer. Example port drivers are the:

  • /OS2/DLL/PARALLEL.PDR for parallel attached printers
  • /OS2/DLL/SERIAL.PDR for COM devices

While different manufacturers may use unique protocols to connect to their printers, developers can write a port driver for a set of printers that use a unique communications mechanism. For example, a port driver can be written for network-wire attached printers.

Our sample infrared port driver, when compiled with the target BIDIDBG, logs informational messages into a file called "C:\ALERT.INF". This is to help with debugging only. To disable logging to this file, change the makefile so that it no longer defines compile-flag DEBUG_ALERT.

The sample infrared port driver can be changed to support a different communications media. Make copies of our port-driver makefile and source; then, change the string "INFRARED" in the names of these files to a string that matches the new port driver being developed-e.g., "MYDRIVER".

Here is a code roadmap:

help.c Initializes and calls help functions. To enable easier loading, this dynamically loads HELPMGR.DLL instead of linking to the help manager.
infrared.c Handles all communication with the infrared device. This code uses some callback functions to interface with the LMDLL.DLL infrared helper DLL. This should be replaced with code specific to the communications media used by your new port driver.
init.c Contains DLL initialization and port-driver initialization routines. This code loads LMDLL.DLL when needed by the port driver.
pdrapi.c Public-entry points for port driver. This contains all the public SplPd APIs for the port driver.
util.c Utility functions for the port driver, including memory-allocation and global semaphore-access routines.

Sample Bidirectional Parallel Port Driver

A port driver is only responsible for transmitting data to and from the printer; it is not responsible for interpreting the information being sent to or returned by the printer. A PM port driver is a DLL that is used to communicate with a printer. The PM port-driver interface now passes all print job data to the port driver and allows the port driver to return information sent by the printer. Example port drivers are the:

  • \OS2\DLL\PARALLEL.PDR for parallel attached printers
  • \OS2\DLL\SERIAL.PDR for COM devices

While different manufacturers may use unique protocols to connect to their printers, developers can write a port driver for a set of printers that use a unique communications mechanism. For example, a port driver can be written for network wire-attached printers.

Our sample parallel port driver, when compiled with the target BIDIDBG, logs informational messages to the debug terminal if the flag /C1 (or /C2 for COM2) is added to the "DEVICE=X:\OS2\PMDD.SYS" statement in CONFIG.SYS. This is to help with debugging only. To disable logging to this file, change the makefile so that it no longer defines compile flag DEBUG_ALERT.

The sample parallel port driver can be changed to support a different communications media. Make copies of the port-driver makefile and source; then, change the string "PARALLEL" in the names of these files to a string that matches the new port driver being developed, for example, "MYDRIVER".

Here is a code roadmap:

help.c Initializes and calls help functions. To enable easier loading, this dynamically loads HELPMGR.DLL instead of linking to the help manager.
par1284.c Handles all communication with the parallel attached printer. This code uses IOCtls supported by the PAR1284.SYS bidirectional parallel device driver. This should be replaced with code specific to the communications media used by your new port driver.

For more information on the IOCtl commands supported, refer to the Category 05H Parallel Port Control IOCtl Commands found in the Physical Device Driver Reference.

init.c Contains DLL initialization and port-driver initialization routines.
pdrapi.c Public entry points for port driver. This contains all the public SplPd APIs for the port driver.
util.c Utility functions for the port driver, including memory-allocation and global semaphore-access routines.

Bidirectional Protocol Converters

A protocol converter is responsible for generating the commands to issue to the printer and for being able to interpret data sent by the printer.

The spooler(PMSPL.DLL) exports two new entry points, as does each bidirectional protocol converter:

  • SplProtSendCmd-route generic Query or Set request to bidirectional-software protocol-specific converter to create command, send command to printer, and wait for printer response.
  • SplProtXlateCmd-convert printer to host message into generic bidirectional command/response packets.

Protocol converters may optionally export SplProtWrite if they need to wrap print job data in a bidirectional-software protocol header.

The spooler handles all inter-process communication for the protocol converter. All calls for sending print jobs, querying, or setting printer configurations are done from the single spooler process.

See the OS/2 Presentation Device Driver Reference for detailed information on these APIs, their syntax, and their parameters.

SplProtWrite|Optional API called by PrtWrite to put a bidirectional protocol wrapper around print data being sent to the printer.
SplProtSendCmd Called by PrtQuery, PrtSet, SplPdQuery, or SplPdSet to convert generic commands and pass bidirectional protocol-specific commands to the printer.
SplProtXlateCmd Called only by port drivers when processing the BIDI_WAIT_ALERT(8016h) command to convert a protocol specific message received from the printer into a generic command structure that can be used by the port driver and spooler.

Protocol-Converter Extensions

The spooler allows protocol converters to be written that make use of a converter already installed on the system. These are extensions to the base protocol converters, and they are called before the base converter is called to allow the extension converters to handle specific printers better than the base converter.

A protocol extension is installed by writing the following to the system INI file:

ApplicationName = "PM_PROTOCOL_EXTENSION"
KeyName = Name of new protocol converter; ex: "MYNPAEXT"
KeyValue = Base protocol converter name followed by a comma and the path to this new protocol converter.
ex: "NPAP,C:\OS2\DLL\MYNPAEXT.CNV"

The initialization sequence for protocol-converter extensions is as follows:

  1. Spooler sends BIDI_Q_PORT to port driver.
  2. Spooler determines base converter to use based on PRTPORT structure.
  3. Spooler starts BIDI_WAIT_ALERT thread for port driver (if none started for the port driver yet).
  4. Spooler sends BIDI_INIT_PROTCNV to base protocol converter.
  5. Spooler calls each extension to the base protocol converter with BIDI_INIT_PROTCNV. If the extension converter supports this specific printer model, it returns 0 for success; otherwise, it returns ERROR_NOT_SUPPORTED.

The spooler uses the first extension to the base protocol converter that returns success. The sequence of calling protocol-converter extensions is indeterminate.

Once an extension converter is loaded for a port, all protocol-converter commands go to the extension converter. The extension converter has the option of calling the base protocol converter to process the command.

Sample Protocol Converter

A protocol converter is a DLL written to support printers capable of returning information to the host PC. A protocol converter is responsible for understanding the control language of the printer. Example control languages are Network Printer Alliance Protocol (NPAP), Printer Job Language (PJL), and Simple Network Management Protocol (SNMP). The protocol converter does not get involved in the physical transmission of commands or data to the printer. The converter generates appropriate printer commands to send to the printer, and the converter is able to translate data sent from the printer to the host PC.

Developers can write a usable protocol converter if they have a port driver that can communicate with a set of printers.

Our sample protocol converter does not support any control language. It uses strings as examples of data sent and received from a printer. An actual protocol converter would use printer control-language commands to query or set information in the printer.

Make copies of our protocol-converter makefile and source; then, change the string "PROTCNV" in the names of these files to a string that matches the new protocol converter being developed-e.g., "MYCNV". The OS2SYS.INI entry to install the sample protocol converter is:

Application - "PM_PROTOCOL_CONVERTER"
KeyName - "PROTCNV" // Change to new converter name
KeyValue - "C:\OS2\DLL\PROTCNV.CNV" // Change to path of new converter

Here is a code roadmap:

buildcmd.c Routines to build commands to send to the printer.
cache.c Routines to store information returned by the printer in the protocol converter's cache.
getcmd.c Routines to get response data from the protocol converter's cache.
init.c DLL initialization and termination routines.
passthru.c Routines to handle a bidirectional passthru session.
proctype.c Routines to process the ulType field to determine whether to get data from the cache or to ask the printer for the information.
sendcmd.c Exports SplProtSendCmd API, which is the main entry point for the protocol converter. For each command, this generates the control-language buffer to send to the port driver for transmission to the printer.
util.c Utility functions for the converter, including memory-allocation and global semaphore-access routines.
xlatecmd.c Exports SplProtXlateCmd API, which the port driver calls when data is received from the printer. These routines interpret the data coming from the printer and look for threads that may be waiting for this data.

Calling Sequence

                                                       Route Alert +---
                                                 +----------------------+
      +------------------+                       |        +--------+    |
      | Printer Driver   |                       | Spooler|Alert Thread |
      | (or Application) |                       |        |             |
      +------------------+                       +----------------------+
          |                                           |
 PrtQuery |     | Query response                      |   | Returned alert
                |                            PrtQuery |   |
        +----------+                                      |
        |       +--| ----------------------+       +---------+
        | Spooler  |-------+               |       | Spooler |
        +----------+       |               |       +---------+
                           |               |          |
                           |               |          +---+-------+
                                           |              |
                        +--------------------------------------------+
                        | Protocol     Wait for     Cache Data Clear |
                        | Converter    Response     Waiting Threads  |
                        |              (Alert)                       |
        SplPdSendCmd    +--------------------------------------------+
           +---------------+                                   +--+
           |                               |              +--------+
               +---------------------------+                   |   |
      +-------------+                                              |
      | Port Driver |                                     +-------------+
      +-------------+                                     | Port Driver |
           |                                              +-------------+
  DosWrite |   |                                               |   | Returned
       +------------+                                 DosRead  |   | Alert data
       | FileSystem |                                              |
       +------------+                                      +------------+
           |                                               | FileSystem |
           |   |                                           +------------+
               |                                               |
      +---------------+                                        |   |
      |    Kernal     |                                            |
      | Device Driver |                                   +---------------+
      +---------------+                                   |    Kernal     |
                                                          | Device Driver |
                                                          +---------------+

For a query request, the following happens:

  PrtQuery(BIDI_Q_INPUTBINS)
    SplProtSendCmd()              // Spooler calls bidirectional
                                  // converter directly.
      if(InputBins information is cached in protocol converter)
        return(cached information)
      do
        Create command sequence to query printer
        SplPdSendCmd(in port driver) to send query command to printer
          DosWrite(send command sequence to printer)
        do
          wait on alert thread
        while(response not yet received)

        At this point, the information requested from printer is in
        the protocol converter's information cache.

      while(more commands need to be issued to satisfy query)
      copy cached information into SplProtSendCmd buffer and return

The following is the initialization sequence for a bidirectional printer port:

         --- Spooler thread 1 ---
  SplPdSet( BIDI_INIT_PORTDRV )            // Initialize port driver.
                                           // If it fails, the port driver is unloaded.
                                           // Network port drivers can open the
                                           // network adapter at this time.

  SplPdQuery( BIDI_Q_PORT )                // Initialize port driver for this port and
                                           // get device ID and protocol used by the printer.

  if query successful and printer is bidirectional capable

    Create Spooler thread 2 to wait for alerts from the port driver

    SplProtSendCmd( BIDI_INIT_PROTCNV )    // Initializes protocol converter.

    SplPdSet( BIDI_RESPONSE_FMT )          // Tell port driver the format of
                                           // printer-to-host messages.

    SplProtSendCmd( BIDI_Q_RESPONSE_FMT )  // Get format of printer-to-host
                                           // messages from protocol converter.

    SplProtSendCmd( BIDI_Q_SW )            // Get bidirectional-software characteristics.

    SplPdSet( BIDI_SET_SW )                // Tell port driver the bidirectional capabilities.

    SplProtSendCmd( BIDI_ENABLE_ALERT )    // Enable core printer alerts.

    SplPdSendCmd()                         // Port driver sends alert-enable commands
                                           // that were generated by the protocol converter.

    SplPdSet( BIDI_NOTIFY_PORT_SELECTED )  // Tell port driver that this port
                                           // is connected to a print queue.

    ....
         --- Spooler thread 2 (one instance per port driver) ---
    do

      PrtQuery( BIDI_WAIT_ALERT )                  // Single spooler thread to get all alerts.

        SplPdQuery( BIDI_WAIT_ALERT )              // PM port driver called to wait for
                                                   // data from any of its printer ports.

          SplProtXlateCmd( in PMSPL.DLL )          // Port driver calls protocol-converter
                                                   // routine to translate alert data received
                                                   // from printer.

            SplProtXlateCmd( protocol converter )  // Translate software protocol-specific
                                                   // alert data into generic alert structure.

              Convert to generic-alert structure

              Update protocol converter's information cache

              if any thread is waiting on response for this port

                Clear Protocol Converter's Port wait semaphore

              return PRTALERT structure to Port Driver, then to spooler

      if Alert

        call alert handling routines in PMSPL

          pass alert to registered applications, take action on the alert, etc...

    while( port driver assigned to printer port )

The following is the termination sequence when the spooler is disabled:

SplProtSendCmd(BIDI_SHUTDOWN) // Tell protocol converter that the
                              // spooler is being shutdown.
SplPdSet(BIDI_SHUTDOWN)       // Tell port driver that the
                              // spooler is being shutdown.

The following is the sequence when a printer port is removed:

SplProtSendCmd(BIDI_DEL_PORT) // Tell protocol converter that
                              // the port has been removed.
SplPdSet(BIDI_DEL_PORT)       // Tell port driver that
                              // port has been removed.

Control-Panel Functions

The print object adds a menu item to the print object and port object to display a control panel if the support for the control panel is in the system. The following APIs add the support for control panels.

See the OS/2 Presentation Device Driver Reference for detailed information on these APIs, their syntax, and their parameters.

SplRegisterControlPanel
Registers a DLL with the spooler that can display a control panel for printers.
SplGetControlPanelList
Queries installed control panel DLLs and protocol converters for support for the printer attached to the given port.
SplQueryControlPanel
Queries specific control panel DLL or protocol converter for support for the printer attached to the given port.
SplDisplayControlPanel
Displays a control panel for a printer.

Separator-Page Processing

The spooler alters the PRTOPENSTRUCT0->ulSpoolerJobID by adding the value 0x10000 to the spooler job ID when sending the separator page. This gives the protocol converter a unique spooler job ID with the separator print job. This is done to assist the protocol converter in choosing a unique printer job ID in case the printer does not support printer job IDs.

Alert Handling

There are two types of alerts that can be returned by the printer:

  • core alerts
  • extended alerts

Core alerts are those alerts that the OS/2 print subsystem can understand and take action on when encountered. These include cover open, out of paper, and job completed printing.

Extended alerts are those alerts that the print subsystem either does not understand or is incapable of responding to. These alerts often have very specific information sent by the printer to the host PC.

The spooler, by default, enables the core alerts when printing to a bidirectional-capable printer. This allows the spooler to take advantage of the capabilities of the advanced printer. Other applications might be interested in these core alerts.

Applications might also be written that can request additional status or information from a particular set of printers. This additional information can be considered an extended alert. The application must know what extended alerts are supported, and this is done by calling PrtQuery(BIDI_Q_CONVERTER_INFO) to determine the protocol converter being used. It is up to the protocol converter to publish any extended-alert definitions it wants to make available to applications or printer drivers.

When an application registers for an alert, the spooler enables that alert in the printer by calling PrtSet(BIDI_ENABLE_ALERT). When the alert is generated, the spooler posts a message to those applications that have registered for the alert. If the alert is an extended alert, the application must call PrtQuery(BIDI_READ_ALERT) to get the extended-alert information. The format of this returned information is defined by the protocol converter supporting the extended alert.

The spooler mediates what alerts are activated on the printer when multiple clients have applications that register for alerts from the same printer. The spooler only posts an alert to those applications that request the particular alert.

Once an extended alert is activated (by an application calling SplRegister), if an event occurs that causes the extended alert:

  • the spooler posts the application that the extended alert has occurred
  • the application must use the command BIDI_READ_ALERT to get the alert data

The spooler issues BIDI_READ_ALERT to save the alert data until the registered application(s) ask for the alert. However, this data is not be kept indefinitely by the spooler.

The following is done to handle printer alerts:

  • SplRegister API available to request alert notification from the spooler.
  • SplUnRegister API available to terminate alert notification from the spooler.
  • PrtQuery(BIDI_READ_ALERT) available to applications to read extended-alert information.

When an application calls SplRegister, the spooler determines whether or not the alert being registered is enabled. If it is not enabled yet, the spooler calls the protocol converter with BIDI_ENABLE_ALERT to enable the alert.

When an application calls SplUnRegister, the spooler determines whether or not the alert being unregistered should be disabled. If another application has registered for this alert, the spooler keeps the alert enabled; otherwise, the spooler calls the protocol converter with BIDI_ DISABLE_ALERT to disable the alert.

See the OS/2 Presentation Device Driver Reference for detailed information on these APIs, their syntax, and their parameters.

SplRegister Allows applications to register for notification of events about printers.
SplUnRegister Allows applications to deregister for notification of events about printers.

Bidirectional Command Structures Format

All structures used by PrtQuery and PrtSet use offsets into the return buffer for variable-length data instead of returning pointers. This is done to make transmission of these bidirectional structures easier between processes and machines.

An example would be a structure that returns a description string. The field that references the storage location of this string would be a ULONG. If zero, then that string was not returned. If non-zero, the field is the offset, from the beginning of the output buffer, to the description string.

All structures with variable-length data should pack this data immediately following all the fixed-length structures. This way, *pcbOutData contains the number of bytes to copy from the beginning of the pOutData buffer.

Note: All returned structure offset fields are offsets from the beginning of the output buffer given, not offsets from the beginning of the structure.

Bidirectional Command Flow

The following are tables of query and set command flows, showing which part of the system gets called for each command.

A First a Second

These lists assume that the port driver exports SplPdSendCmd.

Query Command Command Code (Hex) Sent to Port Driver Sent to Protocol Converter Initial Sequence Number Description
BIDI_Q_CONVERTER_INFO 8021 First Requests protocol-converter information.
BIDI_Q_DEVICE 800D Second First Requests printer device characteristics. Returns capabilities and characteristics of the printer.
BIDI_Q_FONTS 8012 Second First Requests list of fonts for a particular interpreter.
BIDI_Q_INPUTBINS 800F Second First Queries input bins.
BIDI_Q_INTERPRETER 800E Second First Requests information on interpreters available in the printer.
BIDI_Q_JOBID 8017 First Requests printer job ID for job being sent to port. Issued by the spooler during PrtOpen after a successful SplPdOpen to the port driver so that the spooler can synchronize the printer job ID with the spooler job ID.
BIDI_Q_JOBS_COMPLETE 8013 Second First Requests list of completed jobs known to the printer.
BIDI_Q_JOBS_QUEUED 8014 Second First Requests list of queued jobs known to the printer.
BIDI_Q_OUTPUTBINS 8010 Second First Queries output bins.
BIDI_Q_PORT 800B First 2 Requests information about port configuration from port driver. Returns bidirectional capabilities of the port and bidirectional-software protocol of attached printer.
BIDI_Q_PORTDRV 8019 First Requests information on port driver's configuration for the port. Free-format buffer.
BIDI_Q_RESPONSE_FMT 8018 First 5 Requests format of printer-to-host messages.
BIDI_Q_SPOOLER_VERSION 8022 Requests spooler-version information. Handled by spooler.
BIDI_Q_STATUS 8015 Second First Queries protocol converter for the current status of the print device.
BIDI_Q_STORAGE 8023 Second First Returns printer storage-media information.
BIDI_Q_SW 800C First 7 Requests information on bidirectional-software capabilities. Passed to the bidirectional protocol converter to tell the system what bidirectional capabilities are available in the printer.
BIDI_READ_ALERT 801D First Requests extended alert information from the protocol converter.
BIDI_READ_PASSTHRU 8001 First Send protocol-specific command to the printer.
BIDI_WAIT_ALERT 8016 First 3 Wait for an alert. One thread per port driver.
Query Command Command Code (Hex) Sent to Port Driver Sent to Protocol Converter Initial Sequence Number Description
BIDI_ADD_VIRTUAL_PORT 26 First Add a virtual-printer port.
BIDI_CANCELJOB 6 Second First Cancel job in printer.
BIDI_DEL_PORT 28 Second First Sent to the protocol converter and port driver after a spooler port has been removed.
BIDI_DEL_VIRTUAL_PORT 27 First Delete a virtual-printer port.
BIDI_DISABLE_ALERT 25 Second First Disable alerts for a printer.
BIDI_ENABLE_ALERT 24 Second First 9 Enable alerts for a printer.
BIDI_END_PASSTHRU 1B First Terminate a passthru reading session.
BIDI_ENDJOB 3 Second First Issued by port driver during SplPdClose to signal the end of the print job being sent to the printer.
BIDI_HOLDJOB 4 Second First Hold job in printer.
BIDI_INIT B First Set printer in bidirectional mode.
BIDI_INIT_PORTDRV 8 First 1 Initialize a port driver. Once per PortDRV.
BIDI_INIT_PROTCNV F First 4 Initialize protocol converter for a port. Given Q_PORT for each port.
BIDI_NOTIFY_ENDJOBCONNECT 2 First Notify port driver that a job connection is not needed.
BIDI_NOTIFY_PORT_RELEASED 22 First Notify port driver that a port is no longer selected in a print object.
BIDI_NOTIFY_PORT_SELECTED 21 First 10 Notify port driver that a port is selected in a print object.
BIDI_PACKET_SIZE E Second First Define printer-to-host maximum packet size.
BIDI_RELEASEJOB 5 Second First Release job held in printer.
BIDI_RESET 9 Second First Reset the printer.
BIDI_RESPONSE_FMT D First 6 Set format of printer-to-host messages.
BIDI_SEND_PASSTHRU 1 Second First Issue protocol-specific command to printer.
BIDI_SET_DEVICE_ID 23 First Set printer's device ID.
BIDI_SET_PORTDRV 19 First Store port-driver configuration data. Free-format buffer.
BIDI_SET_SW 10 First 8 Tell the port driver the bidirectional software capabilities of the printer.
BIDI_SHUTDOWN A Second First Release all threads waiting for this protocol converter and port driver. Sent to both CNV and PDR.
BIDI_STARTJOB 2 Second First Called during SplPdOpen to signal the protocol converter about the start of the print job.
BIDI_START_PASSTHRU 1A First Start a passthru reading session.
BIDI_TERM C Second First Set printer in unidirectional mode.