The printer is accessible through /dev/lp0;
in the same way, the parallel port itself is accessible through
/dev/parport0. The difference is in the
level of control that you have over the wires in the parallel port
cable. With the printer driver, a user-space program (such as the printer
spooler) can send bytes in "printer protocol".
Briefly, this means that for each byte, the eight data lines are
set up, then a "strobe" line tells the printer to
look at the data lines, and the printer sets an
"acknowledgement" line to say that it got the byte.
The printer driver also allows the user-space program to read
bytes in "nibble mode", which is a way of
transferring data from the peripheral to the computer half a byte
at a time (and so it's quite slow).
In contrast, the ppdev driver (accessed via
/dev/parport0) allows you to:
examine status lines,
set control lines,
set/examine data lines (and control the direction of the data
lines),
wait for an interrupt (triggered by one of the status lines),
find out how many new interrupts have occurred,
set up a response to an interrupt,
use IEEE 1284 negotiation (for telling peripheral which transfer
mode, to use)
transfer data using a specified IEEE 1284 mode.
Programming interface The ppdev interface is largely the same as that
of other character special devices, in that it supports
open, close,
read, write, and
ioctl. The constants for the
ioctl commands are in
include/linux/ppdev.h.
Starting and stopping: open and close The device node /dev/parport0 represents any
device that is connected to parport0, the
first parallel port in the system. Each time the device node is
opened, it represents (to the process doing the opening) a
different device. It can be opened more than once, but only one
instance can actually be in control of the parallel port at any
time. A process that has opened
/dev/parport0 shares the parallel port in
the same way as any other device driver. A user-land driver may
be sharing the parallel port with in-kernel device drivers as
well as other user-land drivers.
Control: ioctl Most of the control is done, naturally enough, via the
ioctl call. Using
ioctl, the user-land driver can control both
the ppdev driver in the kernel and the
physical parallel port itself. The ioctl
call takes as parameters a file descriptor (the one returned from
opening the device node), a command, and optionally (a pointer
to) some data.
PPCLAIMClaims access to the port. As a user-land device driver
writer, you will need to do this before you are able to
actually change the state of the parallel port in any way.
Note that some operations only affect the
ppdev driver and not the port, such as
PPSETMODE; they can be performed while
access to the port is not claimed.
PPEXCLInstructs the kernel driver to forbid any sharing of the port
with other drivers, i.e. it requests exclusivity. The
PPEXCL command is only valid when the
port is not already claimed for use, and it may mean that the
next PPCLAIM ioctl
will fail: some other driver may already have registered
itself on that port.
Most device drivers don't need exclusive access to the port.
It's only provided in case it is really needed, for example
for devices where access to the port is required for extensive
periods of time (many seconds).
Note that the PPEXCL
ioctl doesn't actually claim the port
there and then---action is deferred until the
PPCLAIM ioctl is
performed.
PPRELEASEReleases the port. Releasing the port undoes the effect of
claiming the port. It allows other device drivers to talk to
their devices (assuming that there are any).
PPYIELDYields the port to another driver. This
ioctl is a kind of short-hand for
releasing the port and immediately reclaiming it. It gives
other drivers a chance to talk to their devices, but
afterwards claims the port back. An example of using this
would be in a user-land printer driver: once a few characters
have been written we could give the port to another device
driver for a while, but if we still have characters to send to
the printer we would want the port back as soon as possible.
It is important not to claim the parallel port for too long,
as other device drivers will have no time to service their
devices. If your device does not allow for parallel port
sharing at all, it is better to claim the parallel port
exclusively (see PPEXCL).
PPNEGOTPerforms IEEE 1284 negotiation into a particular mode.
Briefly, negotiation is the method by which the host and the
peripheral decide on a protocol to use when transferring data.
An IEEE 1284 compliant device will start out in compatibility
mode, and then the host can negotiate to another mode (such as
ECP).
The ioctl parameter should be a pointer
to an int; values for this are in
incluce/linux/parport.h and include:
IEEE1284_MODE_COMPAT
IEEE1284_MODE_NIBBLE
IEEE1284_MODE_BYTE
IEEE1284_MODE_EPP
IEEE1284_MODE_ECP
The PPNEGOT ioctl
actually does two things: it performs the on-the-wire
negotiation, and it sets the behaviour of subsequent
read/write calls so
that they use that mode (but see
PPSETMODE).
PPSETMODESets which IEEE 1284 protocol to use for the
read and write
calls.
The ioctl parameter should be a pointer
to an int.
PPGETMODERetrieves the current IEEE 1284 mode to use for
read and write.
PPGETTIMERetrieves the time-out value. The read
and write calls will time out if the
peripheral doesn't respond quickly enough. The
PPGETTIME ioctl
retrieves the length of time that the peripheral is allowed to
have before giving up.
The ioctl parameter should be a pointer
to a struct timeval.
PPSETTIMESets the time-out. The ioctl parameter
should be a pointer to a struct
timeval.
PPGETMODESRetrieves the capabilities of the hardware (i.e. the
modes field of the
parport structure).
PPSETFLAGSSets flags on the ppdev device which can
affect future I/O operations. Available flags are:
PP_FASTWRITE
PP_FASTREAD
PP_W91284PIC
PPWCONTROLSets the control lines. The ioctl
parameter is a pointer to an unsigned char, the
bitwise OR of the control line values in
include/linux/parport.h.
PPRCONTROLReturns the last value written to the control register, in the
form of an unsigned char: each bit corresponds to
a control line (although some are unused). The
ioctl parameter should be a pointer to an
unsigned char.
This doesn't actually touch the hardware; the last value
written is remembered in software. This is because some
parallel port hardware does not offer read access to the
control register.
The control lines bits are defined in
include/linux/parport.h:
PARPORT_CONTROL_STROBE
PARPORT_CONTROL_AUTOFD
PARPORT_CONTROL_SELECT
PARPORT_CONTROL_INIT
PPFCONTROLFrobs the control lines. Since a common operation is to
change one of the control signals while leaving the others
alone, it would be quite inefficient for the user-land driver
to have to use PPRCONTROL, make the
change, and then use PPWCONTROL. Of
course, each driver could remember what state the control
lines are supposed to be in (they are never changed by
anything else), but in order to provide
PPRCONTROL, ppdev
must remember the state of the control lines anyway.
The PPFCONTROL ioctl
is for "frobbing" control lines, and is like
PPWCONTROL but acts on a restricted set
of control lines. The ioctl parameter is
a pointer to a struct
ppdev_frob_struct:
struct ppdev_frob_struct {
unsigned char mask;
unsigned char val;
};
The mask and
val fields are bitwise ORs of
control line names (such as in
PPWCONTROL). The operation performed by
PPFCONTROL is:
new_ctr = (old_ctr & ~mask) | val;
In other words, the signals named in
mask are set to the values in
val.
PPRSTATUSReturns an unsigned char containing bits set for
each status line that is set (for instance,
PARPORT_STATUS_BUSY). The
ioctl parameter should be a pointer to an
unsigned char.
PPDATADIRControls the data line drivers. Normally the computer's
parallel port will drive the data lines, but for byte-wide
transfers from the peripheral to the host it is useful to turn
off those drivers and let the peripheral drive the
signals. (If the drivers on the computer's parallel port are
left on when this happens, the port might be damaged.)
This is only needed in conjunction with
PPWDATA or
PPRDATA.
The ioctl parameter is a pointer to an
int. If the int is zero, the
drivers are turned on (forward direction); if non-zero, the
drivers are turned off (reverse direction).
PPWDATASets the data lines (if in forward mode). The
ioctl parameter is a pointer to an
unsigned char.
PPRDATAReads the data lines (if in reverse mode). The
ioctl parameter is a pointer to an
unsigned char.
PPCLRIRQClears the interrupt count. The ppdev
driver keeps a count of interrupts as they are triggered.
PPCLRIRQ stores this count in an
int, a pointer to which is passed in as the
ioctl parameter.
In addition, the interrupt count is reset to zero.
PPWCTLONIRQSet a trigger response. Afterwards when an interrupt is
triggered, the interrupt handler will set the control lines as
requested. The ioctl parameter is a
pointer to an unsigned char, which is interpreted
in the same way as for PPWCONTROL.
The reason for this ioctl is simply
speed. Without this ioctl, responding to
an interrupt would start in the interrupt handler, switch
context to the user-land driver via poll
or select, and then switch context back
to the kernel in order to handle
PPWCONTROL. Doing the whole lot in the
interrupt handler is a lot faster.
Transferring data: read and write Transferring data using read and
write is straightforward. The data is
transferring using the current IEEE 1284 mode (see the
PPSETMODE ioctl). For
modes which can only transfer data in one direction, only the
appropriate function will work, of course.
Waiting for events: poll and select The ppdev driver provides user-land device
drivers with the ability to wait for interrupts, and this is done
using poll (and select,
which is implemented in terms of poll).
When a user-land device driver wants to wait for an interrupt, it
sleeps with poll. When the interrupt
arrives, ppdev wakes it up (with a
"read" event, although strictly speaking there is
nothing to actually read).
Examples Presented here are two demonstrations of how to write a simple
printer driver for ppdev. Firstly we will
use the write function, and after that we
will drive the control and data lines directly.
The first thing to do is to actually open the device.
int drive_printer (const char *name)
{
int fd;
int mode; /* We'll need this later. */
fd = open (name, O_RDWR);
if (fd == -1) {
perror ("open");
return 1;
}
Here name should be something along the lines
of "/dev/parport0". (If you don't have any
/dev/parport files, you can make them with
mknod; they are character special device nodes
with major 99.)
In order to do anything with the port we need to claim access to
it.
if (ioctl (fd, PPCLAIM)) {
perror ("PPCLAIM");
close (fd);
return 1;
}
Our printer driver will copy its input (from
stdin) to the printer, and it can do that it
one of two ways. The first way is to hand it all off to the
kernel driver, with the knowledge that the protocol that the
printer speaks is IEEE 1284's "compatibility"
mode.
/* Switch to compatibility mode. (In fact we don't need
* to do this, since we start off in compatibility mode
* anyway, but this demonstrates PPNEGOT.)
mode = IEEE1284_MODE_COMPAT;
if (ioctl (fd, PPNEGOT, &mode)) {
perror ("PPNEGOT");
close (fd);
return 1;
}
for (;;) {
char buffer[1000];
char *ptr = buffer;
size_t got;
got = read (0 /* stdin */, buffer, 1000);
if (got < 0) {
perror ("read");
close (fd);
return 1;
}
if (got == 0)
/* End of input */
break;
while (got > 0) {
int written = write_printer (fd, ptr, got);
if (written < 0) {
perror ("write");
close (fd);
return 1;
}
ptr += written;
got -= written;
}
}
The write_printer function is not pictured
above. This is because the main loop that is shown can be used
for both methods of driving the printer. Here is one
implementation of write_printer:
ssize_t write_printer (int fd, const void *ptr, size_t count)
{
return write (fd, ptr, count);
}
We hand the data to the kernel-level driver (using
write) and it handles the printer
protocol.
Now let's do it the hard way! In this particular example there is
no practical reason to do anything other than just call
write, because we know that the printer talks
an IEEE 1284 protocol. On the other hand, this particular example
does not even need a user-land driver since there is already a
kernel-level one; for the purpose of this discussion, try to
imagine that the printer speaks a protocol that is not already
implemented under Linux.
So, here is the alternative implementation of
write_printer (for brevity, error checking
has been omitted):
ssize_t write_printer (int fd, const void *ptr, size_t count)
{
ssize_t wrote = 0;
while (wrote < count) {
unsigned char status, control, data;
unsigned char mask = (PARPORT_STATUS_ERROR
| PARPORT_STATUS_BUSY);
unsigned char val = (PARPORT_STATUS_ERROR
| PARPORT_STATUS_BUSY);
struct ppdev_frob_struct frob;
struct timespec ts;
/* Wait for printer to be ready */
for (;;) {
ioctl (fd, PPRSTATUS, &status);
if ((status & mask) == val)
break;
ioctl (fd, PPRELEASE);
sleep (1);
ioctl (fd, PPCLAIM);
}
/* Set the data lines */
data = * ((char *) ptr)++;
ioctl (fd, PPWDATA, &data);
/* Delay for a bit */
ts.tv_sec = 0;
ts.tv_nsec = 1000;
nanosleep (&ts, NULL);
/* Pulse strobe */
frob.mask = PARPORT_CONTROL_STROBE;
frob.val = PARPORT_CONTROL_STROBE;
ioctl (fd, PPFCONTROL, &frob);
nanosleep (&ts, NULL);
/* End the pulse */
frob.val = 0;
ioctl (fd, PPFCONTROL, &frob);
nanosleep (&ts, NULL);
wrote++;
}
return wrote;
}
To show a bit more of the ppdev interface,
here is a small piece of code that is intended to mimic the
printer's side of printer protocol.
for (;;)
{
int irqc;
int busy = nAck | nFault;
int acking = nFault;
int ready = Busy | nAck | nFault;
char ch;
/* Set up the control lines when an interrupt happens. */
ioctl (fd, PPWCTLONIRQ, &busy);
/* Now we're ready. */
ioctl (fd, PPWCONTROL, &ready);
/* Wait for an interrupt. */
{
fd_set rfds;
FD_ZERO (&rfds);
FD_SET (fd, &rfds);
if (!select (fd + 1, &rfds, NULL, NULL, NULL))
/* Caught a signal? */
continue;
}
/* We are now marked as busy. */
/* Fetch the data. */
ioctl (fd, PPRDATA, &ch);
/* Clear the interrupt. */
ioctl (fd, PPCLRIRQ, &irqc);
if (irqc > 1)
fprintf (stderr, "Arghh! Missed %d interrupt%s!\n",
irqc - 1, irqc == 2 ? "s" : "");
/* Ack it. */
ioctl (fd, PPWCONTROL, &acking);
usleep (2);
ioctl (fd, PPWCONTROL, &busy);
putchar (ch);
}
And here is an example (with no error checking at all) to show how
to read data from the port, using ECP mode, with optional
negotiation to ECP mode first.
{
int fd, mode;
fd = open ("/dev/parport0", O_RDONLY | O_NOCTTY);
ioctl (fd, PPCLAIM);
mode = IEEE1284_MODE_ECP;
if (negotiate_first) {
ioctl (fd, PPNEGOT, &mode);
/* no need for PPSETMODE */
} else {
ioctl (fd, PPSETMODE, &mode);
}
/* Now do whatever we want with fd */
close (0);
dup2 (fd, 0);
if (!fork()) {
/* child */
execlp ("cat", "cat", NULL);
exit (1);
} else {
/* parent */
wait (NULL);
}
/* Okay, finished */
ioctl (fd, PPRELEASE);
close (fd);
}