xref: /openbsd-src/sys/dev/ic/lpt.c (revision 43003dfe3ad45d1698bed8a37f2b0f5b14f20d4f)
1 /*	$OpenBSD: lpt.c,v 1.5 2002/03/14 01:26:54 millert 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/proc.h>
60 #include <sys/user.h>
61 #include <sys/buf.h>
62 #include <sys/kernel.h>
63 #include <sys/ioctl.h>
64 #include <sys/uio.h>
65 #include <sys/device.h>
66 #include <sys/conf.h>
67 #include <sys/syslog.h>
68 
69 #include <machine/bus.h>
70 #include <machine/intr.h>
71 
72 #include <dev/ic/lptreg.h>
73 #include <dev/ic/lptvar.h>
74 
75 #include "lpt.h"
76 
77 #define	TIMEOUT		hz*16	/* wait up to 16 seconds for a ready */
78 #define	STEP		hz/4
79 
80 #define	LPTPRI		(PZERO+8)
81 #define	LPT_BSIZE	1024
82 
83 #if !defined(DEBUG) || !defined(notdef)
84 #define LPRINTF(a)
85 #else
86 #define LPRINTF(a)	if (lptdebug) printf a
87 int lptdebug = 1;
88 #endif
89 
90 /* XXX does not belong here */
91 cdev_decl(lpt);
92 
93 struct cfdriver lpt_cd = {
94 	NULL, "lpt", DV_TTY
95 };
96 
97 #define	LPTUNIT(s)	(minor(s) & 0x1f)
98 #define	LPTFLAGS(s)	(minor(s) & 0xe0)
99 
100 #define	LPS_INVERT	(LPS_SELECT|LPS_NERR|LPS_NBSY|LPS_NACK)
101 #define	LPS_MASK	(LPS_SELECT|LPS_NERR|LPS_NBSY|LPS_NACK|LPS_NOPAPER)
102 #define	NOT_READY() \
103     ((bus_space_read_1(iot, ioh, lpt_status) ^ LPS_INVERT) & LPS_MASK)
104 #define	NOT_READY_ERR() \
105     lpt_not_ready(bus_space_read_1(iot, ioh, lpt_status), sc)
106 
107 int	lpt_not_ready(u_int8_t, struct lpt_softc *);
108 void	lptwakeup(void *arg);
109 int	lptpushbytes(struct lpt_softc *);
110 
111 /*
112  * Internal routine to lptprobe to do port tests of one byte value.
113  */
114 int
115 lpt_port_test(iot, ioh, base, off, data, mask)
116 	bus_space_tag_t iot;
117 	bus_space_handle_t ioh;
118 	bus_addr_t base;
119 	bus_size_t off;
120 	u_int8_t data, mask;
121 {
122 	int timeout;
123 	u_int8_t temp;
124 
125 	data &= mask;
126 	bus_space_write_1(iot, ioh, off, data);
127 	timeout = 1000;
128 	do {
129 		delay(10);
130 		temp = bus_space_read_1(iot, ioh, off) & mask;
131 	} while (temp != data && --timeout);
132 	LPRINTF(("lpt: port=0x%x out=0x%x in=0x%x timeout=%d\n", base + off,
133 	    data, temp, timeout));
134 	return (temp == data);
135 }
136 
137 void
138 lpt_attach_common(sc)
139 	struct lpt_softc *sc;
140 {
141 	printf("\n");
142 
143 	bus_space_write_1(sc->sc_iot, sc->sc_ioh, lpt_control, LPC_NINIT);
144 
145 	timeout_set(&sc->sc_wakeup_tmo, lptwakeup, sc);
146 }
147 
148 /*
149  * Reset the printer, then wait until it's selected and not busy.
150  */
151 int
152 lptopen(dev, flag, mode, p)
153 	dev_t dev;
154 	int flag;
155 	int mode;
156 	struct proc *p;
157 {
158 	int unit = LPTUNIT(dev);
159 	u_int8_t flags = LPTFLAGS(dev);
160 	struct lpt_softc *sc;
161 	bus_space_tag_t iot;
162 	bus_space_handle_t ioh;
163 	u_int8_t control;
164 	int error;
165 	int spin;
166 
167 	if (unit >= lpt_cd.cd_ndevs)
168 		return ENXIO;
169 	sc = lpt_cd.cd_devs[unit];
170 	if (!sc)
171 		return ENXIO;
172 
173 	sc->sc_flags = (sc->sc_flags & LPT_POLLED) | flags;
174 	if ((sc->sc_flags & (LPT_POLLED|LPT_NOINTR)) == LPT_POLLED)
175 		return ENXIO;
176 
177 #ifdef DIAGNOSTIC
178 	if (sc->sc_state)
179 		printf("%s: stat=0x%x not zero\n", sc->sc_dev.dv_xname,
180 		    sc->sc_state);
181 #endif
182 
183 	if (sc->sc_state)
184 		return EBUSY;
185 
186 	sc->sc_state = LPT_INIT;
187 	LPRINTF(("%s: open: flags=0x%x\n", sc->sc_dev.dv_xname, flags));
188 	iot = sc->sc_iot;
189 	ioh = sc->sc_ioh;
190 
191 	if ((flags & LPT_NOPRIME) == 0) {
192 		/* assert INIT for 100 usec to start up printer */
193 		bus_space_write_1(iot, ioh, lpt_control, LPC_SELECT);
194 		delay(100);
195 	}
196 
197 	control = LPC_SELECT | LPC_NINIT;
198 	bus_space_write_1(iot, ioh, lpt_control, control);
199 
200 	/* wait till ready (printer running diagnostics) */
201 	for (spin = 0; NOT_READY_ERR(); spin += STEP) {
202 		if (spin >= TIMEOUT) {
203 			sc->sc_state = 0;
204 			return EBUSY;
205 		}
206 
207 		/* wait 1/4 second, give up if we get a signal */
208 		error = tsleep((caddr_t)sc, LPTPRI | PCATCH, "lptopen", STEP);
209 		if (error != EWOULDBLOCK) {
210 			sc->sc_state = 0;
211 			return error;
212 		}
213 	}
214 
215 	if ((flags & LPT_NOINTR) == 0)
216 		control |= LPC_IENABLE;
217 	if (flags & LPT_AUTOLF)
218 		control |= LPC_AUTOLF;
219 	sc->sc_control = control;
220 	bus_space_write_1(iot, ioh, lpt_control, control);
221 
222 	sc->sc_inbuf = geteblk(LPT_BSIZE);
223 	sc->sc_count = 0;
224 	sc->sc_state = LPT_OPEN;
225 
226 	if ((sc->sc_flags & LPT_NOINTR) == 0)
227 		lptwakeup(sc);
228 
229 	LPRINTF(("%s: opened\n", sc->sc_dev.dv_xname));
230 	return 0;
231 }
232 
233 int
234 lpt_not_ready(status, sc)
235 	u_int8_t status;
236 	struct lpt_softc *sc;
237 {
238 	u_int8_t new;
239 
240 	status = (status ^ LPS_INVERT) & LPS_MASK;
241 	new = status & ~sc->sc_laststatus;
242 	sc->sc_laststatus = status;
243 
244 	if (new & LPS_SELECT)
245 		log(LOG_NOTICE, "%s: offline\n", sc->sc_dev.dv_xname);
246 	else if (new & LPS_NOPAPER)
247 		log(LOG_NOTICE, "%s: out of paper\n", sc->sc_dev.dv_xname);
248 	else if (new & LPS_NERR)
249 		log(LOG_NOTICE, "%s: output error\n", sc->sc_dev.dv_xname);
250 
251 	return status;
252 }
253 
254 void
255 lptwakeup(arg)
256 	void *arg;
257 {
258 	struct lpt_softc *sc = arg;
259 	int s;
260 
261 	s = spltty();
262 	lptintr(sc);
263 	splx(s);
264 
265 	timeout_add(&sc->sc_wakeup_tmo, STEP);
266 }
267 
268 /*
269  * Close the device, and free the local line buffer.
270  */
271 int
272 lptclose(dev, flag, mode, p)
273 	dev_t dev;
274 	int flag;
275 	int mode;
276 	struct proc *p;
277 {
278 	int unit = LPTUNIT(dev);
279 	struct lpt_softc *sc = lpt_cd.cd_devs[unit];
280 	bus_space_tag_t iot = sc->sc_iot;
281 	bus_space_handle_t ioh = sc->sc_ioh;
282 
283 	if (sc->sc_count)
284 		(void) lptpushbytes(sc);
285 
286 	if ((sc->sc_flags & LPT_NOINTR) == 0)
287 		timeout_del(&sc->sc_wakeup_tmo);
288 
289 	bus_space_write_1(iot, ioh, lpt_control, LPC_NINIT);
290 	sc->sc_state = 0;
291 	bus_space_write_1(iot, ioh, lpt_control, LPC_NINIT);
292 	brelse(sc->sc_inbuf);
293 
294 	LPRINTF(("%s: closed\n", sc->sc_dev.dv_xname));
295 	return 0;
296 }
297 
298 int
299 lptpushbytes(sc)
300 	struct lpt_softc *sc;
301 {
302 	bus_space_tag_t iot = sc->sc_iot;
303 	bus_space_handle_t ioh = sc->sc_ioh;
304 	int error;
305 
306 	if (sc->sc_flags & LPT_NOINTR) {
307 		int spin, tic;
308 		u_int8_t control = sc->sc_control;
309 
310 		while (sc->sc_count > 0) {
311 			spin = 0;
312 			while (NOT_READY()) {
313 				if (++spin < sc->sc_spinmax)
314 					continue;
315 				tic = 0;
316 				/* adapt busy-wait algorithm */
317 				sc->sc_spinmax++;
318 				while (NOT_READY_ERR()) {
319 					/* exponential backoff */
320 					tic = tic + tic + 1;
321 					if (tic > TIMEOUT)
322 						tic = TIMEOUT;
323 					error = tsleep((caddr_t)sc,
324 					    LPTPRI | PCATCH, "lptpsh", tic);
325 					if (error != EWOULDBLOCK)
326 						return error;
327 				}
328 				break;
329 			}
330 
331 			bus_space_write_1(iot, ioh, lpt_data, *sc->sc_cp++);
332 			bus_space_write_1(iot, ioh, lpt_control,
333 			    control | LPC_STROBE);
334 			sc->sc_count--;
335 			bus_space_write_1(iot, ioh, lpt_control, control);
336 
337 			/* adapt busy-wait algorithm */
338 			if (spin*2 + 16 < sc->sc_spinmax)
339 				sc->sc_spinmax--;
340 		}
341 	} else {
342 		int s;
343 
344 		while (sc->sc_count > 0) {
345 			/* if the printer is ready for a char, give it one */
346 			if ((sc->sc_state & LPT_OBUSY) == 0) {
347 				LPRINTF(("%s: write %d\n", sc->sc_dev.dv_xname,
348 				    sc->sc_count));
349 				s = spltty();
350 				(void) lptintr(sc);
351 				splx(s);
352 			}
353 			error = tsleep((caddr_t)sc, LPTPRI | PCATCH,
354 			    "lptwrite2", 0);
355 			if (error)
356 				return error;
357 		}
358 	}
359 	return 0;
360 }
361 
362 /*
363  * Copy a line from user space to a local buffer, then call putc to get the
364  * chars moved to the output queue.
365  */
366 int
367 lptwrite(dev, uio, flags)
368 	dev_t dev;
369 	struct uio *uio;
370 	int flags;
371 {
372 	struct lpt_softc *sc = lpt_cd.cd_devs[LPTUNIT(dev)];
373 	size_t n;
374 	int error = 0;
375 
376 	while ((n = min(LPT_BSIZE, uio->uio_resid)) != 0) {
377 		uiomove(sc->sc_cp = sc->sc_inbuf->b_data, n, uio);
378 		sc->sc_count = n;
379 		error = lptpushbytes(sc);
380 		if (error) {
381 			/*
382 			 * Return accurate residual if interrupted or timed
383 			 * out.
384 			 */
385 			uio->uio_resid += sc->sc_count;
386 			sc->sc_count = 0;
387 			return error;
388 		}
389 	}
390 	return 0;
391 }
392 
393 /*
394  * Handle printer interrupts which occur when the printer is ready to accept
395  * another char.
396  */
397 int
398 lptintr(arg)
399 	void *arg;
400 {
401 	struct lpt_softc *sc = arg;
402 	bus_space_tag_t iot = sc->sc_iot;
403 	bus_space_handle_t ioh = sc->sc_ioh;
404 
405 	if (((sc->sc_state & LPT_OPEN) == 0 && sc->sc_count == 0) ||
406 	    (sc->sc_flags & LPT_NOINTR))
407 		return 0;
408 
409 	/* is printer online and ready for output */
410 	if (NOT_READY() && NOT_READY_ERR())
411 		return -1;
412 
413 	if (sc->sc_count) {
414 		u_int8_t control = sc->sc_control;
415 		/* send char */
416 		bus_space_write_1(iot, ioh, lpt_data, *sc->sc_cp++);
417 		delay (50);
418 		bus_space_write_1(iot, ioh, lpt_control, control | LPC_STROBE);
419 		sc->sc_count--;
420 		bus_space_write_1(iot, ioh, lpt_control, control);
421 		sc->sc_state |= LPT_OBUSY;
422 	} else
423 		sc->sc_state &= ~LPT_OBUSY;
424 
425 	if (sc->sc_count == 0) {
426 		/* none, wake up the top half to get more */
427 		wakeup((caddr_t)sc);
428 	}
429 
430 	return 1;
431 }
432 
433 int
434 lptioctl(dev, cmd, data, flag, p)
435 	dev_t dev;
436 	u_long cmd;
437 	caddr_t data;
438 	int flag;
439 	struct proc *p;
440 {
441 	int error = 0;
442 
443 	switch (cmd) {
444 	default:
445 		error = ENODEV;
446 	}
447 
448 	return error;
449 }
450