Line data Source code
1 : /* $OpenBSD: lpt.c,v 1.14 2015/05/11 02:01:01 guenther Exp $ */
2 : /* $NetBSD: lpt.c,v 1.42 1996/10/21 22:41:14 thorpej Exp $ */
3 :
4 : /*
5 : * Copyright (c) 1993, 1994 Charles Hannum.
6 : * Copyright (c) 1990 William F. Jolitz, TeleMuse
7 : * All rights reserved.
8 : *
9 : * Redistribution and use in source and binary forms, with or without
10 : * modification, are permitted provided that the following conditions
11 : * are met:
12 : * 1. Redistributions of source code must retain the above copyright
13 : * notice, this list of conditions and the following disclaimer.
14 : * 2. Redistributions in binary form must reproduce the above copyright
15 : * notice, this list of conditions and the following disclaimer in the
16 : * documentation and/or other materials provided with the distribution.
17 : * 3. All advertising materials mentioning features or use of this software
18 : * must display the following acknowledgement:
19 : * This software is a component of "386BSD" developed by
20 : * William F. Jolitz, TeleMuse.
21 : * 4. Neither the name of the developer nor the name "386BSD"
22 : * may be used to endorse or promote products derived from this software
23 : * without specific prior written permission.
24 : *
25 : * THIS SOFTWARE IS A COMPONENT OF 386BSD DEVELOPED BY WILLIAM F. JOLITZ
26 : * AND IS INTENDED FOR RESEARCH AND EDUCATIONAL PURPOSES ONLY. THIS
27 : * SOFTWARE SHOULD NOT BE CONSIDERED TO BE A COMMERCIAL PRODUCT.
28 : * THE DEVELOPER URGES THAT USERS WHO REQUIRE A COMMERCIAL PRODUCT
29 : * NOT MAKE USE OF THIS WORK.
30 : *
31 : * FOR USERS WHO WISH TO UNDERSTAND THE 386BSD SYSTEM DEVELOPED
32 : * BY WILLIAM F. JOLITZ, WE RECOMMEND THE USER STUDY WRITTEN
33 : * REFERENCES SUCH AS THE "PORTING UNIX TO THE 386" SERIES
34 : * (BEGINNING JANUARY 1991 "DR. DOBBS JOURNAL", USA AND BEGINNING
35 : * JUNE 1991 "UNIX MAGAZIN", GERMANY) BY WILLIAM F. JOLITZ AND
36 : * LYNNE GREER JOLITZ, AS WELL AS OTHER BOOKS ON UNIX AND THE
37 : * ON-LINE 386BSD USER MANUAL BEFORE USE. A BOOK DISCUSSING THE INTERNALS
38 : * OF 386BSD ENTITLED "386BSD FROM THE INSIDE OUT" WILL BE AVAILABLE LATE 1992.
39 : *
40 : * THIS SOFTWARE IS PROVIDED BY THE DEVELOPER ``AS IS'' AND
41 : * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
42 : * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
43 : * ARE DISCLAIMED. IN NO EVENT SHALL THE DEVELOPER BE LIABLE
44 : * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
45 : * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
46 : * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
47 : * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
48 : * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
49 : * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
50 : * SUCH DAMAGE.
51 : */
52 :
53 : /*
54 : * Device Driver for AT parallel printer port
55 : */
56 :
57 : #include <sys/param.h>
58 : #include <sys/systm.h>
59 : #include <sys/buf.h>
60 : #include <sys/kernel.h>
61 : #include <sys/uio.h>
62 : #include <sys/device.h>
63 : #include <sys/conf.h>
64 : #include <sys/syslog.h>
65 :
66 : #include <machine/bus.h>
67 : #include <machine/intr.h>
68 :
69 : #include <dev/ic/lptreg.h>
70 : #include <dev/ic/lptvar.h>
71 :
72 : #include "lpt.h"
73 :
74 : #define TIMEOUT hz*16 /* wait up to 16 seconds for a ready */
75 : #define STEP hz/4
76 :
77 : #define LPTPRI (PZERO+8)
78 : #define LPT_BSIZE 1024
79 :
80 : #if !defined(DEBUG) || !defined(notdef)
81 : #define LPRINTF(a)
82 : #else
83 : #define LPRINTF(a) if (lptdebug) printf a
84 : int lptdebug = 1;
85 : #endif
86 :
87 : /* XXX does not belong here */
88 : cdev_decl(lpt);
89 :
90 : struct cfdriver lpt_cd = {
91 : NULL, "lpt", DV_TTY
92 : };
93 :
94 : #define LPTUNIT(s) (minor(s) & 0x1f)
95 : #define LPTFLAGS(s) (minor(s) & 0xe0)
96 :
97 : #define LPS_INVERT (LPS_SELECT|LPS_NERR|LPS_NBSY|LPS_NACK)
98 : #define LPS_MASK (LPS_SELECT|LPS_NERR|LPS_NBSY|LPS_NACK|LPS_NOPAPER)
99 : #define NOT_READY() \
100 : ((bus_space_read_1(sc->sc_iot, sc->sc_ioh, lpt_status) ^ LPS_INVERT) & LPS_MASK)
101 : #define NOT_READY_ERR() \
102 : lpt_not_ready(bus_space_read_1(sc->sc_iot, sc->sc_ioh, lpt_status), sc)
103 :
104 : int lpt_not_ready(u_int8_t, struct lpt_softc *);
105 : void lptwakeup(void *arg);
106 : int lptpushbytes(struct lpt_softc *);
107 :
108 : /*
109 : * Internal routine to lptprobe to do port tests of one byte value.
110 : */
111 : int
112 0 : lpt_port_test(bus_space_tag_t iot, bus_space_handle_t ioh, bus_addr_t base,
113 : bus_size_t off, u_int8_t data, u_int8_t mask)
114 : {
115 : int timeout;
116 : u_int8_t temp;
117 :
118 0 : data &= mask;
119 0 : bus_space_write_1(iot, ioh, off, data);
120 : timeout = 1000;
121 0 : do {
122 0 : delay(10);
123 0 : temp = bus_space_read_1(iot, ioh, off) & mask;
124 0 : } while (temp != data && --timeout);
125 : LPRINTF(("lpt: port=0x%x out=0x%x in=0x%x timeout=%d\n", base + off,
126 : data, temp, timeout));
127 0 : return (temp == data);
128 : }
129 :
130 : void
131 0 : lpt_attach_common(struct lpt_softc *sc)
132 : {
133 0 : printf("\n");
134 :
135 0 : bus_space_write_1(sc->sc_iot, sc->sc_ioh, lpt_control, LPC_NINIT);
136 :
137 0 : timeout_set(&sc->sc_wakeup_tmo, lptwakeup, sc);
138 0 : }
139 :
140 : void
141 0 : lpt_detach_common(struct lpt_softc *sc)
142 : {
143 0 : timeout_del(&sc->sc_wakeup_tmo);
144 0 : if (sc->sc_state != 0) {
145 0 : sc->sc_state = 0;
146 0 : wakeup(sc);
147 0 : }
148 0 : }
149 :
150 : /*
151 : * Reset the printer, then wait until it's selected and not busy.
152 : */
153 : int
154 0 : lptopen(dev_t dev, int flag, int mode, struct proc *p)
155 : {
156 0 : int unit = LPTUNIT(dev);
157 0 : u_int8_t flags = LPTFLAGS(dev);
158 : struct lpt_softc *sc;
159 : u_int8_t control;
160 : int error;
161 : int spin;
162 :
163 0 : if (unit >= lpt_cd.cd_ndevs)
164 0 : return ENXIO;
165 0 : sc = lpt_cd.cd_devs[unit];
166 0 : if (!sc)
167 0 : return ENXIO;
168 :
169 0 : sc->sc_flags = (sc->sc_flags & LPT_POLLED) | flags;
170 0 : if ((sc->sc_flags & (LPT_POLLED|LPT_NOINTR)) == LPT_POLLED)
171 0 : return ENXIO;
172 :
173 : #ifdef DIAGNOSTIC
174 0 : if (sc->sc_state)
175 0 : printf("%s: stat=0x%x not zero\n", sc->sc_dev.dv_xname,
176 0 : sc->sc_state);
177 : #endif
178 :
179 0 : if (sc->sc_state)
180 0 : return EBUSY;
181 :
182 0 : sc->sc_state = LPT_INIT;
183 : LPRINTF(("%s: open: flags=0x%x\n", sc->sc_dev.dv_xname, flags));
184 :
185 0 : if ((flags & LPT_NOPRIME) == 0) {
186 : /* assert INIT for 100 usec to start up printer */
187 0 : bus_space_write_1(sc->sc_iot, sc->sc_ioh, lpt_control, LPC_SELECT);
188 0 : delay(100);
189 0 : }
190 :
191 : control = LPC_SELECT | LPC_NINIT;
192 0 : bus_space_write_1(sc->sc_iot, sc->sc_ioh, lpt_control, control);
193 :
194 : /* wait till ready (printer running diagnostics) */
195 0 : for (spin = 0; NOT_READY_ERR(); spin += STEP) {
196 0 : if (spin >= TIMEOUT) {
197 0 : sc->sc_state = 0;
198 0 : return EBUSY;
199 : }
200 :
201 : /* wait 1/4 second, give up if we get a signal */
202 0 : error = tsleep((caddr_t)sc, LPTPRI | PCATCH, "lptopen", STEP);
203 0 : if (sc->sc_state == 0)
204 0 : return (EIO);
205 0 : if (error != EWOULDBLOCK) {
206 0 : sc->sc_state = 0;
207 0 : return error;
208 : }
209 : }
210 :
211 0 : if ((flags & LPT_NOINTR) == 0)
212 0 : control |= LPC_IENABLE;
213 0 : if (flags & LPT_AUTOLF)
214 0 : control |= LPC_AUTOLF;
215 0 : sc->sc_control = control;
216 0 : bus_space_write_1(sc->sc_iot, sc->sc_ioh, lpt_control, control);
217 :
218 0 : sc->sc_inbuf = geteblk(LPT_BSIZE);
219 0 : sc->sc_count = 0;
220 0 : sc->sc_state = LPT_OPEN;
221 :
222 0 : if ((sc->sc_flags & LPT_NOINTR) == 0)
223 0 : lptwakeup(sc);
224 :
225 : LPRINTF(("%s: opened\n", sc->sc_dev.dv_xname));
226 0 : return 0;
227 0 : }
228 :
229 : int
230 0 : lpt_not_ready(u_int8_t status, struct lpt_softc *sc)
231 : {
232 : u_int8_t new;
233 :
234 0 : status = (status ^ LPS_INVERT) & LPS_MASK;
235 0 : new = status & ~sc->sc_laststatus;
236 0 : sc->sc_laststatus = status;
237 :
238 0 : if (new & LPS_SELECT)
239 0 : log(LOG_NOTICE, "%s: offline\n", sc->sc_dev.dv_xname);
240 0 : else if (new & LPS_NOPAPER)
241 0 : log(LOG_NOTICE, "%s: out of paper\n", sc->sc_dev.dv_xname);
242 0 : else if (new & LPS_NERR)
243 0 : log(LOG_NOTICE, "%s: output error\n", sc->sc_dev.dv_xname);
244 :
245 0 : return status;
246 : }
247 :
248 : void
249 0 : lptwakeup(void *arg)
250 : {
251 0 : struct lpt_softc *sc = arg;
252 : int s;
253 :
254 0 : s = spltty();
255 0 : lptintr(sc);
256 0 : splx(s);
257 :
258 0 : if (sc->sc_state != 0)
259 0 : timeout_add(&sc->sc_wakeup_tmo, STEP);
260 0 : }
261 :
262 : /*
263 : * Close the device, and free the local line buffer.
264 : */
265 : int
266 0 : lptclose(dev_t dev, int flag, int mode, struct proc *p)
267 : {
268 0 : int unit = LPTUNIT(dev);
269 0 : struct lpt_softc *sc = lpt_cd.cd_devs[unit];
270 0 : bus_space_tag_t iot = sc->sc_iot;
271 0 : bus_space_handle_t ioh = sc->sc_ioh;
272 :
273 0 : if (sc->sc_count)
274 0 : (void) lptpushbytes(sc);
275 :
276 0 : if ((sc->sc_flags & LPT_NOINTR) == 0)
277 0 : timeout_del(&sc->sc_wakeup_tmo);
278 :
279 0 : bus_space_write_1(iot, ioh, lpt_control, LPC_NINIT);
280 0 : sc->sc_state = 0;
281 0 : bus_space_write_1(iot, ioh, lpt_control, LPC_NINIT);
282 0 : brelse(sc->sc_inbuf);
283 :
284 : LPRINTF(("%s: closed\n", sc->sc_dev.dv_xname));
285 0 : return 0;
286 : }
287 :
288 : int
289 0 : lptpushbytes(struct lpt_softc *sc)
290 : {
291 0 : bus_space_tag_t iot = sc->sc_iot;
292 0 : bus_space_handle_t ioh = sc->sc_ioh;
293 : int error;
294 :
295 0 : if (sc->sc_flags & LPT_NOINTR) {
296 : int spin, tic;
297 0 : u_int8_t control = sc->sc_control;
298 :
299 0 : while (sc->sc_count > 0) {
300 : spin = 0;
301 0 : if (sc->sc_state == 0)
302 0 : return (EIO);
303 0 : while (NOT_READY()) {
304 0 : if (++spin < sc->sc_spinmax)
305 0 : continue;
306 : tic = 0;
307 : /* adapt busy-wait algorithm */
308 0 : sc->sc_spinmax++;
309 0 : while (NOT_READY_ERR()) {
310 : /* exponential backoff */
311 0 : tic = tic + tic + 1;
312 0 : if (tic > TIMEOUT)
313 0 : tic = TIMEOUT;
314 0 : error = tsleep((caddr_t)sc,
315 : LPTPRI | PCATCH, "lptpsh", tic);
316 0 : if (sc->sc_state == 0)
317 : error = EIO;
318 0 : if (error != EWOULDBLOCK)
319 0 : return error;
320 : }
321 : break;
322 : }
323 :
324 0 : bus_space_write_1(iot, ioh, lpt_data, *sc->sc_cp++);
325 0 : bus_space_write_1(iot, ioh, lpt_control,
326 : control | LPC_STROBE);
327 0 : sc->sc_count--;
328 0 : bus_space_write_1(iot, ioh, lpt_control, control);
329 :
330 : /* adapt busy-wait algorithm */
331 0 : if (spin*2 + 16 < sc->sc_spinmax)
332 0 : sc->sc_spinmax--;
333 : }
334 0 : } else {
335 : int s;
336 :
337 0 : while (sc->sc_count > 0) {
338 : /* if the printer is ready for a char, give it one */
339 0 : if ((sc->sc_state & LPT_OBUSY) == 0) {
340 : LPRINTF(("%s: write %d\n", sc->sc_dev.dv_xname,
341 : sc->sc_count));
342 0 : s = spltty();
343 0 : (void) lptintr(sc);
344 0 : splx(s);
345 0 : }
346 0 : if (sc->sc_state == 0)
347 0 : return (EIO);
348 0 : error = tsleep((caddr_t)sc, LPTPRI | PCATCH,
349 : "lptwrite2", 0);
350 0 : if (sc->sc_state == 0)
351 : error = EIO;
352 0 : if (error)
353 0 : return error;
354 : }
355 0 : }
356 0 : return 0;
357 0 : }
358 :
359 : /*
360 : * Copy a line from user space to a local buffer, then call putc to get the
361 : * chars moved to the output queue.
362 : */
363 : int
364 0 : lptwrite(dev_t dev, struct uio *uio, int flags)
365 : {
366 0 : struct lpt_softc *sc = lpt_cd.cd_devs[LPTUNIT(dev)];
367 : size_t n;
368 : int error = 0;
369 :
370 0 : while ((n = ulmin(LPT_BSIZE, uio->uio_resid)) != 0) {
371 0 : error = uiomove(sc->sc_cp = sc->sc_inbuf->b_data, n, uio);
372 0 : if (error != 0)
373 0 : return error;
374 0 : sc->sc_count = n;
375 0 : error = lptpushbytes(sc);
376 0 : if (error) {
377 : /*
378 : * Return accurate residual if interrupted or timed
379 : * out.
380 : */
381 0 : uio->uio_resid += sc->sc_count;
382 0 : sc->sc_count = 0;
383 0 : return error;
384 : }
385 : }
386 0 : return 0;
387 0 : }
388 :
389 : /*
390 : * Handle printer interrupts which occur when the printer is ready to accept
391 : * another char.
392 : */
393 : int
394 0 : lptintr(void *arg)
395 : {
396 0 : struct lpt_softc *sc = arg;
397 0 : bus_space_tag_t iot = sc->sc_iot;
398 0 : bus_space_handle_t ioh = sc->sc_ioh;
399 :
400 0 : if (((sc->sc_state & LPT_OPEN) == 0 && sc->sc_count == 0) ||
401 0 : (sc->sc_flags & LPT_NOINTR))
402 0 : return 0;
403 :
404 : /* is printer online and ready for output */
405 0 : if (NOT_READY() && NOT_READY_ERR())
406 0 : return -1;
407 :
408 0 : if (sc->sc_count) {
409 0 : u_int8_t control = sc->sc_control;
410 : /* send char */
411 0 : bus_space_write_1(iot, ioh, lpt_data, *sc->sc_cp++);
412 0 : delay (50);
413 0 : bus_space_write_1(iot, ioh, lpt_control, control | LPC_STROBE);
414 0 : sc->sc_count--;
415 0 : bus_space_write_1(iot, ioh, lpt_control, control);
416 0 : sc->sc_state |= LPT_OBUSY;
417 0 : } else
418 0 : sc->sc_state &= ~LPT_OBUSY;
419 :
420 0 : if (sc->sc_count == 0) {
421 : /* none, wake up the top half to get more */
422 0 : wakeup((caddr_t)sc);
423 0 : }
424 :
425 0 : return 1;
426 0 : }
427 :
428 : int
429 0 : lpt_activate(struct device *self, int act)
430 : {
431 0 : struct lpt_softc *sc = (struct lpt_softc *)self;
432 :
433 0 : switch (act) {
434 : case DVACT_SUSPEND:
435 0 : timeout_del(&sc->sc_wakeup_tmo);
436 0 : break;
437 : case DVACT_RESUME:
438 0 : bus_space_write_1(sc->sc_iot, sc->sc_ioh, lpt_control, LPC_NINIT);
439 :
440 0 : if (sc->sc_state) {
441 : int spin;
442 :
443 0 : if ((sc->sc_flags & LPT_NOPRIME) == 0) {
444 : /* assert INIT for 100 usec to start up printer */
445 0 : bus_space_write_1(sc->sc_iot, sc->sc_ioh,
446 : lpt_control, LPC_SELECT);
447 0 : delay(100);
448 0 : }
449 :
450 0 : bus_space_write_1(sc->sc_iot, sc->sc_ioh, lpt_control,
451 : LPC_SELECT | LPC_NINIT);
452 :
453 : /* wait till ready (printer running diagnostics) */
454 0 : for (spin = 0; NOT_READY_ERR(); spin += STEP) {
455 0 : if (spin >= TIMEOUT) {
456 0 : sc->sc_state = 0;
457 0 : goto fail;
458 : }
459 :
460 : /* wait 1/4 second, give up if we get a signal */
461 0 : delay(STEP * 1000);
462 : }
463 :
464 0 : bus_space_write_1(sc->sc_iot, sc->sc_ioh,
465 : lpt_control, sc->sc_control);
466 0 : wakeup(sc);
467 0 : }
468 : fail:
469 : break;
470 : }
471 :
472 0 : return (0);
473 0 : }
|