xref: /netbsd-src/sys/dev/ppbus/lpt.c (revision c46d1cc12b16d59020e018f03ab9fb25d2584664)
1*c46d1cc1Smsaitoh /* $NetBSD: lpt.c,v 1.32 2019/12/27 09:28:41 msaitoh Exp $ */
2f7820334Sbjh21 
3e23cd1a7Sjdolecek /*
4e23cd1a7Sjdolecek  * Copyright (c) 1990 William F. Jolitz, TeleMuse
5e23cd1a7Sjdolecek  * All rights reserved.
6e23cd1a7Sjdolecek  *
7e23cd1a7Sjdolecek  * Redistribution and use in source and binary forms, with or without
8e23cd1a7Sjdolecek  * modification, are permitted provided that the following conditions
9e23cd1a7Sjdolecek  * are met:
10e23cd1a7Sjdolecek  * 1. Redistributions of source code must retain the above copyright
11e23cd1a7Sjdolecek  *    notice, this list of conditions and the following disclaimer.
12e23cd1a7Sjdolecek  * 2. Redistributions in binary form must reproduce the above copyright
13e23cd1a7Sjdolecek  *    notice, this list of conditions and the following disclaimer in the
14e23cd1a7Sjdolecek  *    documentation and/or other materials provided with the distribution.
15e23cd1a7Sjdolecek  * 3. All advertising materials mentioning features or use of this software
16e23cd1a7Sjdolecek  *    must display the following acknowledgement:
17e23cd1a7Sjdolecek  *	This software is a component of "386BSD" developed by
18e23cd1a7Sjdolecek  *	William F. Jolitz, TeleMuse.
19e23cd1a7Sjdolecek  * 4. Neither the name of the developer nor the name "386BSD"
20e23cd1a7Sjdolecek  *    may be used to endorse or promote products derived from this software
21e23cd1a7Sjdolecek  *    without specific prior written permission.
22e23cd1a7Sjdolecek  *
23e23cd1a7Sjdolecek  * THIS SOFTWARE IS A COMPONENT OF 386BSD DEVELOPED BY WILLIAM F. JOLITZ
24e23cd1a7Sjdolecek  * AND IS INTENDED FOR RESEARCH AND EDUCATIONAL PURPOSES ONLY. THIS
25e23cd1a7Sjdolecek  * SOFTWARE SHOULD NOT BE CONSIDERED TO BE A COMMERCIAL PRODUCT.
26e23cd1a7Sjdolecek  * THE DEVELOPER URGES THAT USERS WHO REQUIRE A COMMERCIAL PRODUCT
27e23cd1a7Sjdolecek  * NOT MAKE USE OF THIS WORK.
28e23cd1a7Sjdolecek  *
29e23cd1a7Sjdolecek  * FOR USERS WHO WISH TO UNDERSTAND THE 386BSD SYSTEM DEVELOPED
30e23cd1a7Sjdolecek  * BY WILLIAM F. JOLITZ, WE RECOMMEND THE USER STUDY WRITTEN
31e23cd1a7Sjdolecek  * REFERENCES SUCH AS THE  "PORTING UNIX TO THE 386" SERIES
32e23cd1a7Sjdolecek  * (BEGINNING JANUARY 1991 "DR. DOBBS JOURNAL", USA AND BEGINNING
33e23cd1a7Sjdolecek  * JUNE 1991 "UNIX MAGAZIN", GERMANY) BY WILLIAM F. JOLITZ AND
34e23cd1a7Sjdolecek  * LYNNE GREER JOLITZ, AS WELL AS OTHER BOOKS ON UNIX AND THE
35e23cd1a7Sjdolecek  * ON-LINE 386BSD USER MANUAL BEFORE USE. A BOOK DISCUSSING THE INTERNALS
36e23cd1a7Sjdolecek  * OF 386BSD ENTITLED "386BSD FROM THE INSIDE OUT" WILL BE AVAILABLE LATE 1992.
37e23cd1a7Sjdolecek  *
38e23cd1a7Sjdolecek  * THIS SOFTWARE IS PROVIDED BY THE DEVELOPER ``AS IS'' AND
39e23cd1a7Sjdolecek  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
40e23cd1a7Sjdolecek  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
41e23cd1a7Sjdolecek  * ARE DISCLAIMED.  IN NO EVENT SHALL THE DEVELOPER BE LIABLE
42e23cd1a7Sjdolecek  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
43e23cd1a7Sjdolecek  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
44e23cd1a7Sjdolecek  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
45e23cd1a7Sjdolecek  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
46e23cd1a7Sjdolecek  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
47e23cd1a7Sjdolecek  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
48e23cd1a7Sjdolecek  * SUCH DAMAGE.
49e23cd1a7Sjdolecek  *
50e23cd1a7Sjdolecek  *	from: unknown origin, 386BSD 0.1
51e23cd1a7Sjdolecek  *	From Id: lpt.c,v 1.55.2.1 1996/11/12 09:08:38 phk Exp
52e23cd1a7Sjdolecek  *	From Id: nlpt.c,v 1.14 1999/02/08 13:55:43 des Exp
531c34707aSbjh21  * FreeBSD: src/sys/dev/ppbus/lpt.c,v 1.15.2.3 2000/07/07 00:30:40 obrien Exp
54e23cd1a7Sjdolecek  */
55e23cd1a7Sjdolecek 
56e23cd1a7Sjdolecek /*
57e23cd1a7Sjdolecek  * Device Driver for AT parallel printer port
58e23cd1a7Sjdolecek  * Written by William Jolitz 12/18/90
59e23cd1a7Sjdolecek  */
60e23cd1a7Sjdolecek 
61e23cd1a7Sjdolecek /*
62e23cd1a7Sjdolecek  * Updated for ppbus by Nicolas Souchu
63e23cd1a7Sjdolecek  * [Mon Jul 28 1997]
64e23cd1a7Sjdolecek  */
65e23cd1a7Sjdolecek 
661c34707aSbjh21 #include <sys/cdefs.h>
67*c46d1cc1Smsaitoh __KERNEL_RCSID(0, "$NetBSD: lpt.c,v 1.32 2019/12/27 09:28:41 msaitoh Exp $");
681c34707aSbjh21 
69e23cd1a7Sjdolecek #include "opt_ppbus_lpt.h"
70e23cd1a7Sjdolecek 
71e23cd1a7Sjdolecek #include <sys/param.h>
72e23cd1a7Sjdolecek #include <sys/systm.h>
73e23cd1a7Sjdolecek #include <sys/conf.h>
74e23cd1a7Sjdolecek #include <sys/kernel.h>
75e23cd1a7Sjdolecek #include <sys/proc.h>
76e23cd1a7Sjdolecek #include <sys/malloc.h>
77e23cd1a7Sjdolecek #include <sys/file.h>
78e23cd1a7Sjdolecek #include <sys/uio.h>
79e23cd1a7Sjdolecek #include <sys/ioctl.h>
80e23cd1a7Sjdolecek #include <sys/types.h>
81e23cd1a7Sjdolecek #include <sys/syslog.h>
82e23cd1a7Sjdolecek 
83a2a38285Sad #include <sys/bus.h>
84e23cd1a7Sjdolecek 
85e23cd1a7Sjdolecek #include <dev/ppbus/ppbus_1284.h>
86e23cd1a7Sjdolecek #include <dev/ppbus/ppbus_base.h>
87e23cd1a7Sjdolecek #include <dev/ppbus/ppbus_io.h>
88e23cd1a7Sjdolecek #include <dev/ppbus/ppbus_msq.h>
89e23cd1a7Sjdolecek #include <dev/ppbus/ppbus_var.h>
90e23cd1a7Sjdolecek 
91e23cd1a7Sjdolecek #include <dev/ppbus/lptvar.h>
92e23cd1a7Sjdolecek #include <dev/ppbus/lptreg.h>
93e23cd1a7Sjdolecek #include <dev/ppbus/lptio.h>
94e23cd1a7Sjdolecek 
95e23cd1a7Sjdolecek /* Autoconf functions */
96b849cd90Scegger static int lpt_probe(device_t, cfdata_t, void *);
970ba85a6fSdyoung static void lpt_attach(device_t, device_t, void *);
980ba85a6fSdyoung static int lpt_detach(device_t, int);
99e23cd1a7Sjdolecek 
100e23cd1a7Sjdolecek /* Autoconf structure */
101b849cd90Scegger CFATTACH_DECL_NEW(lpt_ppbus, sizeof(struct lpt_softc), lpt_probe, lpt_attach,
102e23cd1a7Sjdolecek 	lpt_detach, NULL);
103e23cd1a7Sjdolecek 
104e23cd1a7Sjdolecek extern struct cfdriver lpt_cd;
105e23cd1a7Sjdolecek 
106e23cd1a7Sjdolecek dev_type_open(lptopen);
107e23cd1a7Sjdolecek dev_type_close(lptclose);
108e23cd1a7Sjdolecek dev_type_read(lptread);
109e23cd1a7Sjdolecek dev_type_write(lptwrite);
110e23cd1a7Sjdolecek dev_type_ioctl(lptioctl);
111e23cd1a7Sjdolecek 
112e23cd1a7Sjdolecek const struct cdevsw lpt_cdevsw = {
113a68f9396Sdholland         .d_open = lptopen,
114a68f9396Sdholland 	.d_close = lptclose,
115a68f9396Sdholland 	.d_read = lptread,
116a68f9396Sdholland 	.d_write = lptwrite,
117a68f9396Sdholland 	.d_ioctl = lptioctl,
118a68f9396Sdholland 	.d_stop = nostop,
119a68f9396Sdholland 	.d_tty = notty,
120a68f9396Sdholland 	.d_poll = nopoll,
121a68f9396Sdholland 	.d_mmap = nommap,
122a68f9396Sdholland 	.d_kqfilter = nokqfilter,
123f9228f42Sdholland 	.d_discard = nodiscard,
124a68f9396Sdholland 	.d_flag = D_OTHER
125e23cd1a7Sjdolecek };
126e23cd1a7Sjdolecek 
127e23cd1a7Sjdolecek 
128e23cd1a7Sjdolecek /* Function prototypes */
1290ba85a6fSdyoung static int lpt_detect(device_t);
130e23cd1a7Sjdolecek static int lpt_request_ppbus(struct lpt_softc *, int);
131e23cd1a7Sjdolecek static int lpt_release_ppbus(struct lpt_softc *, int);
1320ba85a6fSdyoung static int lpt_logstatus(const device_t, const unsigned char);
133e23cd1a7Sjdolecek 
134e23cd1a7Sjdolecek /*
135e23cd1a7Sjdolecek  * lpt_probe()
136e23cd1a7Sjdolecek  */
137e23cd1a7Sjdolecek static int
lpt_probe(device_t parent,cfdata_t match,void * aux)138b849cd90Scegger lpt_probe(device_t parent, cfdata_t match, void *aux)
139e23cd1a7Sjdolecek {
140e23cd1a7Sjdolecek 	/* Test ppbus's capability */
141e23cd1a7Sjdolecek 	return lpt_detect(parent);
142e23cd1a7Sjdolecek }
143e23cd1a7Sjdolecek 
144e23cd1a7Sjdolecek static void
lpt_attach(device_t parent,device_t self,void * aux)1450ba85a6fSdyoung lpt_attach(device_t parent, device_t self, void *aux)
146e23cd1a7Sjdolecek {
147ec03de0cSthorpej 	struct lpt_softc * sc = device_private(self);
148e23cd1a7Sjdolecek 	struct ppbus_device_softc * ppbdev = &(sc->ppbus_dev);
149e23cd1a7Sjdolecek 	struct ppbus_attach_args * args = aux;
150e23cd1a7Sjdolecek 	char buf[64];
151e23cd1a7Sjdolecek 	int error;
152e23cd1a7Sjdolecek 
153376411d2Scegger 	ppbdev->sc_dev = self;
154376411d2Scegger 
155e23cd1a7Sjdolecek 	error = lpt_request_ppbus(sc, 0);
156e23cd1a7Sjdolecek 	if(error) {
157e23cd1a7Sjdolecek 		printf("%s(%s): error (%d) requesting bus(%s). Device not "
1581b044f41Scegger 			"properly attached.\n", __func__, device_xname(self),
1591b044f41Scegger 			error, device_xname(parent));
160e23cd1a7Sjdolecek 		return;
161e23cd1a7Sjdolecek 	}
162e23cd1a7Sjdolecek 
163e23cd1a7Sjdolecek 	/* Record capabilities */
164e23cd1a7Sjdolecek 	ppbdev->capabilities = args->capabilities;
165e23cd1a7Sjdolecek 
166e23cd1a7Sjdolecek 	/* Allocate memory buffers */
167e23cd1a7Sjdolecek 	if(ppbdev->capabilities & PPBUS_HAS_DMA) {
168e23cd1a7Sjdolecek 		if(ppbus_dma_malloc(parent, &(sc->sc_inbuf),
169e23cd1a7Sjdolecek 			&(sc->sc_in_baddr), BUFSIZE)) {
170e23cd1a7Sjdolecek 
171e23cd1a7Sjdolecek 			printf(" : cannot allocate input DMA buffer. Device "
172e23cd1a7Sjdolecek 				"not properly attached!\n");
173e23cd1a7Sjdolecek 			return;
174e23cd1a7Sjdolecek 		}
175e23cd1a7Sjdolecek 		if(ppbus_dma_malloc(parent, &(sc->sc_outbuf),
176e23cd1a7Sjdolecek 			&(sc->sc_out_baddr), BUFSIZE)) {
177e23cd1a7Sjdolecek 
178e23cd1a7Sjdolecek 			ppbus_dma_free(parent, &(sc->sc_inbuf),
179e23cd1a7Sjdolecek 				&(sc->sc_in_baddr), BUFSIZE);
180e23cd1a7Sjdolecek 			printf(" : cannot allocate output DMA buffer. Device "
181e23cd1a7Sjdolecek 				"not properly attached!\n");
182e23cd1a7Sjdolecek 			return;
183e23cd1a7Sjdolecek 		}
1848081dfaaSjdolecek 	} else {
185e23cd1a7Sjdolecek 		sc->sc_inbuf = malloc(BUFSIZE, M_DEVBUF, M_WAITOK);
186e23cd1a7Sjdolecek 		sc->sc_outbuf = malloc(BUFSIZE, M_DEVBUF, M_WAITOK);
187e23cd1a7Sjdolecek 	}
188e23cd1a7Sjdolecek 
189e23cd1a7Sjdolecek 	/* Print out mode */
190e23cd1a7Sjdolecek         ppbdev->ctx.mode = ppbus_get_mode(parent);
1919a5d3f28Schristos 	snprintb(buf, sizeof(buf),
1929a5d3f28Schristos 	    "\20\1COMPATIBLE\2NIBBLE\3PS2\4EPP\5ECP\6FAST_CENTR",
1939a5d3f28Schristos 	    ppbdev->ctx.mode);
194ce4629f6Sjdolecek 	printf(": port mode = %s\n", buf);
195e23cd1a7Sjdolecek 
1969300beccSjdolecek 	/* Initialize the device on open by default */
1979300beccSjdolecek 	sc->sc_flags = LPT_PRIME;
1989300beccSjdolecek 
199e23cd1a7Sjdolecek 	lpt_release_ppbus(sc, 0);
200e23cd1a7Sjdolecek }
201e23cd1a7Sjdolecek 
202e23cd1a7Sjdolecek static int
lpt_detach(device_t self,int flags)2030ba85a6fSdyoung lpt_detach(device_t self, int flags)
204e23cd1a7Sjdolecek {
205ec03de0cSthorpej 	struct lpt_softc * lpt = device_private(self);
206e23cd1a7Sjdolecek 	struct ppbus_device_softc * ppbdev = (struct ppbus_device_softc *) lpt;
207e23cd1a7Sjdolecek 	int err;
208e23cd1a7Sjdolecek 
209e23cd1a7Sjdolecek 	if(lpt->sc_state & HAVEBUS) {
210e23cd1a7Sjdolecek 		err = lpt_release_ppbus(lpt, 0);
211e23cd1a7Sjdolecek 		if(err) {
212e23cd1a7Sjdolecek 			printf("%s error (%d) while releasing bus",
2131b044f41Scegger 				device_xname(self), err);
214e23cd1a7Sjdolecek 			if(flags & DETACH_FORCE) {
215e23cd1a7Sjdolecek 				printf(", continuing (DETACH_FORCE)!\n");
216e23cd1a7Sjdolecek 			}
217e23cd1a7Sjdolecek 			else {
218e23cd1a7Sjdolecek 				printf(", terminating!\n");
219e23cd1a7Sjdolecek 				return 0;
220e23cd1a7Sjdolecek 			}
221e23cd1a7Sjdolecek 		}
222e23cd1a7Sjdolecek 		lpt->sc_state &= ~HAVEBUS;
223e23cd1a7Sjdolecek 	}
224e23cd1a7Sjdolecek 
225e23cd1a7Sjdolecek 	ppbdev->ctx.valid = 0;
226e23cd1a7Sjdolecek 
227e23cd1a7Sjdolecek 	/* Free memory buffers */
228e23cd1a7Sjdolecek 	if(ppbdev->capabilities & PPBUS_HAS_DMA) {
2295887891aSthorpej 		ppbus_dma_free(device_parent(self), &(lpt->sc_inbuf),
230e23cd1a7Sjdolecek 			&(lpt->sc_in_baddr), BUFSIZE);
2315887891aSthorpej 		ppbus_dma_free(device_parent(self), &(lpt->sc_outbuf),
232e23cd1a7Sjdolecek 			&(lpt->sc_out_baddr), BUFSIZE);
2338081dfaaSjdolecek 	} else {
234e23cd1a7Sjdolecek 		free(lpt->sc_inbuf, M_DEVBUF);
235e23cd1a7Sjdolecek 		free(lpt->sc_outbuf, M_DEVBUF);
236e23cd1a7Sjdolecek 	}
237e23cd1a7Sjdolecek 
238e23cd1a7Sjdolecek 	return 1;
239e23cd1a7Sjdolecek }
240e23cd1a7Sjdolecek 
241e23cd1a7Sjdolecek /* Grab bus for lpt device */
242e23cd1a7Sjdolecek static int
lpt_request_ppbus(struct lpt_softc * lpt,int how)243e23cd1a7Sjdolecek lpt_request_ppbus(struct lpt_softc * lpt, int how)
244e23cd1a7Sjdolecek {
245b849cd90Scegger 	device_t dev = lpt->ppbus_dev.sc_dev;
246e23cd1a7Sjdolecek 	int error;
247e23cd1a7Sjdolecek 
2485887891aSthorpej 	error = ppbus_request_bus(device_parent(dev), dev, how, (hz));
249e23cd1a7Sjdolecek 	if (!(error)) {
250e23cd1a7Sjdolecek 		lpt->sc_state |= HAVEBUS;
251e23cd1a7Sjdolecek 	}
252e23cd1a7Sjdolecek 	else {
2539bf083c3Sjdolecek 		LPT_DPRINTF(("%s(%s): error %d requesting bus.\n", __func__,
2541b044f41Scegger 			device_xname(dev), error));
255e23cd1a7Sjdolecek 	}
256e23cd1a7Sjdolecek 
257e23cd1a7Sjdolecek 	return error;
258e23cd1a7Sjdolecek }
259e23cd1a7Sjdolecek 
260e23cd1a7Sjdolecek /* Release ppbus to enable other devices to use it. */
261e23cd1a7Sjdolecek static int
lpt_release_ppbus(struct lpt_softc * lpt,int how)262e23cd1a7Sjdolecek lpt_release_ppbus(struct lpt_softc * lpt, int how)
263e23cd1a7Sjdolecek {
264b849cd90Scegger 	device_t dev = lpt->ppbus_dev.sc_dev;
265e23cd1a7Sjdolecek 	int error;
266e23cd1a7Sjdolecek 
267e23cd1a7Sjdolecek 	if(lpt->sc_state & HAVEBUS) {
2685887891aSthorpej 		error = ppbus_release_bus(device_parent(dev), dev, how, (hz));
269e23cd1a7Sjdolecek 		if(!(error))
270e23cd1a7Sjdolecek 			lpt->sc_state &= ~HAVEBUS;
2712ae1aec4Scegger 		else {
272e23cd1a7Sjdolecek 			LPT_DPRINTF(("%s(%s): error releasing bus.\n", __func__,
2731b044f41Scegger 				device_xname(dev)));
274e23cd1a7Sjdolecek 		}
2752ae1aec4Scegger 	}
276e23cd1a7Sjdolecek 	else {
277e23cd1a7Sjdolecek 		error = EINVAL;
278e23cd1a7Sjdolecek 		LPT_DPRINTF(("%s(%s): device does not own bus.\n", __func__,
2791b044f41Scegger 			device_xname(dev)));
280e23cd1a7Sjdolecek 	}
281e23cd1a7Sjdolecek 
282e23cd1a7Sjdolecek 	return error;
283e23cd1a7Sjdolecek }
284e23cd1a7Sjdolecek 
285e23cd1a7Sjdolecek 
286e23cd1a7Sjdolecek /*
287e23cd1a7Sjdolecek  * Probe simplified by replacing multiple loops with a hardcoded
288e23cd1a7Sjdolecek  * test pattern - 1999/02/08 des@freebsd.org
289e23cd1a7Sjdolecek  *
290e23cd1a7Sjdolecek  * New lpt port probe Geoff Rehmet - Rhodes University - 14/2/94
291e23cd1a7Sjdolecek  * Based partially on Rod Grimes' printer probe
292e23cd1a7Sjdolecek  *
293e23cd1a7Sjdolecek  * Logic:
294e23cd1a7Sjdolecek  *	1) If no port address was given, use the bios detected ports
295e23cd1a7Sjdolecek  *	   and autodetect what ports the printers are on.
296e23cd1a7Sjdolecek  *	2) Otherwise, probe the data port at the address given,
297e23cd1a7Sjdolecek  *	   using the method in Rod Grimes' port probe.
298e23cd1a7Sjdolecek  *	   (Much code ripped off directly from Rod's probe.)
299e23cd1a7Sjdolecek  *
300e23cd1a7Sjdolecek  * Comments from Rod's probe:
301e23cd1a7Sjdolecek  * Logic:
302e23cd1a7Sjdolecek  *	1) You should be able to write to and read back the same value
303e23cd1a7Sjdolecek  *	   to the data port.  Do an alternating zeros, alternating ones,
304e23cd1a7Sjdolecek  *	   walking zero, and walking one test to check for stuck bits.
305e23cd1a7Sjdolecek  *
306e23cd1a7Sjdolecek  *	2) You should be able to write to and read back the same value
307e23cd1a7Sjdolecek  *	   to the control port lower 5 bits, the upper 3 bits are reserved
308*c46d1cc1Smsaitoh  *	   per the IBM PC technical reference manuals and different boards
309e23cd1a7Sjdolecek  *	   do different things with them.  Do an alternating zeros, alternating
310e23cd1a7Sjdolecek  *	   ones, walking zero, and walking one test to check for stuck bits.
311e23cd1a7Sjdolecek  *
312e23cd1a7Sjdolecek  *	   Some printers drag the strobe line down when the are powered off
313e23cd1a7Sjdolecek  * 	   so this bit has been masked out of the control port test.
314e23cd1a7Sjdolecek  *
315e23cd1a7Sjdolecek  *	   XXX Some printers may not like a fast pulse on init or strobe, I
316e23cd1a7Sjdolecek  *	   don't know at this point, if that becomes a problem these bits
317e23cd1a7Sjdolecek  *	   should be turned off in the mask byte for the control port test.
318e23cd1a7Sjdolecek  *
319e23cd1a7Sjdolecek  *	   We are finally left with a mask of 0x14, due to some printers
320e23cd1a7Sjdolecek  *	   being adamant about holding other bits high ........
321e23cd1a7Sjdolecek  *
322e23cd1a7Sjdolecek  *	   Before probing the control port, we write a 0 to the data port -
323e23cd1a7Sjdolecek  *	   If not, some printers chuck out garbage when the strobe line
324e23cd1a7Sjdolecek  *	   gets toggled.
325e23cd1a7Sjdolecek  *
326e23cd1a7Sjdolecek  *	3) Set the data and control ports to a value of 0
327e23cd1a7Sjdolecek  *
328e23cd1a7Sjdolecek  *	This probe routine has been tested on Epson Lx-800, HP LJ3P,
329e23cd1a7Sjdolecek  *	Epson FX-1170 and C.Itoh 8510RM
330e23cd1a7Sjdolecek  *	printers.
331e23cd1a7Sjdolecek  *	Quick exit on fail added.
332e23cd1a7Sjdolecek  */
333e23cd1a7Sjdolecek static int
lpt_detect(device_t dev)3340ba85a6fSdyoung lpt_detect(device_t dev)
335e23cd1a7Sjdolecek {
336f06d010fSjdolecek 	static const u_char testbyte[18] = {
337e23cd1a7Sjdolecek 		0x55,			/* alternating zeros */
338e23cd1a7Sjdolecek 		0xaa,			/* alternating ones */
339e23cd1a7Sjdolecek 		0xfe, 0xfd, 0xfb, 0xf7,
340e23cd1a7Sjdolecek 		0xef, 0xdf, 0xbf, 0x7f,	/* walking zero */
341e23cd1a7Sjdolecek 		0x01, 0x02, 0x04, 0x08,
342e23cd1a7Sjdolecek 		0x10, 0x20, 0x40, 0x80	/* walking one */
343e23cd1a7Sjdolecek 	};
344e23cd1a7Sjdolecek 	int i, status;
345e23cd1a7Sjdolecek 	u_char dtr, ctr, str, var;
346e23cd1a7Sjdolecek 
347e23cd1a7Sjdolecek 	/* Save register contents */
348e23cd1a7Sjdolecek 	dtr = ppbus_rdtr(dev);
349e23cd1a7Sjdolecek 	ctr = ppbus_rctr(dev);
350e23cd1a7Sjdolecek 	str = ppbus_rstr(dev);
351e23cd1a7Sjdolecek 
352e23cd1a7Sjdolecek 	status = 1;				/* assume success */
353e23cd1a7Sjdolecek 
354e23cd1a7Sjdolecek 	/* Test data port */
355e23cd1a7Sjdolecek 	for(i = 0; i < 18; i++) {
356e23cd1a7Sjdolecek 		ppbus_wdtr(dev, testbyte[i]);
357e23cd1a7Sjdolecek 		if((var = ppbus_rdtr(dev)) != testbyte[i]) {
358e23cd1a7Sjdolecek 			status = 0;
359e23cd1a7Sjdolecek 			LPT_DPRINTF(("%s(%s): byte value %x cannot be written "
360e23cd1a7Sjdolecek 				"and read from data port (got %x instead).\n",
3611b044f41Scegger 				__func__, device_xname(dev), testbyte[i], var));
362e23cd1a7Sjdolecek 			goto end;
363e23cd1a7Sjdolecek 		}
364e23cd1a7Sjdolecek 	}
365e23cd1a7Sjdolecek 
366e23cd1a7Sjdolecek 	/* Test control port */
367e23cd1a7Sjdolecek 	ppbus_wdtr(dev, 0);
368e23cd1a7Sjdolecek 	for(i = 0; i < 18; i++) {
369e23cd1a7Sjdolecek 		ppbus_wctr(dev, (testbyte[i] & 0x14));
370e23cd1a7Sjdolecek 		if(((var = ppbus_rctr(dev)) & 0x14) != (testbyte[i] & 0x14)) {
371e23cd1a7Sjdolecek 			status = 0;
372e23cd1a7Sjdolecek 			LPT_DPRINTF(("%s(%s): byte value %x (unmasked value "
373e23cd1a7Sjdolecek 				"%x) cannot be written and read from control "
374e23cd1a7Sjdolecek 				"port (got %x instead).\n", __func__,
3751b044f41Scegger 				device_xname(dev), (testbyte[i] & 0x14),
376e23cd1a7Sjdolecek 				testbyte[i], (var & 0x14)));
377e23cd1a7Sjdolecek 			break;
378e23cd1a7Sjdolecek 		}
379e23cd1a7Sjdolecek 	}
380e23cd1a7Sjdolecek 
381e23cd1a7Sjdolecek end:
382e23cd1a7Sjdolecek 	/* Restore contents of registers */
383e23cd1a7Sjdolecek 	ppbus_wdtr(dev, dtr);
384e23cd1a7Sjdolecek 	ppbus_wctr(dev, ctr);
385e23cd1a7Sjdolecek 	ppbus_wstr(dev, str);
386e23cd1a7Sjdolecek 
387e23cd1a7Sjdolecek 	return status;
388e23cd1a7Sjdolecek }
389e23cd1a7Sjdolecek 
390e23cd1a7Sjdolecek /* Log status of status register for printer port */
391e23cd1a7Sjdolecek static int
lpt_logstatus(const device_t dev,const unsigned char status)3920ba85a6fSdyoung lpt_logstatus(const device_t dev, const unsigned char status)
393e23cd1a7Sjdolecek {
394e23cd1a7Sjdolecek 	int err;
395e23cd1a7Sjdolecek 
396e23cd1a7Sjdolecek 	err = EIO;
397e23cd1a7Sjdolecek 	if(!(status & LPS_SEL)) {
3981b044f41Scegger 		log(LOG_ERR, "%s: offline.", device_xname(dev));
399e23cd1a7Sjdolecek 	}
400e23cd1a7Sjdolecek 	else if(!(status & LPS_NBSY)) {
4011b044f41Scegger 		log(LOG_ERR, "%s: busy.", device_xname(dev));
402e23cd1a7Sjdolecek 	}
403e23cd1a7Sjdolecek 	else if(status & LPS_OUT) {
4041b044f41Scegger 		log(LOG_ERR, "%s: out of paper.", device_xname(dev));
405e23cd1a7Sjdolecek 		err = EAGAIN;
406e23cd1a7Sjdolecek 	}
407e23cd1a7Sjdolecek 	else if(!(status & LPS_NERR)) {
4081b044f41Scegger 		log(LOG_ERR, "%s: output error.", device_xname(dev));
409e23cd1a7Sjdolecek 	}
410e23cd1a7Sjdolecek 	else {
4111b044f41Scegger 		log(LOG_ERR, "%s: no error indication.", device_xname(dev));
412e23cd1a7Sjdolecek 		err = 0;
413e23cd1a7Sjdolecek 	}
414e23cd1a7Sjdolecek 
415e23cd1a7Sjdolecek 	return err;
416e23cd1a7Sjdolecek }
417e23cd1a7Sjdolecek 
418e23cd1a7Sjdolecek /*
419e23cd1a7Sjdolecek  * lptopen -- reset the printer, then wait until it's selected and not busy.
420e23cd1a7Sjdolecek  */
421e23cd1a7Sjdolecek int
lptopen(dev_t dev_id,int flags,int fmt,struct lwp * l)422e868ae92Srpaulo lptopen(dev_t dev_id, int flags, int fmt, struct lwp *l)
423e23cd1a7Sjdolecek {
4249300beccSjdolecek 	int trys, err;
425e23cd1a7Sjdolecek 	u_int8_t status;
4260ba85a6fSdyoung 	device_t dev;
427e23cd1a7Sjdolecek 	struct lpt_softc * lpt;
428e23cd1a7Sjdolecek 	struct ppbus_device_softc * ppbus_dev;
4290ba85a6fSdyoung 	device_t ppbus;
430e23cd1a7Sjdolecek 
431e23cd1a7Sjdolecek 	dev = device_lookup(&lpt_cd, LPTUNIT(dev_id));
432e23cd1a7Sjdolecek 	if(!dev) {
433e23cd1a7Sjdolecek 		LPT_DPRINTF(("%s(): device not configured.\n", __func__));
434e23cd1a7Sjdolecek 		return ENXIO;
435e23cd1a7Sjdolecek 	}
436e23cd1a7Sjdolecek 
437b849cd90Scegger 	lpt = device_private(dev);
438e23cd1a7Sjdolecek 
4395887891aSthorpej 	ppbus = device_parent(dev);
440e23cd1a7Sjdolecek 	ppbus_dev = &(lpt->ppbus_dev);
441e23cd1a7Sjdolecek 
442e23cd1a7Sjdolecek 	/* Request the ppbus */
443e23cd1a7Sjdolecek 	err = lpt_request_ppbus(lpt, PPBUS_WAIT|PPBUS_INTR);
444e23cd1a7Sjdolecek 	if(err) {
445e23cd1a7Sjdolecek 		LPT_DPRINTF(("%s(%s): error (%d) while requesting bus.\n",
4461b044f41Scegger 			__func__, device_xname(dev), err));
447e23cd1a7Sjdolecek 		return (err);
448e23cd1a7Sjdolecek 	}
449e23cd1a7Sjdolecek 
450e23cd1a7Sjdolecek 	/* Update bus mode */
451e23cd1a7Sjdolecek 	ppbus_dev->ctx.mode = ppbus_get_mode(ppbus);
452e23cd1a7Sjdolecek 
453e23cd1a7Sjdolecek 	/* init printer */
4549300beccSjdolecek 	if ((lpt->sc_flags & LPT_PRIME) && !LPTCTL(dev_id)) {
455e23cd1a7Sjdolecek 		LPT_VPRINTF(("%s(%s): initializing printer.\n", __func__,
4561b044f41Scegger 			device_xname(dev)));
457e23cd1a7Sjdolecek 		lpt->sc_state |= LPTINIT;
458e23cd1a7Sjdolecek 		ppbus_wctr(ppbus, LPC_SEL | LPC_NINIT);
459e23cd1a7Sjdolecek 
460e23cd1a7Sjdolecek 		/* wait till ready (printer running diagnostics) */
461e23cd1a7Sjdolecek 		for(trys = 0, status = ppbus_rstr(ppbus); (status & RDY_MASK)
462e23cd1a7Sjdolecek 			!= LP_READY; trys += LPT_STEP, status =
463e23cd1a7Sjdolecek 			ppbus_rstr(ppbus)) {
464e23cd1a7Sjdolecek 
465e23cd1a7Sjdolecek 			/* Time up waiting for the printer */
466e23cd1a7Sjdolecek 			if(trys >= LPT_TIMEOUT)
467e23cd1a7Sjdolecek 				break;
468e23cd1a7Sjdolecek 			/* wait LPT_STEP ticks, give up if we get a signal */
469e23cd1a7Sjdolecek 			else {
47053524e44Schristos 				err = tsleep((void *)lpt, LPPRI|PCATCH,
471e23cd1a7Sjdolecek 					"lptinit", LPT_STEP);
472e23cd1a7Sjdolecek 				if((err) && (err != EWOULDBLOCK)) {
473e23cd1a7Sjdolecek 					lpt->sc_state &= ~LPTINIT;
474e23cd1a7Sjdolecek 					LPT_DPRINTF(("%s(%s): interrupted "
475e23cd1a7Sjdolecek 					"during initialization.\n", __func__,
4761b044f41Scegger 					device_xname(dev)));
477e23cd1a7Sjdolecek 					lpt_release_ppbus(lpt, PPBUS_WAIT);
478e23cd1a7Sjdolecek 					return (err);
479e23cd1a7Sjdolecek 				}
480e23cd1a7Sjdolecek 			}
481e23cd1a7Sjdolecek 		}
482e23cd1a7Sjdolecek 
483e23cd1a7Sjdolecek 		lpt->sc_state &= ~LPTINIT;
484e23cd1a7Sjdolecek 		if(trys >= LPT_TIMEOUT) {
485e23cd1a7Sjdolecek 			LPT_DPRINTF(("%s(%s): timed out while initializing "
486e23cd1a7Sjdolecek 				"printer. [status %x]\n", __func__,
4871b044f41Scegger 				device_xname(dev), status));
488e23cd1a7Sjdolecek 			err = lpt_logstatus(dev, status);
489e23cd1a7Sjdolecek 			lpt_release_ppbus(lpt, PPBUS_WAIT);
490e23cd1a7Sjdolecek 			return (err);
491e23cd1a7Sjdolecek 		}
4922ae1aec4Scegger 		else {
493e23cd1a7Sjdolecek 			LPT_VPRINTF(("%s(%s): printer ready.\n", __func__,
4941b044f41Scegger 				device_xname(dev)));
495e23cd1a7Sjdolecek 		}
4962ae1aec4Scegger 	}
497e23cd1a7Sjdolecek 
4989300beccSjdolecek 	/* Set autolinefeed if requested */
4999300beccSjdolecek 	if (lpt->sc_flags & LPT_AUTOLF)
5009300beccSjdolecek 		ppbus_wctr(ppbus, LPC_AUTOL);
5019300beccSjdolecek 	else
5029300beccSjdolecek 		ppbus_wctr(ppbus, 0);
503e23cd1a7Sjdolecek 
5049300beccSjdolecek 	/* ready now */
505e23cd1a7Sjdolecek 	lpt->sc_state |= OPEN;
506e23cd1a7Sjdolecek 
507e23cd1a7Sjdolecek 	return 0;
508e23cd1a7Sjdolecek }
509e23cd1a7Sjdolecek 
510e23cd1a7Sjdolecek /*
511e23cd1a7Sjdolecek  * lptclose -- close the device, free the local line buffer.
512e23cd1a7Sjdolecek  *
513e23cd1a7Sjdolecek  * Check for interrupted write call added.
514e23cd1a7Sjdolecek  */
515e23cd1a7Sjdolecek int
lptclose(dev_t dev_id,int flags,int fmt,struct lwp * l)516e868ae92Srpaulo lptclose(dev_t dev_id, int flags, int fmt, struct lwp *l)
517e23cd1a7Sjdolecek {
5180ba85a6fSdyoung 	device_t dev = device_lookup(&lpt_cd, LPTUNIT(dev_id));
5190ba85a6fSdyoung 	struct lpt_softc *sc = device_private(dev);
520e23cd1a7Sjdolecek 	int err;
521e23cd1a7Sjdolecek 
522e23cd1a7Sjdolecek 	err = lpt_release_ppbus(sc, PPBUS_WAIT|PPBUS_INTR);
523e23cd1a7Sjdolecek 	if(err) {
524e23cd1a7Sjdolecek 		LPT_DPRINTF(("%s(%s): error (%d) while releasing ppbus.\n",
5251b044f41Scegger 			__func__, device_xname(dev), err));
526e23cd1a7Sjdolecek 	}
527e23cd1a7Sjdolecek 
528e23cd1a7Sjdolecek 	sc->sc_state = 0;
529e23cd1a7Sjdolecek 
530e23cd1a7Sjdolecek 	return err;
531e23cd1a7Sjdolecek }
532e23cd1a7Sjdolecek 
533e23cd1a7Sjdolecek /*
534e23cd1a7Sjdolecek  * lptread --retrieve printer status in IEEE1284 NIBBLE mode
535e23cd1a7Sjdolecek  */
536e23cd1a7Sjdolecek int
lptread(dev_t dev_id,struct uio * uio,int ioflag)537e23cd1a7Sjdolecek lptread(dev_t dev_id, struct uio *uio, int ioflag)
538e23cd1a7Sjdolecek {
53958859f40Sbjh21 	size_t len = 0;
540e23cd1a7Sjdolecek 	int error = 0;
5410ba85a6fSdyoung 	device_t dev = device_lookup(&lpt_cd, LPTUNIT(dev_id));
5420ba85a6fSdyoung 	struct lpt_softc *sc = device_private(dev);
543e23cd1a7Sjdolecek 
544e23cd1a7Sjdolecek 	if(!(sc->sc_state & HAVEBUS)) {
545e23cd1a7Sjdolecek 		LPT_DPRINTF(("%s(%s): attempt to read using device which does "
5461b044f41Scegger 			"not own the bus(%s).\n", __func__, device_xname(dev),
5471b044f41Scegger 			device_xname(device_parent(dev))));
548e23cd1a7Sjdolecek 		return (ENODEV);
549e23cd1a7Sjdolecek 	}
550e23cd1a7Sjdolecek 
551e23cd1a7Sjdolecek 	sc->sc_state &= ~INTERRUPTED;
552e23cd1a7Sjdolecek 	while (uio->uio_resid) {
5535887891aSthorpej 		error = ppbus_read(device_parent(dev), sc->sc_outbuf,
554d1579b2dSriastradh 			uimin(BUFSIZE, uio->uio_resid), 0, &len);
555e23cd1a7Sjdolecek 
556e23cd1a7Sjdolecek 		/* If error or no more data, stop */
557e23cd1a7Sjdolecek 		if (error) {
558e23cd1a7Sjdolecek 			if (error != EWOULDBLOCK)
559e23cd1a7Sjdolecek 				sc->sc_state |= INTERRUPTED;
560e23cd1a7Sjdolecek 			break;
561e23cd1a7Sjdolecek 		}
5628081dfaaSjdolecek 		if (len == 0)
563e23cd1a7Sjdolecek 			break;
564e23cd1a7Sjdolecek 
5658081dfaaSjdolecek 		if ((error = uiomove(sc->sc_outbuf, len, uio)))
566e23cd1a7Sjdolecek 			break;
567e23cd1a7Sjdolecek 	}
568e23cd1a7Sjdolecek 
569e23cd1a7Sjdolecek 	return error;
570e23cd1a7Sjdolecek }
571e23cd1a7Sjdolecek 
572e23cd1a7Sjdolecek /*
573e23cd1a7Sjdolecek  * lptwrite --copy a line from user space to a local buffer, then call
574e23cd1a7Sjdolecek  * putc to get the chars moved to the output queue.
575e23cd1a7Sjdolecek  *
576e23cd1a7Sjdolecek  * Flagging of interrupted write added.
577e23cd1a7Sjdolecek  */
578e23cd1a7Sjdolecek int
lptwrite(dev_t dev_id,struct uio * uio,int ioflag)579e23cd1a7Sjdolecek lptwrite(dev_t dev_id, struct uio * uio, int ioflag)
580e23cd1a7Sjdolecek {
5819898c64cSjdolecek 	int error=0;
5829898c64cSjdolecek 	size_t n, cnt;
5830ba85a6fSdyoung 	device_t dev = device_lookup(&lpt_cd, LPTUNIT(dev_id));
5840ba85a6fSdyoung 	struct lpt_softc * sc = device_private(dev);
585e23cd1a7Sjdolecek 
586e23cd1a7Sjdolecek 	/* Check state and flags */
587e23cd1a7Sjdolecek 	if(!(sc->sc_state & HAVEBUS)) {
588e23cd1a7Sjdolecek 		LPT_DPRINTF(("%s(%s): attempt to write using device which does "
5891b044f41Scegger 			"not own the bus(%s).\n", __func__, device_xname(dev),
5901b044f41Scegger 			device_xname(device_parent(dev))));
591e23cd1a7Sjdolecek 		return EINVAL;
592e23cd1a7Sjdolecek 	}
593e23cd1a7Sjdolecek 
5947b523083Scegger 	LPT_VPRINTF(("%s(%s): writing %zu bytes\n", __func__,
5951b044f41Scegger 	    device_xname(dev), uio->uio_resid));
5969898c64cSjdolecek 
597e23cd1a7Sjdolecek 	/* Write the data */
598e23cd1a7Sjdolecek 	sc->sc_state &= ~INTERRUPTED;
599e23cd1a7Sjdolecek 	while (uio->uio_resid) {
6009898c64cSjdolecek 		n = MIN(BUFSIZE, uio->uio_resid);
6019898c64cSjdolecek 		error = uiomove(sc->sc_inbuf, n, uio);
6029898c64cSjdolecek 		if (error)
603e23cd1a7Sjdolecek 			break;
604e23cd1a7Sjdolecek 
6055887891aSthorpej 		error = ppbus_write(device_parent(dev), sc->sc_inbuf, n, ioflag,
606e23cd1a7Sjdolecek 			&cnt);
6079898c64cSjdolecek 		if (error) {
6089898c64cSjdolecek 			if (error != EWOULDBLOCK)
609e23cd1a7Sjdolecek 				sc->sc_state |= INTERRUPTED;
610e23cd1a7Sjdolecek 			break;
611e23cd1a7Sjdolecek 		}
612e23cd1a7Sjdolecek 	}
613e23cd1a7Sjdolecek 
6149898c64cSjdolecek 	LPT_VPRINTF(("%s(%s): transfer finished, error %d.\n", __func__,
6151b044f41Scegger 	    device_xname(dev), error));
616e23cd1a7Sjdolecek 
6179898c64cSjdolecek 	return error;
618e23cd1a7Sjdolecek }
619e23cd1a7Sjdolecek 
620e23cd1a7Sjdolecek /* Printer ioctl */
621e23cd1a7Sjdolecek int
lptioctl(dev_t dev_id,u_long cmd,void * data,int flags,struct lwp * l)62253524e44Schristos lptioctl(dev_t dev_id, u_long cmd, void *data, int flags, struct lwp *l)
623e23cd1a7Sjdolecek {
6240ba85a6fSdyoung 	device_t dev = device_lookup(&lpt_cd, LPTUNIT(dev_id));
6250ba85a6fSdyoung 	struct lpt_softc *sc = device_private(dev);
626f44dc9edSjdolecek 	int val, fl;
627e23cd1a7Sjdolecek 	int error=0;
628e23cd1a7Sjdolecek 
629e23cd1a7Sjdolecek 	if(!(sc->sc_state & HAVEBUS)) {
630e23cd1a7Sjdolecek 		LPT_DPRINTF(("%s(%s): attempt to perform ioctl on device which "
6311b044f41Scegger 			"does not own the bus(%s).\n", __func__, device_xname(dev),
6321b044f41Scegger 			device_xname(device_parent(dev))));
633e23cd1a7Sjdolecek 		return EBUSY;
634e23cd1a7Sjdolecek 	}
635e23cd1a7Sjdolecek 
636e23cd1a7Sjdolecek 	switch (cmd) {
637f44dc9edSjdolecek 	case LPTGMODE:
6385887891aSthorpej         	switch (ppbus_get_mode(device_parent(dev))) {
639f44dc9edSjdolecek 		case PPBUS_COMPATIBLE:
640f44dc9edSjdolecek 			val = mode_standard;
641f44dc9edSjdolecek 			break;
642f44dc9edSjdolecek 		case PPBUS_NIBBLE:
643f44dc9edSjdolecek 			val = mode_nibble;
644f44dc9edSjdolecek 			break;
645f44dc9edSjdolecek 		case PPBUS_PS2:
646f44dc9edSjdolecek 			val = mode_ps2;
647f44dc9edSjdolecek 			break;
648f44dc9edSjdolecek 		case PPBUS_FAST:
649f44dc9edSjdolecek 			val = mode_fast;
650f44dc9edSjdolecek 			break;
651f44dc9edSjdolecek 		case PPBUS_EPP:
652f44dc9edSjdolecek 			val = mode_epp;
653f44dc9edSjdolecek 			break;
654f44dc9edSjdolecek 		case PPBUS_ECP:
655f44dc9edSjdolecek 			val = mode_ecp;
656f44dc9edSjdolecek 			break;
657f44dc9edSjdolecek 		default:
658f44dc9edSjdolecek 			error = EINVAL;
659f44dc9edSjdolecek 			val = mode_unknown;
660f44dc9edSjdolecek 			break;
661f44dc9edSjdolecek 		}
662f44dc9edSjdolecek 		*(int *)data = val;
663f44dc9edSjdolecek 		break;
664f44dc9edSjdolecek 
665f44dc9edSjdolecek 	case LPTSMODE:
666f44dc9edSjdolecek         	switch (*(int *)data) {
667f44dc9edSjdolecek 		case mode_standard:
668f44dc9edSjdolecek 			val = PPBUS_COMPATIBLE;
669f44dc9edSjdolecek 			break;
670f44dc9edSjdolecek 		case mode_nibble:
671f44dc9edSjdolecek 			val = PPBUS_NIBBLE;
672f44dc9edSjdolecek 			break;
673f44dc9edSjdolecek 		case mode_ps2:
674f44dc9edSjdolecek 			val = PPBUS_PS2;
675f44dc9edSjdolecek 			break;
676f44dc9edSjdolecek 		case mode_fast:
677f44dc9edSjdolecek 			val = PPBUS_FAST;
678f44dc9edSjdolecek 			break;
679f44dc9edSjdolecek 		case mode_epp:
680f44dc9edSjdolecek 			val = PPBUS_EPP;
681f44dc9edSjdolecek 			break;
682f44dc9edSjdolecek 		case mode_ecp:
683f44dc9edSjdolecek 			val = PPBUS_ECP;
684f44dc9edSjdolecek 			break;
685f44dc9edSjdolecek 		default:
686f44dc9edSjdolecek 			error = EINVAL;
687f44dc9edSjdolecek 			val = mode_unknown;
6888081dfaaSjdolecek 			break;
689e23cd1a7Sjdolecek 		}
6908081dfaaSjdolecek 
691f44dc9edSjdolecek 		if (!error)
6925887891aSthorpej 			error = ppbus_set_mode(device_parent(dev), val, 0);
693f44dc9edSjdolecek 
694e23cd1a7Sjdolecek 		break;
695e23cd1a7Sjdolecek 
696f44dc9edSjdolecek 	case LPTGFLAGS:
697f44dc9edSjdolecek 		fl = 0;
698e23cd1a7Sjdolecek 
6999300beccSjdolecek 		/* DMA */
7005887891aSthorpej 		error = ppbus_read_ivar(device_parent(dev), PPBUS_IVAR_DMA, &val);
70189ba3070Sjdolecek 		if (error)
702e23cd1a7Sjdolecek 			break;
703f44dc9edSjdolecek 		if (val)
704f44dc9edSjdolecek 			fl |= LPT_DMA;
705e23cd1a7Sjdolecek 
7069300beccSjdolecek 		/* IEEE mode negotiation */
7075887891aSthorpej 		error = ppbus_read_ivar(device_parent(dev), PPBUS_IVAR_IEEE, &val);
70889ba3070Sjdolecek 		if (error)
709e23cd1a7Sjdolecek 			break;
710f44dc9edSjdolecek 		if (val)
711f44dc9edSjdolecek 			fl |= LPT_IEEE;
712e23cd1a7Sjdolecek 
7139300beccSjdolecek 		/* interrupts */
7145887891aSthorpej 		error = ppbus_read_ivar(device_parent(dev), PPBUS_IVAR_INTR, &val);
7159300beccSjdolecek 		if (error)
7169300beccSjdolecek 			break;
7179300beccSjdolecek 		if (val)
7189300beccSjdolecek 			fl |= LPT_INTR;
7199300beccSjdolecek 
7209300beccSjdolecek 		/* lpt-only flags */
7219300beccSjdolecek 		fl |= sc->sc_flags;
7229300beccSjdolecek 
723f44dc9edSjdolecek 		*(int *)data = fl;
724e23cd1a7Sjdolecek 		break;
725f44dc9edSjdolecek 
726f44dc9edSjdolecek 	case LPTSFLAGS:
727f44dc9edSjdolecek 		fl = *(int *)data;
728f44dc9edSjdolecek 
7299300beccSjdolecek 		/* DMA */
730f44dc9edSjdolecek 		val = (fl & LPT_DMA);
7315887891aSthorpej 		error = ppbus_write_ivar(device_parent(dev), PPBUS_IVAR_DMA, &val);
732f44dc9edSjdolecek 		if (error)
733e23cd1a7Sjdolecek 			break;
734f44dc9edSjdolecek 
7359300beccSjdolecek 		/* IEEE mode negotiation */
736f44dc9edSjdolecek 		val = (fl & LPT_IEEE);
7375887891aSthorpej 		error = ppbus_write_ivar(device_parent(dev), PPBUS_IVAR_IEEE, &val);
7389300beccSjdolecek 		if (error)
7399300beccSjdolecek 			break;
7409300beccSjdolecek 
7419300beccSjdolecek 		/* interrupts */
7429300beccSjdolecek 		val = (fl & LPT_INTR);
7435887891aSthorpej 		error = ppbus_write_ivar(device_parent(dev), PPBUS_IVAR_INTR, &val);
7449300beccSjdolecek 		if (error)
7459300beccSjdolecek 			break;
7469300beccSjdolecek 
7479300beccSjdolecek 		/* lpt-only flags */
7489300beccSjdolecek 		sc->sc_flags = fl & (LPT_PRIME|LPT_AUTOLF);
7499300beccSjdolecek 
750e23cd1a7Sjdolecek 		break;
75189ba3070Sjdolecek 
752e23cd1a7Sjdolecek 	default:
753e23cd1a7Sjdolecek 		error = EINVAL;
754f44dc9edSjdolecek 		break;
755e23cd1a7Sjdolecek 	}
756e23cd1a7Sjdolecek 
757e23cd1a7Sjdolecek 	return error;
758e23cd1a7Sjdolecek }
759e23cd1a7Sjdolecek 
760