> 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.
-----------------------------------------------------------------------------

Reply via email to