home *** CD-ROM | disk | FTP | other *** search
- /*
- $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);
- }
- }
-