FreeBSD Handbook : Printing : Simple Printer Setup : Software Setup
Previous: Hardware Setup
Next: Using Printers

7.4.2. Software Setup

This section describes the software setup necessary to print with the LPD spooling system in FreeBSD.

Here is an outline of the steps involved:

  1. Configure your kernel, if necessary, for the port you are using for the printer; section Kernel Configuration tells you what you need to do.
  2. Set the communications mode for the parallel port, if you are using a parallel port; section Setting the Communication Mode for the Parallel Port gives details.
  3. Test if the operating system can send data to the printer. Section Checking Printer Communications gives some suggestions on how to do this.
  4. Set up LPD for the printer by modifying the file /etc/printcap. Section The /etc/printcap File shows you how.

7.4.2.1. Kernel Configuration

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 is not already configured for one.

To find out if the kernel you are currently using supports a serial interface, type

dmesg | grep sioN
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 lptN
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 are 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.

Adding /dev Entries for the Ports

Even though the kernel may support communication along a serial or parallel port, you will still need a software interface through which programs running on the system can send and receive data. That is what entries in the /dev directory are for.

To add a /dev entry for a port:

  1. Become root with the su command. Enter the root password when prompted.
  2. Change to the /dev directory:
    cd /dev
    
  3. Type
    ./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.
  4. Type
    ls -l port
    to make sure the device entry got created.

Setting the Communication Mode for the Parallel Port

When you are 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:

  1. Edit your kernel configuration file. Look for or add an lpt0 entry. If you are setting up the second parallel port, use lpt1 instead. Use lpt2 for the third port, and so on.
  2. Save the file. Then configure, build, and install the kernel, then reboot. See kernel configuration for more details.

To set the communications mode with lptcontrol:

You could put these commands in your /etc/rc.local file to set the mode each time your system boots. See lptcontrol(8) for more information.

Checking Printer Communications

Before proceeding to configure the spooling system, you should make sure the operating system can successfully send data to your printer. It is a lot easier to debug printer communication and the spooling system separately.

To test the printer, we will 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 will 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

Note: When this document refers to a printer language, I am assuming a language like PostScript, and not Hewlett Packard's PCL. Although PCL has great functionality, you can intermingle plain text with its escape sequences. PostScript cannot directly print plain text, and that is the kind of printer language for which we must make special accommodations.

Checking a Parallel Printer

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:

  1. Become root with su.
  2. Send data to the printer.

You should see something print. Do not worry if the text does not look right; we will fix such things later.

Checking a Serial Printer

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:

  1. Become root with su.
  2. Edit the file /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 is 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
    

  3. Connect to the printer with tip. Type:
    tip printer
    
    If this step does not work, edit the file /etc/remote again and try using /dev/cuaaN instead of /dev/ttydN.
  4. Send data to the printer.

You should see something print. Do not worry if the text does not look right; we will fix that later.

7.4.2.2. Enabling the Spooler: The /etc/printcap File

At this point, your printer should be hooked up, your kernel configured to communicate with it (if necessary), and you have been able to send some simple data to the printer. Now, we are 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:

  1. Pick a name (and a few convenient aliases) for the printer, and put them in the /etc/printcap file; see Naming the Printer.
  2. Turn off header pages (which are on by default) by inserting the sh capability; see Suppressing Header Pages.
  3. Make a spooling directory, and specify its location with the sd capability; see Making the Spooling Directory.
  4. Set the /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 is on a serial port, set up the communication parameters with the fs, fc, xs, and xc capabilities; see Configuring Spooler Communications Parameters.
  5. Install a plain text input filter; see Installing the Text Filter
  6. Test the setup by printing something with the lpr command; see Trying It Out and Troubleshooting.

Note: Language-based printers, such as PostScript printers, cannot directly print plain text. The simple setup outlined above and described in the following sections assumes that if you are installing such a printer you will 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 are installing such a printer and want to be able to print jobs in the printer language and print plain text jobs, you are 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 Accommodating Plain Text Jobs on PostScript Printers tells how to do this.

Naming the Printer

The first (easy) step is to pick a name for your printer. It really does not 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 do not 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 is common practice to make the last alias for a printer be a full description of the printer, including make and model.

Once you have 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:

In this example, the first printer is named rattan and has as aliases line, diablo, lp, and Diablo 630 Line Printer. Since it has the alias lp, it is also the default printer. The second is named bamboo, and has as aliases ps, PS, S, panasonic, and Panasonic KX-P4455 PostScript v51.4.

Suppressing Header Pages

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 will suppress header pages.

To suppress header pages, add the sh capability to the entry for the printer in /etc/printcap. Here is 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:

Note how we used the correct format: the first line starts in the leftmost column, and subsequent lines are indented with a single TAB. Every line in an entry except the last ends in a backslash character.

Making the Spooling Directory

The next step in the simple spooler setup is to make a spooling directory, a directory where print jobs reside until they are printed, and where a number of other spooler support files live.

Because of the variable nature of spooling directories, it is customary to put these directories under /var/spool. It is not necessary to backup the contents of spooling directories, either. Recreating them is as simple as running mkdir.

It is also customary to make the directory with a name that is 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 will 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 are concerned about the privacy of jobs that users print, you might want to protect the spooling directory so it is 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 will 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:

Note that the name of the printer starts in the first column but all other entries describing the printer should be indented with a tab and each line escaped with a backslash.

If you do not specify a spooling directory with sd, the spooling system will use /var/spool/lpd as a default.

Identifying the Printer Device

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 us 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 do not specify the lp capability for a printer in your /etc/printcap file, LPD uses /dev/lp as a default. /dev/lp currently does not exist in FreeBSD.

If the printer you are 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.

Configuring Spooler Communication Parameters

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

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.

For more information on the bits for the 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 us add to our example printer on the sixth serial port. We will set the bps rate to 38400. For the flag bits, we will set the TANDEM, ANYP, LITOUT, FLUSHO, and PASS8 flags. For the local mode bits, we will 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:

Installing the Text Filter

We are 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, perform 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 us 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

Make the file executable:
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 will 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:

Trying It Out

You have reached the end of the simple LPD setup. Unfortunately, congratulations are not quite yet in order, since we still have 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 are 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 are 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 did not work, see the next section, Troubleshooting.

Troubleshooting

After performing the simple test with lptest, you might have gotten one of the following results instead of the correct printout:

It worked, after awhile; or, it did not eject a full sheet.

The printer printed the above, but it sat for awhile and did nothing. In fact, you might have 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 is also useful to make sure each print job ends on a full sheet, so the next job does not 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

It produced the ``staircase effect.''

You got the following on paper:

!"#$%&'()*+,-./01234
                        "#$%&'()*+,-./012345
                                                #$%&'()*+,-./0123456
You have 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 is what the carriage return is for: to move the location of the next character to print to the left edge of the paper.

Here is 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:

It overprinted each line.

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

The printer lost characters.

While printing, the printer did not print a few characters in each line. The problem might have gotten worse as the printer ran, losing more and more characters.

The problem is that the printer cannot keep up with the speed at which the computer sends data over a serial line. (This problem should not occur with printers on parallel ports.) There are two ways to overcome the problem:

It printed garbage.

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.

Nothing happened.

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 are debugging in the /etc/printcap file. For example, here is 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 do not specify a lf capability, LPD uses /dev/console as a default.


FreeBSD Handbook : Printing : Simple Printer Setup : Software Setup
Previous: Hardware Setup
Next: Using Printers