lpt_isa.c revision 1.33 1 /* $NetBSD: lpt_isa.c,v 1.33 1996/03/17 00:53:41 thorpej Exp $ */
2
3 /*
4 * Copyright (c) 1993, 1994 Charles Hannum.
5 * Copyright (c) 1990 William F. Jolitz, TeleMuse
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 * must display the following acknowledgement:
18 * This software is a component of "386BSD" developed by
19 * William F. Jolitz, TeleMuse.
20 * 4. Neither the name of the developer nor the name "386BSD"
21 * may be used to endorse or promote products derived from this software
22 * without specific prior written permission.
23 *
24 * THIS SOFTWARE IS A COMPONENT OF 386BSD DEVELOPED BY WILLIAM F. JOLITZ
25 * AND IS INTENDED FOR RESEARCH AND EDUCATIONAL PURPOSES ONLY. THIS
26 * SOFTWARE SHOULD NOT BE CONSIDERED TO BE A COMMERCIAL PRODUCT.
27 * THE DEVELOPER URGES THAT USERS WHO REQUIRE A COMMERCIAL PRODUCT
28 * NOT MAKE USE OF THIS WORK.
29 *
30 * FOR USERS WHO WISH TO UNDERSTAND THE 386BSD SYSTEM DEVELOPED
31 * BY WILLIAM F. JOLITZ, WE RECOMMEND THE USER STUDY WRITTEN
32 * REFERENCES SUCH AS THE "PORTING UNIX TO THE 386" SERIES
33 * (BEGINNING JANUARY 1991 "DR. DOBBS JOURNAL", USA AND BEGINNING
34 * JUNE 1991 "UNIX MAGAZIN", GERMANY) BY WILLIAM F. JOLITZ AND
35 * LYNNE GREER JOLITZ, AS WELL AS OTHER BOOKS ON UNIX AND THE
36 * ON-LINE 386BSD USER MANUAL BEFORE USE. A BOOK DISCUSSING THE INTERNALS
37 * OF 386BSD ENTITLED "386BSD FROM THE INSIDE OUT" WILL BE AVAILABLE LATE 1992.
38 *
39 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPER ``AS IS'' AND
40 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
41 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
42 * ARE DISCLAIMED. IN NO EVENT SHALL THE DEVELOPER BE LIABLE
43 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
44 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
45 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
46 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
47 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
48 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
49 * SUCH DAMAGE.
50 */
51
52 /*
53 * Device Driver for AT parallel printer port
54 */
55
56 #include <sys/param.h>
57 #include <sys/systm.h>
58 #include <sys/proc.h>
59 #include <sys/user.h>
60 #include <sys/buf.h>
61 #include <sys/kernel.h>
62 #include <sys/ioctl.h>
63 #include <sys/uio.h>
64 #include <sys/device.h>
65 #include <sys/syslog.h>
66
67 #include <machine/cpu.h>
68 #include <machine/bus.h>
69
70 #include <dev/isa/isavar.h>
71 #include <dev/isa/lptreg.h>
72
73 #define TIMEOUT hz*16 /* wait up to 16 seconds for a ready */
74 #define STEP hz/4
75
76 #define LPTPRI (PZERO+8)
77 #define LPT_BSIZE 1024
78
79 #if !defined(DEBUG) || !defined(notdef)
80 #define lprintf
81 #else
82 #define lprintf if (lptdebug) printf
83 int lptdebug = 1;
84 #endif
85
86 struct lpt_softc {
87 struct device sc_dev;
88 void *sc_ih;
89
90 size_t sc_count;
91 struct buf *sc_inbuf;
92 u_char *sc_cp;
93 int sc_spinmax;
94 int sc_iobase;
95 bus_chipset_tag_t sc_bc;
96 bus_io_handle_t sc_ioh;
97 int sc_irq;
98 u_char sc_state;
99 #define LPT_OPEN 0x01 /* device is open */
100 #define LPT_OBUSY 0x02 /* printer is busy doing output */
101 #define LPT_INIT 0x04 /* waiting to initialize for open */
102 u_char sc_flags;
103 #define LPT_AUTOLF 0x20 /* automatic LF on CR */
104 #define LPT_NOPRIME 0x40 /* don't prime on open */
105 #define LPT_NOINTR 0x80 /* do not use interrupt */
106 u_char sc_control;
107 u_char sc_laststatus;
108 };
109
110 int lptprobe __P((struct device *, void *, void *));
111 void lptattach __P((struct device *, struct device *, void *));
112 int lptintr __P((void *));
113
114 struct cfattach lpt_ca = {
115 sizeof(struct lpt_softc), lptprobe, lptattach
116 };
117
118 struct cfdriver lpt_cd = {
119 NULL, "lpt", DV_TTY
120 };
121
122 #define LPTUNIT(s) (minor(s) & 0x1f)
123 #define LPTFLAGS(s) (minor(s) & 0xe0)
124
125 #define LPS_INVERT (LPS_SELECT|LPS_NERR|LPS_NBSY|LPS_NACK)
126 #define LPS_MASK (LPS_SELECT|LPS_NERR|LPS_NBSY|LPS_NACK|LPS_NOPAPER)
127 #define NOT_READY() ((bus_io_read_1(bc, ioh, lpt_status) ^ LPS_INVERT) & LPS_MASK)
128 #define NOT_READY_ERR() not_ready(bus_io_read_1(bc, ioh, lpt_status), sc)
129 static int not_ready __P((u_char, struct lpt_softc *));
130
131 static void lptwakeup __P((void *arg));
132 static int pushbytes __P((struct lpt_softc *));
133
134 /*
135 * Internal routine to lptprobe to do port tests of one byte value.
136 */
137 int
138 lpt_port_test(ioh, off, data, mask, base)
139 bus_io_handle_t ioh;
140 size_t off;
141 u_char data, mask;
142 u_long base;
143 {
144 int timeout;
145 u_char temp;
146
147 data &= mask;
148 bus_io_write_1(bc, ioh, off, data);
149 timeout = 1000;
150 do {
151 delay(10);
152 temp = bus_io_read_1(bc, ioh, off) & mask;
153 } while (temp != data && --timeout);
154 lprintf("lpt: port=0x%x out=0x%x in=0x%x timeout=%d\n", base + off,
155 data, temp, timeout);
156 return (temp == data);
157 }
158
159 /*
160 * Logic:
161 * 1) You should be able to write to and read back the same value
162 * to the data port. Do an alternating zeros, alternating ones,
163 * walking zero, and walking one test to check for stuck bits.
164 *
165 * 2) You should be able to write to and read back the same value
166 * to the control port lower 5 bits, the upper 3 bits are reserved
167 * per the IBM PC technical reference manauls and different boards
168 * do different things with them. Do an alternating zeros, alternating
169 * ones, walking zero, and walking one test to check for stuck bits.
170 *
171 * Some printers drag the strobe line down when the are powered off
172 * so this bit has been masked out of the control port test.
173 *
174 * XXX Some printers may not like a fast pulse on init or strobe, I
175 * don't know at this point, if that becomes a problem these bits
176 * should be turned off in the mask byte for the control port test.
177 *
178 * 3) Set the data and control ports to a value of 0
179 */
180 int
181 lptprobe(parent, match, aux)
182 struct device *parent;
183 void *match, *aux;
184 {
185 struct isa_attach_args *ia = aux;
186 bus_chipset_tag_t bc;
187 bus_io_handle_t ioh;
188 u_long base;
189 u_char mask, data;
190 int i, rv;
191
192 #ifdef DEBUG
193 #define ABORT do {printf("lptprobe: mask %x data %x failed\n", mask, data); \
194 goto out;} while (0)
195 #else
196 #define ABORT goto out
197 #endif
198
199 bc = ia->ia_bc;
200 base = ia->ia_iobase;
201 if (bus_io_map(bc, base, LPT_NPORTS, &ioh))
202 return 0;
203
204 rv = 0;
205 mask = 0xff;
206
207 data = 0x55; /* Alternating zeros */
208 if (!lpt_port_test(ioh, lpt_data, data, mask, base))
209 ABORT;
210
211 data = 0xaa; /* Alternating ones */
212 if (!lpt_port_test(ioh, lpt_data, data, mask, base))
213 ABORT;
214
215 for (i = 0; i < CHAR_BIT; i++) { /* Walking zero */
216 data = ~(1 << i);
217 if (!lpt_port_test(ioh, lpt_data, data, mask, base))
218 ABORT;
219 }
220
221 for (i = 0; i < CHAR_BIT; i++) { /* Walking one */
222 data = (1 << i);
223 if (!lpt_port_test(ioh, lpt_data, data, mask, base))
224 ABORT;
225 }
226
227 bus_io_write_1(bc, ioh, lpt_data, 0);
228 bus_io_write_1(bc, ioh, lpt_control, 0);
229
230 ia->ia_iosize = LPT_NPORTS;
231 ia->ia_msize = 0;
232
233 rv = 1;
234
235 out:
236 bus_io_unmap(bc, ioh, LPT_NPORTS);
237 return rv;
238 }
239
240 void
241 lptattach(parent, self, aux)
242 struct device *parent, *self;
243 void *aux;
244 {
245 struct lpt_softc *sc = (void *)self;
246 struct isa_attach_args *ia = aux;
247 bus_chipset_tag_t bc;
248 bus_io_handle_t ioh;
249
250 if (ia->ia_irq != IRQUNK)
251 printf("\n");
252 else
253 printf(": polled\n");
254
255 sc->sc_iobase = ia->ia_iobase;
256 sc->sc_irq = ia->ia_irq;
257 sc->sc_state = 0;
258
259 bc = sc->sc_bc = ia->ia_bc;
260 if (bus_io_map(bc, sc->sc_iobase, LPT_NPORTS, &ioh))
261 panic("lptattach: couldn't map I/O ports");
262 sc->sc_ioh = ioh;
263
264 bus_io_write_1(bc, ioh, lpt_control, LPC_NINIT);
265
266 if (ia->ia_irq != IRQUNK)
267 sc->sc_ih = isa_intr_establish(ia->ia_irq, IST_EDGE, IPL_NONE,
268 lptintr, sc);
269 }
270
271 /*
272 * Reset the printer, then wait until it's selected and not busy.
273 */
274 int
275 lptopen(dev, flag)
276 dev_t dev;
277 int flag;
278 {
279 int unit = LPTUNIT(dev);
280 u_char flags = LPTFLAGS(dev);
281 struct lpt_softc *sc;
282 bus_chipset_tag_t bc;
283 bus_io_handle_t ioh;
284 u_char control;
285 int error;
286 int spin;
287
288 if (unit >= lpt_cd.cd_ndevs)
289 return ENXIO;
290 sc = lpt_cd.cd_devs[unit];
291 if (!sc)
292 return ENXIO;
293
294 if (sc->sc_irq == IRQUNK && (flags & LPT_NOINTR) == 0)
295 return ENXIO;
296
297 #ifdef DIAGNOSTIC
298 if (sc->sc_state)
299 printf("%s: stat=0x%x not zero\n", sc->sc_dev.dv_xname,
300 sc->sc_state);
301 #endif
302
303 if (sc->sc_state)
304 return EBUSY;
305
306 sc->sc_state = LPT_INIT;
307 sc->sc_flags = flags;
308 lprintf("%s: open: flags=0x%x\n", sc->sc_dev.dv_xname, flags);
309 bc = sc->sc_bc;
310 ioh = sc->sc_ioh;
311
312 if ((flags & LPT_NOPRIME) == 0) {
313 /* assert INIT for 100 usec to start up printer */
314 bus_io_write_1(bc, ioh, lpt_control, LPC_SELECT);
315 delay(100);
316 }
317
318 control = LPC_SELECT | LPC_NINIT;
319 bus_io_write_1(bc, ioh, lpt_control, control);
320
321 /* wait till ready (printer running diagnostics) */
322 for (spin = 0; NOT_READY_ERR(); spin += STEP) {
323 if (spin >= TIMEOUT) {
324 sc->sc_state = 0;
325 return EBUSY;
326 }
327
328 /* wait 1/4 second, give up if we get a signal */
329 if (error = tsleep((caddr_t)sc, LPTPRI | PCATCH, "lptopen",
330 STEP) != EWOULDBLOCK) {
331 sc->sc_state = 0;
332 return error;
333 }
334 }
335
336 if ((flags & LPT_NOINTR) == 0)
337 control |= LPC_IENABLE;
338 if (flags & LPT_AUTOLF)
339 control |= LPC_AUTOLF;
340 sc->sc_control = control;
341 bus_io_write_1(bc, ioh, lpt_control, control);
342
343 sc->sc_inbuf = geteblk(LPT_BSIZE);
344 sc->sc_count = 0;
345 sc->sc_state = LPT_OPEN;
346
347 if ((sc->sc_flags & LPT_NOINTR) == 0)
348 lptwakeup(sc);
349
350 lprintf("%s: opened\n", sc->sc_dev.dv_xname);
351 return 0;
352 }
353
354 int
355 not_ready(status, sc)
356 u_char status;
357 struct lpt_softc *sc;
358 {
359 u_char new;
360
361 status = (status ^ LPS_INVERT) & LPS_MASK;
362 new = status & ~sc->sc_laststatus;
363 sc->sc_laststatus = status;
364
365 if (new & LPS_SELECT)
366 log(LOG_NOTICE, "%s: offline\n", sc->sc_dev.dv_xname);
367 else if (new & LPS_NOPAPER)
368 log(LOG_NOTICE, "%s: out of paper\n", sc->sc_dev.dv_xname);
369 else if (new & LPS_NERR)
370 log(LOG_NOTICE, "%s: output error\n", sc->sc_dev.dv_xname);
371
372 return status;
373 }
374
375 void
376 lptwakeup(arg)
377 void *arg;
378 {
379 struct lpt_softc *sc = arg;
380 int s;
381
382 s = spltty();
383 lptintr(sc);
384 splx(s);
385
386 timeout(lptwakeup, sc, STEP);
387 }
388
389 /*
390 * Close the device, and free the local line buffer.
391 */
392 lptclose(dev, flag)
393 dev_t dev;
394 int flag;
395 {
396 int unit = LPTUNIT(dev);
397 struct lpt_softc *sc = lpt_cd.cd_devs[unit];
398 bus_chipset_tag_t bc = sc->sc_bc;
399 bus_io_handle_t ioh = sc->sc_ioh;
400
401 if (sc->sc_count)
402 (void) pushbytes(sc);
403
404 if ((sc->sc_flags & LPT_NOINTR) == 0)
405 untimeout(lptwakeup, sc);
406
407 bus_io_write_1(bc, ioh, lpt_control, LPC_NINIT);
408 sc->sc_state = 0;
409 bus_io_write_1(bc, ioh, lpt_control, LPC_NINIT);
410 brelse(sc->sc_inbuf);
411
412 lprintf("%s: closed\n", sc->sc_dev.dv_xname);
413 return 0;
414 }
415
416 int
417 pushbytes(sc)
418 struct lpt_softc *sc;
419 {
420 bus_chipset_tag_t bc = sc->sc_bc;
421 bus_io_handle_t ioh = sc->sc_ioh;
422 int error;
423
424 if (sc->sc_flags & LPT_NOINTR) {
425 int spin, tic;
426 u_char control = sc->sc_control;
427
428 while (sc->sc_count > 0) {
429 spin = 0;
430 while (NOT_READY()) {
431 if (++spin < sc->sc_spinmax)
432 continue;
433 tic = 0;
434 /* adapt busy-wait algorithm */
435 sc->sc_spinmax++;
436 while (NOT_READY_ERR()) {
437 /* exponential backoff */
438 tic = tic + tic + 1;
439 if (tic > TIMEOUT)
440 tic = TIMEOUT;
441 error = tsleep((caddr_t)sc,
442 LPTPRI | PCATCH, "lptpsh", tic);
443 if (error != EWOULDBLOCK)
444 return error;
445 }
446 break;
447 }
448
449 bus_io_write_1(bc, ioh, lpt_data, *sc->sc_cp++);
450 bus_io_write_1(bc, ioh, lpt_control, control | LPC_STROBE);
451 sc->sc_count--;
452 bus_io_write_1(bc, ioh, lpt_control, control);
453
454 /* adapt busy-wait algorithm */
455 if (spin*2 + 16 < sc->sc_spinmax)
456 sc->sc_spinmax--;
457 }
458 } else {
459 int s;
460
461 while (sc->sc_count > 0) {
462 /* if the printer is ready for a char, give it one */
463 if ((sc->sc_state & LPT_OBUSY) == 0) {
464 lprintf("%s: write %d\n", sc->sc_dev.dv_xname,
465 sc->sc_count);
466 s = spltty();
467 (void) lptintr(sc);
468 splx(s);
469 }
470 if (error = tsleep((caddr_t)sc, LPTPRI | PCATCH,
471 "lptwrite2", 0))
472 return error;
473 }
474 }
475 return 0;
476 }
477
478 /*
479 * Copy a line from user space to a local buffer, then call putc to get the
480 * chars moved to the output queue.
481 */
482 lptwrite(dev, uio)
483 dev_t dev;
484 struct uio *uio;
485 {
486 struct lpt_softc *sc = lpt_cd.cd_devs[LPTUNIT(dev)];
487 size_t n;
488 int error = 0;
489
490 while (n = min(LPT_BSIZE, uio->uio_resid)) {
491 uiomove(sc->sc_cp = sc->sc_inbuf->b_data, n, uio);
492 sc->sc_count = n;
493 error = pushbytes(sc);
494 if (error) {
495 /*
496 * Return accurate residual if interrupted or timed
497 * out.
498 */
499 uio->uio_resid += sc->sc_count;
500 sc->sc_count = 0;
501 return error;
502 }
503 }
504 return 0;
505 }
506
507 /*
508 * Handle printer interrupts which occur when the printer is ready to accept
509 * another char.
510 */
511 int
512 lptintr(arg)
513 void *arg;
514 {
515 struct lpt_softc *sc = arg;
516 bus_chipset_tag_t bc = sc->sc_bc;
517 bus_io_handle_t ioh = sc->sc_ioh;
518
519 #if 0
520 if ((sc->sc_state & LPT_OPEN) == 0)
521 return 0;
522 #endif
523
524 /* is printer online and ready for output */
525 if (NOT_READY() && NOT_READY_ERR())
526 return 0;
527
528 if (sc->sc_count) {
529 u_char control = sc->sc_control;
530 /* send char */
531 bus_io_write_1(bc, ioh, lpt_data, *sc->sc_cp++);
532 bus_io_write_1(bc, ioh, lpt_control, control | LPC_STROBE);
533 sc->sc_count--;
534 bus_io_write_1(bc, ioh, lpt_control, control);
535 sc->sc_state |= LPT_OBUSY;
536 } else
537 sc->sc_state &= ~LPT_OBUSY;
538
539 if (sc->sc_count == 0) {
540 /* none, wake up the top half to get more */
541 wakeup((caddr_t)sc);
542 }
543
544 return 1;
545 }
546
547 int
548 lptioctl(dev, cmd, data, flag)
549 dev_t dev;
550 u_long cmd;
551 caddr_t data;
552 int flag;
553 {
554 int error = 0;
555
556 switch (cmd) {
557 default:
558 error = ENODEV;
559 }
560
561 return error;
562 }
563