[comp.unix.xenix] Lpt device driver for the Interactive UNIX

dirk@altger.UUCP (dirk) (03/12/88)

Since today I became four IBM AT parallel device drivers for the
Interactive 386/ix UNIX from Interactive. None of them worked the
way I wanted to have it. The new 1.0.4 release update driver seems
very good, but it stops after each line and waits long times between
lines. The release update paper says that this new driver does not
stop at end of line, but the driver does.

Due to this I rewrote the lp driver some time before. Posted here,
you'll find an older version which was limited to one port only.
But you can easy append code to it. The one I am using supports
128 ports and includes mapping tables to translate control-sequences.
This driver should work on XENIX and Microport as well, if you
change some code or append facilities to it, pls. send me a mail.

cu,
dirk :-)
Dirk Koeppen, DK-SOFT, West Germany, ..!unido!cosa!dirk

INSTALLATION ON 386/ix:
	1. Create a directory /etc/atconf/modules/lpt
	2. Copy this file into the directory and unbundle it
	3. Run the Makefile
	4. Run kconfig 
		- Use the remove driver option and remove the lp driver
		- Use the add driver option to select the new driver

---------------------------> CUT HERE <--------------------------------

# To unbundle, sh(ell) this file
# ==============================
echo README 1>&2
cat >README <<'End of ===README==='
Sine today I became four IBM AT parallel device drivers for the
Interactive 386/ix UNIX from Interactive. None of them worked the
way I wanted to have it. The new 1.0.4 release update driver seems
very good, but it stops after each line and waits long times between
lines. The release update paper says that this new driver does not
stop at end of line, but the driver does.

Due to this I rewrote the lp driver some time before. Posted here,
you'll find an older version which was limited to one port only.
But you can easy append code to it. The one I am using supports
128 ports and includes mapping tables to translate control-sequences.
This driver should work on XENIX and Microport as well, if you
change some code or append facilities to it, pls. send me a mail.

cu,
dirk :-)
Dirk Koeppen, DK-SOFT, West Germany, ..!unido!cosa!dirk

INSTALLATION ON 386/ix:
	1. Create a directory /etc/atconf/modules/lpt
	2. Copy this file into the directory and unbundle it
	3. Run the Makefile
	4. Run kconfig 
		- Use the remove driver option and remove the lp driver
		- Use the add driver option to select the new driver

End of ===README===
echo Makefile 1>&2
cat >Makefile <<'End of ===Makefile==='
#ident @(#) Makefile v1.0 of 88/12/03 , dirk
# Makefile for lpt.o
# Copyright (c) 1987, 1988 DK-Soft, Dirk Koeppen, .. unido!cosa!dirk
# All Rights Reserved

SHELL		=/bin/sh
CFLAGS		=-O -c -DLIMITED
SILENTCC	=YES

all: lpt.o ../description

lpt.o:	lpt.c config
	cc $(CFLAGS) lpt.c 

../description: description
	mv ../description ../description-
	cat ../description- description > ../description

clean:
	rm -f foo Mout Lout a.out core *.i *.obj *.o
	rm -f lpt.o 
End of ===Makefile===
echo config 1>&2
cat >config <<'End of ===config==='
* 1 "lpt/config"

*	Copyright (c) 1987, 1988 DK-Soft, Dirk Koeppen, .. unido!cosa!dirk
*	All Rights Reserved

*ident	"@(#)config	1.0 - 87/12/23"

character(7)

prefix = lpt

intvec = 7

intpri = SPL3

functions = open, close, write, start, intr, init
End of ===config===
echo description 1>&2
cat >description <<'End of ===description==='
lpt     -    -     io     -             The DK-Soft AT Line Printer Driver
End of ===description===
echo lpt.c 1>&2
cat >lpt.c <<'End of ===lpt.c==='
/*
 * COPYRIGHT (c) 1987-88, dksoft, .. unido!cosa!dirk
 * char SCCS_id[]= "%Z% %M% v%I% of %D% %Q%, dirk"
 * LPT line printer driver
 *
 * delta date      modification					 done by
 * ----- --------- --------------------------------------------- -------------
 *  1.3  25.Dec'87 up to 128 ports support			 dirk
 *  1.2  24.Dec'87 device polling on lost intr.			 dirk
 *  1.1  23.Dec'87 first steps					 dirk
 */

#include <sys/types.h>
#include <sys/param.h>
#include <sys/tty.h>

/*
 * The registers for the printer port
 */
struct lptdevice
{
	unsigned	data;			/* lpt data port */
	unsigned	status;			/* lpt status port */
	unsigned	control;		/* lpt control port */
	unsigned	flags;			/* driver status flag */
	struct clist	queue;			/* clist head */
};

/************************* tuneable parameters ****************************/

#define	LPTINTR		7			/* Interrupt, at least one */
#define MINORTYPE	unsigned char		/* type of the minor number */
#define	LPTSPL		spl3 ()			/* task-time interrupt disab. */
#define	LPPRI		PZERO+3			/* priority of the driver */
#define	LOWAT		50			/* wakeup if clist below 50 */
#define	HIWAT		150			/* go to sleep if more 150 */

#ifndef LIMITED
# define 	MAXPORT		3			/* number of ports */
#else
# define	MAXPORT		1			/* number of ports */
#endif /* LIMITED */

struct lptdevice	lptdev[MAXPORT]= {
			{	
						/* Monochrome Card port */
				0x3bc,		/* 1. port data address */
				0x3bd,		/* 1. port status address */
				0x3be,		/* 1. port control address */
				0,		/* 1. port initial status */
						/* 1. port initial queue */	
			 }
#ifndef LIMITED
			 ,
			 {
						/* First parallel port */	
				0x378,		/* 2. port data address */
				0x379,		/* 2. port status address */
				0x37a,		/* 2. port control address */
				0,		/* 2. port initial status */
						/* 2. port initial queue */
			 },
			 {
						/* Second parallel port */	
				0x278,		/* 3. port data address */
				0x279,		/* 3. port status address */
				0x27a,		/* 3. port control address */
				0,		/* 3. port initial status */
						/* 3. port initial queue */
			 }
#endif /* LIMITED */
			 };

/**************************************************************************/

/*
 * The bits in the printer status port
 */

#define	LPERROR		0x08			/* lpt error detected 
						 * zero on error    
						 */ 
#define	LPSELECT	0x10			/* lpt is powered and selected
						 * zero on power fail or
						 * not selected
						 */
#define	LPPAPEROUT	0x20			/* lpt is out of paper
						 * zero if paper out
						 * only regarded if together
						 * with LPERROR
						 */
#define	LPACKNLG	0x40			/* lpt acknowledged data
						 * goes zero for appr. 7 usec							 */
#define	LPBUSY		0x80			/* lpt is busy
						 * zero if free for more data
						 */

/*
 * The bits in the printer control port
 */
#define	LPSTROBE	0x01			/* strobe lpt to accept data */
#define	LPLF		0x02			/* line feed is remote */
#define	LPINIT		0x04			/* init the lpt port */
#define	LPSELIN		0x08			/* select lpt */
#define	LPIENB		0x10			/* intr. enable */

/*
 * lpt_flags definitions (lpt_flags is an internal status flag variable)
 */
#define	LPTSLEEPING	0x001			/* is sleeping */
#define	LPTBUSY		0x002			/* is busy */
#define LPTTOPENDING	0x004			/* timeout flag */
#define	LPTNOPAPER	0x008			/* paper is out */
#define LPTNOSELECT	0x010			/* printer not selected */
#define	LPTNOPRINTER	0x020			/* no printer or cable */
#define	LPTNOPORT	0x040			/* no printer port */
#define	LPTERROR	0x080			/* error on printer */
#define	LPTOFFLINE	0x100			/* printer is offline */

/*
 * current printing printer for sheduling
 */
MINORTYPE	lpt_curr;

/*
 * LPTSTATUS
 * Get the status of the printerport
 *
 *		      BUSY    ACKNLG  PAPEROUT SELECT ERROR 
 * normal		+	+	-	+	+	
 * paper end		+	+	+	+	+	LPTNOPAPAPER
 * printer turned off	+	-	-	-	-	LPTNOSELECT
 * no printer cable	-	+	+	+	-	LPTNOPRINTER
 * offline		-	+	-	+	-	LPTOFFLINE
 */
unsigned
lptstatus(dev)
MINORTYPE	dev;
{
	extern struct lptdevice	lptdev[MAXPORT];
	register int		status;

	if (! (lptdev[dev].flags & LPTNOPORT))
	{
		status= inb (lptdev[dev].status);
		lptdev[dev].flags&= (LPTSLEEPING | LPTBUSY | LPTTOPENDING);
		if (status & LPPAPEROUT)
			lptdev[dev].flags|= LPTNOPAPER;
		if (~status & LPERROR)
		{
			lptdev[dev].flags|= LPTERROR;
			switch (status & (LPBUSY | LPACKNLG | LPPAPEROUT |
					           LPSELECT | LPERROR))
			{
				case LPBUSY        : lptdev[dev].flags|=
								LPTNOSELECT;
				case (LPACKNLG   | 
				      LPPAPEROUT |
				      LPSELECT   ) : lptdev[dev].flags|=
								LPTNOPRINTER;
				case (LPACKNLG   |
				      LPSELECT   ) : lptdev[dev].flags|=
								LPTOFFLINE;
			}
	       
		}
	}
	return (lptdev[dev].flags);
}


/*
 * LPTINIT
 * Look for printer ports and reset them at boot time
 */
lptinit()
{
	extern struct lptdevice	lptdev[MAXPORT];
	register int		status;
	int			i, try;

	printf ("Parallel Port Driver Copyright (c) 1987, 1988 Dirk Koeppen\n");
	printf ("All Rights Reserved\n");
	lpt_curr= 0;
	for (i= 0; i < MAXPORT; i++)
	{
		printf ("  port #%d:", i);
		printf ("(0x%X:0x%X:0x%X) ", lptdev[i].data,
					 lptdev[i].status, lptdev[i].control);
		lptdev[i].flags= 0;
		for (try= 0; try < 3; try++)
		{
			outb (lptdev[i].data, 0xaa);
			if (inb (lptdev[i].data) != 0xaa)
				lptdev[i].flags|= (LPTERROR | LPTNOPORT);
			outb (lptdev[i].data, 0x55);
			if (inb (lptdev[i].data) != 0x55)
				lptdev[i].flags|= (LPTERROR | LPTNOPORT);
		}
		status= lptstatus(i);
		if (status & LPTERROR)
			printf ("Error ");
		if (status & LPTNOPAPER)
			printf ("No_paper ");
		if (status & LPTNOSELECT)
			printf ("No_select ");
		if (status & LPTNOPRINTER)
			printf ("No_printer ");
		if (status & LPTNOPORT)
			printf ("No_port ");
		if (status & LPTERROR)
			printf ("check_printer_!\n");
		else
			printf ("passed\n");
	}
	printf ("\n");
}


/*
 * LPTOPEN
 * Open the device by reseting the data port
 */
lptopen(dev, flag, id)
MINORTYPE	dev;
int		flag, id;
{
	extern struct lptdevice	lptdev[MAXPORT];

#ifdef LIMITED
	if (dev >= MAXPORT)
		dev= MAXPORT - 1;
#endif /* LIMITED */

	outb (lptdev[dev].data, 0);
	outb (lptdev[dev].control, (LPLF | LPSELIN | LPIENB));
	outb (lptdev[dev].control, (LPLF | LPINIT | LPSELIN | LPIENB));
}


/*
 * LPTCLOSE
 * Close the device
 */
lptclose(dev, flag)
MINORTYPE	dev;
int		flag;
{
	lptopen(dev, flag, 0);
}


/*
 * LPTWRITE
 * Write to the printer queue
 */
lptwrite(dev)
MINORTYPE	dev;
{
	extern struct lptdevice	lptdev[MAXPORT];
	register int		c;
	int			x;

#ifdef LIMITED
	if (dev >= MAXPORT)
		dev= MAXPORT - 1;
#endif /* LIMITED */

	/* get a char from the process's memory until no more */
	while ((c= cpass ()) >= 0)
	{
		/* is the clist filling up ? */
		if (lptdev[dev].queue.c_cc > HIWAT)
		{
			/* yes...start the device and go to sleep */
			x= LPTSPL; 
			while (lptdev[dev].queue.c_cc > HIWAT)
			{
				lptstart();
				lptdev[dev].flags|= LPTSLEEPING;
				sleep (&lptdev[dev].queue, LPPRI);
			}
			splx (x);
		}
		/* put the char on the clist */
		putc (c, &lptdev[dev].queue);
	}
	/* summon lptstart to start the printer */
	x=  LPTSPL;
	lptstart();
	splx (x);
}


/*
 * LPTRESTART
 * Restart the printer when timing out
 */
lptrestart()
{
	extern struct lptdevice	lptdev[MAXPORT];
	extern MINORTYPE	lpt_curr;
	int			x;


	lptdev[lpt_curr].flags&= ~LPTTOPENDING;
	x= LPTSPL;
	lptintr(LPTINTR);
	splx (x);
}


/*
 * LPTSTART 
 * Print out from the queue
 */
lptstart()
{
	extern struct lptdevice	lptdev[MAXPORT];
	extern MINORTYPE	lpt_curr;
	int			tmp;

	lptdev[lpt_curr].flags|= LPTBUSY;

	/* while the printer is READY and there is stuff on the clist */
	while (((inb (lptdev[lpt_curr].status) &
		(LPERROR | LPSELECT | LPACKNLG | LPBUSY | LPPAPEROUT)) ==
	 	(LPERROR | LPSELECT | LPACKNLG | LPBUSY)) &&
	       ((tmp=getc(&lptdev[lpt_curr].queue)) >= 0))
		{	
			outb (lptdev[lpt_curr].data, tmp);

			/* 0.5 usecs for drain data */
			outb (lptdev[lpt_curr].control, (LPSTROBE |
					    LPLF | LPINIT | LPSELIN | LPIENB));

			/* 0.5 usecs for settling strobe pulse */
			outb (lptdev[lpt_curr].control, (
					    LPLF | LPINIT | LPSELIN | LPIENB));

			/* 0.5 usecs min. here */
			/* Acknowledge will be checked by interrupt */
		}

	/* time to wake up the lptwrite routine ? */
	if ((lptdev[lpt_curr].queue.c_cc < LOWAT) &&
	    (lptdev[lpt_curr].flags & LPTSLEEPING))
	{
		lptdev[lpt_curr].flags&= ~LPTSLEEPING;
		wakeup (&lptdev[lpt_curr].queue);
	}
	if (lptdev[lpt_curr].queue.c_cc <= 0)
		lptdev[lpt_curr].flags &= ~LPTBUSY;
	else
		if ((lptdev[lpt_curr].flags & LPTTOPENDING) == 0)
		{
			timeout (lptrestart, 0, 1);
			lptdev[lpt_curr].flags|= LPTTOPENDING;
		}
}


/*
 * LPTINTR
 * Catch the interrupt if there is one coming
 */
lptintr(vec)
int	vec;
{
	extern struct lptdevice	lptdev[MAXPORT];
	int			i;

	for (i= 0; i < MAXPORT; i++)
	{
		if ((lptdev[i].flags & LPTBUSY) != 0)
			if (lptdev[i].queue.c_cc > 0)
				lptstart();
	}
	return;
}
End of ===lpt.c===