home *** CD-ROM | disk | FTP | other *** search
/ NetNews Usenet Archive 1993 #1 / NN_1993_1.iso / spool / comp / unix / pcclone / 32bit / 1033 / lp.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-01-12  |  9.6 KB  |  505 lines

  1. /*
  2.     "Copyright 1993 John Hood. All rights reserved.";
  3. */
  4.  
  5. /*
  6.     This driver is free software; you can redistribute it and/or
  7.     modify it under the terms of the GNU General Public License as
  8.     published by the Free Software Foundation; either version 1, or
  9.     (at your option) any later version.
  10.  
  11.     As a special case, this driver may be incorporated in any OS kernel,
  12.     whether the GNU General Public License applies to it or not.
  13.  
  14.     This driver is distributed in the hope that it will be useful,
  15.     but WITHOUT ANY WARRANTY; without even the implied warranty of
  16.     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17.     GNU General Public License for more details.
  18.  
  19.     You may have received a copy of the GNU General Public License
  20.     along with this driver; if not, write to the Free Software
  21.     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  22. */
  23.  
  24.  
  25.  
  26. #define VERSION "0.1BETA"
  27.  
  28. #ifdef DEBUG
  29. #define PRIVATE
  30. #else
  31. #define PRIVATE static
  32. #endif
  33.  
  34. /* #define ASM */
  35.  
  36. /* #define STROBE_PROT */
  37.  
  38.  
  39.  
  40. /* These defaults are tuned for fast, buffered printers. */
  41. #ifndef SPINCNT
  42. #define SPINCNT 10 /* iterations */
  43. #endif
  44.  
  45. #ifndef SLEEPTIME
  46. #define SLEEPTIME 1 /* HZ */
  47. #endif
  48.  
  49. #ifndef BUFSIZ
  50. #define BUFSIZ 1024 /* bytes */
  51. #endif
  52.  
  53. #ifndef OPENWAIT
  54. #define OPENWAIT 60 /* seconds */
  55. #endif
  56.  
  57. #ifdef M_XENIX
  58. #include "/usr/sys/h/errno.h"
  59. #include "/usr/sys/h/types.h"
  60. #include "/usr/sys/h/param.h"
  61. #include "/usr/sys/h/dir.h"
  62. #include "/usr/sys/h/signal.h"
  63. #include "/usr/sys/h/page.h"
  64. #include "/usr/sys/h/seg.h"
  65. #include "/usr/sys/h/user.h"
  66. #include "/usr/sys/h/tty.h"
  67. #include "/usr/sys/h/sysmacros.h"
  68. #else
  69. #include "sys/param.h"
  70. #include "sys/types.h"
  71. #include "sys/dir.h"
  72. #include "sys/signal.h"
  73. #include "sys/user.h"
  74. #include "sys/buf.h"
  75. #include "sys/errno.h"
  76. #include "sys/immu.h"
  77. #include "sys/sysmacros.h"
  78. #include "sys/inline.h"
  79. #endif
  80.  
  81. /* Local definitions of Stuff. */
  82.  
  83. /* extract masks for device minor # */
  84. #define PORTMASK    3
  85. /* #define TYPEMASK    ~3 */
  86.  
  87. #define INITMASK    0x80
  88.  
  89. /* parallel port offsets from base address */
  90. #define    P_DATA        0
  91. #define P_STATUS    1
  92. #define P_CONTROL    2
  93.  
  94. /* bitmap of parallel i/o ports */
  95.  
  96. #define P_IRQEN        0x10
  97. #define    P_SELECT    8
  98. #define P_NOT_INIT    4
  99. #define    P_AUTOFEED    2
  100. #define    P_STROBE    1
  101.  
  102. #define P_NOT_BUSY    0x80
  103. #define    P_NOT_ACK    0x40
  104. #define P_PERROR    0x20
  105. #define P_SELECTED    0x10
  106. #define P_NOT_ERROR    0x08
  107.  
  108. extern time_t lbolt;
  109.  
  110. #ifdef M_XENIX
  111. extern int outb(unsigned int port, unsigned char io);
  112. extern unsigned char inb(unsigned int port);
  113. #endif
  114.  
  115. /* Various nasty debug goo */
  116. /* #define outb(port, byte) printf("out %x, %x ",port,byte), outb(port,byte) */
  117. /* #define inb(port) (printf("in %x ", port),inb(port)) */
  118. /*
  119. #define inb(port) _inb(port)
  120. int _inb(port)
  121.      unsigned int port;
  122.  
  123. {
  124.   int foo;
  125.   foo = inb(port);
  126.   printf("in %x = %x", port, foo);
  127.   return foo;
  128. }
  129. */
  130. /*
  131. #define sleep(addr, pri) printf("sleep %x at %x ", addr, pri), sleep( addr, pri)
  132. */
  133.  
  134. /* My recollection of system-independent timing code suggested by
  135.    somebody from Dell or Compaq on Usenet.  If I could find the original
  136.    article, I'd be much happier. */
  137. #ifdef DO_IOWAITS
  138. #define IOWAIT outb(0x84,0)
  139. #else
  140. #define IOWAIT
  141. #endif
  142.  
  143. /* old version; not so good */
  144. /* #define IOWAIT {int sp; for (sp = 0; sp < 10; sp++) ww();} */
  145.  
  146. #ifdef STROBE_PROT
  147.  
  148. #ifdef M_XENIX
  149. #define INT_VAR    int oldspl
  150. #define INT_DI    oldspl = spl7()
  151. #define INT_RE    splx(oldspl)
  152. #else
  153. #define INT_VAR
  154. #define INT_DI  intr_disable()
  155. #define INT_RE    intr_restore()
  156. #endif
  157.  
  158. #else /* STROBE_PROT */
  159.  
  160. #define INT_VAR
  161. #define INT_DI
  162. #define    INT_RE
  163.  
  164. #endif
  165.  
  166. PRIVATE unsigned int ports[] = { 
  167. #ifdef M_XENIX
  168.   0x378, 0x3bc, 0x278, 0x178
  169. #else
  170.   0x3bc, 0x378, 0x278, 0x178
  171. #endif
  172.   };
  173.  
  174.  
  175. PRIVATE unsigned int portstat[] = { 0, 0, 0, 0 };
  176.  
  177. PRIVATE time_t closetime[] = { 0, 0, 0, 0 };
  178.  
  179. #define PS_OK 1
  180. #define PS_OPEN 2
  181.  
  182. PRIVATE unsigned char llbufs[4][BUFSIZ];
  183.  
  184. /* initialize printer */
  185. PRIVATE void llreset(unit)
  186.      int unit;
  187. {
  188.   int portbase = ports[unit];
  189.   outb(portbase+P_CONTROL, 0);
  190.   {
  191.     int foo;
  192.     for (foo = 0; foo < 10; foo++)
  193.       IOWAIT;
  194.   }
  195.   outb(portbase+P_CONTROL, P_SELECT | P_NOT_INIT);
  196. }
  197.  
  198. PRIVATE llwake(caddr_t top)
  199.      
  200. {
  201.   wakeup(top);
  202. }
  203.  
  204. /* device xx_init code */
  205. llinit()
  206. {
  207.   int unit;
  208.   
  209.   for (unit = 0; unit < 4; unit++)
  210.     {
  211.       unsigned int portbase = ports[unit];
  212.  
  213.       char *found = "not found";
  214.       
  215.       /* is port configured in driver? */
  216.       if (portbase == 0) continue;
  217.  
  218.       /* is port really out there? */
  219.       outb(portbase, 0xaa);
  220.       IOWAIT;
  221.       if ( 0xaa == inb(portbase) )
  222.     {
  223.       portstat[unit] |= PS_OK;
  224.       found = "found";
  225.     }
  226.       /* let the world know what we've found */
  227. #ifdef M_XENIX
  228.       (void) printcfg ("ll", portbase, 3,
  229.                -1, -1,
  230.                "version %s unit=%d %s",
  231.                VERSION, unit, found);
  232. #else
  233.       (void) printf("ll %s parallel driver: port %d at ioaddr %x %s\n", 
  234.             VERSION, unit, portbase, found);
  235. #endif
  236. #ifdef INIT
  237.       /* reset the port */
  238.       if (portstat[unit] & PS_OK)
  239.     llreset(unit);
  240. #endif
  241.     }
  242. }
  243.  
  244. /* device xx_open code */
  245. void llopen(dev, flag)
  246.      int dev, flag;
  247.  
  248. {
  249.   int unit = minor(dev) & PORTMASK;
  250.  
  251.   /* Is the port available? */
  252.   if (!(portstat[unit] & PS_OK))
  253.     {
  254.       u.u_error = ENXIO;
  255.       return;
  256.     }
  257.   /* is it busy? */
  258.   if (portstat[unit] & PS_OPEN)
  259.     {
  260.       u.u_error = EBUSY;
  261.       return;
  262.     }
  263.  
  264.   /* save opened state */
  265.   portstat[unit] |= PS_OPEN;
  266.  
  267.   /* init the printer if selected in dev */
  268.   if ( minor(dev) & INITMASK )
  269.     {
  270.       /* make sure we have waited at least OPENWAIT before printer init */
  271.       if (closetime[unit] > lbolt)
  272.     delay(closetime[unit] - lbolt);
  273.       llreset(unit);
  274.     }
  275. }
  276.  
  277. /* device xx_close code */
  278. void llclose(dev, flag)
  279.      int dev, flag;
  280.  
  281. {
  282.   int unit = minor(dev) & PORTMASK;
  283.  
  284.   /* leave a little clue for waiting upon the printer init; it's cheap */
  285.   closetime[unit] = lbolt + (OPENWAIT * HZ);
  286.  
  287.   /* we are done... */
  288.   portstat[unit] &= ~PS_OPEN;
  289. }
  290.  
  291. /* Device xx_write routine */
  292. void llwrite(dev)
  293.      int dev;
  294. {
  295.   unsigned char *buf;
  296.   unsigned int count;
  297.   int portno, oldspl;
  298.   caddr_t sleeploc;
  299.   
  300.   portno = minor(dev) & PORTMASK;
  301.   buf        = &llbufs[portno][0];
  302.   sleeploc = ((caddr_t)&ports[portno])+1;
  303.   for
  304.     (
  305.      u.u_base, u.u_count;
  306.      (count = min(u.u_count,BUFSIZ)) != 0
  307.      && copyin(u.u_base,buf,count) == 0;
  308.      u.u_base += count, u.u_count -= count
  309.      )
  310.       {
  311.     if (-1 == llp(portno,buf,buf+count))
  312.       {
  313.         u.u_error = EINTR;
  314.         return;
  315.       }
  316.     /* be a good driver and yield the cpu once in a while */
  317.     /* delay(0) is the right idea, but doesn't work under Xenix */
  318.     /* delay(0); */
  319.     oldspl = spl7();
  320.     timeout(llwake, sleeploc, 0);
  321.     if (1 == sleep(sleeploc, PCATCH|PWAIT))
  322.       {
  323.         splx(oldspl);
  324.         u.u_error = EINTR;
  325.         return;
  326.       }
  327.     splx(oldspl);
  328.       }
  329. }
  330.  
  331. #ifdef ASM
  332.  
  333. #ifdef M_XENIX
  334.  
  335. extern unsigned char *llstuff();
  336.  
  337. #else
  338.  
  339. /*
  340.   if this code is ugly, that's because I've left it that way.
  341.   It's hand-optimized compiler output that is functionally identical
  342.   to the C code.
  343.  
  344.   This code has little to no performance improvement over the C code
  345.   on SVR4, at least on my 16 MHz 386sx system.  The Xenix assembler,
  346.   on the other hand, is twice as fast as the C code.
  347.  
  348.   If nobody speaks up during the beta test, I'm removing it for the final
  349.   release.
  350.   */
  351.  
  352. /* first define our code */
  353. asm unsigned char *_llstuff( portbase, buf, endbuf, spincount)
  354. {
  355. %mem    portbase,buf,endbuf,spincount;
  356.  
  357.     pushl    %esi
  358.     pushl    %edi
  359.     pushl    %ebx
  360.  
  361. / reg esi = buf
  362.     movl    buf, %esi
  363. / reg %edi = endbuf
  364.     movl    endbuf, %edi
  365. / reg ebx = portbase
  366.     movl    portbase, %edx
  367. /    spincount = 20
  368. / reg ecx = inspincount
  369. / loaded when needed in .wait
  370.  
  371.     cmpl      %edi, %esi
  372.     jae      .exit
  373.  
  374.     incl    %edx
  375.  
  376.     .align    4
  377. .top:
  378.     inb    (%dx)
  379.     testb     $128, %al
  380.     je      .wait
  381. .ready:
  382.     decl    %edx
  383.     lodsb
  384.     outb    (%dx)
  385.  
  386.     outb    $132
  387.  
  388.     pushfl
  389.  
  390.     movb    $13,%al
  391.     leal     2(%edx), %edx
  392.  
  393.     cli
  394.  
  395.     outb    (%dx)
  396.  
  397.     outb    $132
  398.  
  399.     movb    $12,%al
  400.     outb    (%dx)
  401.  
  402.     popfl
  403.     decl    %edx
  404.  
  405.     cmpl     %edi, %esi
  406.     jb      .top
  407.     jmp    .exit
  408.  
  409.     .align    4
  410. .wait:
  411.     movl    spincount, %ecx
  412. .waitloop:
  413.     inb    (%dx)
  414.     testb     $128, %al
  415.     jne    .ready
  416.     decl      %ecx
  417.     jne    .waitloop
  418.     jmp    .exit
  419.  
  420.     .align 4
  421. .exit:
  422.     movl      %esi, %eax
  423.  
  424.     popl    %ebx
  425.     popl    %edi
  426.     popl    %esi
  427.     }
  428.  
  429. /* ...then actually use it */
  430.  
  431. PRIVATE unsigned char *llstuff(portbase, buf, endbuf, spincount)
  432.      int portbase;
  433.      unsigned char *buf, *endbuf;
  434.      int spincount;
  435.      
  436. {
  437.   _llstuff(portbase, buf, endbuf, spincount);
  438. }
  439.  
  440. #endif
  441.  
  442. #else /* ASM */
  443.  
  444. /* Write character buffer to ioaddr 'portbase', return if printer busy */
  445. /* Usable at task or interrupt time */
  446. PRIVATE unsigned char *llstuff(portbase, buf, endbuf, spincount)
  447.      register int portbase;
  448.      register unsigned char *buf, *endbuf;
  449.      int spincount;
  450.      
  451. {
  452.   int inspincount;
  453.   INT_VAR;
  454.   
  455.   while (buf < endbuf)
  456.     {
  457.       inspincount = spincount;
  458.       while (!(P_NOT_BUSY & inb(portbase+1)))
  459.     if (--inspincount == 0) return buf;
  460.       outb(portbase,*buf);
  461.       buf++;
  462.       /* this is a vaguely critical section of code */
  463.       IOWAIT;
  464.       INT_DI;
  465.       outb(portbase+2, P_SELECT | P_NOT_INIT | P_STROBE);
  466.       IOWAIT;
  467.       outb(portbase+2, P_SELECT | P_NOT_INIT);
  468.       INT_RE;
  469.       /* end vaguely critical */
  470.     }
  471.   return buf;
  472. }
  473.  
  474. #endif  /* ASM */
  475.  
  476. /* Write output to printer, yielding timeslice occasionally */
  477. PRIVATE int llp(port, buf, endbuf)
  478.  
  479. int port;
  480. unsigned char *buf, *endbuf;
  481.  
  482. {
  483.   while ((buf = llstuff(ports[port], buf, endbuf, SPINCNT)) != endbuf)
  484.     {
  485.       int oldspl;
  486.       caddr_t sleeploc;
  487.  
  488.       sleeploc = (caddr_t) &ports[port];
  489.  
  490.       /* stuff could be added here to check for printer errors */
  491.       /* sleep for a little bit; allow interrupts */
  492.       oldspl = spl7();
  493.       timeout(llwake, sleeploc, SLEEPTIME);
  494.       if (1 == sleep(sleeploc, PCATCH|PWAIT))
  495.     /* sleep interrupted-- drop out of loop */
  496.     {
  497.       splx(oldspl);
  498.       return -1;
  499.     }
  500.       splx(oldspl);
  501.     }
  502.   return 0;
  503. }
  504.  
  505.