hpoj reference: ptal-mlcd

The ptal-mlcd daemon implements the HP MLC (Multiple Logical Channels) and IEEE 1284.4 packetized transport protocols over parallel ports and USB, and enables access to the various MLC/1284.4 services on the device, such as print, scan, and PML. Applications use the PTAL frontend API to access local or remote devices, and libptal accesses locally-connected devices controlled by ptal-mlcd through the use of Unix-domain sockets, which, unlike TCP/IP sockets, are not accessible across a network.

Currently, ptal-mlcd performs parallel-port I/O by accessing the hardware registers and performing the IEEE 1284 ECP- and nibble-mode signalling directly from user mode, with no kernel assistance. In most cases it now makes use of bounded ECP mode and hardware assistance for faster ECP transfers, by writing/reading bytes to/from the ECP FIFO registers. However, it doesn't make use of hardware interrupts or DMA, given that it runs in user mode.

On the other hand, ptal-mlcd's USB support depends on libusb and/or the Linux kernel USB printer-class driver (printer.o). libusb is required in order to access all device functions on certain "composite USB" models and to work around SMP issues in printer.o, but otherwise the two methods should work equally well. See the hpoj Supported Devices page for more information about requirements for different models.

Click here for more information on setting up basic device connectivity with the hpoj software.


The ptal-mlcd command-line syntax is as follows:
	ptal-mlcd [mlc:]bus:name [options...]

General notes

ptal-mlcd and ptal-printd are typically called by ptal-init, and it's generally unnecessary to invoke them manually from the command line except for development or debugging purposes.

A ptal-mlcd process exists in one of several overall states. When startup is complete, it is in the inactive state. When an application tries to access the device, ptal-mlcd starts to activate, or in other words, establish an MLC/1284.4 communication session with the device. Once that is complete, ptal-mlcd is in the active state. If a communication error happens, which could be the result of a power-off or disconnection of the device or a protocol error, ptal-mlcd deactivates, which includes closing all active application sessions and returning to the inactive state.

For each ptal-mlcd instance, there is a main process that services application requests and implements the MLC/1284.4 protocols. When ptal-mlcd activates as described above, it forks a sub-process which handles low-level I/O to/from the device and passes data to/from the main process using anonymous pipes. In addition, when operating in composite USB mode, it forks a separate sub-process to service application connections to the raw 7/1/2 print interface outside of the MLC/1284.4 session; this sub-process exits when the application closes the connection or when ptal-mlcd deactivates.

ptal-mlcd special-cases the PML (Peripheral Management Language) service on the device, by keeping open a single PML channel while activated, virtualizing application requests to open the PML service, and multiplexing PML requests (gets and sets but not yet traps) from possibly multiple applications over a single PML session with the device. On the other hand, if you specify the -nopml switch, then this behavior is disabled, and only one application may open PML at a time but has exclusive access to the device's PML service.

Parallel-specific notes

The typical invocation for ptal-mlcd on parallel, set up automatically by "ptal-init setup", is something like the following (split into multiple lines for clarity):
	ptal-mlcd mlc:par:OfficeJet_K80 [-remconsole] \
		-devidmatch "MDL:OfficeJet K80;" \
		-devidmatch "SERN:000000000010;" \
		-base 0x378 -basehigh 0x778 -device /dev/lp0
Specifying the -device switch is recommended if your system has kernel parallel-port support, because it gives ptal-mlcd a mechanism to lock the parallel port and prevent other processes from opening it and interfering with its signalling. Just make sure you specify the right device node, or it won't do any good. Also, unlike for USB, do not specify multiple device nodes, including using wildcards.

In order to take advantage of ptal-mlcd's new hardware-assisted parallel-port I/O, you must configure your parallel port to ECP in your BIOS setup.

USB-specific notes

The typical invocation for ptal-mlcd on USB, set up automatically by "ptal-init setup", is something like the following (split into multiple lines for clarity):
	ptal-mlcd mlc:usb:OfficeJet_K80 [-remconsole] \
		-devidmatch "MDL:OfficeJet K80;" \
		-devidmatch "SERN:000000000010;" \
		-device "/dev/usb/lp*"
This means that whenever ptal-mlcd tries to activate, it will open up each USB printer device node in turn (including the implicitly-globbed libusb devices), search the device ID string for the specified string(s) (normally the model field and possibly also the serial number field), and use the first matching device it finds.

Alternatively, if you only have one USB-connected printer device, you can use a simpler command line such as the following:

	ptal-mlcd mlc:usb:0 -device /dev/usb/lp0 -noglobusb [-remconsole]
-noglobusb is required in this case to prevent the implicit globbing of libusb devices and guarantee that you'll really get the device you want. This usage is not recommended, however, because if you add another USB printer, you'll find that the /dev/usb/lp0 device node assignment may change over time, depending on what order the devices are connected and powered on. Even if you have multiple USB printers with only one connected and powered on at a time, having a separate instance of the daemons for each model makes it easier to have different print queues/drivers and scanning profiles set up for the different devices as needed.

Raw printing directly to the 7/1/2 print interface (on a composite-USB device) is very slow if done through libusb instead of Linux printer.o. libusb and/or Linux usbdevfs don't indicate how many bytes were successfully transferred at a timeout condition, which can happen if the printer is busy or out of paper. Therefore, this communication path had to be reduced to sending only one "runt" USB packet (63 or 31 bytes) at a time, to prevent data corruption when recovering from timeout conditions.

User interface

ptal-mlcd includes the following user-interface features: ptal-mlcd supports the following "virtual" services through ptal-connect: ptal-mlcd logs various kinds of messages to standard output (which may not be visible unless started in a particular terminal window) and syslog (/var/log/messsages): Here is a sample log message followed by an explanation of the components:
	ptal-mlcd: ERROR at ExMgr.cpp:871, dev=<mlc:usb:OfficeJet_K80@/dev/usb/lp0>, pid=17306, e=19 t=1064276197
ptal-mlcd's debug console is accessible in either of the following ways: When the debug console is active, the following commands may be used:

File-system usage

ptal-mlcd creates Unix-domain sockets in the directory /var/run/ptal-mlcd with the filename based on the first command-line parameter, such as par:OfficeJet_Series_700 or usb:OfficeJet_G85.

When libptal first opens the Unix-domain socket, it exchanges several request/reply packets with ptal-mlcd, which might include getting the device ID string, service name to socket ID lookup, socket ID to service name ("reverse") lookup, and channel open. In most cases, ptal-mlcd attempts to activate if it's not already active before processing one of these requests. After a successful open reply, for all practical purposes the connection is then a pass-through connection to the requested service on the device.

ptal-mlcd maintains a fixed-maximum-size table of various kinds of "session" structures. Whenever a new connection is received from the named socket, it is assigned to a "command session", which handles the request/reply command interaction with libptal. When an open request is received, the command session is linked with either a "transport session" or virtual "PML session", depending on whether the open request was for a peripheral socket ID corresponding to the PML service (which is virtualized) or for a different service. As the open is processed the linked sessions go through several state transitions together. If/when the open succeeds, the connection is fully transferred from the command session to the transport or PML session, but if the open fails then the linked session is freed and the connection stays with the command session ready for possibly other commands.

ptal-mlcd depends on the /dev/null "bit-bucket" device for several purposes. First of all, it substitutes /dev/null for the standard input, output, and/or error file descriptors if they aren't already open, which is the case when invoked from a hotplug script (not recommended). Second, it substitutes /dev/null file descriptors for sessions which don't have an associated file descriptor (such as the master PML session) or sessions which have been closed by libptal but are not ready to be completely torn down by ptal-mlcd (such as when ptal-mlcd gets an error writing to a session's file descriptor, which is not the right state for tearing down the session). The basic requirements for the /dev/null device are that select() should indicate ready to read and write, read() should return an end-of-file condition (zero bytes read), and write() should always "succeed" in writing all of the requested bytes.

Class hierarchy

ParPort: ExMgr: ExTransport:

Porting considerations

The ExTransport classes were ported directly from the USB I/O firmware of the HP JetDirect 175X external print server, which is a VxWorks-based embedded system. The porting task was greatly simplified because the code has almost no system-level dependencies. It was designed to run in a task context with other code, so it doesn't block on anything. The rest of the necessary functionality was largely re-written for Linux and (for better or for worse) dumped into the ExMgr class, with an auxilliary ParPort class. The following system-level assumptions are made by ptal-mlcd: