This commit was generated by cvs2svn to track changes on a CVS vendor
[unix-history] / sys / i386 / isa / lpt.c
CommitLineData
15637ed4
RG
1/*
2 * Copyright (c) 1990 William F. Jolitz, TeleMuse
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This software is a component of "386BSD" developed by
16 * William F. Jolitz, TeleMuse.
17 * 4. Neither the name of the developer nor the name "386BSD"
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS A COMPONENT OF 386BSD DEVELOPED BY WILLIAM F. JOLITZ
22 * AND IS INTENDED FOR RESEARCH AND EDUCATIONAL PURPOSES ONLY. THIS
23 * SOFTWARE SHOULD NOT BE CONSIDERED TO BE A COMMERCIAL PRODUCT.
24 * THE DEVELOPER URGES THAT USERS WHO REQUIRE A COMMERCIAL PRODUCT
25 * NOT MAKE USE OF THIS WORK.
26 *
27 * FOR USERS WHO WISH TO UNDERSTAND THE 386BSD SYSTEM DEVELOPED
28 * BY WILLIAM F. JOLITZ, WE RECOMMEND THE USER STUDY WRITTEN
29 * REFERENCES SUCH AS THE "PORTING UNIX TO THE 386" SERIES
30 * (BEGINNING JANUARY 1991 "DR. DOBBS JOURNAL", USA AND BEGINNING
31 * JUNE 1991 "UNIX MAGAZIN", GERMANY) BY WILLIAM F. JOLITZ AND
32 * LYNNE GREER JOLITZ, AS WELL AS OTHER BOOKS ON UNIX AND THE
33 * ON-LINE 386BSD USER MANUAL BEFORE USE. A BOOK DISCUSSING THE INTERNALS
34 * OF 386BSD ENTITLED "386BSD FROM THE INSIDE OUT" WILL BE AVAILABLE LATE 1992.
35 *
36 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPER ``AS IS'' AND
37 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
38 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
39 * ARE DISCLAIMED. IN NO EVENT SHALL THE DEVELOPER BE LIABLE
40 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
41 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
42 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
43 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
44 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
45 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
46 * SUCH DAMAGE.
47 *
48 * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE
49 * -------------------- ----- ----------------------
50 * CURRENT PATCH LEVEL: 2 00164
51 * -------------------- ----- ----------------------
52 *
53 * 06 Apr 93 Eric Haug Fixed comments and includes. [Ed: I did
54 * not include the unit-1 thing, that is a
55 * DOSism, fixed the config file instead]
56 * 06 Apr 93 Rodney W. Grimes A real probe routine, may even cause on
57 * interrupt if a printer is attached.
58 *
59 * 01 Jun 93 Rodney W. Grimes Made lpflag uniq now is lptflag
60 * Added timeout loop to lpt_port_test.
61 * lpt_port_test should move to a common
62 * routine..
63 *
64 */
65
66/*
67 * Device Driver for AT parallel printer port
68 * Written by William Jolitz 12/18/90
69 */
70
71#include "lpt.h"
72#if NLPT > 0
73
74#include "param.h"
75#include "systm.h"
76#include "proc.h"
77#include "user.h"
78#include "buf.h"
79#include "kernel.h"
80#include "ioctl.h"
81#include "tty.h"
82#include "uio.h"
83
84#include "i386/isa/isa.h"
85#include "i386/isa/isa_device.h"
86#include "i386/isa/lptreg.h"
87
88#define LPINITRDY 4 /* wait up to 4 seconds for a ready */
89#define LPTOUTTIME 4 /* wait up to 4 seconds for a ready */
90#define LPPRI (PZERO+8)
91#define BUFSIZE 1024
92
93#ifndef DEBUG
94#define lprintf
95#else
96#define lprintf if (lptflag) printf
97int lptflag = 1;
98#endif
99
100int lptout();
101#ifdef DEBUG
102int lptflag = 1;
103#endif
104
105int lptprobe(), lptattach(), lptintr();
106
107struct isa_driver lptdriver = {
108 lptprobe, lptattach, "lpt"
109};
110
111#define LPTUNIT(s) (((s)>>6)&0x3)
112#define LPTFLAGS(s) ((s)&0x3f)
113
114struct lpt_softc {
115 short sc_port;
116 short sc_state;
117 /* default case: negative prime, negative ack, handshake strobe,
118 prime once */
119 u_char sc_control;
120 char sc_flags;
121#define LP_POS_INIT 0x01 /* if we are a postive init signal */
122#define LP_POS_ACK 0x02 /* if we are a positive going ack */
123#define LP_NO_PRIME 0x04 /* don't prime the printer at all */
124#define LP_PRIMEOPEN 0x08 /* prime on every open */
125#define LP_AUTOLF 0x10 /* tell printer to do an automatic lf */
126#define LP_BYPASS 0x20 /* bypass printer ready checks */
127 struct buf *sc_inbuf;
128 short sc_xfercnt ;
129 char sc_primed;
130 char *sc_cp ;
131} lpt_sc[NLPT] ;
132
133/* bits for state */
134#define OPEN (1<<0) /* device is open */
135#define ASLP (1<<1) /* awaiting draining of printer */
136#define ERROR (1<<2) /* error was received from printer */
137#define OBUSY (1<<3) /* printer is busy doing output */
138#define LPTOUT (1<<4) /* timeout while not selected */
139#define TOUT (1<<5) /* timeout while not selected */
140#define INIT (1<<6) /* waiting to initialize for open */
141
142/*
143 * Internal routine to lptprobe to do port tests of one byte value
144 */
145int
146lpt_port_test(short port, u_char data, u_char mask)
147 {
148 int temp, timeout;
149
150 data = data & mask;
151 outb(port, data);
152 timeout = 100;
153 do
154 temp = inb(port) & mask;
155 while (temp != data && --timeout);
156 lprintf("Port 0x%x\tout=%x\tin=%x\n", port, data, temp);
157 return (temp == data);
158 }
159
160/*
161 * New lptprobe routine written by Rodney W. Grimes, 3/25/1993
162 *
163 * Logic:
164 * 1) You should be able to write to and read back the same value
165 * to the data port. Do an alternating zeros, alternating ones,
166 * walking zero, and walking one test to check for stuck bits.
167 *
168 * 2) You should be able to write to and read back the same value
169 * to the control port lower 5 bits, the upper 3 bits are reserved
170 * per the IBM PC technical reference manauls and different boards
171 * do different things with them. Do an alternating zeros, alternating
172 * ones, walking zero, and walking one test to check for stuck bits.
173 *
174 * Some printers drag the strobe line down when the are powered off
175 * so this bit has been masked out of the control port test.
176 *
177 * XXX Some printers may not like a fast pulse on init or strobe, I
178 * don't know at this point, if that becomes a problem these bits
179 * should be turned off in the mask byte for the control port test.
180 *
181 * 3) Set the data and control ports to a value of 0
182 */
183
184int
185lptprobe(struct isa_device *dvp)
186 {
187 int status;
188 short port;
189 u_char data;
190 u_char mask;
191 int i;
192
193 status = IO_LPTSIZE;
194
195 port = dvp->id_iobase + lpt_data;
196 mask = 0xff;
197 while (mask != 0)
198 {
199 data = 0x55; /* Alternating zeros */
200 if (!lpt_port_test(port, data, mask)) status = 0;
201
202 data = 0xaa; /* Alternating ones */
203 if (!lpt_port_test(port, data, mask)) status = 0;
204
205 for (i = 0; i < 8; i++) /* Walking zero */
206 {
207 data = ~(1 << i);
208 if (!lpt_port_test(port, data, mask)) status = 0;
209 }
210
211 for (i = 0; i < 8; i++) /* Walking one */
212 {
213 data = (1 << i);
214 if (!lpt_port_test(port, data, mask)) status = 0;
215 }
216
217 if (port == dvp->id_iobase + lpt_data)
218 {
219 port = dvp->id_iobase + lpt_control;
220 mask = 0x1e;
221 }
222 else
223 mask = 0;
224 }
225 outb(dvp->id_iobase+lpt_data, 0);
226 outb(dvp->id_iobase+lpt_control, 0);
227 return (status);
228 }
229
230lptattach(isdp)
231 struct isa_device *isdp;
232{
233 struct lpt_softc *sc;
234
235 sc = lpt_sc + isdp->id_unit;
236 sc->sc_port = isdp->id_iobase;
237 outb(sc->sc_port+lpt_control, LPC_NINIT);
238 return (1);
239}
240
241/*
242 * lptopen -- reset the printer, then wait until it's selected and not busy.
243 */
244
245lptopen(dev, flag)
246 dev_t dev;
247 int flag;
248{
249 struct lpt_softc *sc = lpt_sc + LPTUNIT(minor(dev));
250 int s;
251 int trys, port;
252
253 if (sc->sc_state) {
254lprintf("lp: still open\n") ;
255printf("still open %x\n", sc->sc_state);
256 return(EBUSY);
257 } else sc->sc_state |= INIT;
258
259 s = spltty();
260 sc->sc_flags = LPTFLAGS(minor(dev));
261lprintf("lp flags 0x%x\n", sc->sc_flags);
262 port = sc->sc_port;
263
264 /* init printer */
265 if((sc->sc_flags & LP_NO_PRIME) == 0) {
266 if((sc->sc_flags & LP_PRIMEOPEN) || sc->sc_primed == 0) {
267 outb(port+lpt_control, 0);
268 sc->sc_primed++;
269 DELAY(500);
270 }
271 }
272 outb(port+lpt_control, LPC_SEL|LPC_NINIT);
273
274 /* wait till ready (printer running diagnostics) */
275 trys = 0;
276 do {
277 /* ran out of waiting for the printer */
278 if (trys++ >= LPINITRDY*4) {
279 splx(s);
280 sc->sc_state = 0;
281printf ("status %x\n", inb(port+lpt_status) );
282 return (EBUSY);
283 }
284
285 /* wait 1/4 second, give up if we get a signal */
286 if (tsleep (sc, LPPRI|PCATCH, "lptinit", hz/4) != EWOULDBLOCK) {
287 sc->sc_state = 0;
288 splx(s);
289 return (EBUSY);
290 }
291
292 /* is printer online and ready for output */
293 } while ((inb(port+lpt_status) & (LPS_SEL|LPS_OUT|LPS_NBSY|LPS_NERR)) !=
294 (LPS_SEL|LPS_NBSY|LPS_NERR));
295
296 if(sc->sc_flags&LP_AUTOLF) {
297 outb(port+lpt_control, LPC_SEL|LPC_NINIT|LPC_ENA|LPC_AUTOL);
298 sc->sc_control = LPC_SEL|LPC_NINIT|LPC_ENA|LPC_AUTOL;
299 } else {
300 outb(port+lpt_control, LPC_SEL|LPC_NINIT|LPC_ENA);
301 sc->sc_control = LPC_SEL|LPC_NINIT|LPC_ENA;
302 }
303
304 sc->sc_state = OPEN | TOUT;
305 sc->sc_inbuf = geteblk(BUFSIZE);
306 sc->sc_xfercnt = 0;
307 splx(s);
308 timeout (lptout, sc, hz/2);
309lprintf("opened.\n");
310 return(0);
311}
312
313lptout (sc)
314 struct lpt_softc *sc;
315{ int pl;
316
317lprintf ("T %x ", inb(sc->sc_port+lpt_status));
318 if (sc->sc_state&OPEN)
319 timeout (lptout, sc, hz/2);
320 else sc->sc_state &= ~TOUT;
321
322 if (sc->sc_state & ERROR)
323 sc->sc_state &= ~ERROR;
324
325 /*
326 * Avoid possible hangs do to missed interrupts
327 */
328 if (sc->sc_xfercnt) {
329 pl = spltty();
330 lptintr(sc - lpt_sc);
331 splx(pl);
332 } else {
333 sc->sc_state &= ~OBUSY;
334 wakeup((caddr_t)sc);
335 }
336}
337
338/*
339 * lptclose -- close the device, free the local line buffer.
340 */
341
342lptclose(dev, flag)
343 int flag;
344{
345 struct lpt_softc *sc = lpt_sc + LPTUNIT(minor(dev));
346 int port = sc->sc_port;
347
348 sc->sc_state &= ~OPEN;
349 while ((inb(port+lpt_status) & (LPS_SEL|LPS_OUT|LPS_NBSY|LPS_NERR)) !=
350 (LPS_SEL|LPS_NBSY|LPS_NERR) || sc->sc_xfercnt)
351 /* wait 1/4 second, give up if we get a signal */
352 if (tsleep (sc, LPPRI|PCATCH, "lpclose", hz) != EWOULDBLOCK)
353 break;
354
355 sc->sc_state = 0;
356 sc->sc_xfercnt = 0;
357 outb(sc->sc_port+lpt_control, LPC_NINIT);
358 brelse(sc->sc_inbuf);
359lprintf("closed.\n");
360 return(0);
361}
362
363/*
364 * lptwrite --copy a line from user space to a local buffer, then call
365 * putc to get the chars moved to the output queue.
366 */
367
368lptwrite(dev, uio)
369 dev_t dev;
370 struct uio *uio;
371{
372 register unsigned n;
373 int pl, err;
374 struct lpt_softc *sc = lpt_sc + LPTUNIT(minor(dev));
375
376 while (n = MIN(BUFSIZE, uio->uio_resid)) {
377 sc->sc_cp = sc->sc_inbuf->b_un.b_addr ;
378 uiomove(sc->sc_cp, n, uio);
379 sc->sc_xfercnt = n ;
380 while (sc->sc_xfercnt > 0) {
381 /* if the printer is ready for a char, give it one */
382 if ((sc->sc_state & OBUSY) == 0){
383lprintf("\nC %d. ", sc->sc_xfercnt);
384 pl = spltty();
385 lptintr(sc - lpt_sc);
386 (void) splx(pl);
387 }
388lprintf("W ");
389 if (err = tsleep (sc, LPPRI|PCATCH, "lpwrite", 0))
390 return(err);
391 }
392 }
393 return(0);
394}
395
396/*
397 * lptintr -- handle printer interrupts which occur when the printer is
398 * ready to accept another char.
399 */
400
401lptintr(unit)
402{
403 struct lpt_softc *sc = lpt_sc + unit;
404 int port = sc->sc_port,sts;
405
406 /* is printer online and ready for output */
407 if (((sts=inb(port+lpt_status)) & (LPS_SEL|LPS_OUT|LPS_NBSY|LPS_NERR/*|LPS_NACK*/)) ==
408 (LPS_SEL|LPS_NBSY|LPS_NERR)) {
409 /* is this a false interrupt ? */
410 if ((sc->sc_state & OBUSY)
411 && (sts & LPS_NACK) == 0) return;
412 sc->sc_state |= OBUSY; sc->sc_state &= ~ERROR;
413
414 if (sc->sc_xfercnt) {
415 /* send char */
416/*lprintf("%x ", *sc->sc_cp); */
417 outb(port+lpt_data, *sc->sc_cp++) ; sc->sc_xfercnt-- ;
418 outb(port+lpt_control, sc->sc_control|LPC_STB);
419 /* DELAY(X) */
420 outb(port+lpt_control, sc->sc_control);
421 }
422
423 /* any more bytes for the printer? */
424 if (sc->sc_xfercnt > 0) return;
425
426 /* none, wake up the top half to get more */
427 sc->sc_state &= ~OBUSY;
428 wakeup((caddr_t)sc);
429lprintf("w ");
430return;
431 } else sc->sc_state |= ERROR;
432lprintf("sts %x ", sts);
433}
434
435int
436lptioctl(dev_t dev, int cmd, caddr_t data, int flag)
437{
438 int error;
439
440 error = 0;
441 switch (cmd) {
442#ifdef THISISASAMPLE
443 case XXX:
444 dothis; andthis; andthat;
445 error=x;
446 break;
447#endif /* THISISASAMPLE */
448 default:
449 error = ENODEV;
450 }
451
452 return(error);
453}
454
455#endif /* NLPT */