This section describes the software setup necessary to print with the LPD spooling system in FreeBSD.
Here's an outline of the steps involved:
/etc/printcap
. Section
The /etc/printcap File
shows you how.
The operating system kernel is compiled to work with a specific set of devices. The serial or parallel interface for your printer is a part of that set. Therefore, it might be necessary to add support for an additional serial or parallel port if your kernel isn't already configured for one.
To find out if the kernel you're currently using supports a serial interface, type
dmesg | grep sio
N
where N is the number of the serial port, starting
from zero. If you see output similar to the following
sio2 at 0x3e8-0x3ef irq 5 on isa
sio2: type 16550A
then the kernel supports the port.
To find out if the kernel supports a parallel interface, type
dmesg | grep lpt
N
where N is the number of the parallel port, starting
from zero. If you see output similar to the following
lpt0 at 0x378-0x37f on isa
then the kernel supports the port.
You might have to reconfigure your kernel in order for the operating system to recognize and use the parallel or serial port you're using for the printer.
To add support for a serial port, see the section on kernel configuration. To add support for a parallel port, see that section and the section that follows.
/dev
Entries for the Ports
Even though the kernel may support communication along
a serial or parallel port, you'll still need a software
interface through which programs running on the system
can send and receive data. That's what entries in the
/dev
directory are for.
To add a /dev
entry for a port:
su
command. Enter
the root password when prompted.
/dev
directory:
cd /dev
./MAKEDEV
port
where port is the device entry for the port you
want to make. Use lpt0
for the first parallel
port, lpt1
for the second, and so on; use
ttyd0
for the first serial port, ttyd1
for
the second, and so on.
ls -l
port
to make sure the device entry got created.
When you're using the parallel interface, you can choose whether FreeBSD should use interrupt-driven or polled communication with the printer.
The interrupt-driven method is somewhat faster but uses up a precious IRQ line. You should use whichever one works.
You can set the communications mode in two ways: by
configuring the kernel or by using the lptcontrol
program.
To set the communications mode by configuring the kernel:
lpt0
entry. If you're setting up the
second parallel port, use lpt1
instead. Use
lpt2
for the third port, and so on.
irq
specifer:
device lpt0 at isa? port? tty irq N vector lptintr
where N is the IRQ number for your
computer's parallel port.
irq
specifier:
device lpt0 at isa? port? tty vector lptintr
To set the communications mode with
lptcontrol
:
lptcontrol -i -u N
to set interrupt-driven mode for lptN
.
lptcontrol -p -u N
to set polled-mode for lptN
.
/etc/rc.local
file to set the mode each time
your system boots. See lptcontrol(8) for more
information.
Before proceeding to configure the spooling system, you should make sure the operating system can successfully send data to your printer. It's a lot easier to debug printer communication and the spooling system separately.
To test the printer, we'll send some text to it. For
printers that can immediately print characters sent to
them, the program lptest
is perfect: it generates
all 96 printable ASCII characters in 96 lines.
For a PostScript (or other language-based) printer, we'll need a more sophisticated test. A small PostScript program, such as the following, will suffice:
%!PS 100 100 moveto 300 300 lineto stroke 310 310 moveto /Helvetica findfont 12 scalefont setfont (Is this thing working?) show showpage
This section tells you how to check if FreeBSD can communicate with a printer connected to a parallel port.
To test a printer on a parallel port:
su
.
lptest
. Type:
lptest > /dev/lptN
where N is the number of the parallel
port, starting from zero.
cat > /dev/lptN
Then, line by line, type the program
carefully as you can't edit a line once
you've pressed RETURN or ENTER. When you've
finished entering the program, press
CONTROL+D, or whatever your end of file key
is.
Alternatively, you can put the program in a file and type
cat file > /dev/lptN
where file is the name of the file
containing the program you want to send to
the printer.
You should see something print. Don't worry if the text doesn't look right; we'll fix such things later.
This section tells you how to check if FreeBSD can communicate with a printer on a serial port.
To test a printer on a serial port:
su
.
/etc/remote
. Add the
following entry:
printer:dv=/dev/port:br#bps-rate:pa=parity
where port is the device entry for the serial
port (ttyd0
, ttyd1
, etc.), bps-rate
is the bits-per-second rate at which the printer
communicates, and parity is the parity
required by the printer (either even
,
odd
, none
, or zero
).
Here's a sample entry for a printer connected via a serial line to the third serial port at 19200 bps with no parity:
printer:dv=/dev/ttyd2:br#19200:pa=none
tip
. Type:
tip printer
If this step doesn't work, edit the file
/etc/remote
again and try using
/dev/cuaaN
instead of
/dev/ttydN
.
lptest
. Type:
~$lptest
Alternatively, you can put the program in a file and type
~>file
where file is the name of the file
containing the program. After tip
sends the file, press any required
end-of-file key.
You should see something print. Don't worry if the text doesn't look right; we'll fix that later.
/etc/printcap
File
At this point, your printer should be hooked up, your kernel configured to communicate with it (if necessary), and you've been able to send some simple data to the printer. Now, we're ready to configure LPD to control access to your printer.
You configure LPD by editing the file
/etc/printcap
. The LPD spooling system reads
this file each time the spooler is used, so updates to the
file take immediate effect.
The format of the printcap
file is straightforward.
Use your favorite text editor to make changes to
/etc/printcap
. The format is identical to other
capability files like /usr/share/misc/termcap
and
/etc/remote
. For complete information about the
format, see the cgetent(3).
The simple spooler configuration consists of the following steps:
/etc/printcap
file; see
Naming the Printer
.
sh
capability; see
Suppressing Header Pages
.
sd
capability; see
Making the Spooling Directory
.
/dev
entry to use for the
printer, and note it in /etc/printcap
with
the lp
capability; see
Identifying the Printer Device
. Also, if the
printer's on a serial port, set up the communication
parameters with the fs
, fc
, xs
, and
xc
capabilities; see
Configuring Spooler Communications Parameters
.
lpr
command; see
Trying It Out
and
Troubleshooting
.
Note: Language-based printers, such as PostScript printers, can't directly print plain text. The simple setup outlined above and described in the following sections assumes that if you're installing such a printer you'll print only files that the printer can understand.
Users often expect that they can print plain text to any of the printers installed on your system. Programs that interface to LPD to do their printing usually make the same assumption. If you're installing such a printer and want to be able to print jobs in the printer language and print plain text jobs, you're strongly urged to add an additional step to the simple setup outlined above: install an automatic plain-text--to--PostScript (or other printer language) conversion program. Section Accomodating Plain Text Jobs on PostScript Printers tells how to do this.
The first (easy) step is to pick a name for your printer. It really doesn't matter whether you choose functional or whimsical names since you can also provide a number aliases for the printer.
At least one of the printers specified in the
/etc/printcap
should have the alias
lp
. This is the default printer's name. If users
don't have the PRINTER environment variable nor
specify a printer name on the command line of any of the
LPD commands, then lp
will be the default printer
they get to use.
Also, it's common practice to make the last alias for a printer be a full description of the printer, including make and model.
Once you've picked a name and some common aliases, put
them in the /etc/printcap
file. The name of
the printer should start in the leftmost column.
Separate each alias with a vertical bar and put a colon
after the last alias.
In the following example, we start with a skeletal
/etc/printcap
that defines two printers (a
Diablo 630 line printer and a Panasonic KX-P4455
PostScript laser printer):
# # /etc/printcap for host rose # rattan|line|diablo|lp|Diablo 630 Line Printer: bamboo|ps|PS|S|panasonic|Panasonic KX-P4455 PostScript v51.4:
rattan
and has as aliases line
, diablo
, lp
, and
Diablo 630 Line Printer
. Since it has the alias
lp
, it's also the default printer. The second is
named bamboo
, and has as aliases ps
, PS
,
S
, panasonic
, and Panasonic KX-P4455
PostScript v51.4
.
The LPD spooling system will by default print a header page for each job. The header page contains the user name who requested the job, the host from which the job came, and the name of the job, in nice large letters. Unfortunately, all this extra text gets in the way of debugging the simple printer setup, so we'll suppress header pages.
To suppress header pages, add the sh
capability to
the entry for the printer in
/etc/printcap
. Here's the example
/etc/printcap
with sh
added:
# # /etc/printcap for host rose - no header pages anywhere # rattan|line|diablo|lp|Diablo 630 Line Printer:\ :sh: bamboo|ps|PS|S|panasonic|Panasonic KX-P4455 PostScript v51.4:\ :sh:
The next step in the simple spooler setup is to make a spooling directory, a directory where print jobs reside until they're printed, and where a number of other spooler support files live.
Because of the variable nature of spooling directories,
it's customary to put these directories under
/var/spool
. It's not necessary to backup the
contents of spooling directories, either. Recreating
them is as simple as running mkdir
.
It's also customary to make the directory with a name that's identical to the name of the printer, as shown below:
mkdir /var/spool/printer-name
However, if you have a lot of printers on your network,
you might want to put the spooling directories under a
single directory that you reserve just for printing with
LPD. We'll do this for our two example printers
rattan
and bamboo
:
mkdir /var/spool/lpd
mkdir /var/spool/lpd/rattan
mkdir /var/spool/lpd/bamboo
Note: If you're concerned about the privacy of jobs that users print, you might want to protect the spooling directory so it's not publicly accessible. Spooling directories should be owned and be readable, writable, and searchable by user daemon and group daemon, and no one else. We'll do this for our example printers:
chown daemon.daemon /var/spool/lpd/rattan
chown daemon.daemon /var/spool/lpd/bamboo
chmod 770 /var/spool/lpd/rattan
chmod 770 /var/spool/lpd/bamboo
Finally, you need to tell LPD about these directories
using the /etc/printcap
file. You specify the
pathname of the spooling directory with the sd
capability:
# # /etc/printcap for host rose - added spooling directories # rattan|line|diablo|lp|Diablo 630 Line Printer:\ :sh:sd=/var/spool/lpd/rattan: bamboo|ps|PS|S|panasonic|Panasonic KX-P4455 PostScript v51.4:\ :sh:sd=/var/spool/lpd/bamboo:
If you don't specify a spooling directory with sd
,
the spooling system will use /var/spool/lpd
as
a default.
In section
Adding /dev Entries for the Ports
, we identified which
entry in the /dev
directory FreeBSD will use
to communicate with the printer. Now, we tell LPD
that information. When the spooling system has a job
to print, it will open the specified device on behalf
of the filter program (which is responsible for
passing data to the printer).
List the /dev
entry pathname in the
/etc/printcap
file using the lp
capability.
In our running example, let's assume that rattan
is
on the first parallel port, and bamboo
is on a
sixth serial port; here are the additions to
/etc/printcap
:
# # /etc/printcap for host rose - identified what devices to use # rattan|line|diablo|lp|Diablo 630 Line Printer:\ :sh:sd=/var/spool/lpd/rattan:\ :lp=/dev/lpt0: bamboo|ps|PS|S|panasonic|Panasonic KX-P4455 PostScript v51.4:\ :sh:sd=/var/spool/lpd/bamboo:\ :lp=/dev/ttyd5:
If you don't specify the lp
capability for a
printer in your /etc/printcap
file, LPD uses
/dev/lp
as a default. /dev/lp
currently doesn't exist in FreeBSD.
If the printer you're installing is connected to a parallel port, skip to the section Installing the Text Filter . Otherwise, be sure to follow the instructions in the next section.
For printers on serial ports, LPD can set up the bps rate, parity, and other serial communication parameters on behalf of the filter program that sends data to the printer. This is advantageous since
/etc/printcap
file; you don't have to
recompile the filter program.
The following /etc/printcap
capabilities
control serial communication parameters of the device
listed in the lp
capability:
br#bps-rate
Sets the communications speed of the device to bps-rate, where bps-rate can be 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, 9600, 19200, or 38400 bits-per-second.
fc#clear-bits
Clears the flag bits clear-bits in the
sgttyb
structure after opening the device.
fs#set-bits
Sets the flag bits set-bits in the sgttyb
structure.
xc#clear-bits
Clears local mode bits clear-bits after opening the device.
xs#set-bits
Sets local mode bits set-bits.
fc
,
fs
, xc
, and xs
capabilities, see the file
/usr/include/sys/ioctl_compat.h
.
When LPD opens the device specified by the lp
capability, it reads the flag bits in the sgttyb
structure; it clears any bits in the fc
capability,
then sets bits in the fs
capability, then applies
the resultant setting. It does the same for the local
mode bits as well.
Let's add to our example printer on the sixth serial port. We'll set the bps rate to 38400. For the flag bits, we'll set the TANDEM, ANYP, LITOUT, FLUSHO, and PASS8 flags. For the local mode bits, we'll set the LITOUT and PASS8 flags:
bamboo|ps|PS|S|panasonic|Panasonic KX-P4455 PostScript v51.4:\
:sh:sd=/var/spool/lpd/bamboo:\
:lp=/dev/ttyd5:fs#0x82000c1:xs#0x820:
We're now ready to tell LPD what text filter to use to
send jobs to the printer. A text filter, also
known as an input filter, is a program that LPD
runs when it has a job to print. When LPD runs the text
filter for a printer, it sets the filter's standard
input to the job to print, and its standard output to
the printer device specified with the lp
capability. The filter is expected to read the job from
standard input, peform any necessary translation for the
printer, and write the results to standard output, which
will get printed. For more information on the text
filter, see section
Filters
.
For our simple printer setup, the text filter can be a
small shell script that just executes /bin/cat
to send the job to the printer. FreeBSD comes with
another filter called lpf
that handles backspacing
and underlining for printers that might not deal with
such character streams well. And, of course, you can
use any other filter program you want. The filter
lpf
is described in detail in section
lpf: a Text Filter
.
First, let's make the shell script
/usr/local/libexec/if-simple
be a simple text
filter. Put the following text into that file with your
favorite text editor:
#!/bin/sh # # if-simple - Simple text input filter for lpd # Installed in /usr/local/libexec/if-simple # # Simply copies stdin to stdout. Ignores all filter arguments. /bin/cat && exit 0 exit 2
chmod 555 /usr/local/libexec/if-simple
And then tell LPD to use it by specifying it with the
if
capability in /etc/printcap
. We'll add
it to the two printers we have so far in the example
/etc/printcap
:
# # /etc/printcap for host rose - added text filter # rattan|line|diablo|lp|Diablo 630 Line Printer:\ :sh:sd=/var/spool/lpd/rattan:\ :lp=/dev/lpt0:\ :if=/usr/local/libexec/if-simple: bamboo|ps|PS|S|panasonic|Panasonic KX-P4455 PostScript v51.4:\ :sh:sd=/var/spool/lpd/bamboo:\ :lp=/dev/ttyd5:fs#0x82000e1:xs#0x820:\ :if=/usr/local/libexec/if-simple:
You've reached the end of the simple LPD setup.
Unfortunately, congratulations are not quite yet in
order, since we've still got to test the setup and
correct any problems. To test the setup, try printing
something. To print with the LPD system, you use the
command lpr
, which submits a job for printing.
You can combine lpr
with the lptest
program,
introduced in section
Checking Printer Communications
to generate some
test text.
To test the simple LPD setup:
Type:
lptest 20 5 | lpr -Pprinter-name
where printer-name is a the name of a printer (or
an alias) specified in /etc/printcap
. To test
the default printer, type lpr
without any -P
argument. Again, if you're testing a printer that
expects PostScript, send a PostScript program in that
language instead of using lptest
. You can do so by
putting the program in a file and typing lpr
file
.
For a PostScript printer, you should get the results
of the program. If you're using lptest
, then your
results should look like the following:
!"#$%&'()*+,-./01234
"#$%&'()*+,-./012345
#$%&'()*+,-./0123456
$%&'()*+,-./01234567
%&'()*+,-./012345678
To further test the printer, try downloading larger
programs (for language-based printers) or running
lptest
with different arguments. For example,
lptest 80 60
will produce 60 lines of 80 characters
each.
If the printer didn't work, see the next section, Troubleshooting .
After performing the simple test with lptest
, you
might've gotten one of the following results instead of
the correct printout:
The printer printed the above, but it sat for awhile and did nothing. In fact, you might've needed to press a PRINT REMAINING or FORM FEED button on the printer to get any results to appear.
If this is the case, the printer was probably waiting to see if there was any more data for your job before it printed anything. To fix this problem, you can have the text filter send a FORM FEED character (or whatever is necessary) to the printer. This is usually sufficient to have the printer immediately print any text remaining in its internal buffer. It's also useful to make sure each print job ends on a full sheet, so the next job doesn't start somewhere on the middle of the last page of the previous job.
The following replacement for the shell script
/usr/local/libexec/if-simple
prints a form
feed after it sends the job to the printer:
#!/bin/sh # # if-simple - Simple text input filter for lpd # Installed in /usr/local/libexec/if-simple # # Simply copies stdin to stdout. Ignores all filter arguments. # Writes a form feed character (\f) after printing job. /bin/cat && printf "\f" && exit 0 exit 2
You got the following on paper:
!"#$%&'()*+,-./01234
"#$%&'()*+,-./012345
#$%&'()*+,-./0123456
You've become another victim of the staircase
effect, caused by conflicting interpretations of
what characters should indicate a new-line.
UNIX-style operating systems use a single character:
ASCII code 10, the line feed (LF). MS-DOS, OS/2,
and others uses a pair of characters, ASCII code 10
and ASCII code 13 (the carriage return or CR).
Many printers use the MS-DOS convention for
representing new-lines.
When you print with FreeBSD, your text used just the line feed character. The printer, upon seeing a line feed character, advanced the paper one line, but maintained the same horizontal position on the page for the next character to print. That's what the carriage return is for: to move the location of the next character to print to the left edge of the paper.
Here's what FreeBSD wants your printer to do:
Printer received CR Printer prints CR
Printer received LF Printer prints CR + LF
Here are some ways to achieve this:
Note: If you boot your system into other operating systems besides FreeBSD, you may have to reconfigure the printer to use a an interpretation for CR and LF characters that those other operating systems use. You might prefer one of the other solutions, below.
fs
capability in the
/etc/printcap
file for the printer.
Here's an example text filter for printers that understand the Hewlett-Packard PCL escape codes. This filter makes the printer treat LF characters as a LF and CR; then it sends the job; then it sends a form feed to eject the last page of the job. It should work with nearly all Hewlett Packard printers.
#!/bin/sh # # hpif - Simple text input filter for lpd for HP-PCL based printers # Installed in /usr/local/libexec/hpif # # Simply copies stdin to stdout. Ignores all filter arguments. # Tells printer to treat LF as CR+LF. Writes a form feed character # after printing job. printf "\033&k2G" && cat && printf "\f" && exit 0 exit 2
Here's an example /etc/printcap
from
a host called orchid. It has a single printer
attached to its first parallel port, a Hewlett
Packard LaserJet 3Si named teak
. It's
using the above script as its text filter:
# # /etc/printcap for host orchid # teak|hp|laserjet|Hewlett Packard LaserJet 3Si:\ :lp=/dev/lpt0:sh:sd=/var/spool/lpd/teak:mx#0:\ :if=/usr/local/libexec/hpif:
The printer never advanced a line. All of the lines of text were printed on top of each other on one line.
This problem is the ``opposite'' of the staircase effect, described above, and is much rarer. Somewhere, the LF characters that FreeBSD uses to end a line are being treated as CR characters to return the print location to the left edge of the paper, but not also down a line.
Use the printer's configuration switches or control panel to enforce the following interpretation of LF and CR characters:
Printer received CR Printer prints CR
Printer received LF Printer prints CR + LF
While printing, the printer didn't print a few characters in each line. The problem might've gotten worse as the printer ran, losing more and more characters.
The problem is that the printer can't keep up with the speed at which the computer sends data over a serial line. (This problem shouldn't occur with printers on parallel ports.) There are two ways to overcome the problem:
fs
capability.
fs
capability. Make sure the cable connecting the
printer to the computer is correctly wired for
carrier flow control.
fs
capability to add appropriate delays
to the stream of data sent to the printer.
The printer printed what appeared to be random garbage, but not the desired text.
This is usually another symptom of incorrect
communications parameters with a serial printer.
Double-check the bps rate in the br
capability,
and the parity bits in the fs
and fc
capabilities; make sure the printer is using the
same settings as specified in the
/etc/printcap
file.
If nothing happened, the problem is probably within
FreeBSD and not the hardware. Add the log file
(lf
) capability to the entry for the printer
you're debugging in the /etc/printcap
file.
For example, here's the entry for rattan
, with
the lf
capability:
rattan|line|diablo|lp|Diablo 630 Line Printer:\
:sh:sd=/var/spool/lpd/rattan:\
:lp=/dev/lpt0:\
:if=/usr/local/libexec/if-simple:\
:lf=/var/log/rattan.log
Then, try printing again. Check the log file (in
our example, /var/log/rattan.log
) to see
any error messages that might appear. Based on the
messages you see, try to correct the problem.
If you don't specify a lf
capability, LPD uses
/dev/console
as a default.