home *** CD-ROM | disk | FTP | other *** search
- /*
- "Copyright 1993 John Hood. 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.
- */
-
-
-
- #define VERSION "0.1BETA"
-
- #ifdef DEBUG
- #define PRIVATE
- #else
- #define PRIVATE static
- #endif
-
- /* #define ASM */
-
- /* #define STROBE_PROT */
-
-
-
- /* These defaults are tuned for fast, buffered printers. */
- #ifndef SPINCNT
- #define SPINCNT 10 /* iterations */
- #endif
-
- #ifndef SLEEPTIME
- #define SLEEPTIME 1 /* HZ */
- #endif
-
- #ifndef BUFSIZ
- #define BUFSIZ 1024 /* bytes */
- #endif
-
- #ifndef OPENWAIT
- #define OPENWAIT 60 /* seconds */
- #endif
-
- #ifdef M_XENIX
- #include "/usr/sys/h/errno.h"
- #include "/usr/sys/h/types.h"
- #include "/usr/sys/h/param.h"
- #include "/usr/sys/h/dir.h"
- #include "/usr/sys/h/signal.h"
- #include "/usr/sys/h/page.h"
- #include "/usr/sys/h/seg.h"
- #include "/usr/sys/h/user.h"
- #include "/usr/sys/h/tty.h"
- #include "/usr/sys/h/sysmacros.h"
- #else
- #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"
- #endif
-
- /* Local definitions of Stuff. */
-
- /* extract masks for device minor # */
- #define PORTMASK 3
- /* #define TYPEMASK ~3 */
-
- #define INITMASK 0x80
-
- /* parallel port offsets from base address */
- #define P_DATA 0
- #define P_STATUS 1
- #define P_CONTROL 2
-
- /* bitmap of parallel i/o ports */
-
- #define P_IRQEN 0x10
- #define P_SELECT 8
- #define P_NOT_INIT 4
- #define P_AUTOFEED 2
- #define P_STROBE 1
-
- #define P_NOT_BUSY 0x80
- #define P_NOT_ACK 0x40
- #define P_PERROR 0x20
- #define P_SELECTED 0x10
- #define P_NOT_ERROR 0x08
-
- extern time_t lbolt;
-
- #ifdef M_XENIX
- extern int outb(unsigned int port, unsigned char io);
- extern unsigned char inb(unsigned int port);
- #endif
-
- /* Various nasty debug goo */
- /* #define outb(port, byte) printf("out %x, %x ",port,byte), outb(port,byte) */
- /* #define inb(port) (printf("in %x ", port),inb(port)) */
- /*
- #define inb(port) _inb(port)
- int _inb(port)
- unsigned int port;
-
- {
- int foo;
- foo = inb(port);
- printf("in %x = %x", port, foo);
- return foo;
- }
- */
- /*
- #define sleep(addr, pri) printf("sleep %x at %x ", addr, pri), sleep( addr, pri)
- */
-
- /* My recollection of system-independent timing code suggested by
- somebody from Dell or Compaq on Usenet. If I could find the original
- article, I'd be much happier. */
- #ifdef DO_IOWAITS
- #define IOWAIT outb(0x84,0)
- #else
- #define IOWAIT
- #endif
-
- /* old version; not so good */
- /* #define IOWAIT {int sp; for (sp = 0; sp < 10; sp++) ww();} */
-
- #ifdef STROBE_PROT
-
- #ifdef M_XENIX
- #define INT_VAR int oldspl
- #define INT_DI oldspl = spl7()
- #define INT_RE splx(oldspl)
- #else
- #define INT_VAR
- #define INT_DI intr_disable()
- #define INT_RE intr_restore()
- #endif
-
- #else /* STROBE_PROT */
-
- #define INT_VAR
- #define INT_DI
- #define INT_RE
-
- #endif
-
- PRIVATE unsigned int ports[] = {
- #ifdef M_XENIX
- 0x378, 0x3bc, 0x278, 0x178
- #else
- 0x3bc, 0x378, 0x278, 0x178
- #endif
- };
-
-
- PRIVATE unsigned int portstat[] = { 0, 0, 0, 0 };
-
- PRIVATE time_t closetime[] = { 0, 0, 0, 0 };
-
- #define PS_OK 1
- #define PS_OPEN 2
-
- PRIVATE unsigned char llbufs[4][BUFSIZ];
-
- /* initialize printer */
- PRIVATE void llreset(unit)
- int unit;
- {
- int portbase = ports[unit];
- outb(portbase+P_CONTROL, 0);
- {
- int foo;
- for (foo = 0; foo < 10; foo++)
- IOWAIT;
- }
- outb(portbase+P_CONTROL, P_SELECT | P_NOT_INIT);
- }
-
- PRIVATE llwake(caddr_t top)
-
- {
- wakeup(top);
- }
-
- /* device xx_init code */
- llinit()
- {
- int unit;
-
- for (unit = 0; unit < 4; unit++)
- {
- unsigned int portbase = ports[unit];
-
- char *found = "not found";
-
- /* is port configured in driver? */
- if (portbase == 0) continue;
-
- /* is port really out there? */
- outb(portbase, 0xaa);
- IOWAIT;
- if ( 0xaa == inb(portbase) )
- {
- portstat[unit] |= PS_OK;
- found = "found";
- }
- /* let the world know what we've found */
- #ifdef M_XENIX
- (void) printcfg ("ll", portbase, 3,
- -1, -1,
- "version %s unit=%d %s",
- VERSION, unit, found);
- #else
- (void) printf("ll %s parallel driver: port %d at ioaddr %x %s\n",
- VERSION, unit, portbase, found);
- #endif
- #ifdef INIT
- /* reset the port */
- if (portstat[unit] & PS_OK)
- llreset(unit);
- #endif
- }
- }
-
- /* device xx_open code */
- void llopen(dev, flag)
- int dev, flag;
-
- {
- int unit = minor(dev) & PORTMASK;
-
- /* Is the port available? */
- if (!(portstat[unit] & PS_OK))
- {
- u.u_error = ENXIO;
- return;
- }
- /* is it busy? */
- if (portstat[unit] & PS_OPEN)
- {
- u.u_error = EBUSY;
- return;
- }
-
- /* save opened state */
- portstat[unit] |= PS_OPEN;
-
- /* init the printer if selected in dev */
- if ( minor(dev) & INITMASK )
- {
- /* make sure we have waited at least OPENWAIT before printer init */
- if (closetime[unit] > lbolt)
- delay(closetime[unit] - lbolt);
- llreset(unit);
- }
- }
-
- /* device xx_close code */
- void llclose(dev, flag)
- int dev, flag;
-
- {
- int unit = minor(dev) & PORTMASK;
-
- /* leave a little clue for waiting upon the printer init; it's cheap */
- closetime[unit] = lbolt + (OPENWAIT * HZ);
-
- /* we are done... */
- portstat[unit] &= ~PS_OPEN;
- }
-
- /* Device xx_write routine */
- void llwrite(dev)
- int dev;
- {
- unsigned char *buf;
- unsigned int count;
- int portno, oldspl;
- caddr_t sleeploc;
-
- portno = minor(dev) & PORTMASK;
- buf = &llbufs[portno][0];
- sleeploc = ((caddr_t)&ports[portno])+1;
- for
- (
- u.u_base, u.u_count;
- (count = min(u.u_count,BUFSIZ)) != 0
- && copyin(u.u_base,buf,count) == 0;
- u.u_base += count, u.u_count -= count
- )
- {
- if (-1 == llp(portno,buf,buf+count))
- {
- u.u_error = EINTR;
- return;
- }
- /* be a good driver and yield the cpu once in a while */
- /* delay(0) is the right idea, but doesn't work under Xenix */
- /* delay(0); */
- oldspl = spl7();
- timeout(llwake, sleeploc, 0);
- if (1 == sleep(sleeploc, PCATCH|PWAIT))
- {
- splx(oldspl);
- u.u_error = EINTR;
- return;
- }
- splx(oldspl);
- }
- }
-
- #ifdef ASM
-
- #ifdef M_XENIX
-
- extern unsigned char *llstuff();
-
- #else
-
- /*
- if this code is ugly, that's because I've left it that way.
- It's hand-optimized compiler output that is functionally identical
- to the C code.
-
- This code has little to no performance improvement over the C code
- on SVR4, at least on my 16 MHz 386sx system. The Xenix assembler,
- on the other hand, is twice as fast as the C code.
-
- If nobody speaks up during the beta test, I'm removing it for the final
- release.
- */
-
- /* first define our code */
- asm unsigned char *_llstuff( portbase, buf, endbuf, spincount)
- {
- %mem portbase,buf,endbuf,spincount;
-
- pushl %esi
- pushl %edi
- pushl %ebx
-
- / reg esi = buf
- movl buf, %esi
- / reg %edi = endbuf
- movl endbuf, %edi
- / reg ebx = portbase
- movl portbase, %edx
- / spincount = 20
- / reg ecx = inspincount
- / loaded when needed in .wait
-
- cmpl %edi, %esi
- jae .exit
-
- incl %edx
-
- .align 4
- .top:
- inb (%dx)
- testb $128, %al
- je .wait
- .ready:
- decl %edx
- lodsb
- outb (%dx)
-
- outb $132
-
- pushfl
-
- movb $13,%al
- leal 2(%edx), %edx
-
- cli
-
- outb (%dx)
-
- outb $132
-
- movb $12,%al
- outb (%dx)
-
- popfl
- decl %edx
-
- cmpl %edi, %esi
- jb .top
- jmp .exit
-
- .align 4
- .wait:
- movl spincount, %ecx
- .waitloop:
- inb (%dx)
- testb $128, %al
- jne .ready
- decl %ecx
- jne .waitloop
- jmp .exit
-
- .align 4
- .exit:
- movl %esi, %eax
-
- popl %ebx
- popl %edi
- popl %esi
- }
-
- /* ...then actually use it */
-
- PRIVATE unsigned char *llstuff(portbase, buf, endbuf, spincount)
- int portbase;
- unsigned char *buf, *endbuf;
- int spincount;
-
- {
- _llstuff(portbase, buf, endbuf, spincount);
- }
-
- #endif
-
- #else /* ASM */
-
- /* Write character buffer to ioaddr 'portbase', return if printer busy */
- /* Usable at task or interrupt time */
- PRIVATE unsigned char *llstuff(portbase, buf, endbuf, spincount)
- register int portbase;
- register unsigned char *buf, *endbuf;
- int spincount;
-
- {
- int inspincount;
- INT_VAR;
-
- while (buf < endbuf)
- {
- inspincount = spincount;
- while (!(P_NOT_BUSY & inb(portbase+1)))
- if (--inspincount == 0) return buf;
- outb(portbase,*buf);
- buf++;
- /* this is a vaguely critical section of code */
- IOWAIT;
- INT_DI;
- outb(portbase+2, P_SELECT | P_NOT_INIT | P_STROBE);
- IOWAIT;
- outb(portbase+2, P_SELECT | P_NOT_INIT);
- INT_RE;
- /* end vaguely critical */
- }
- return buf;
- }
-
- #endif /* ASM */
-
- /* Write output to printer, yielding timeslice occasionally */
- PRIVATE int llp(port, buf, endbuf)
-
- int port;
- unsigned char *buf, *endbuf;
-
- {
- while ((buf = llstuff(ports[port], buf, endbuf, SPINCNT)) != endbuf)
- {
- int oldspl;
- caddr_t sleeploc;
-
- sleeploc = (caddr_t) &ports[port];
-
- /* stuff could be added here to check for printer errors */
- /* sleep for a little bit; allow interrupts */
- oldspl = spl7();
- timeout(llwake, sleeploc, SLEEPTIME);
- if (1 == sleep(sleeploc, PCATCH|PWAIT))
- /* sleep interrupted-- drop out of loop */
- {
- splx(oldspl);
- return -1;
- }
- splx(oldspl);
- }
- return 0;
- }
-
-