From: jhood@smoke.marlboro.vt.us (Lord Yertle)
Subject: Yet another (fast) parallel driver for PCs (beta)
Organization: Cthulhu Worship Meeting of Southern Vermont
Date: Tue, 12 Jan 1993 07:20:22 GMT
Summary: faster with a little assembler 
Keywords: driver parallel lp

Here's yet another parallel driver for Unix and PC-bus printer cards.
The notable features of this one are

* a fast inner loop of assembler to stuff those characters out in a hurry
* nice behavior towards interactive users
* written/tested on SVR4 and Xenix, but easily ported to other systems

It works nicely on my systems, but I don't believe for a second that
it will do the same for everybody else yet.  So I'll release it again
within a month or so after people tell me how it breaks for them.

  --jh


#! /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 shell archive."
# Contents:  INSTALL Makefile Master Name Node README System
#   link_xenix.ll llstuff.asm lp.c master.ll xenixconf.ll
# Wrapped by jhood@blurfl on Tue Jan 12 01:58:22 1993
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'INSTALL' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'INSTALL'\"
else
echo shar: Extracting \"'INSTALL'\" \(3511 characters\)
sed "s/^X//" >'INSTALL' <<'END_OF_FILE'
XI've only installed and tested this driver on Esix 4.0.3a, and Xenix
X2.3.2 with kernel upgrades to 2.3.3.  I'm assuming you know enough
Xabout drivers to install them by hand if need be; the driver is not
Xvery automated about installation and requires some manual editing and
Xthink time.
X
XHardware
X========
X
XIf you intend to try and use this driver at the same time as another,
Xinterrupt-using printer driver, it may be wise to disable interrupts
Xon your parallel ports.  If your parallel ports are really evil and
Xgenerate interrupts even when software tells them not to, you'll
Xprobably have to disable interrupts then too.  (I know of no such
Xcards, but given the dubious quality of a lot of printer ports, I
Xthink it's highly likely some exist somewhere.)  I assume you know how
Xto do this, but in brief you will have to disable interrupt 7, or
Xpossibly interrupt 5 for a "secondary" serial port at address 0x2f8.
XYou might have to do this with an Xacto knife.
X
XXenix
X=====
X
XThis driver is set up to replace the Xenix 'pa' driver.
X
XTo install:
X
XEdit Makefile and/or lp.c as necessary.  Compile with the system
Xcompiler, not gcc.
X
X $ make xenix
X $ su root
X # make x_install
X
XThis last step will copy the object files for the device driver into a
Xnew directory /usr/sys/io/ll.  At this point, you will have to edit
Xconfiguration files in /usr/sys/conf: master, xenixconf, and
Xlink_xenix.  Delete the lines referring to the Xenix parallel driver
Xin master and xenixconf (they both start with 'pa') and replace them
Xwith the single-line excerpts included with this driver.  (Do save the
Xold 'pa' lines so that you can easily reinstall the Xenix driver if
Xneed be.)  Also insert link_xenix.ll amidst the list of object files
Xin link_xenix.
X
XThen type make, in /usr/sys/conf, to rebuild the kernel.  Install the
Xnew kernel however you like and try it out.  This driver uses the same
Xdevice nodes that the Xenix parallel driver does, so no
Xreconfiguration there is necessary.
X
XSysVr4
X======
X
XThis driver should coexist with the SVR4 'lp' driver, but I can't be
Xsure of that.  I deleted my copy of that driver in a fit of debugging,
Xand I'm not about to go reinstall it.
X
XTo install:
X
XEdit Makefile and/or lp.c as necessary.  Compile with the system
Xcompiler, not gcc.  (If you're trying to get this going on SCO Unix,
Xyou should use rcc.)
X
X $ make sysv
X $ su root
X # make v_install
X
XThis installs the driver and all of its autoconfig files into
X/etc/conf as necessary.  If you wish to delete the old driver, type
X
X  # /etc/conf/bin/idinstall -d lp
X
X(However, it's wise not to do this until you know my driver is working
Xfor you.)  Then rebuild the kernel; in SVR4, this is
X
X  # /etc/conf/bin/idbuild
X
XThen, to install the kernel and its new device files, reboot the
Xsystem, going through init state 0 or 6.
X
XIt should also be possible to install this driver in SVR3.2, but it
Xwill be a bit more complicated.  I don't know the exact details there.
XIn SVR3.0 you will have to do the whole install by hand; no, I'm not
Xabout to excavate my copy of Microport 3.0e and tell you how :)
X
XApplications
X============
X
XYou will probably have to reconfigure stuff that uses the printers,
Xwhatever system you're on.  Most spoolers are set up to use termio
Xfeatures in typical lp drivers; you will have to change this so that
Xsome user program does the necessary translation (MS-DOS text
Xconversion programs will do nicely).  MS-DOS emulators and other
Xnonesuch can now feed stuff directly to the driver without any
Xhocus-pocus.
END_OF_FILE
if test 3511 -ne `wc -c <'INSTALL'`; then
    echo shar: \"'INSTALL'\" unpacked with wrong size!
fi
# end of 'INSTALL'
fi
if test -f 'Makefile' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'Makefile'\"
else
echo shar: Extracting \"'Makefile'\" \(2013 characters\)
sed "s/^X//" >'Makefile' <<'END_OF_FILE'
X# Optional tunable parameters set by default in lp.c:
X# SPINCNT	when the printer goes busy, number of polls before the driver
X#		gives up and sleeps.  (default 10)
X# SLEEPTIME	Number of clock ticks to sleep on a busy printer.  (default 1)
X# BUFSIZ	Size of internal buffer, in bytes.  Affects driver efficiency
X#		and length of time driver runs before yielding to user
X#		processes.  (default 1024, size of stdio buffers)
X# OPENWAIT	Number of seconds driver waits between end of previous job and
X#		initialization of printer, when init-on-open devices are used.
X#		(default 60)
X# Driver options, set in DEFS and system targets:
X# INIT		for printer reset on system startup
X# STROBE_PROT	in C code, disables interrupts on output of data strobe
X#		to printer.  Could cause interrupt latency problems,
X#		especially on Xenix, which has no inline assembler.
X#		Always enabled in assembler, because the protected section of
X#		code is very short.
X# DEBUG		make structures public and visible to debuggers.
X# ASM		Enables assembler code.  Recommended for Xenix, not
X#		recommended for SysV.
X# DO_IOWAITS	add code to wait for I/O bus settle; the safest case is to
X#		use this.  Always enabled in assembler code.
X
X# configuration defines; see also targets 'xenix' and 'sysv'
X# DEFS=-DDO_IOWAITS -DINIT -DSTROBE_PROT -DASM
X
XCFLAGS=-O $(DEFS)
X
Xall:
X	@echo 'Type "make FOO" where FOO is one of xenix, sysv'
X
X# main system targets
Xxenix:
X	make DEFS="-DDO_IOWAITS -DINIT -DSTROBE_PROT -DASM" llstuff.o ll.o
X
Xsysv:
X	make DEFS="-DDO_IOWAITS -DINIT -DSTROBE_PROT" Driver.o
X
X# object targets for Xenix
Xllstuff.o:	llstuff.asm
X	masm $(DEFS) $<
X
Xll.o:	lp.c
X	cc $(CFLAGS) -c lp.c
X	mv lp.o ll.o
X
X# object targets for SysV
XDriver.o:	lp.c
X	cc $(CFLAGS) -c lp.c
X	mv lp.o Driver.o
X
X# other miscellanea
Xx_install: xenix
X	-mkdir /usr/sys/io/ll
X	cp llstuff.o ll.o /usr/sys/io/ll
X	@echo "Finish install by editing master, xenixconf, and link_xenix"
X
Xv_install:	sysv
X	/etc/conf/bin/idinstall -kd ll
X	/etc/conf/bin/idinstall -ka ll
X
Xclean:
X	rm *.o
END_OF_FILE
if test 2013 -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'\" \(27 characters\)
sed "s/^X//" >'Master' <<'END_OF_FILE'
Xll	ocwI	icHO	ll	0	0	0	3	-1
END_OF_FILE
if test 27 -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'\" \(48 characters\)
sed "s/^X//" >'Name' <<'END_OF_FILE'
Xll Fast polled parallel port driver, raw output
END_OF_FILE
if test 48 -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'\" \(54 characters\)
sed "s/^X//" >'Node' <<'END_OF_FILE'
Xll	ll0	c	0
Xll	ll	c	0
Xll	ll1	c	1
Xll	ll2	c	2
Xll	ll3	c	3
END_OF_FILE
if test 54 -ne `wc -c <'Node'`; then
    echo shar: \"'Node'\" unpacked with wrong size!
fi
# end of 'Node'
fi
if test -f 'README' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'README'\"
else
echo shar: Extracting \"'README'\" \(7543 characters\)
sed "s/^X//" >'README' <<'END_OF_FILE'
XOh No!
X
XThis is yet another parallel printer driver for Unix on PC-clone
Xsystems.  (I count four others that I've had floating around, and I
Xthink I'm missing one or two in my archive directories...) This one
Xuses a chunk of assembler code to greatly speed up printer i/o.  It's
Xquite similar in concept to Piercarlo Grandi's and Mike Grenier's
Xparallel drivers, but hopefully considerably faster because of some
Xthoughtful optimization and assembler code.  It runs out of the box on
XXenix/386 2.3.2 and Esix 4.0.3, and should port easily to other
Xsystems (it uses a bare minimum of kernel features and should port
Xwith minimal work to PC/IX, which is an ancient SysIII.)
X
XThis is a beta version; I'll release something improved by comments
Xand my own thought in a couple of weeks.
X
XI recently got a Canon BJ-200 printer; along with it, I installed
XGhostscript.  Since the Canon is a 360 dpi printer, this meant I had
Xhuge bitmaps that printed rather slowly through either the standard
XXenix 'pa' driver or Joe Allen's trivial 30 line parallel driver.  So,
Xnaturally, I sat down and created a new driver.
X
XMy goals in creating this thing were speed, speed, straightforward,
Xraw operation, portability between Unices, minimal impact on
Xinteractive usage, and a bit more speed.  It succeeds pretty well.
XWhere the Xenix driver used to take about 1m40s to print a full-page
Xbitmap, using essentially 100% cpu the whole time and doing horrid
Xthings to character echo in applications running in raw mode, my
Xdriver takes about 50 seconds real and 20 seconds cpu time and has no
Xnoticeable impact on character echo.
X
XCredits are due where credits are due:
X
XJoe Allen, for writing the itty bitty driver that I started hacking up
Xinto this thing.
X
XPiercarlo Grandi and Mike Grenier, for their work on other parallel
Xdrivers that I took useful ideas and code fragments from.
X
XUwe Doering, for showing us all how fast a driver can really run with
Xsome appropriate design, and for a useful hint or two.
X
XWhat I think this driver does well:
X
X* It's zippy.  It's considerably faster than my printer, and should do
Xabout as well as anything can on laser printers with parallel ports.
XIt stuffs at least 50KB/s out the parallel port on my 16 MHz system.
XConservatively, I'd expect it to be able to go at most twice as fast
Xon faster systems (I/O timing and Centronics timing impose some limits
Xhere).
X
X* It has minimal impact on interactive usage.  Most of the other
Xparallel port drivers I've seen can freeze non-interrupt processing on
Xthe system for anywhere from 1/4 second to 60 seconds.  This driver
Xyields to other processes after every 1024 characters-- approximately
X1/50 second on my 16 MHz systems.  It works gracefully in the case of
Xlarge writes, which many drivers don't.  Yet it still seems to runs
Xquickly on a loaded system.
X
X* It's easily interruptable.  It's impossible to get a program hung in
Xthis driver (a little voice is going "never say never", however).
X
X* It's fairly simple (although it's grown quite a bit from Joe Allen's
Xdriver, which was my starting point).  It handles raw I/O and nothing
Xelse-- there's no termio processing or other character filtering.
XThis is a bit Procrustean, but also very simple to understand and
Xmanage.
X
X* It's designed for page-oriented printers.  Timeouts before
Xinitializing the printer on an open are designed to accommodate lasers
Xand whatnot that may need some think time, page-feed time, etc.
X
X* It's portable.  Although the driver has assembler code in it, the
Xoriginal C code remains and is tested, and is still fairly fast.  This
Xdriver should be easy to port to anything that can use an IBM-style
Xparallel port and can use a Sys III device driver.  I've written it
Xwith PC/IX in mind, although I don't have a machine set up with that
Xantique right now.  It should port very easily to it or any of the
Xother AT&T-derived PC Unii, and without much difficulty to just about
Xanything that can accommodate a PC printer card in one of its slots.
X
X* And of course, it doesn't use any interrupts, so you can wedge some
Xother 8-bit card into your system.
X
XWhat I think this driver doesn't do well:
X
X*  Error handling.  It's basically non existent-- the driver just
Xwaits until the printer comes on line again.  This is fine in a
Xsingle-user, local environment but doesn't work so well in a more
Xcomplex setting.  It doesn't even handle the case of a powered-down or
Xnon-existent printer and stuffs characters out the port anyway.
X
X* Buffering.  The driver doesn't buffer data from the user process so
Xit can keep going when the user process isn't ready with more data.
XHowever, the combination of disk i/o buffering and the printer's
Xinternal buffering seem to hide this problem fairly well for me.  I
Xhaven't done anything about this yet, although I have spent some time
Xrewriting code to make it easy to do in the future.  This may be a
Xsignificant bottleneck on laser printers.
X
X* Handling slower, older printers.  If you have an ancient printer
Xthat has no buffering, and accepts a single character at a time, this
Xdriver is guaranteed to lose.  If you have a very old printer that has
Xsynchronous line buffering, and accepts a single line at a time but
Xdemands it fairly quickly (i.e., Epson MX-80 or Star Gemini 10), the
Xdriver may be somewhat slow.  If you have a modestly old printer that
Xhas some buffering, but takes a relatively long time to accept each
Xnew character, the driver may be quite slow, although this can be
Xalleviated by some adjustment of timing parameters.  This driver is
Xreally intended for printers that can accept streams of characters at
Xmore than a few thousand characters per second; this should be true of
Xany recent laser printer and most recent dot-matrix printers.
X
X* It's got crude interlocking.  Only one open can be active on it at
Xonce.  This is partly by intent because of its simple, non-buffered
Xdesign.
X
X* It's got some ugly assembler in it.  This stuff is obnoxious to deal
Xwith.  (sigh)
X
X* Normal tty-style i/o, a la termio, doesn't exist.  If you just want
Xto feed standard text files to a printer, you'll need to do some
Xfiltering beforehand.  It's really intended for graphics drivers.
X
X* Support for SVR3.2 or SVR4 style configuration is not very complete.
X
XThis is as yet a beta version of the driver.  It works well for me
Xunder SCO Xenix 2.3 and Esix SVR4.0.3 and with my Canon BJ-200, but I
Xcan't test it on other variants of Unix and other printers.
X
XIn particular, I need some help on whether or not the driver works
Xproperly on 1) old 286 and 386 systems which may have less-tidy ISA
Xbuses that require I/O waits, 2) faster systems, 3) different
Xprinters, and 4) different variants of Unix.
X
XSome last-minute experimentation with the driver indicates that the
Xscheduling caused by the driver's attempts to be nice to other
Xprocesses may be a significant bottleneck with really, really fast
Xprinters.  If you install this driver, and you see your fast laser
Xprinter running slowly when you're feeding it large bitmaps, and it's
Xnot because the cpu is completely busy stuffing chars out the port,
Xplease let me know something about your situation.
X
XAlso, contrary to various comments in the makefile and source code, it
Xappears that using assembler on SVR4 can considerably speed things up;
XI had wanted to warn people away from assembler that didn't seem to
Xgive any gain, and which I don't trust as well as the C code.  "Go
Xahead, try it," I say.
X
XI can be contacted at jhood@smoke.marlboro.vt.us, or via phone at +1
X802 254 5652.  Enjoy!
END_OF_FILE
if test 7543 -ne `wc -c <'README'`; then
    echo shar: \"'README'\" unpacked with wrong size!
fi
# end of 'README'
fi
if test -f 'System' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'System'\"
else
echo shar: Extracting \"'System'\" \(100 characters\)
sed "s/^X//" >'System' <<'END_OF_FILE'
Xll	Y	0	0	0	0	3bc	3be	0	0
Xll	Y	1	0	0	0	378	37f	0	0
Xll	Y	2	0	0	0	278	27f	0	0
Xll	Y	3	0	0	0	178	17f	0	0
END_OF_FILE
if test 100 -ne `wc -c <'System'`; then
    echo shar: \"'System'\" unpacked with wrong size!
fi
# end of 'System'
fi
if test -f 'link_xenix.ll' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'link_xenix.ll'\"
else
echo shar: Extracting \"'link_xenix.ll'\" \(36 characters\)
sed "s/^X//" >'link_xenix.ll' <<'END_OF_FILE'
X	../io/ll/ll.o ../io/ll/llstuff.o \
END_OF_FILE
if test 36 -ne `wc -c <'link_xenix.ll'`; then
    echo shar: \"'link_xenix.ll'\" unpacked with wrong size!
fi
# end of 'link_xenix.ll'
fi
if test -f 'llstuff.asm' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'llstuff.asm'\"
else
echo shar: Extracting \"'llstuff.asm'\" \(2348 characters\)
sed "s/^X//" >'llstuff.asm' <<'END_OF_FILE'
X;
X;   "Copyright 1993 John Hood. 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; Assembler code for Xenix/386
X; This is hand-optimized C compiler code.  Don't expect it to be
X; pretty, or commented, or anything like that.  It might get
X; replaced at a sneeze.
X
X	TITLE	llstuff
X
X	.386
X
Xifdef	ASM
X
XDGROUP	GROUP	CONST, _BSS, _DATA
XPUBLIC  _llstuff
XCONST	SEGMENT  DWORD USE32 PUBLIC 'CONST'
XCONST      ENDS
X_DATA	SEGMENT  DWORD USE32 PUBLIC 'DATA'
X_DATA      ENDS
X_BSS	SEGMENT  DWORD USE32 PUBLIC 'BSS'
X_BSS      ENDS
X_TEXT	SEGMENT  DWORD USE32 PUBLIC 'CODE'
X	ASSUME   CS: _TEXT, DS: DGROUP, SS: DGROUP, ES: DGROUP
XEXTRN	_spl7:NEAR
XEXTRN	_outb:NEAR
XEXTRN	_inb:NEAR
XEXTRN	_splx:NEAR
X
X_llstuff	PROC NEAR
X	push 	ebp
X	mov  	ebp, esp
X	sub  	esp, 8
X	push 	edi
X	push 	esi
X
X; reg esi = buf
X	mov	esi, dword ptr [ebp+12]
X; reg edi = endbuf
X	mov	edi, dword ptr [ebp+16]
X; reg ebx = portbase
X	mov	edx, dword ptr [ebp+8]
X;	spincount = 20
X; reg ecx = inspincount
X; loaded when needed in $wait
X
X
X	cmp  	esi, edi
X	jae  	SHORT $exit
X
X	inc	edx
X
X	align	4
X$top:
X	in	al,edx
X	test 	al, 128
X	je  	SHORT $wait
X$ready:
X	dec	edx
X	lodsb
X	out	edx,al
X
X	out	132,al
X
X	pushfd
X
X	mov	al,13
X	lea	edx, dword ptr [edx+2]
X
X	cli
X
X	out	edx,al
X
X	out	132,al
X
X	mov	al,12
X	out	edx,al
X
X	popfd
X	dec	edx
X
X	cmp  	esi, edi
X	jb  	SHORT $top
X
X$exit:
X	mov  	eax, esi
X	pop  	esi
X	pop  	edi
X	leave	
X	ret  	
X
X
X$wait:
X	mov  	ecx, DWORD PTR [ebp+20]
X$waitloop:
X	in	al,edx
X	test 	al, 128
X	jne	SHORT $ready
X	dec  	ecx
X	jne  	SHORT $waitloop
X	jmp	short $exit
X
X_llstuff	ENDP
X
X_TEXT	ENDS
X
Xendif	; ASM
X
X
XEND
END_OF_FILE
if test 2348 -ne `wc -c <'llstuff.asm'`; then
    echo shar: \"'llstuff.asm'\" unpacked with wrong size!
fi
# end of 'llstuff.asm'
fi
if test -f 'lp.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'lp.c'\"
else
echo shar: Extracting \"'lp.c'\" \(9872 characters\)
sed "s/^X//" >'lp.c' <<'END_OF_FILE'
X/*
X    "Copyright 1993 John Hood. 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
X#define VERSION "0.1BETA"
X
X#ifdef DEBUG
X#define PRIVATE
X#else
X#define PRIVATE static
X#endif
X
X/* #define ASM */
X
X/* #define STROBE_PROT */
X
X
X
X/* These defaults are tuned for fast, buffered printers. */
X#ifndef SPINCNT
X#define SPINCNT 10 /* iterations */
X#endif
X
X#ifndef SLEEPTIME
X#define SLEEPTIME 1 /* HZ */
X#endif
X
X#ifndef BUFSIZ
X#define BUFSIZ 1024 /* bytes */
X#endif
X
X#ifndef OPENWAIT
X#define OPENWAIT 60 /* seconds */
X#endif
X
X#ifdef M_XENIX
X#include "/usr/sys/h/errno.h"
X#include "/usr/sys/h/types.h"
X#include "/usr/sys/h/param.h"
X#include "/usr/sys/h/dir.h"
X#include "/usr/sys/h/signal.h"
X#include "/usr/sys/h/page.h"
X#include "/usr/sys/h/seg.h"
X#include "/usr/sys/h/user.h"
X#include "/usr/sys/h/tty.h"
X#include "/usr/sys/h/sysmacros.h"
X#else
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/buf.h"
X#include "sys/errno.h"
X#include "sys/immu.h"
X#include "sys/sysmacros.h"
X#include "sys/inline.h"
X#endif
X
X/* Local definitions of Stuff. */
X
X/* extract masks for device minor # */
X#define PORTMASK	3
X/* #define TYPEMASK	~3 */
X
X#define INITMASK	0x80
X
X/* parallel port offsets from base address */
X#define	P_DATA		0
X#define P_STATUS	1
X#define P_CONTROL	2
X
X/* bitmap of parallel i/o ports */
X
X#define P_IRQEN		0x10
X#define	P_SELECT	8
X#define P_NOT_INIT	4
X#define	P_AUTOFEED	2
X#define	P_STROBE	1
X
X#define P_NOT_BUSY	0x80
X#define	P_NOT_ACK	0x40
X#define P_PERROR	0x20
X#define P_SELECTED	0x10
X#define P_NOT_ERROR	0x08
X
Xextern time_t lbolt;
X
X#ifdef M_XENIX
Xextern int outb(unsigned int port, unsigned char io);
Xextern unsigned char inb(unsigned int port);
X#endif
X
X/* Various nasty debug goo */
X/* #define outb(port, byte) printf("out %x, %x ",port,byte), outb(port,byte) */
X/* #define inb(port) (printf("in %x ", port),inb(port)) */
X/*
X#define inb(port) _inb(port)
Xint _inb(port)
X     unsigned int port;
X
X{
X  int foo;
X  foo = inb(port);
X  printf("in %x = %x", port, foo);
X  return foo;
X}
X*/
X/*
X#define sleep(addr, pri) printf("sleep %x at %x ", addr, pri), sleep( addr, pri)
X*/
X
X/* My recollection of system-independent timing code suggested by
X   somebody from Dell or Compaq on Usenet.  If I could find the original
X   article, I'd be much happier. */
X#ifdef DO_IOWAITS
X#define IOWAIT outb(0x84,0)
X#else
X#define IOWAIT
X#endif
X
X/* old version; not so good */
X/* #define IOWAIT {int sp; for (sp = 0; sp < 10; sp++) ww();} */
X
X#ifdef STROBE_PROT
X
X#ifdef M_XENIX
X#define INT_VAR	int oldspl
X#define INT_DI	oldspl = spl7()
X#define INT_RE	splx(oldspl)
X#else
X#define INT_VAR
X#define INT_DI  intr_disable()
X#define INT_RE	intr_restore()
X#endif
X
X#else /* STROBE_PROT */
X
X#define INT_VAR
X#define INT_DI
X#define	INT_RE
X
X#endif
X
XPRIVATE unsigned int ports[] = { 
X#ifdef M_XENIX
X  0x378, 0x3bc, 0x278, 0x178
X#else
X  0x3bc, 0x378, 0x278, 0x178
X#endif
X  };
X
X
XPRIVATE unsigned int portstat[] = { 0, 0, 0, 0 };
X
XPRIVATE time_t closetime[] = { 0, 0, 0, 0 };
X
X#define PS_OK 1
X#define PS_OPEN 2
X
XPRIVATE unsigned char llbufs[4][BUFSIZ];
X
X/* initialize printer */
XPRIVATE void llreset(unit)
X     int unit;
X{
X  int portbase = ports[unit];
X  outb(portbase+P_CONTROL, 0);
X  {
X    int foo;
X    for (foo = 0; foo < 10; foo++)
X      IOWAIT;
X  }
X  outb(portbase+P_CONTROL, P_SELECT | P_NOT_INIT);
X}
X
XPRIVATE llwake(caddr_t top)
X     
X{
X  wakeup(top);
X}
X
X/* device xx_init code */
Xllinit()
X{
X  int unit;
X  
X  for (unit = 0; unit < 4; unit++)
X    {
X      unsigned int portbase = ports[unit];
X
X      char *found = "not found";
X      
X      /* is port configured in driver? */
X      if (portbase == 0) continue;
X
X      /* is port really out there? */
X      outb(portbase, 0xaa);
X      IOWAIT;
X      if ( 0xaa == inb(portbase) )
X	{
X	  portstat[unit] |= PS_OK;
X	  found = "found";
X	}
X      /* let the world know what we've found */
X#ifdef M_XENIX
X      (void) printcfg ("ll", portbase, 3,
X		       -1, -1,
X		       "version %s unit=%d %s",
X		       VERSION, unit, found);
X#else
X      (void) printf("ll %s parallel driver: port %d at ioaddr %x %s\n", 
X		    VERSION, unit, portbase, found);
X#endif
X#ifdef INIT
X      /* reset the port */
X      if (portstat[unit] & PS_OK)
X	llreset(unit);
X#endif
X    }
X}
X
X/* device xx_open code */
Xvoid llopen(dev, flag)
X     int dev, flag;
X
X{
X  int unit = minor(dev) & PORTMASK;
X
X  /* Is the port available? */
X  if (!(portstat[unit] & PS_OK))
X    {
X      u.u_error = ENXIO;
X      return;
X    }
X  /* is it busy? */
X  if (portstat[unit] & PS_OPEN)
X    {
X      u.u_error = EBUSY;
X      return;
X    }
X
X  /* save opened state */
X  portstat[unit] |= PS_OPEN;
X
X  /* init the printer if selected in dev */
X  if ( minor(dev) & INITMASK )
X    {
X      /* make sure we have waited at least OPENWAIT before printer init */
X      if (closetime[unit] > lbolt)
X	delay(closetime[unit] - lbolt);
X      llreset(unit);
X    }
X}
X
X/* device xx_close code */
Xvoid llclose(dev, flag)
X     int dev, flag;
X
X{
X  int unit = minor(dev) & PORTMASK;
X
X  /* leave a little clue for waiting upon the printer init; it's cheap */
X  closetime[unit] = lbolt + (OPENWAIT * HZ);
X
X  /* we are done... */
X  portstat[unit] &= ~PS_OPEN;
X}
X
X/* Device xx_write routine */
Xvoid llwrite(dev)
X     int dev;
X{
X  unsigned char *buf;
X  unsigned int count;
X  int portno, oldspl;
X  caddr_t sleeploc;
X  
X  portno = minor(dev) & PORTMASK;
X  buf	    = &llbufs[portno][0];
X  sleeploc = ((caddr_t)&ports[portno])+1;
X  for
X    (
X     u.u_base, u.u_count;
X     (count = min(u.u_count,BUFSIZ)) != 0
X     && copyin(u.u_base,buf,count) == 0;
X     u.u_base += count, u.u_count -= count
X     )
X      {
X	if (-1 == llp(portno,buf,buf+count))
X	  {
X	    u.u_error = EINTR;
X	    return;
X	  }
X	/* be a good driver and yield the cpu once in a while */
X	/* delay(0) is the right idea, but doesn't work under Xenix */
X	/* delay(0); */
X	oldspl = spl7();
X	timeout(llwake, sleeploc, 0);
X	if (1 == sleep(sleeploc, PCATCH|PWAIT))
X	  {
X	    splx(oldspl);
X	    u.u_error = EINTR;
X	    return;
X	  }
X	splx(oldspl);
X      }
X}
X
X#ifdef ASM
X
X#ifdef M_XENIX
X
Xextern unsigned char *llstuff();
X
X#else
X
X/*
X  if this code is ugly, that's because I've left it that way.
X  It's hand-optimized compiler output that is functionally identical
X  to the C code.
X
X  This code has little to no performance improvement over the C code
X  on SVR4, at least on my 16 MHz 386sx system.  The Xenix assembler,
X  on the other hand, is twice as fast as the C code.
X
X  If nobody speaks up during the beta test, I'm removing it for the final
X  release.
X  */
X
X/* first define our code */
Xasm unsigned char *_llstuff( portbase, buf, endbuf, spincount)
X{
X%mem	portbase,buf,endbuf,spincount;
X
X	pushl	%esi
X	pushl	%edi
X	pushl	%ebx
X
X/ reg esi = buf
X	movl	buf, %esi
X/ reg %edi = endbuf
X	movl	endbuf, %edi
X/ reg ebx = portbase
X	movl	portbase, %edx
X/	spincount = 20
X/ reg ecx = inspincount
X/ loaded when needed in .wait
X
X	cmpl  	%edi, %esi
X	jae  	.exit
X
X	incl	%edx
X
X	.align	4
X.top:
X	inb	(%dx)
X	testb 	$128, %al
X	je  	.wait
X.ready:
X	decl	%edx
X	lodsb
X	outb	(%dx)
X
X	outb	$132
X
X	pushfl
X
X	movb	$13,%al
X	leal	 2(%edx), %edx
X
X	cli
X
X	outb	(%dx)
X
X	outb	$132
X
X	movb	$12,%al
X	outb	(%dx)
X
X	popfl
X	decl	%edx
X
X	cmpl 	%edi, %esi
X	jb  	.top
X	jmp	.exit
X
X	.align	4
X.wait:
X	movl	spincount, %ecx
X.waitloop:
X	inb	(%dx)
X	testb 	$128, %al
X	jne	.ready
X	decl  	%ecx
X	jne	.waitloop
X	jmp	.exit
X
X	.align 4
X.exit:
X	movl  	%esi, %eax
X
X	popl	%ebx
X	popl	%edi
X	popl	%esi
X	}
X
X/* ...then actually use it */
X
XPRIVATE unsigned char *llstuff(portbase, buf, endbuf, spincount)
X     int portbase;
X     unsigned char *buf, *endbuf;
X     int spincount;
X     
X{
X  _llstuff(portbase, buf, endbuf, spincount);
X}
X
X#endif
X
X#else /* ASM */
X
X/* Write character buffer to ioaddr 'portbase', return if printer busy */
X/* Usable at task or interrupt time */
XPRIVATE unsigned char *llstuff(portbase, buf, endbuf, spincount)
X     register int portbase;
X     register unsigned char *buf, *endbuf;
X     int spincount;
X     
X{
X  int inspincount;
X  INT_VAR;
X  
X  while (buf < endbuf)
X    {
X      inspincount = spincount;
X      while (!(P_NOT_BUSY & inb(portbase+1)))
X	if (--inspincount == 0) return buf;
X      outb(portbase,*buf);
X      buf++;
X      /* this is a vaguely critical section of code */
X      IOWAIT;
X      INT_DI;
X      outb(portbase+2, P_SELECT | P_NOT_INIT | P_STROBE);
X      IOWAIT;
X      outb(portbase+2, P_SELECT | P_NOT_INIT);
X      INT_RE;
X      /* end vaguely critical */
X    }
X  return buf;
X}
X
X#endif  /* ASM */
X
X/* Write output to printer, yielding timeslice occasionally */
XPRIVATE int llp(port, buf, endbuf)
X
Xint port;
Xunsigned char *buf, *endbuf;
X
X{
X  while ((buf = llstuff(ports[port], buf, endbuf, SPINCNT)) != endbuf)
X    {
X      int oldspl;
X      caddr_t sleeploc;
X
X      sleeploc = (caddr_t) &ports[port];
X
X      /* stuff could be added here to check for printer errors */
X      /* sleep for a little bit; allow interrupts */
X      oldspl = spl7();
X      timeout(llwake, sleeploc, SLEEPTIME);
X      if (1 == sleep(sleeploc, PCATCH|PWAIT))
X	/* sleep interrupted-- drop out of loop */
X	{
X	  splx(oldspl);
X	  return -1;
X	}
X      splx(oldspl);
X    }
X  return 0;
X}
X
END_OF_FILE
if test 9872 -ne `wc -c <'lp.c'`; then
    echo shar: \"'lp.c'\" unpacked with wrong size!
fi
# end of 'lp.c'
fi
if test -f 'master.ll' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'master.ll'\"
else
echo shar: Extracting \"'master.ll'\" \(72 characters\)
sed "s/^X//" >'master.ll' <<'END_OF_FILE'
Xll       0  0132 004     ll  0   0   6    1   0    0     0     0   0   
END_OF_FILE
if test 72 -ne `wc -c <'master.ll'`; then
    echo shar: \"'master.ll'\" unpacked with wrong size!
fi
# end of 'master.ll'
fi
if test -f 'xenixconf.ll' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'xenixconf.ll'\"
else
echo shar: Extracting \"'xenixconf.ll'\" \(11 characters\)
sed "s/^X//" >'xenixconf.ll' <<'END_OF_FILE'
Xll       1
END_OF_FILE
if test 11 -ne `wc -c <'xenixconf.ll'`; then
    echo shar: \"'xenixconf.ll'\" unpacked with wrong size!
fi
# end of 'xenixconf.ll'
fi
echo shar: End of shell archive.
exit 0
-- 
John Hood					Cthulhu-- just imagine it!
jhood@smoke.marlboro.vt.us
Duke U, 1980:  "Okay, so a few systems have the net started. What next?"
Newsgroups: comp.unix.pc-clone.32bit,comp.unix.sysv386,comp.unix.sys5.r4