xref: /netbsd-src/sys/arch/x68k/dev/par.c (revision 6b664a713479c31d4f17b38b42182a5d5fa21802)
1*6b664a71Sisaki /*	$NetBSD: par.c,v 1.45 2024/01/07 07:58:33 isaki Exp $	*/
2d21a1f43Sad 
3d21a1f43Sad /*
4d21a1f43Sad  * Copyright (c) 1982, 1990 The Regents of the University of California.
5d21a1f43Sad  * All rights reserved.
6d21a1f43Sad  *
7d21a1f43Sad  * Redistribution and use in source and binary forms, with or without
8d21a1f43Sad  * modification, are permitted provided that the following conditions
9d21a1f43Sad  * are met:
10d21a1f43Sad  * 1. Redistributions of source code must retain the above copyright
11d21a1f43Sad  *    notice, this list of conditions and the following disclaimer.
12d21a1f43Sad  * 2. Redistributions in binary form must reproduce the above copyright
13d21a1f43Sad  *    notice, this list of conditions and the following disclaimer in the
14d21a1f43Sad  *    documentation and/or other materials provided with the distribution.
15d21a1f43Sad  * 3. Neither the name of the University nor the names of its contributors
16d21a1f43Sad  *    may be used to endorse or promote products derived from this software
17d21a1f43Sad  *    without specific prior written permission.
18d21a1f43Sad  *
19d21a1f43Sad  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20d21a1f43Sad  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21d21a1f43Sad  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22d21a1f43Sad  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23d21a1f43Sad  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24d21a1f43Sad  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25d21a1f43Sad  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26d21a1f43Sad  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27d21a1f43Sad  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28d21a1f43Sad  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29d21a1f43Sad  * SUCH DAMAGE.
30d21a1f43Sad  *
31d21a1f43Sad  *	@(#)ppi.c	7.3 (Berkeley) 12/16/90
32d21a1f43Sad  */
33d21a1f43Sad 
34d21a1f43Sad /*
35d21a1f43Sad  * parallel port interface
36d21a1f43Sad  */
37d21a1f43Sad 
38d21a1f43Sad #include <sys/cdefs.h>
39*6b664a71Sisaki __KERNEL_RCSID(0, "$NetBSD: par.c,v 1.45 2024/01/07 07:58:33 isaki Exp $");
40d21a1f43Sad 
41d21a1f43Sad #include <sys/param.h>
42d21a1f43Sad #include <sys/errno.h>
43d21a1f43Sad #include <sys/uio.h>
44d21a1f43Sad #include <sys/device.h>
45d21a1f43Sad #include <sys/malloc.h>
46d21a1f43Sad #include <sys/file.h>
47d21a1f43Sad #include <sys/systm.h>
48d21a1f43Sad #include <sys/callout.h>
49d21a1f43Sad #include <sys/proc.h>
50d21a1f43Sad #include <sys/conf.h>
51d21a1f43Sad #include <sys/kernel.h>
52d21a1f43Sad 
53d21a1f43Sad #include <machine/bus.h>
54d21a1f43Sad #include <machine/cpu.h>
55d21a1f43Sad #include <machine/parioctl.h>
56d21a1f43Sad 
57d21a1f43Sad #include <arch/x68k/dev/intiovar.h>
58d21a1f43Sad 
59623e78a6Stsutsui #include "ioconf.h"
60623e78a6Stsutsui 
61d21a1f43Sad struct	par_softc {
622830b81aSisaki 	device_t		sc_dev;
63d21a1f43Sad 
64d21a1f43Sad 	bus_space_tag_t		sc_bst;
65d21a1f43Sad 	bus_space_handle_t	sc_bsh;
66d21a1f43Sad 	int			sc_flags;
67d21a1f43Sad 	struct parparam		sc_param;
68d21a1f43Sad #define sc_burst	sc_param.burst
69d21a1f43Sad #define sc_timo		sc_param.timo
70d21a1f43Sad #define sc_delay	sc_param.delay
71d21a1f43Sad 	struct callout		sc_timo_ch;
72d21a1f43Sad 	struct callout		sc_start_ch;
73d21a1f43Sad } ;
74d21a1f43Sad 
75d21a1f43Sad /* par registers */
76d21a1f43Sad #define PAR_DATA	1
77d21a1f43Sad #define PAR_STROBE	3
78d21a1f43Sad 
79d21a1f43Sad /* sc_flags values */
80d21a1f43Sad #define	PARF_ALIVE	0x01
81d21a1f43Sad #define	PARF_OPEN	0x02
82d21a1f43Sad #define PARF_UIO	0x04
83d21a1f43Sad #define PARF_TIMO	0x08
84d21a1f43Sad #define PARF_DELAY	0x10
85d21a1f43Sad #define PARF_OREAD	0x40	/* no support */
86d21a1f43Sad #define PARF_OWRITE	0x80
87d21a1f43Sad 
88d21a1f43Sad 
89d21a1f43Sad void partimo(void *);
90d21a1f43Sad void parstart(void *);
91d21a1f43Sad void parintr(void *);
92d21a1f43Sad int parrw(dev_t, struct uio *);
93d21a1f43Sad int parhztoms(int);
94d21a1f43Sad int parmstohz(int);
95d21a1f43Sad int parsendch(struct par_softc *, u_char);
96d21a1f43Sad int parsend(struct par_softc *, u_char *, int);
97d21a1f43Sad 
9861a446b0Sisaki static struct callout intr_callout;
99d21a1f43Sad 
100d21a1f43Sad #define UNIT(x)		minor(x)
101d21a1f43Sad 
102d21a1f43Sad #ifdef DEBUG
103d21a1f43Sad #define PDB_FOLLOW	0x01
104d21a1f43Sad #define PDB_IO		0x02
105d21a1f43Sad #define PDB_INTERRUPT   0x04
106d21a1f43Sad #define PDB_NOCHECK	0x80
107d21a1f43Sad #ifdef PARDEBUG
108d21a1f43Sad int	pardebug = PDB_FOLLOW | PDB_IO | PDB_INTERRUPT;
109d21a1f43Sad #else
110d21a1f43Sad int	pardebug = 0;
111d21a1f43Sad #endif
112d21a1f43Sad #endif
113d21a1f43Sad 
1142830b81aSisaki int parmatch(device_t, cfdata_t, void *);
1152830b81aSisaki void parattach(device_t, device_t, void *);
116d21a1f43Sad 
1172830b81aSisaki CFATTACH_DECL_NEW(par, sizeof(struct par_softc),
118d21a1f43Sad     parmatch, parattach, NULL, NULL);
119d21a1f43Sad 
120d21a1f43Sad static int par_attached;
121d21a1f43Sad 
122d21a1f43Sad dev_type_open(paropen);
123d21a1f43Sad dev_type_close(parclose);
124d21a1f43Sad dev_type_write(parwrite);
125d21a1f43Sad dev_type_ioctl(parioctl);
126d21a1f43Sad 
127d21a1f43Sad const struct cdevsw par_cdevsw = {
128a68f9396Sdholland 	.d_open = paropen,
129a68f9396Sdholland 	.d_close = parclose,
130a68f9396Sdholland 	.d_read = noread,
131a68f9396Sdholland 	.d_write = parwrite,
132a68f9396Sdholland 	.d_ioctl = parioctl,
133a68f9396Sdholland 	.d_stop = nostop,
134a68f9396Sdholland 	.d_tty = notty,
135a68f9396Sdholland 	.d_poll = nopoll,
136a68f9396Sdholland 	.d_mmap = nommap,
137a68f9396Sdholland 	.d_kqfilter = nokqfilter,
138f9228f42Sdholland 	.d_discard = nodiscard,
139a68f9396Sdholland 	.d_flag = 0
140d21a1f43Sad };
141d21a1f43Sad 
142d21a1f43Sad int
parmatch(device_t parent,cfdata_t cf,void * aux)1430c3cdf00Stsutsui parmatch(device_t parent, cfdata_t cf, void *aux)
144d21a1f43Sad {
145d21a1f43Sad 	struct intio_attach_args *ia = aux;
146d21a1f43Sad 
147d21a1f43Sad 	/* X680x0 has only one parallel port */
148d21a1f43Sad 	if (strcmp(ia->ia_name, "par") || par_attached)
149d21a1f43Sad 		return 0;
150d21a1f43Sad 
151d21a1f43Sad 	if (ia->ia_addr == INTIOCF_ADDR_DEFAULT)
152d21a1f43Sad 		ia->ia_addr = 0xe8c000;
153d21a1f43Sad 	ia->ia_size = 0x2000;
1540c3cdf00Stsutsui 	if (intio_map_allocate_region(parent, ia, INTIO_MAP_TESTONLY))
155d21a1f43Sad 		return 0;
156d21a1f43Sad 	if (ia->ia_intr == INTIOCF_INTR_DEFAULT)
157d21a1f43Sad 		ia->ia_intr = 99;
158d21a1f43Sad #if DIAGNOSTIC
159d21a1f43Sad 	if (ia->ia_intr != 99)
160d21a1f43Sad 		return 0;
161d21a1f43Sad #endif
162d21a1f43Sad 
163d21a1f43Sad 	return 1;
164d21a1f43Sad }
165d21a1f43Sad 
166d21a1f43Sad void
parattach(device_t parent,device_t self,void * aux)1670c3cdf00Stsutsui parattach(device_t parent, device_t self, void *aux)
168d21a1f43Sad {
1690c3cdf00Stsutsui 	struct par_softc *sc = device_private(self);
170d21a1f43Sad 	struct intio_attach_args *ia = aux;
1714c3cdcf0Schristos 	int r __diagused;
172d21a1f43Sad 
173d21a1f43Sad 	par_attached = 1;
174d21a1f43Sad 
1750c3cdf00Stsutsui 	sc->sc_dev = self;
176d21a1f43Sad 	sc->sc_flags = PARF_ALIVE;
1772830b81aSisaki 	aprint_normal(": parallel port (write only, interrupt)\n");
178d21a1f43Sad 	ia->ia_size = 0x2000;
1790c3cdf00Stsutsui 	r = intio_map_allocate_region(parent, ia, INTIO_MAP_ALLOCATE);
180d21a1f43Sad #ifdef DIAGNOSTIC
181d21a1f43Sad 	if (r)
182d21a1f43Sad 		panic("IO map for PAR corruption??");
183d21a1f43Sad #endif
184d21a1f43Sad 	sc->sc_bst = ia->ia_bst;
185d21a1f43Sad 	r = bus_space_map(sc->sc_bst,
186d21a1f43Sad 			   ia->ia_addr, ia->ia_size,
187d21a1f43Sad 			   BUS_SPACE_MAP_SHIFTED,
188d21a1f43Sad 			   &sc->sc_bsh);
189d21a1f43Sad #ifdef DIAGNOSTIC
190d21a1f43Sad 	if (r)
191d21a1f43Sad 		panic("Cannot map IO space for PAR.");
192d21a1f43Sad #endif
193d21a1f43Sad 
194d21a1f43Sad 	intio_set_sicilian_intr(intio_get_sicilian_intr() &
195d21a1f43Sad 				~SICILIAN_INTR_PAR);
196d21a1f43Sad 
197d21a1f43Sad 	intio_intr_establish(ia->ia_intr, "par",
198d21a1f43Sad 			     (intio_intr_handler_t)parintr, (void *)1);
199d21a1f43Sad 
200d21a1f43Sad 	callout_init(&sc->sc_timo_ch, 0);
201d21a1f43Sad 	callout_init(&sc->sc_start_ch, 0);
20261a446b0Sisaki 	callout_init(&intr_callout, 0);
203d21a1f43Sad }
204d21a1f43Sad 
205d21a1f43Sad int
paropen(dev_t dev,int flags,int mode,struct lwp * l)206d21a1f43Sad paropen(dev_t dev, int flags, int mode, struct lwp *l)
207d21a1f43Sad {
208d21a1f43Sad 	int unit = UNIT(dev);
209d21a1f43Sad 	struct par_softc *sc;
210d21a1f43Sad 
21140b72ee8Stsutsui 	sc = device_lookup_private(&par_cd, unit);
212d21a1f43Sad 	if (sc == NULL || !(sc->sc_flags & PARF_ALIVE))
213d21a1f43Sad 		return(ENXIO);
214d21a1f43Sad 	if (sc->sc_flags & PARF_OPEN)
215d21a1f43Sad 		return(EBUSY);
216d21a1f43Sad 	/* X680x0 can't read */
217d21a1f43Sad 	if ((flags & FREAD) == FREAD)
218d21a1f43Sad 		return (EINVAL);
219d21a1f43Sad 
220d21a1f43Sad 	sc->sc_flags |= PARF_OPEN;
221d21a1f43Sad 
222d21a1f43Sad 	sc->sc_flags |= PARF_OWRITE;
223d21a1f43Sad 
224d21a1f43Sad 	sc->sc_burst = PAR_BURST;
225d21a1f43Sad 	sc->sc_timo = parmstohz(PAR_TIMO);
226d21a1f43Sad 	sc->sc_delay = parmstohz(PAR_DELAY);
227d21a1f43Sad 
228d21a1f43Sad 	return(0);
229d21a1f43Sad }
230d21a1f43Sad 
231d21a1f43Sad int
parclose(dev_t dev,int flags,int mode,struct lwp * l)232d21a1f43Sad parclose(dev_t dev, int flags, int mode, struct lwp *l)
233d21a1f43Sad {
234d21a1f43Sad 	int unit = UNIT(dev);
235d21a1f43Sad 	int s;
23640b72ee8Stsutsui 	struct par_softc *sc = device_lookup_private(&par_cd, unit);
237d21a1f43Sad 
238d21a1f43Sad 	sc->sc_flags &= ~(PARF_OPEN|PARF_OWRITE);
239d21a1f43Sad 
240d21a1f43Sad 	/* don't allow interrupts any longer */
241d21a1f43Sad 	s = spl1();
242d21a1f43Sad 	intio_set_sicilian_intr(intio_get_sicilian_intr() &
243d21a1f43Sad 				~SICILIAN_INTR_PAR);
244d21a1f43Sad 	splx(s);
245d21a1f43Sad 
246d21a1f43Sad 	return (0);
247d21a1f43Sad }
248d21a1f43Sad 
249d21a1f43Sad void
parstart(void * arg)250d21a1f43Sad parstart(void *arg)
251d21a1f43Sad {
252d21a1f43Sad 	struct par_softc *sc = arg;
253d21a1f43Sad #ifdef DEBUG
254d21a1f43Sad 	if (pardebug & PDB_FOLLOW)
2552830b81aSisaki 		printf("parstart(%x)\n", device_unit(sc->sc_dev));
256d21a1f43Sad #endif
257d21a1f43Sad 	sc->sc_flags &= ~PARF_DELAY;
258d21a1f43Sad 	wakeup(sc);
259d21a1f43Sad }
260d21a1f43Sad 
261d21a1f43Sad void
partimo(void * arg)262d21a1f43Sad partimo(void *arg)
263d21a1f43Sad {
264d21a1f43Sad 	struct par_softc *sc = arg;
265d21a1f43Sad #ifdef DEBUG
266d21a1f43Sad 	if (pardebug & PDB_FOLLOW)
2672830b81aSisaki 		printf("partimo(%x)\n", device_unit(sc->sc_dev));
268d21a1f43Sad #endif
269d21a1f43Sad 	sc->sc_flags &= ~(PARF_UIO|PARF_TIMO);
270d21a1f43Sad 	wakeup(sc);
271d21a1f43Sad }
272d21a1f43Sad 
273d21a1f43Sad int
parwrite(dev_t dev,struct uio * uio,int flag)274d21a1f43Sad parwrite(dev_t dev, struct uio *uio, int flag)
275d21a1f43Sad {
276d21a1f43Sad 
277d21a1f43Sad #ifdef DEBUG
278d21a1f43Sad 	if (pardebug & PDB_FOLLOW)
2798cf7d2d7Sisaki 		printf("parwrite(%x, %p)\n", UNIT(dev), uio);
280d21a1f43Sad #endif
281d21a1f43Sad 	return (parrw(dev, uio));
282d21a1f43Sad }
283d21a1f43Sad 
284d21a1f43Sad int
parrw(dev_t dev,struct uio * uio)285d21a1f43Sad parrw(dev_t dev, struct uio *uio)
286d21a1f43Sad {
287d21a1f43Sad 	int unit = UNIT(dev);
28840b72ee8Stsutsui 	struct par_softc *sc = device_lookup_private(&par_cd, unit);
289d21a1f43Sad 	int len=0xdeadbeef;	/* XXX: shutup gcc */
290d21a1f43Sad 	int s, cnt=0;
291d21a1f43Sad 	char *cp;
292d21a1f43Sad 	int error = 0;
293d21a1f43Sad 	int buflen;
294d21a1f43Sad 	char *buf;
295d21a1f43Sad 
296d21a1f43Sad 	if (!!(sc->sc_flags & PARF_OREAD) ^ (uio->uio_rw == UIO_READ))
297d21a1f43Sad 		return EINVAL;
298d21a1f43Sad 
299d21a1f43Sad 	if (uio->uio_resid == 0)
300d21a1f43Sad 		return(0);
301d21a1f43Sad 
302d1579b2dSriastradh 	buflen = uimin(sc->sc_burst, uio->uio_resid);
303d21a1f43Sad 	buf = (char *)malloc(buflen, M_DEVBUF, M_WAITOK);
304d21a1f43Sad 	sc->sc_flags |= PARF_UIO;
305d21a1f43Sad 	if (sc->sc_timo > 0) {
306d21a1f43Sad 		sc->sc_flags |= PARF_TIMO;
307d21a1f43Sad 		callout_reset(&sc->sc_timo_ch, sc->sc_timo, partimo, sc);
308d21a1f43Sad 	}
309d21a1f43Sad 	while (uio->uio_resid > 0) {
310d1579b2dSriastradh 		len = uimin(buflen, uio->uio_resid);
311d21a1f43Sad 		cp = buf;
312d21a1f43Sad 		if (uio->uio_rw == UIO_WRITE) {
313d21a1f43Sad 			error = uiomove(cp, len, uio);
314d21a1f43Sad 			if (error)
315d21a1f43Sad 				break;
316d21a1f43Sad 		}
317d21a1f43Sad 	      again:
318d21a1f43Sad 		s = splsoftclock();
319d21a1f43Sad 		/*
320d21a1f43Sad 		 * Check if we timed out during sleep or uiomove
321d21a1f43Sad 		 */
322d21a1f43Sad 		if ((sc->sc_flags & PARF_UIO) == 0) {
323d21a1f43Sad #ifdef DEBUG
324d21a1f43Sad 			if (pardebug & PDB_IO)
325d21a1f43Sad 				printf("parrw: uiomove/sleep timo, flags %x\n",
326d21a1f43Sad 				       sc->sc_flags);
327d21a1f43Sad #endif
328d21a1f43Sad 			if (sc->sc_flags & PARF_TIMO) {
329d21a1f43Sad 				callout_stop(&sc->sc_timo_ch);
330d21a1f43Sad 				sc->sc_flags &= ~PARF_TIMO;
331d21a1f43Sad 			}
332d21a1f43Sad 			splx(s);
333d21a1f43Sad 			break;
334d21a1f43Sad 		}
335d21a1f43Sad 		splx(s);
336d21a1f43Sad 		/*
337d21a1f43Sad 		 * Perform the operation
338d21a1f43Sad 		 */
339d21a1f43Sad 		cnt = parsend(sc, cp, len);
340d21a1f43Sad 		if (cnt < 0) {
341d21a1f43Sad 			error = -cnt;
342d21a1f43Sad 			break;
343d21a1f43Sad 		}
344d21a1f43Sad 
345d21a1f43Sad 		s = splsoftclock();
346d21a1f43Sad 		/*
347d21a1f43Sad 		 * Operation timeout (or non-blocking), quit now.
348d21a1f43Sad 		 */
349d21a1f43Sad 		if ((sc->sc_flags & PARF_UIO) == 0) {
350d21a1f43Sad #ifdef DEBUG
351d21a1f43Sad 			if (pardebug & PDB_IO)
352d21a1f43Sad 				printf("parrw: timeout/done\n");
353d21a1f43Sad #endif
354d21a1f43Sad 			splx(s);
355d21a1f43Sad 			break;
356d21a1f43Sad 		}
357d21a1f43Sad 		/*
358d21a1f43Sad 		 * Implement inter-read delay
359d21a1f43Sad 		 */
360d21a1f43Sad 		if (sc->sc_delay > 0) {
361d21a1f43Sad 			sc->sc_flags |= PARF_DELAY;
362d21a1f43Sad 			callout_reset(&sc->sc_start_ch, sc->sc_delay,
363d21a1f43Sad 			    parstart, sc);
364d21a1f43Sad 			error = tsleep(sc, PCATCH|(PZERO-1), "par-cdelay", 0);
365d21a1f43Sad 			if (error) {
366d21a1f43Sad 				splx(s);
367d21a1f43Sad 				break;
368d21a1f43Sad 			}
369d21a1f43Sad 		}
370d21a1f43Sad 		splx(s);
371d21a1f43Sad 		/*
372d21a1f43Sad 		 * Must not call uiomove again til we've used all data
373d21a1f43Sad 		 * that we already grabbed.
374d21a1f43Sad 		 */
375d21a1f43Sad 		if (uio->uio_rw == UIO_WRITE && cnt != len) {
376d21a1f43Sad 			cp += cnt;
377d21a1f43Sad 			len -= cnt;
378d21a1f43Sad 			cnt = 0;
379d21a1f43Sad 			goto again;
380d21a1f43Sad 		}
381d21a1f43Sad 	}
382d21a1f43Sad 	s = splsoftclock();
383d21a1f43Sad 	if (sc->sc_flags & PARF_TIMO) {
384d21a1f43Sad 		callout_stop(&sc->sc_timo_ch);
385d21a1f43Sad 		sc->sc_flags &= ~PARF_TIMO;
386d21a1f43Sad 	}
387d21a1f43Sad 	if (sc->sc_flags & PARF_DELAY)	{
388d21a1f43Sad 		callout_stop(&sc->sc_start_ch);
389d21a1f43Sad 		sc->sc_flags &= ~PARF_DELAY;
390d21a1f43Sad 	}
391d21a1f43Sad 	splx(s);
392d21a1f43Sad 	/*
393d21a1f43Sad 	 * Adjust for those chars that we uiomove'ed but never wrote
394d21a1f43Sad 	 */
395d21a1f43Sad 	/*
396d21a1f43Sad 	 * XXXjdolecek: this len usage is wrong, this will be incorrect
397d21a1f43Sad 	 * if the transfer size is longer than sc_burst
398d21a1f43Sad 	 */
399d21a1f43Sad 	if (uio->uio_rw == UIO_WRITE && cnt != len) {
400d21a1f43Sad 		uio->uio_resid += (len - cnt);
401d21a1f43Sad #ifdef DEBUG
402d21a1f43Sad 			if (pardebug & PDB_IO)
403d21a1f43Sad 				printf("parrw: short write, adjust by %d\n",
404d21a1f43Sad 				       len-cnt);
405d21a1f43Sad #endif
406d21a1f43Sad 	}
407d21a1f43Sad 	free(buf, M_DEVBUF);
408d21a1f43Sad #ifdef DEBUG
409d21a1f43Sad 	if (pardebug & (PDB_FOLLOW|PDB_IO))
410d21a1f43Sad 		printf("parrw: return %d, resid %d\n", error, uio->uio_resid);
411d21a1f43Sad #endif
412d21a1f43Sad 	return (error);
413d21a1f43Sad }
414d21a1f43Sad 
415d21a1f43Sad int
parioctl(dev_t dev,u_long cmd,void * data,int flag,struct lwp * l)416d21a1f43Sad parioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
417d21a1f43Sad {
41840b72ee8Stsutsui 	struct par_softc *sc = device_lookup_private(&par_cd, UNIT(dev));
419d21a1f43Sad 	struct parparam *pp, *upp;
420d21a1f43Sad 	int error = 0;
421d21a1f43Sad 
422d21a1f43Sad 	switch (cmd) {
423d21a1f43Sad 	case PARIOCGPARAM:
424d21a1f43Sad 		pp = &sc->sc_param;
425d21a1f43Sad 		upp = (struct parparam *)data;
426d21a1f43Sad 		upp->burst = pp->burst;
427d21a1f43Sad 		upp->timo = parhztoms(pp->timo);
428d21a1f43Sad 		upp->delay = parhztoms(pp->delay);
429d21a1f43Sad 		break;
430d21a1f43Sad 
431d21a1f43Sad 	case PARIOCSPARAM:
432d21a1f43Sad 		pp = &sc->sc_param;
433d21a1f43Sad 		upp = (struct parparam *)data;
434d21a1f43Sad 		if (upp->burst < PAR_BURST_MIN || upp->burst > PAR_BURST_MAX ||
435d21a1f43Sad 		    upp->delay < PAR_DELAY_MIN || upp->delay > PAR_DELAY_MAX)
436d21a1f43Sad 			return(EINVAL);
437d21a1f43Sad 		pp->burst = upp->burst;
438d21a1f43Sad 		pp->timo = parmstohz(upp->timo);
439d21a1f43Sad 		pp->delay = parmstohz(upp->delay);
440d21a1f43Sad 		break;
441d21a1f43Sad 
442d21a1f43Sad 	default:
443d21a1f43Sad 		return(EINVAL);
444d21a1f43Sad 	}
445d21a1f43Sad 	return (error);
446d21a1f43Sad }
447d21a1f43Sad 
448d21a1f43Sad int
parhztoms(int h)449d21a1f43Sad parhztoms(int h)
450d21a1f43Sad {
451d21a1f43Sad 	int m = h;
452d21a1f43Sad 
453d21a1f43Sad 	if (m > 0)
454d21a1f43Sad 		m = m * 1000 / hz;
455d21a1f43Sad 	return(m);
456d21a1f43Sad }
457d21a1f43Sad 
458d21a1f43Sad int
parmstohz(int m)459d21a1f43Sad parmstohz(int m)
460d21a1f43Sad {
461d21a1f43Sad 	int h = m;
462d21a1f43Sad 
463d21a1f43Sad 	if (h > 0) {
464d21a1f43Sad 		h = h * hz / 1000;
465d21a1f43Sad 		if (h == 0)
466d21a1f43Sad 			h = 1000 / hz;
467d21a1f43Sad 	}
468d21a1f43Sad 	return(h);
469d21a1f43Sad }
470d21a1f43Sad 
471d21a1f43Sad /* stuff below here if for interrupt driven output of data thru
472d21a1f43Sad    the parallel port. */
473d21a1f43Sad 
474d21a1f43Sad int partimeout_pending;
475d21a1f43Sad int parsend_pending;
476d21a1f43Sad 
477d21a1f43Sad void
parintr(void * arg)478d21a1f43Sad parintr(void *arg)
479d21a1f43Sad {
480d21a1f43Sad 	int s, mask;
481d21a1f43Sad 
482d21a1f43Sad 	mask = (int)arg;
483d21a1f43Sad 	s = splclock();
484d21a1f43Sad 
485d21a1f43Sad 	intio_set_sicilian_intr(intio_get_sicilian_intr() &
486d21a1f43Sad 				~SICILIAN_INTR_PAR);
487d21a1f43Sad 
488d21a1f43Sad #ifdef DEBUG
489d21a1f43Sad 	if (pardebug & PDB_INTERRUPT)
490d21a1f43Sad 		printf("parintr %d(%s)\n", mask, mask ? "FLG" : "tout");
491d21a1f43Sad #endif
492d21a1f43Sad 	/* if invoked from timeout handler, mask will be 0,
493d21a1f43Sad 	 * if from interrupt, it will contain the cia-icr mask,
494d21a1f43Sad 	 * which is != 0
495d21a1f43Sad 	 */
496d21a1f43Sad 	if (mask) {
497d21a1f43Sad 		if (partimeout_pending)
498d21a1f43Sad 			callout_stop(&intr_callout);
499d21a1f43Sad 		if (parsend_pending)
500d21a1f43Sad 			parsend_pending = 0;
501d21a1f43Sad 	}
502d21a1f43Sad 
503d21a1f43Sad 	/* either way, there won't be a timeout pending any longer */
504d21a1f43Sad 	partimeout_pending = 0;
505d21a1f43Sad 
506d21a1f43Sad 	wakeup(parintr);
507d21a1f43Sad 	splx(s);
508d21a1f43Sad }
509d21a1f43Sad 
510d21a1f43Sad int
parsendch(struct par_softc * sc,u_char ch)511d21a1f43Sad parsendch(struct par_softc *sc, u_char ch)
512d21a1f43Sad {
513d21a1f43Sad 	int error = 0;
514d21a1f43Sad 	int s;
515d21a1f43Sad 
516d21a1f43Sad 	/* if either offline, busy or out of paper, wait for that
517d21a1f43Sad 	   condition to clear */
518d21a1f43Sad 	s = spl1();
519d21a1f43Sad 	while (!error
520d21a1f43Sad 	       && (parsend_pending
521d21a1f43Sad 		   || !(intio_get_sicilian_intr() & SICILIAN_STAT_PAR)))
522d21a1f43Sad 	{
523d21a1f43Sad 		/* wait a second, and try again */
524d21a1f43Sad 		callout_reset(&intr_callout, hz, parintr, 0);
525d21a1f43Sad 		partimeout_pending = 1;
526d21a1f43Sad 		/* this is essentially a flipflop to have us wait for the
527d21a1f43Sad 		   first character being transmitted when trying to transmit
528d21a1f43Sad 		   the second, etc. */
529d21a1f43Sad 		parsend_pending = 0;
530d21a1f43Sad 		/* it's quite important that a parallel putc can be
531d21a1f43Sad 		   interrupted, given the possibility to lock a printer
532d21a1f43Sad 		   in an offline condition.. */
533d21a1f43Sad 		if ((error = tsleep(parintr, PCATCH|(PZERO-1), "parsendch", 0))) {
534d21a1f43Sad #ifdef DEBUG
535d21a1f43Sad 			if (pardebug & PDB_INTERRUPT)
536d21a1f43Sad 				printf("parsendch interrupted, error = %d\n", error);
537d21a1f43Sad #endif
538d21a1f43Sad 			if (partimeout_pending)
539d21a1f43Sad 				callout_stop(&intr_callout);
540d21a1f43Sad 
541d21a1f43Sad 			partimeout_pending = 0;
542d21a1f43Sad 		}
543d21a1f43Sad 	}
544d21a1f43Sad 
545d21a1f43Sad 	if (!error) {
546d21a1f43Sad #ifdef DEBUG
547d21a1f43Sad 		if (pardebug & PDB_INTERRUPT)
548d21a1f43Sad 			printf("#%d", ch);
549d21a1f43Sad #endif
550d21a1f43Sad 		bus_space_write_1(sc->sc_bst, sc->sc_bsh, PAR_DATA, ch);
551d21a1f43Sad 		DELAY(1);	/* (DELAY(1) == 1us) > 0.5us */
552d21a1f43Sad 		bus_space_write_1(sc->sc_bst, sc->sc_bsh, PAR_STROBE, 0);
553d21a1f43Sad 		intio_set_sicilian_intr(intio_get_sicilian_intr() |
554d21a1f43Sad 					 SICILIAN_INTR_PAR);
555d21a1f43Sad 		DELAY(1);
556d21a1f43Sad 		bus_space_write_1(sc->sc_bst, sc->sc_bsh, PAR_STROBE, 1);
557d21a1f43Sad 		parsend_pending = 1;
558d21a1f43Sad 	}
559d21a1f43Sad 
560d21a1f43Sad 	splx(s);
561d21a1f43Sad 
562d21a1f43Sad 	return error;
563d21a1f43Sad }
564d21a1f43Sad 
565d21a1f43Sad 
566d21a1f43Sad int
parsend(struct par_softc * sc,u_char * buf,int len)567d21a1f43Sad parsend(struct par_softc *sc, u_char *buf, int len)
568d21a1f43Sad {
569d21a1f43Sad 	int err, orig_len = len;
570d21a1f43Sad 
571d21a1f43Sad 	for (; len; len--, buf++)
572d21a1f43Sad 		if ((err = parsendch(sc, *buf)))
573d21a1f43Sad 			return err < 0 ? -EINTR : -err;
574d21a1f43Sad 
575d21a1f43Sad 	/* either all or nothing.. */
576d21a1f43Sad 	return orig_len;
577d21a1f43Sad }
578