*BSD News Article 4227


Return to BSD News archive

Newsgroups: comp.unix.bsd
Path: sserve!manuel!munnari.oz.au!uniwa!cujo!marsh!cproto
From: cproto@marsh.cs.curtin.edu.au (Computer Protocol)
Subject: 386bsd 0.1 printer driver source resubmitted
Message-ID: <cproto.715011443@marsh>
Summary: 386bsd printer driver source resubmitted
Keywords: 386bsd /dev/lp0 lpt printer parallel port
Sender: news@cujo.curtin.edu.au (News Manager)
Organization: Curtin University of Technology
Date: Fri, 28 Aug 1992 14:17:23 GMT
Lines: 585


Hi there,

as a number of people requested it, I resubmit my trivial but working
printer driver.

Add file lp.c and apply the other changes.

Do the following:

    config YOURCONF
    cd ../../compile/YOURCONF
    make depend
    make
    mv /386bsd /386bsd.save
    mv 386bsd /
    
Create the device entry by doing a

    mknod /dev/lp0 c 14 0

Reboot using:

    sync;sync;fastboot

Print using something like:

    cat printfile > /dev/lp0

Have fun

Regards - Tibor Sashegyi - cproto@abel.cs.curtin.edu.au

------------- /sys/i386/isa/lp.c --------------

/*
** parallel port printer driver
** by Tibor Sashegyi 19/04/92
** (ideas from everywhere)
** stage 2
*/

#include "lp.h"

#if NLP > 0

#include "param.h"
#include "systm.h"
#include "proc.h"
#include "user.h"
#include "conf.h"
#include "file.h"
#include "uio.h"
#include "kernel.h"
#include "syslog.h"
#include "buf.h"

#include "i386/isa/isa_device.h"

/*
** printer port base addresses
*/


#define LPB_1_BASE 0x378	/* base i/o address printer 1 */
#define LPB_2_BASE 0x3BC	/* base i/o address printer 2 */
#define LPB_3_BASE 0x278	/* base i/o address printer 3 */

/*
** i/o registers
*/

#define LPR_DATA 0x00		/* data register */
#define LPR_STAT 0x01		/* status register */
#define LPR_CTRL 0x02		/* control register */

/*
** control bits
*/

#define LPC_INIT   0x08		/* select and init printer */
#define LPC_STROBE 0x1D		/* strobe data */
#define LPC_READY  0x1C		/* idle IRQ enabled */
#define LPC_SELECT 0x0C		/* select */

/*
** status bits
*/

#define LPS_ERROR  0x08		/* 0 general error */
#define LPS_SELECT 0x10		/* 1 selected */
#define LPS_PAPER  0x20		/* 1 out of paper */
#define LPS_BUSY   0x80		/* 0 busy */

/*
** lp control block
*/

#define LPF_ACTIVE 0x01
#define LPF_OPEN   0x02
#define LPF_BUSY   0x04
#define LPF_PAPER  0x08
#define LPF_ERROR  0x10

#define MAXBUF     0x100

struct lpctrl
	{
	int			unit;
	int			port;
	char		flags;
	int			error;
	int			count;
	char		*cp;
	char		buf[MAXBUF];
	} lp_ctrl[NLP];

/*
** misc
*/

#define LPWATCH   1

int lpprobe(), lpattach(), lpphysio(), lpstrategy();
void lpwatchdog(), lpintr(), lpiowait(), lpiodone();

/*
** define autoconfig entries
*/

struct isa_driver lpdriver =
	{
	lpprobe, lpattach, "lp"
	};

#define	UNIT(x)		minor(x)

/*
** the following delay value is about right for my machine
** fix it for anything else
*/

#define CPUSPEED 7
#define	LPDELAY(n)	{ register int N = (n) * CPUSPEED; while (--N > 0); }

/*
** probe for device 
** I really should probe for the parallel port
** and maybe even the printer, but I trust the
** configuration (silly me).
*/

int lpprobe(idev)
struct isa_device *idev;
	{
	if (idev->id_unit >= NLP)
		return(0);

	if (   idev->id_iobase != LPB_1_BASE
	    && idev->id_iobase != LPB_2_BASE
	    && idev->id_iobase != LPB_3_BASE)
		{
		printf("lp base out of range:%x\n", idev->id_iobase);
		return(0);
		}

	return(1);
	}

/*
** attach device
*/

int lpattach(idev)
struct isa_device *idev;
	{
	int unit = idev->id_unit;
	int port = idev->id_iobase;
	struct lpctrl *lpc = &lp_ctrl[unit];


	lpc->unit = unit;
	lpc->port = port;
	lpc->flags = LPF_ACTIVE;

	/*
	** init printer (pulse width at least 50 us)
	*/

	outb(port + LPR_CTRL, LPC_INIT);
	LPDELAY(51);

	/*
	** keep selected
	*/

	outb(port + LPR_CTRL, LPC_SELECT);

	return (1);
	}

/*
** open device
*/

lpopen(dev, flag, mode, p)
	dev_t dev;
	int flag, mode;
	struct proc *p;
	{
	int unit = UNIT(dev);
	struct lpctrl *lpc = &lp_ctrl[unit];
	char val;
 
	/*
	** make sure device is configured 
	*/

	if (unit >= NLP || (lpc->flags & LPF_ACTIVE) == 0)
		return(ENXIO);

	/*
	** only one open !
	*/

	if (lpc->flags & LPF_OPEN)
		return(EBUSY);

	/*
	**	check direction
	*/

	if (flag & FREAD)
		return(ENODEV);

	/*
	** check printer status
	*/

	val = inb(lpc->port + LPR_STAT);
	if (val & LPS_PAPER)
		{
		uprintf("lp%d: out of paper\n", unit);
		return(EIO);
		}

	if ((val & LPS_ERROR) == 0 || (val & LPS_SELECT) == 0)
		{
		uprintf("lp%d: not ready\n", unit);
		return(EIO);
		}

	/*
	** start watchdog timer
	*/

	timeout(lpwatchdog, lpc, LPWATCH*hz);
	
	/*
	** enable interrupts and mark as opened
	*/

	outb(lpc->port + LPR_CTRL, LPC_READY);
	lpc->flags |= LPF_OPEN;

	return(0);
	}
 
/*
** close device
*/

int lpclose(dev, flag, mode, p)
	dev_t dev;
	int flag, mode;
	struct proc *p;
	{
	int unit = UNIT(dev);
	struct lpctrl *lpc = &lp_ctrl[unit];

	/*
	** disable interrupts but keep selected, mark as closed
	*/

	outb(lpc->port + LPR_CTRL, LPC_SELECT);
	lpc->flags &= ~LPF_OPEN;

	return(0);
	}
 
/*
** write to device
*/

int lpwrite(dev, uio)
	dev_t dev;
	struct uio *uio;
{
	return (uioapply(lpphysio, lpstrategy, dev, uio));
}

/*
** check access, lock and call lpstrategy 
** to output next printer buffer
*/

int lpphysio(strat, dev, off, rw, base, len, p)
	int (*strat)(); 
	dev_t dev;
	int rw, off;
	caddr_t base;
	int *len;
	struct proc *p;
{
	int error;
	int rest = *len;
	int cnt;
	caddr_t addr = base;

	rw = rw == UIO_READ ? B_READ : 0;

	/*
	** check if accessible
	*/

	if (rw == B_READ && !useracc(base, *len, B_WRITE))
		return (EFAULT);

	if (rw == B_WRITE && !useracc(base, *len, B_READ))
		return (EFAULT);

	/*
	** lock in core
	*/

	vslock (base, *len);

	/*
	** perform transfer
	*/

	error = 0;
	while(error == 0 && rest > 0)
		{
		cnt = min(rest, MAXBUF);
		rest -= cnt;
		error = lpstrategy(UNIT(dev), addr, cnt);
		addr += cnt;
		}

	/*
	** unlock
	*/

	vsunlock (base, *len, 0);

	*len = 0;
	return (error);
	}

/*
** fetch next buffer and start output
*/

int lpstrategy(unit, addr, len)
	int unit;
	caddr_t addr;
	int len;
	{
	struct lpctrl *lpc = &lp_ctrl[unit];
	int s;

	lpc->count = len;
	if (copyin(addr, lpc->buf, len))
		{
		lpiodone(lpc);
		return(EFAULT);
		}

	lpc->cp = lpc->buf;
	lpc->flags |= LPF_BUSY;
	lpc->error = 0;

	s = splhigh();
	lpintr(unit);
	splx(s);
	
	/*
	** wait for i/o to complete
	*/

	lpiowait(lpc);

	return(lpc->error);
	}

/*
** output next characters until printer becomes busy
** interrupt driven except for the first time
*/
 
void lpintr(unit)
	int unit;
{
	struct lpctrl *lpc = &lp_ctrl[unit];
	int port = lpc->port;
	char val;

	/*
	** check for spurious interrupt
	*/

	if (!(lpc->flags & LPF_BUSY))
		return;

	/*
	** any more to print
	*/

	while (lpc->count)
		{
		val = inb(lpc->port + LPR_STAT);
		if ((val & LPS_BUSY) == 0)
			return;
		/*
		** output the character
		*/

		outb(port + LPR_DATA, *lpc->cp++);
		--lpc->count;

		/*
		** valid data must be present for at least 0.5 us
		*/

		LPDELAY(1);

		/*
		** strobe for 1 us
		*/

		outb(port + LPR_CTRL, LPC_STROBE);
		LPDELAY(1);
		outb(port + LPR_CTRL, LPC_READY);

		/*
		** valid data must be present for at least 0.5 us
		*/

		LPDELAY(1);
		}

	lpiodone(lpc);
	}

/*
** wait for i/o to finish
*/

void lpiowait(lpc)
	struct lpctrl *lpc;
	{
	while (lpc->flags & LPF_BUSY) 
		{
		if (lpc->error = tsleep(lpc, PRIBIO|PCATCH, NULL, 0))
			lpc->flags &= ~LPF_BUSY;
		}
	}

/*
** wakeup strategy function
*/

void lpiodone(lpc)
	struct lpctrl *lpc;
	{
	lpc->flags &= ~LPF_BUSY;
	wakeup(lpc);
	}

/*
** handle watchdog timeout
*/

void lpwatchdog(lpc)
	struct lpctrl *lpc;
	{
	char val;
	int s;

	/*
	** are we still running ?
	*/

	s = splhigh();	/* bit of an overkill ?!? */

	if ((lpc->flags & LPF_OPEN) == 0)
		{
		splx(s);
		return;
		}

	/*
	** check printer status
	*/

	val = inb(lpc->port + LPR_STAT);

	/*
	** report errors only once
	*/

	if (val & LPS_PAPER && (lpc->flags & LPF_PAPER) == 0)
		{
		printf("lp%d: out of paper, please fix\n", lpc->unit);
		lpc->flags |= LPF_PAPER;
		}
	if (lpc->flags & LPF_PAPER && (val & LPS_PAPER) == 0)
		lpc->flags &= ~LPF_PAPER;

	if ((val & LPS_ERROR) == 0 && (lpc->flags & LPF_ERROR) == 0)
		{
		printf("lp%d: not ready, please fix\n", lpc->unit);
		lpc->flags |= LPF_ERROR;
		}
	if (lpc->flags & LPF_ERROR && (val & LPS_ERROR) == 0)
		lpc->flags &= ~LPF_ERROR;
	
	/*
	** restart watchdog timer
	*/

	timeout(lpwatchdog, lpc, LPWATCH*hz);
	splx(s);
	}

#endif
----------------- /sys/i386/conf/files.i386 --------------

add this:

i386/isa/lp.c		optional lp device-driver

--------------- /sys/i386/conf/YOURCONF -------------------

add this:

device		lp0		at isa? port "IO_LPT1" irq 7 vector lpintr

--------------- /sys/i386/i386/conf.c ----------------------

apply the following patch:

*** /sys/i386/i386/conf.c	Sat May 30 23:48:08 1992
--- conf.c	Tue Aug 25 22:34:52 1992
***************
*** 165,170 ****
--- 165,179 ----
  #define	com_tty		NULL
  #endif
  
+ #include "lp.h"
+ #if NLP > 0
+ int lpopen(),lpclose(),lpwrite();
+ #else
+ #define lpopen		enxio
+ #define lpclose		enxio
+ #define lpwrite		enxio
+ #endif
+ 
  int	logopen(),logclose(),logread(),logioctl(),logselect();
  
  int	ttselect(), seltrue();
***************
*** 214,219 ****
--- 223,231 ----
  	{ asopen,	asclose,	rawread,	rawwrite,	/*D*/
  	  asioctl,	enodev,		nullop,		NULL,
  	  seltrue,	enodev,		asstrategy },
+ 	{ lpopen,	lpclose,	enodev,		lpwrite,	/*E*/
+ 	  enodev,	enodev,		nullop,		NULL,
+ 	  seltrue,	enodev,		NULL},
  };
  int	nchrdev = sizeof (cdevsw) / sizeof (cdevsw[0]);
-------------------------- end of hack -------------------------