pcg@cs.aber.ac.uk (Piercarlo Grandi) (07/25/90)
OOOOOPS. The parallel port driver source I posted one or two days ago was the *wrong version*. I picked up the wrong directory. This would not be too bad, except that it was half baked, and most importantly, it would spin (instead of sleep) on the printer buffer being full, contrarily to the description I had given. For a "production" version this would be antisocial (I used it to test the response of my printer's buffer). This is the latest version, which I have slightly revised. There are some parameters here and there, but I have set them conservatively and I think that they are going to be pretty good if not optimal on any machine (I did run a few tests). Here is the introduction in the original posting, which however described this version, and is still valid: I am posting here the full source of a parallel port driver, inspired by one posted on these screen my Mike Grenier, that has some qualities that may make it preferable over the one shipped standard with the various versions of System V/386. They are mainly that it is small, does not use termio, does not use any interrupt lines, thus using polling, and this polling mode probably consumes less CPU than interrupt overheads. The driver has been developed on ESIX, but should work on nearly every other System V/386. The only thing I am not sure exists in other versions is a referenc eto 'tenmicrosec', a procedure that just busy waits for 10 microseconds; if you don;t have it, it's fairly easy to roll your own (no, it's not important that it last exactly ten microseconds). Also, the conditions for the two polling loops have been designed around my printer, an Epson SQ850 inkjet; I suspect that its parallel interface is a bit odd. If you have problems with excessive busy waits (espcially for offline or powered off printers), slow printing, etc..., revise the polling conditions (but it should not be really necessary). As you will notice I have used a reference to the GPL, with the concession that linking it with a kernel will not require distributing the rest of the kernel be distributed uder the GPL as well. I can add that the most obvious user visible improvement over Grenier's driver is that I have been careful to make it interruptible. Internally it is completely different, and it uses busy waits to minimize the need for sleep waits, and the attending overheads. -----------------------------cut here------------------------ #! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh <file", e.g.. If this archive is complete, you # will see the following message at the end: # "End of archive 1 (of 1)." # Contents: MANIFEST Makefile Master Name Node README Space.c System # pp.c pp.h # Wrapped by sw@aware on Wed Jul 25 14:34:58 1990 PATH=/bin:/usr/bin:/usr/ucb ; export PATH echo ' Copyright 1990 Piercarlo Grandi. All rights reserved. This shar archive contains free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 1, or (at your option) any later version. As a special case, the driver contained herein may be incorporated in any OS kernel, whether the GNU General Public License applies to it or not. This driver is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You may have received a copy of the GNU General Public License along with this driver; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ' echo 4 if test -f 'README' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'README'\" else echo shar: Extracting \"'README'\" \(2006 characters\) sed "s/^X//" >'README' <<'END_OF_FILE' XThis is the source of a parallel port driver for System V/386, rel. 3.2. X XIt is different from the standard lp(7) driver because it does only raw Xoutput (while lp(7) uses the termio(4) line discipline), and it does not use Xinterrupts. It assumes that the printer has an internal buffer, so that the Xmost efficient style of communication is to pump characters as fast as Xpossible until the buffer fills, then sleep for a relatively long period Xwhen it signals not ready, and try again thereafter. X XThis implies that no interrupt processing is done, which has the advantages Xof simplicity and low overhead, and, perhaps most importantly, of not using Xone of the scarce interurpt lines of the ISA bus. X XOutput is done entirely in the top half of the driver, and therefore not Xinside a critical region. This could mean that on a very busy system the Xpumping could be interrupted by task switching. This, if important, can be Xeasily obviated, by turning the pumping loop into a critical region; if this Xis done it becomes important to limit the number of characters pumped in one Xgo, by having two loops, the outer one that runs until busy is signaled, the Xinner one for a fixed number of characters, and put only the inner one in Xthe critical region. X XA similar driver has been posted by Mike Grenier; this version is completely Xrewritten and uses different logic. In particular the polling waits are Xeither very short if busy or interruptible if sleeping. X XAs it stands it is especially designed for a stock System V/386 rel. 3.2 Xsystem, e.g. ESIX, especially inasmuch installation is concerned. It should Xbe sufficient to say 'make install' to install it, and then you should say X'idconfig' and 'idmkunix' to rebuild the kernel. X XIt is so simple that it should port very quickly to other flavours of Unix; Xthe only thing that may not be portable is the reference to a kernel Xprocedure called 'tenmicrosec()' in pp.c, which does a busy loop of 10 Xmicroseconds. It is fairly easy to roll your own, of course. END_OF_FILE if test 2006 -ne `wc -c <'README'`; then echo shar: \"'README'\" unpacked with wrong size! fi # end of 'README' fi if test -f 'MANIFEST' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'MANIFEST'\" else echo shar: Extracting \"'MANIFEST'\" \(406 characters\) sed "s/^X//" >'MANIFEST' <<'END_OF_FILE' X File Name Archive # Description X----------------------------------------------------------- X README 1 X MANIFEST 1 X Makefile 1 X Master 1 X Name 1 X Node 1 X Space.c 1 X System 1 X pp.c 1 X pp.h 1 END_OF_FILE if test 406 -ne `wc -c <'MANIFEST'`; then echo shar: \"'MANIFEST'\" unpacked with wrong size! fi # end of 'MANIFEST' fi if test -f 'Makefile' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'Makefile'\" else echo shar: Extracting \"'Makefile'\" \(229 characters\) sed "s/^X//" >'Makefile' <<'END_OF_FILE' XCONF =/etc/conf/bin/ XCC =cc XCFLAGS =-O -DINKERNEL X XDriver.o: pp.o; ln $? $@ XSpace.c: space.c; ln $? $@ X Xpp.o: pp.h Xspace.o: pp.h X Xinstall: Driver.o Space.c Master System Node Name X $(CONF)idinstall -keu pp X $(CONF)idmknod END_OF_FILE if test 229 -ne `wc -c <'Makefile'`; then echo shar: \"'Makefile'\" unpacked with wrong size! fi # end of 'Makefile' fi if test -f 'Master' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'Master'\" else echo shar: Extracting \"'Master'\" \(29 characters\) sed "s/^X//" >'Master' <<'END_OF_FILE' Xpp Iocw icoHGO pp 0 0 1 3 -1 END_OF_FILE if test 29 -ne `wc -c <'Master'`; then echo shar: \"'Master'\" unpacked with wrong size! fi # end of 'Master' fi if test -f 'Name' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'Name'\" else echo shar: Extracting \"'Name'\" \(43 characters\) sed "s/^X//" >'Name' <<'END_OF_FILE' Xpp Parallel port driver, raw polled output END_OF_FILE if test 43 -ne `wc -c <'Name'`; then echo shar: \"'Name'\" unpacked with wrong size! fi # end of 'Name' fi if test -f 'Node' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'Node'\" else echo shar: Extracting \"'Node'\" \(33 characters\) sed "s/^X//" >'Node' <<'END_OF_FILE' Xpp pp0 c 0 Xpp pp1 c 1 Xpp pp2 c 2 END_OF_FILE if test 33 -ne `wc -c <'Node'`; then echo shar: \"'Node'\" unpacked with wrong size! fi # end of 'Node' fi if test -f 'Space.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'Space.c'\" else echo shar: Extracting \"'Space.c'\" \(2497 characters\) sed "s/^X//" >'Space.c' <<'END_OF_FILE' X/* X $Header: /aware0/src/pp/space.c,v 2.2 90/07/25 14:27:17 sw Exp $ X*/ X X/* X "Copyright 1990 Piercarlo Grandi. All rights reserved."; X*/ X X/* X This driver is free software; you can redistribute it and/or X modify it under the terms of the GNU General Public License as X published by the Free Software Foundation; either version 1, or X (at your option) any later version. X X As a special case, this driver may be incorporated in any OS kernel, X whether the GNU General Public License applies to it or not. X X This driver is distributed in the hope that it will be useful, X but WITHOUT ANY WARRANTY; without even the implied warranty of X MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the X GNU General Public License for more details. X X You may have received a copy of the GNU General Public License X along with this driver; if not, write to the Free Software X Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. X*/ X X#include "sys/pp.h" X X/* X The exit conditions from the two polling loops here are a bit funny, X my printer (EPSON SQ850) and parallel port give the following statuses: X X NOTBUSY NOTACK NOPAPER ONLINE NOTERROR X X port missing 1 1 1 1 1 X printer power off 1 0 0 0 0 X printer not there 1 1 0 1 0 X offline 0 1 0 1 0 X online 1 1 0 1 1 X X The printer puts NOTBUSY low while we strobe the character in, X then strobes NOTACK low for 10 usecs afterwards to signal we can X strobe again. The relationship between the two is to say the X last bizarre. X X Our problem is that we want to distinguish between long term and X short term busy, where short term busy is defined as the time taken X by the interface to read a character, and long term busy as paper X end, offline, power off, interface buffer full. X*/ X Xstruct pp_guard pp_guard = X{ X { X (ppONLINE|ppNOTERROR|ppNOPAPER), /* pause.mask */ X (ppONLINE|ppNOTERROR) /* pause.done */ X }, X { X (ppNOTACK|ppNOTBUSY), /* spin.mask */ X (ppNOTACK|ppNOTBUSY) /* spin.done */ X }, X}; X Xstruct pp_config pp_config[] = X{ X /* data, status, control */ X { 0x3bc, 0x3bd, 0x3be }, /* /dev/pp0 */ X { 0x378, 0x379, 0x37a }, /* /dev/pp1 */ X { 0x278, 0x279, 0x27a } /* /dev/pp2 */ X}; X Xshort pp_max = sizeof pp_config / sizeof (struct pp_config); X X/* X Maximum # of times we busy wait, after which we sleep wait; should X be just a tad larger than the usual inter character processing time X of the interface. X*/ X Xunsigned pp_maxspins = 5; END_OF_FILE if test 2497 -ne `wc -c <'Space.c'`; then echo shar: \"'Space.c'\" unpacked with wrong size! fi # end of 'Space.c' fi if test -f 'System' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'System'\" else echo shar: Extracting \"'System'\" \(75 characters\) sed "s/^X//" >'System' <<'END_OF_FILE' Xpp Y 1 0 0 0 3bc 3be 0 0 Xpp N 2 0 0 0 378 37a 0 0 Xpp N 3 0 0 0 278 27a 0 0 END_OF_FILE if test 75 -ne `wc -c <'System'`; then echo shar: \"'System'\" unpacked with wrong size! fi # end of 'System' fi if test -f 'pp.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'pp.c'\" else echo shar: Extracting \"'pp.c'\" \(5980 characters\) sed "s/^X//" >'pp.c' <<'END_OF_FILE' X/* X $Header: /aware0/src/pp/pp.c,v 2.2 90/07/25 14:27:34 sw Exp $ X*/ X Xstatic char Notice[] = X "Copyright 1990 Piercarlo Grandi. All rights reserved."; X X/* X This driver is free software; you can redistribute it and/or X modify it under the terms of the GNU General Public License as X published by the Free Software Foundation; either version 1, or X (at your option) any later version. X X As a special case, this driver may be incorporated in any OS kernel, X whether the GNU General Public License applies to it or not. X X This driver is distributed in the hope that it will be useful, X but WITHOUT ANY WARRANTY; without even the implied warranty of X MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the X GNU General Public License for more details. X X You may have received a copy of the GNU General Public License X along with this driver; if not, write to the Free Software X Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. X*/ X X/* X Fast parallel port driver, polling, no interrupts (somewhat X inspired by a driver from Michael Grenier <mike@cimcor.mn.org>) X*/ X X#include "sys/param.h" X#include "sys/types.h" X#include "sys/dir.h" X#include "sys/signal.h" X#include "sys/user.h" X#include "sys/errno.h" X#include "sys/sysmacros.h" X X#ifdef ppDEBUG X unsigned pp_debug = 1; X# define DEBUG(STMTS) do { if (pp_debug) { STMTS; }; } while (0) X X unsigned pp_nchar; X unsigned pp_nlongpause; X unsigned pp_nshortpause; X unsigned pp_nspin; X#else X# define DEBUG(list) /* skip */ X#endif X X#include "sys/pp.h" X X/* X A long pause for polling the printer until it is online again X the pause while the strobe is high, and a short pause to wait X for the interface to become ready for the next character. X The longer pause is done sleeping, the other two are done X spinning, so they had better be few. X*/ X Xextern int tenmicrosec(); Xextern int Hz; Xextern int wakeup(); Xextern int timeout(); Xextern int sleep(); X X#define ppSPIN() (void) (tenmicrosec()) X#define ppPAUSE(U,N) (void) (timeout(wakeup,(U),(N)),sleep((U),PSLEP)) X Xextern int inb(); Xextern outb(); X X/* X We have two pauses; one to wait for the interface buffer to clear up a bit, X the other to wait for the printer to come online (if offline or paper end X or error or similar things. X*/ X Xunsigned pp_longpause; Xunsigned pp_shortpause; X Xextern int ppinit() X{ X int unit; X X pp_longpause = Hz/2; X pp_shortpause = Hz/10; X X for (unit = 0; unit < pp_max; unit++) X { X outb(pp_config[unit].control,(ppSELECT|ppNOTRESET)); X DEBUG(printf("Initial status of /dev/pp%d is %x\n", X unit,inb(pp_config[unit].status))); X } X} X Xextern int ppopen(dev,mode) X int dev,mode; X{ X register int unit = minor(dev); X register unsigned status; X X if (unit >= pp_max) X { X u.u_error = EIO; X return; X } X X status = inb(pp_config[unit].status); X X if ((status&0xff) == 0xff) X { X u.u_error = ENXIO; X DEBUG(printf("Printer /dev/pp%d does not exist\n",unit)); X return; X } X X /* X We ignore error condition except when printer is online ready. X */ X X if ((status&(ppONLINE|ppNOTBUSY|ppNOTERROR)) == (ppONLINE|ppNOTBUSY)) X { X u.u_error = EIO; X DEBUG(printf("Error status %x detected on /dev/pp%d\n",status,unit)); X return; X } X X DEBUG((pp_nchar = pp_nlongpause = pp_nshortpause = pp_nspin = 0)); X} X Xextern int ppclose(dev) X int dev; X{ X DEBUG(printf("Printed %d characters, paused %d and spun %d (%d) times\n", X pp_nchar,pp_longpause,pp_nspin,pp_nshortpause)); X} X Xextern int ppwrite(dev) X int dev; X{ X register struct pp_config *pp; X register unsigned status; X register int statusp; X register int ch; X X pp = &pp_config[minor(dev)]; X statusp = pp->status; X X while ((ch = cpass()) >= 0) X { X DEBUG(pp_nchar++); X X /* X Here we wait for the printer to be ready, and this may X be a longish wait, because of paper end, offline, power off. X We sleep, polling a few times per second, as this is simpler X than taking an interrupt and the cost is very low. X */ X X wait_printer_online: X X for X ( X status = inb(statusp); X (status & pp_guard.pause.mask) != pp_guard.pause.done X && (status & pp_guard.spin.mask) != pp_guard.spin.done; X status = inb(statusp) X ) X { X ppPAUSE((caddr_t) &pp->status,pp_longpause); X DEBUG(pp_nlongpause++); X } X X latch_character: X X /* X We do not wait for any settling time on this X */ X X outb(pp->data,ch); X X /* X We wait for the printer to have a good chance to X see the strobe high, and then we wait for it to latch X in the character when we drive it back down. X */ X X strobe_awhile: X X outb(pp->control,(ppSELECT|ppNOTRESET|ppSTROBE)); X ppSPIN(); /* This should last at least one microsecond */ X outb(pp->control,(ppSELECT|ppNOTRESET)); X X /* X Here we wait for the interface to be ready to accept another X character, and thus we spin, because we expect the wait to be X short (e.g. 2-4 turns). This is cheaper than the overheads X involved in sleeping or even in taking an interrupt. X */ X X wait_interface_ready: X X { X register unsigned spins; X X for X ( X spins = 0, status = inb(statusp); X spins < pp_maxspins /* Don't waste too much time spinning */ X && (status & pp_guard.spin.mask) != pp_guard.spin.done X && (status & pp_guard.pause.mask) == (pp_guard.pause.done); X spins++, status = inb(statusp) X ) X { X ppSPIN(); X DEBUG(pp_nspin++); X } X X /* X This is not really necessary; we could just have one pause X above, before latching the character. We do not do that X simply because we want to have a shorter pause for buffer X full here, to have snappier response. Well, in theory :->. X */ X X if (spins == pp_maxspins) X { X ppPAUSE((caddr_t) &pp->status,pp_shortpause); X DEBUG(pp_nshortpause++); X } X } X } X} END_OF_FILE if test 5980 -ne `wc -c <'pp.c'`; then echo shar: \"'pp.c'\" unpacked with wrong size! fi # end of 'pp.c' fi if test -f 'pp.h' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'pp.h'\" else echo shar: Extracting \"'pp.h'\" \(1936 characters\) sed "s/^X//" >'pp.h' <<'END_OF_FILE' X/* X $Header: /aware0/src/pp/pp.h,v 2.2 90/07/25 14:28:24 sw Exp $ X*/ X X/* X "Copyright 1990 Piercarlo Grandi. All rights reserved."; X*/ X X/* X This driver is free software; you can redistribute it and/or X modify it under the terms of the GNU General Public License as X published by the Free Software Foundation; either version 1, or X (at your option) any later version. X X As a special case, this driver may be incorporated in any OS kernel, X whether the GNU General Public License applies to it or not. X X This driver is distributed in the hope that it will be useful, X but WITHOUT ANY WARRANTY; without even the implied warranty of X MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the X GNU General Public License for more details. X X You may have received a copy of the GNU General Public License X along with this driver; if not, write to the Free Software X Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. X*/ X X/* X Status port defines X*/ X X#define ppNOTERROR 0x08 /* Online, paper present, OK */ X#define ppONLINE 0x10 /* Printer is online */ X#define ppNOPAPER 0x20 /* Paper missing */ X#define ppNOTACK 0x40 /* Data have been read */ X#define ppNOTBUSY 0x80 /* Interface ready */ X X/* X Control port defines X*/ X X#define ppSTROBE 0x01 /* Strobe a character in */ X#define ppAUTOLF 0x02 /* Enable auto line feed */ X#define ppNOTRESET 0x04 /* Normal operation */ X#define ppSELECT 0x08 /* Put the printer online */ X#define ppINTERRUPT 0x10 /* Send interrupt when NOTACK is reset */ X X#ifdef INKERNEL Xstruct pp_guard X{ X struct X { X char unsigned mask; X char unsigned done; X } X pause,spin; X}; X Xstruct pp_config X{ X short data; X short status; X short control; X}; X Xextern struct pp_guard pp_guard; Xextern struct pp_config pp_config[]; Xextern short pp_max; X Xextern unsigned pp_maxspins; X#endif END_OF_FILE if test 1936 -ne `wc -c <'pp.h'`; then echo shar: \"'pp.h'\" unpacked with wrong size! fi # end of 'pp.h' fi echo shar: End of archive 1 \(of 1\). cp /dev/null ark1isdone MISSING="" for I in 1 ; do if test ! -f ark${I}isdone ; then MISSING="${MISSING} ${I}" fi done if test "${MISSING}" = "" ; then echo You have the archive. rm -f ark[1-9]isdone else echo You still need to unpack the following archives: echo " " ${MISSING} fi ## End of shell archive. exit 0 -- Piercarlo "Peter" Grandi | ARPA: pcg%cs.aber.ac.uk@nsfnet-relay.ac.uk Dept of CS, UCW Aberystwyth | UUCP: ...!mcsun!ukc!aber-cs!pcg Penglais, Aberystwyth SY23 3BZ, UK | INET: pcg@cs.aber.ac.uk