home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Source Code 1992 March
/
Source_Code_CD-ROM_Walnut_Creek_March_1992.iso
/
usenet
/
altsrcs
/
1
/
1742
/
pp.c
< prev
next >
Wrap
C/C++ Source or Header
|
1990-12-28
|
7KB
|
319 lines
/*
$Id: pp.c,v 2.6 90/08/24 11:48:24 sw Exp $
*/
static char Notice[] =
"Copyright 1990 Piercarlo Grandi. All rights reserved.";
/*
This driver is 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, this driver 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.
*/
/*
Fast parallel port driver, polling, no interrupts (somewhat
inspired by a driver from Michael Grenier <mike@cimcor.mn.org>)
*/
#include "sys/param.h"
#include "sys/types.h"
#include "sys/dir.h"
#include "sys/signal.h"
#include "sys/user.h"
#include "sys/buf.h"
#include "sys/errno.h"
#include "sys/immu.h"
#include "sys/sysmacros.h"
#include "sys/inline.h"
#ifdef ppDEBUG
unsigned pp_debug = 1;
# define DEBUG(STMTS) do { if (pp_debug) { STMTS; }; } while (0)
unsigned pp_nchar;
unsigned pp_nlongpause;
unsigned pp_nshortpause;
unsigned pp_nspin;
#else
# define DEBUG(list) /* skip */
#endif
#include "sys/pp.h"
#if (ppSTATICSZ == 0)
# define ppBUFSZ SBUFSIZE
#else
# define ppBUFSZ ppSTATICSZ
#endif
/*
A long pause for polling the printer until it is online again
the pause while the strobe is high, and a short pause to wait
for the interface to become ready for the next character.
The longer pause is done sleeping, the other two are done
spinning, so they had better be few.
*/
extern int min();
extern int tenmicrosec();
extern int wakeup();
extern int timeout();
extern int sleep();
#if (ppSTATICSZ == 0)
extern struct buf *geteblk();
#endif
#define ppSPIN() (void) (tenmicrosec())
#define ppPAUSE(U,N) (void) (timeout(wakeup,(U),(N)),sleep((U),PSLEP))
extern int copyin();
extern int ppinit()
{
register struct pp_config *pp;
for (pp = pp_config; pp < (pp_config+pp_max); pp++)
{
# if (ppSTATICSZ != 0)
pp->buf = &pp_sbuf[pp-pp_config][0];
# endif
outb(pp->control,(ppSELECT|ppNOTRESET));
DEBUG(printf("Initial status of /dev/pp%d is %x\n",
pp-pp_config,inb(pp->status)));
}
}
/* ARGSUSED */
extern int ppopen(dev,mode)
int dev,mode;
{
register int unit = minor(dev);
register unsigned status;
register struct pp_config *pp;
if (unit >= pp_max)
{
u.u_error = EIO;
return;
}
pp = &pp_config[unit];
status = inb(pp->status);
if ((status&0xff) == 0xff)
{
u.u_error = ENXIO;
DEBUG(printf("Printer /dev/pp%d does not exist\n",unit));
return;
}
/*
We ignore error condition except when printer is online ready.
*/
if ((status&(ppONLINE|ppNOTBUSY|ppNOTERROR)) == (ppONLINE|ppNOTBUSY))
{
u.u_error = EIO;
DEBUG(printf("Error status %x detected on /dev/pp%d\n",status,unit));
return;
}
DEBUG((pp_nchar = pp_nlongpause = pp_nshortpause = pp_nspin = 0));
# if (ppSTATICSZ == 0)
{
int s;
s = spl3();
{
if (pp->hdr)
{
splx(s);
DEBUG(printf("Printer /dev/pp%d already open (%x)\n",
unit,pp->hdr));
return;
}
pp->hdr = (struct buf *) 1;
}
splx(s);
pp->hdr = geteblk();
if (pp->hdr)
{
pp->hdr->b_flags |= B_PRIVLG;
pp->buf = pp->hdr->b_un.b_addr;
}
else
{
u.u_error = ENOMEM;
DEBUG(printf("Cannot allocate buffer for /dev/pp%d\n",unit));
}
DEBUG(printf("Printer /dev/pp%d has bufhdr %x, buf %x\n",
unit,pp->hdr,pp->buf));
}
#endif
}
/* ARGSUSED */
extern int ppclose(dev)
int dev;
{
DEBUG(printf("Printed %d characters, paused %d and spun %d (%d) times\n",
pp_nchar,pp_longpause,pp_nspin,pp_nshortpause));
# if (ppSTATICSZ == 0)
{
register struct pp_config *pp;
pp = &pp_config[minor(dev)];
if (pp->hdr)
{
pp->buf = (caddr_t) 0;
pp->hdr->b_flags &=~ B_PRIVLG;
brelse(pp->hdr);
pp->hdr = (struct buf *) 0;
}
else
DEBUG(printf("Impossible close without buffer of /dev/pp%d\n",
pp-pp_config));
}
# endif
}
static void ppbufwrite(pp,buf,endbuf)
register struct pp_config *pp;
caddr_t buf;
register caddr_t endbuf;
{
register unsigned status;
register int statusp;
register char *ch;
statusp = pp->status;
for (ch = (char *) buf; ch < (char *) endbuf; ch++)
{
DEBUG(pp_nchar++);
latch_character:
outb(pp->data,*ch);
/*
Here we wait for the printer to be ready, and this may
be a longish wait, because of paper end, offline, power off.
We sleep, polling a few times per second, as this is simpler
than taking an interrupt and the cost is very low.
*/
wait_printer_online:
for
(
status = inb(statusp);
(status & pp_guard.pause.mask) != pp_guard.pause.done
|| (status & pp_guard.spin.mask) != pp_guard.spin.done;
status = inb(statusp)
)
{
ppPAUSE((caddr_t) &pp->status,pp_longpause);
DEBUG(pp_nlongpause++);
}
/*
Allelluiah! The printer is ready. Strobe the character
we had already latched in.
*/
strobe_awhile:
outb(pp->control,(ppSELECT|ppNOTRESET|ppSTROBE));
ppSPIN(); /* This should last at least one microsecond */
outb(pp->control,(ppSELECT|ppNOTRESET));
/*
Here we wait for the interface to be ready to accept another
character, and thus we spin, because we expect the wait to be
short (e.g. 3-6 turns). This is cheaper than the overheads
involved in sleeping or even in taking an interrupt.
*/
wait_interface_ready:
{
register unsigned spins;
for
(
spins = 0, status = inb(statusp);
spins < pp_maxspins /* Don't waste too much time spinning */
&& (status & pp_guard.spin.mask) != pp_guard.spin.done
&& (status & pp_guard.pause.mask) == (pp_guard.pause.done);
spins++, status = inb(statusp)
)
{
ppSPIN();
DEBUG(pp_nspin++);
}
/*
This is not really necessary; we could just have one pause
above, before latching the character. We do not do that
simply because we want to have a shorter pause for buffer
full here, to have snappier response. Well, in theory :->.
*/
if (spins == pp_maxspins)
{
ppPAUSE((caddr_t) &pp->status,pp_shortpause);
DEBUG(pp_nshortpause++);
}
}
}
}
extern int ppwrite(dev)
int dev;
{
register struct pp_config *pp;
register caddr_t buf;
register unsigned count;
pp = &pp_config[minor(dev)];
buf = pp->buf;
DEBUG(printf("Print total %d chars from %x on /dev/pp%d\n",
u.u_count,u.u_base,pp-pp_config));
for
(
u.u_base, u.u_count;
(count = min(u.u_count,ppBUFSZ)) != 0
&& copyin(u.u_base,buf,count) == 0;
u.u_base += count, u.u_count -= count
)
{
DEBUG(printf("Print %d chars from %x on /dev/pp%d\n",
count,u.u_base,pp-pp_config));
ppbufwrite(pp,buf,buf+count);
}
}