> From [EMAIL PROTECTED] Tue Jun 27 07:36:45 2000
> From: [EMAIL PROTECTED]
> Date: Tue, 27 Jun 2000 15:15:09 +0200
> Subject: LPRng: parallel port printing (anybody)
> To: [EMAIL PROTECTED]
>
> I cannot print on my parallel port (SGI box, IRIX 6.5).
> with all versions from 3.6.14 upto now (3.6.19)
>
> BUT I've just reinstalled LPRng3.6.14beta23 and it prints
> on just this parallel port printer just fine.
>
> So, what's been happening between LPRng3.6.14beta23 and
> more recent versions of LPRng.
>
> Thanks for any hints
>
> Helmut Jarausch
>
> Lehrstuhl fuer Numerische Mathematik
> Institute of Technology, RWTH Aachen
> D 52056 Aachen, Germany
Summary:
parallel port printing support is broken.
Set the ':rw@' flag in the printcap entry for this device
Use the latest version of LPRng and ifhp and see if this helps.
Details:
I have just looked at the differences between the two,
and here is what I have come up with.
(This is a little long, and possibly wrong, but it matches what I
have seen on other systems)
A bit of background on system calls, IO facilities, and the dreaded
fcntl() function. Plus what various systems have in the way of
support. And why I refuse to take responsibility for general
problems with 'birectional parallel port' devices.
When you open a file, device, or socket using the appropriate
open() or socket() call, most UNIX (and other) systems provide
a 'file descriptor' that has a 'standard set of attributes' that
you can manipulate with various system calls.
The ones that I use are:
NONBLOCKING READ - set the 'do not block on read' attribute
so that when I do a read and there is no data, return immediately
NONBLOCKING WRITE - set the 'do not block on write' attribute
so that when I do a write and I fill the internal buffer for
the device I will return with the number of bytes written.
If non bytes were put into the output buffer, return with a
-1 error code, and set the appropriate status in errno to
indicate that I cannot write.
This is from the write(2) man page on most BSD and LINUX systems:
RETURN VALUES
Upon successful completion the number of bytes which were written is re-
turned. Otherwise a -1 is returned and the global variable errno is set
to indicate the error.
ERRORS
Write() and writev() will fail and the file pointer will remain unchanged
if:
[EAGAIN] The file was marked for non-blocking I/O, and no data could
be written immediately.
For all 'sane' devices and files this makes perfect sense.
Most 'reasonable' (you will note the quotes around this) device
driver implementors make the decision to provide a low level buffer
in the device driver that is used by the device driver to hold
data. When a user does a write() to the device, data is extracted
from the user address space and put into the buffer. If the device
has had the NONBLOCKING flag set, then the device will return
immediately with the return value set to the number of bytes copied.
If no bytes could be copied because the buffer was full, then -1
would be returned and the error code set to EAGAIN.
But what about INPUT? Ummm.... well, there are two possiblities:
When you do a read from a parallel port, since it is 'unidirectional'
then you would get an error, return of -1 and errno set to EBADF.
This is the classical, vintage, or legacy behavior, cira 1982.
Or Version 7 PDP11 UNIX. Or BSD 1.0 UNIX. Or ATT Unix.
OR:
You fail during the open with a EACCES if
you tried to open the device for reading. Some systems did this
as well. But most simply ignored the O_RDWR and O_APPEND flags
and only opened the device for writing.
The Following Contains Material Suitable Only For Mature
Device Driver Writers and Kernel Hackers
YOU HAVE BEEN WARNED
Please have your upchuck bags handy
Thank you
Now we come to the wonderful world of the PC, and Bidirectional
Parallel Port devices. Also 'high throughput' parallel port devices.
If you think that I am making this stuff up, the information is
courtesy of Warp Nine Engineering of San Diego, CA, from the
http://www.fapo.com/1284int.htm web page, and from horror stories
exchanged over coffee and donuts over the years with engineers in
the various printer manufacturing companies.
Lets look at the signals that are used on the Parallel Port
Group SPP Signal In/Out Signal Description
Control
nSTROBE Out Active low. Indicates valid data is on the data lines.
nAUTOFEED Out Active low. Instructs the printer to automatically insert a
line feed for each carriage return
nSELECTIN Out Active low. Used to indicate to the printer that it is
selected.
nINIT Out Active low. Used to reset the printer.
Status
nACK In A low asserted pulse used to indicate that the last
character was received.
BUSY In A high signal asserted by the printer to indicate that it
is busy and cannot take data.
PE In Paper Empty
SELECT In Asserted high to indicate that the printer is online.
nERROR In Asserted low to indicate that some error condition exists
Data
DATA[8:1] Out 8 data lines- output only in older SPP
In the early days of the PC, there were a group of people who were
desperately looking for ways to interface hardware to the PC
platform. The problem was that the only way that was possible was
using one of the serial ports. This was limited to 9600 bps, and
TOO SLOW for their applications.
The parallel port had 8 DATA OUTPUT (DATA) signals, STROBE output
that indicated that data was present on the DO signals,
and nACK input that was used by the printer to indicate that it
had accepted the data. There were 4 other status bits as well,
that provided some information about the printer.
Then one day a Silicon Valley logic design engineer
realized that there were 4 input signals available-
BUSY, BE, SELECT and ERROR. Since all they needed was a limited
rate INPUT channel, by clever use of these signals they could
transfer 4 bits (a nybble or nibble, since it was half a byte or
bite) at a time. Two nybbles gave a byte, and there was singing
and dancing in the lab.
So the company whipped up a product and put it on the market.
Soon their competitors ripped it apart, looked at the way that the
transfer was being done, and did it in a TOTALLY DIFFERENT WAY
(Compatibility? Hey, man, we wanna sell OUR stuff, not YOUR stuff).
For example, a not uncommon usual method of using this channel was to use
combinations of the nAUTOFEED, nSELECTIN, and nINIT signals to
control the direction and meaning of the data, and then to use
the 4 input signals for input, and the 8 DATA signals for output.
A command/status sequence would consist of setting, say, the
INIT signal LOW, pulsing STROBE, then sending a fixed number of
bytes to the device on the 8 DATA signals, then reading status
back to the device by using the NIBBLE method.
Now this meant that only one output device could be on the parallel
port at a time, so you had a choice of having a printer on the
parallel port or one of these devices. After another night at
a Janis Joplin concert, somebody realized that you did not need
to use the 'STROBE' signal, you could use some other signal,
and by cleverly 'reassigning' functions to the other signals you
could have one of these bidirectional devices AND a printer
SHARE the same port.
There was singing and ... well ok. There was sitting around with
glazed eyes and muttering 'Cool, man... it really works.'
Now the fun part starts. Each time a byte was written to the
parallel port the CPU would either have to wait in a busy loop
or would have to enable an interrupt which would occur when the
byte was delivered to the printer.
So data transfer was limited by the rate at which interrupts could
be handled. This was solved on the Serial Ports by using a FIFO,
so some folks decided that this should be done the same way on the
parallel port. Now you would get an interrupt only when the FIFO
needed filling. The next step was to make this even faster with
lower overhead by putting in DMA access. Now you only needed to
set a count, the starting address, and pull the starting cord
on the DMA engine to get lots of data transferred very fast.
And, of course, the parallel port was very fast compared to the
serial port, Centronics sold lot of printers, HP sold lots of
Printers, everybody sold lots of printers and everybody was happy.
Except when you got a paper jam or a printer error and you lost
the print job. But this was a RARE event (:-).
Ummm... Oh, yes. Bidirectional input. Ummm... well, the folks
who were using the parallel port for bidirectional input now had
lots of capability for output.
And if they wanted only to do 'send a command, get a response'
type of things, they were OK. And if they had only the printer
active or their device active, they were OK. And if they NEVER
wanted to get asynchronous error indications (i.e. - Help! I need
a floppy!) they were OK.
But what happens if you have a printer AND one of these devices?
The answer is that the printer manufacturers decided that they could
use this to report bidirectional status information from the printer.
And if you are using DMA access or the FIFO thing, then until
the DMA completes or the FIFO drains, you probably will not get
an interrupt. Now the situation is not quite as bad as this,
but you get the idea. This is UGLY stuff, with timeouts, retries,
and bizzarities all over the place.
Select, Nonblocking IO, and Multiple Devices
At this point, I hope that you realize that the parallel port
low level driver is, at any one time, talking (sending/receiving)
to only one device at a time, and CANNOT do simultaneous sending
and receiving. This means that there is NO WAY to cleanly implement
a 'select' for 'read' operations, UNLESS the low level driver
actually runs around, polls the devices, reads the input into
a buffer AND THEN does the magic to tell the processes which posted
the 'select' call that there is input.
So to make this work, you have to 'poll' the printer.
Now we get to the fun part: how much input data should the driver return?
Ummm... well, if the process is posting a read, then either as much
as the read length was OR the length of the data that is read from the
device. When has the device sent all of its data? Ummm... See IEEE 1284
for details of how this is to be done.
Now, in the driver do you block until you get all the data? Or do you want to
return immediately if there is none? And what about that 'NONBLOCKING' mode.
If NONBLOCKING is set then we need to return immediately, but this means that
we will not poll the device for status... Because we don't have a buffer to
put it in...
Well, if we are NOT in NONBLOCKING, i.e. - we are in BLOCKING mode, then
we block until there is data... So this means the process needs to have
an external method of terminating the read, i.e. - with a signal (SIGALRM).
Now if we terminate the read with a signal, what do we return to the read()
call? The number of bytes read and put into the buffer? -1 and errno = EINTR?
So we have the following:
SELECT does not work correctly, so you cannot use select.
You must POLL for status
NONBLOCKING reads MAY or MAY NOT work correctly
(You must have a timeout on the read)
And finally:
Setting the device into NONBLOCKING mode may or MAY NOT work
correctly.
At this point, I give up. The parallel port printer interface is
just too broken for reliable bidirectional use.
----- APPENDIX -----
Lets look at the support provided at the system call level
for parallel port printer support:
------------ FreeBSD ----
DESCRIPTION
The current lpt driver is the port of the original lpt driver to the pp-
bus(4) system.
One purpose of this port was to allow parallel port sharing with other
parallel devices. Secondly, inb()/outb() calls have been replaced by pp-
bus function calls. lpt is now arch-independent thanks to the ppbus in-
terface. See ppbus(4) for more info about the ppbus system.
The parallel port bus is allocated by lpt when the printer device is
opened and released only when the transfer is completed: either when the
device is closed or when the entire buffer is sent in interrupt driven
mode.
The driver can be configured to be either interrupt-driven, or to poll
the printer. Ports that are configured to be interrupt-driven can be
switched to polled mode by using the lptcontrol(8) command.
Depending on your hardware, extended capabilities may be configured with
the lptcontrol(8) command. With an ECP/ISA port, you can take advantage
of FIFO and DMA.
In order to retrieve printer info from /dev/lpt0, just apply the cat com-
mand to the device. If the printer supports IEEE1284 nibble mode and has
data to send to the host, you'll get it.
Ummmm... this is interesting... but how do you KNOW that the
printer has status? Oh... right... you need to POLL the device.
You write a block, then read a block. Umm... What happens if
there is no data? Ummm... good question.
h9: {35} % time cat </dev/lpt0
0.0u 0.0s 0:00.09 0.0% 0+0k 0+0io 0pf+0w
Hurk!!! 10 milliseconds!!! Actually, if you try this in a tight
loop from a C program it appears to be more like 1 millisec. So
the code will have to resemble:
while( data to be sent){
write some data
read status
}
-------------- Linux --------------
LP(4) Special files LP(4)
NAME
lp - line printer devices
SYNOPSIS
#include <linux/lp.h>
DESCRIPTION
The following ioctl(2) calls are supported:
int ioctl(int fd, LPCAREFUL, int arg)
If arg is 0, then the out-of-paper, offline and error signals are
required to be false on all writes, otherwise they are ignored. The
default is to ignore them.
int ioctl(int fd, LPGETSTATUS, int *arg)
Stores the value of the status port in arg. The bits have the fol�
lowing meaning:
LP_PBUSY inverted busy input, active high
LP_PACK unchanged acknowledge input, active low
LP_POUTPA unchanged out-of-paper input, active high
LP_PSELECD unchanged selected input, active high
LP_PERRORP unchanged error input, active low
Refer to your printer manual for the meaning of the signals. Note
that undocumented bits may also be set, depending on your printer.
How interesting - you can set the status and get it.
Now, of course, how can you READ the status? Or can you
read the status?
Well, extensive experimentation (OK, I did it twice):
[root@h11 kernel-doc-2.2.14]# time cat </dev/lp0
0.000u 0.010s 0:00.08 12.5% 0+0k 0+0io 105pf+0w
My idiot level 'try reading program' again came back with about 1 millisec -
it looks like there is some sort of timing.
------------------- Solaris SPARC --------------------
Devices bpp(7D)
NAME
bpp - bi-directional parallel port driver
SYNOPSIS
SUNW,bpp@slot,offset:bppn
DESCRIPTION
The bpp driver provides a general-purpose bi-directional
interface to parallel devices. It supports a variety of out-
put (printer) and input (scanner) devices, using programm-
able timing relationships between the various handshake sig-
nals.
APPLICATION PROGRAMMING INTERFACE
The bpp driver is an exclusive-use device. If the device
has already been opened, subsequent opens fail with EBUSY.
Read/Write Operation
When the driver is opened for reading and writing, it is
assumed that scanning will take place, as scanners are the
only devices supported by this mode. Most scanners require
that the SLCT_IN or AFX pin be set to tell the scanner the
direction of the transfer. The AFX line is set when the
read_handshake element of the bpp_transfer_parms structure
is set to BPP_HSCAN_HS, otherwise the SLCT_IN pin is set.
Normally, scanning starts by writing a command to the
scanner, at which time the pin is set. When the scan data
is read back, the pin is reset.
Ummm... OK, then we can't use this device...
Devices ecpp(7D)
NAME
ecpp - IEEE 1284 ecp, nibble and centronics compatible
parallel port driver
Read/Write Operation
ecpp is a full duplex STREAMS device driver. While an
application is writing to an IEEE 1284 compliant device,
another thread may read from it. write(2) will return when
all the data has been successfully transferred to the dev-
ice.
Write Operation
write() returns the number of bytes successfully written to
the stream head. If a failure occurs while a Centronics
device is transfering data, the content of the status bits
will be captured at the time of the error, and can be
retrieved by the application program, using the
ECPPIOC_GETERR ioctl() call. The captured status information
will be overwritten each time an attempted transfer or a
ECPPIOC_TESTIO ioctl() occurs.
Intelligent IEEE 1284 compliant devices, such as Postscript
printers, return error information through a backchannel.
This data may be retrieved with the read(2) call.
Read Operation
If a failure or error condition occurs during a read(2), the
number of bytes successfully read is returned (short read).
When attempting to read the port that has no data currently
available, read() returns 0 if O_NDELAY is set. If
O_NONBLOCK is set, read() returns -1 and sets errno to
EAGAIN. If O_NDELAY and O_NONBLOCK are clear, read()
blocks until data become available.
BPPIOC_GETERR
Get last error status.
The argument is a pointer to a struct
bpp_error_status. This structure is described
below. This structure indicates the status of
all the appropriate status bits at the time of the
most recent error condition during a write()
call, or the status of the bits at the most recent
BPPIOC_TESTIO ioctl() call.
struct bpp_error_status {
char timeout_occurred; /* 1=timeout */
char bus_error; /* not used */
uchar_t pin_status; /*
* status of pins
* which could cause
* error.
*/
};
/* pin_status values */
#define BPP_ERR_ERR 0x01 /* nErr=0 */
#define BPP_SLCT_ERR 0x02 /* Select=1 */
#define BPP_PE_ERR 0x04 /* PE =1 */
#define BPP_BUSY_ERR 0x40 /* Busy = 1 */
Hmmm.... but not on X86
Devices lp(7D)
NAME
lp - driver for parallel port
SYNOPSIS
include <sys/bpp_io.h>
DESCRIPTION
The lp driver provides the interface to the parallel ports
used by printers for x86 based systems. The lp driver is
implemented as a STREAMS device.
OK, so we have the same thing.
now for the fun part:
If you do a read and there is nothing to report, what
happens?
h2.private: {2} % cat </dev/lp1
ARGH! It blocks INDEFINATELY.
------------------------------------------------------------------
So this means that you cannot even POLL devices in a clean and simple
manner... Sigh...
-----------------------------------------------------------------------------
If you need help, send email to [EMAIL PROTECTED] (or lprng-requests
or lprng-digest-requests) with the word 'help' in the body. For the impatient,
to subscribe to a list with name LIST, send mail to [EMAIL PROTECTED]
with: | example:
subscribe LIST <mailaddr> | subscribe lprng-digest [EMAIL PROTECTED]
unsubscribe LIST <mailaddr> | unsubscribe lprng [EMAIL PROTECTED]
If you have major problems, send email to [EMAIL PROTECTED] with the word
LPRNGLIST in the SUBJECT line.
-----------------------------------------------------------------------------