xref: /netbsd-src/sys/arch/x68k/dev/par.c (revision 81b108b45f75f89f1e3ffad9fb6f074e771c0935)
1 /*	$NetBSD: par.c,v 1.2 1996/05/21 15:32:42 oki Exp $	*/
2 
3 /*
4  * Copyright (c) 1982, 1990 The Regents of the University of California.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *	This product includes software developed by the University of
18  *	California, Berkeley and its contributors.
19  * 4. Neither the name of the University nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  *
35  *	@(#)ppi.c	7.3 (Berkeley) 12/16/90
36  */
37 
38 /*
39  * parallel port interface
40  */
41 
42 #include "par.h"
43 #if NPAR > 0
44 
45 #if NPAR > 1
46 #undef NPAR
47 #define NPAR 1
48 #endif
49 
50 #include <sys/param.h>
51 #include <sys/errno.h>
52 #include <sys/uio.h>
53 #include <sys/device.h>
54 #include <sys/malloc.h>
55 #include <sys/file.h>
56 #include <sys/systm.h>
57 #include <sys/proc.h>
58 
59 #include <x68k/x68k/iodevice.h>
60 #include <x68k/dev/parioctl.h>
61 
62 void partimo();
63 void parstart();
64 void parintr();
65 
66 struct	par_softc {
67 	struct	device sc_dev;
68 	int	sc_flags;
69 	struct	parparam sc_param;
70 #define sc_burst sc_param.burst
71 #define sc_timo  sc_param.timo
72 #define sc_delay sc_param.delay
73 } ;
74 
75 /* sc_flags values */
76 #define	PARF_ALIVE	0x01
77 #define	PARF_OPEN	0x02
78 #define PARF_UIO	0x04
79 #define PARF_TIMO	0x08
80 #define PARF_DELAY	0x10
81 #define PARF_OREAD	0x40	/* no support */
82 #define PARF_OWRITE	0x80
83 
84 #define UNIT(x)		minor(x)
85 
86 #ifdef DEBUG
87 #define PDB_FOLLOW	0x01
88 #define PDB_IO		0x02
89 #define PDB_INTERRUPT   0x04
90 #define PDB_NOCHECK	0x80
91 #if 0
92 int	pardebug = PDB_FOLLOW | PDB_IO | PDB_INTERRUPT;
93 #else
94 int	pardebug = 0;
95 #endif
96 #endif
97 
98 #define	PRTI_EN	0x01
99 #define	PRT_INT	0x20
100 
101 int parmatch __P((struct device *, struct cfdata *, void *));
102 void parattach __P((struct device *, struct device *, void *));
103 
104 struct cfattach par_ca = {
105 	sizeof(struct par_softc), (void *)parmatch, parattach
106 };
107 
108 struct cfdriver par_cd = {
109 	NULL, "par", DV_DULL
110 };
111 
112 int
113 parmatch(pdp, cfp, aux)
114 	struct device *pdp;
115 	struct cfdata *cfp;
116 	void *aux;
117 {
118 	/* X680x0 has only one parallel port */
119 	if (strcmp(aux, "par") || cfp->cf_unit > 0)
120 		return 0;
121 	return 1;
122 }
123 
124 void
125 parattach(pdp, dp, aux)
126 	struct device *pdp, *dp;
127 	void *aux;
128 {
129 	register struct par_softc *sc = (struct par_softc *)dp;
130 
131 	sc->sc_flags = PARF_ALIVE;
132 	printf(": parallel port (write only, interrupt)\n");
133 	ioctlr.intr &= (~PRTI_EN);
134 }
135 
136 int
137 paropen(dev, flags)
138 	dev_t dev;
139 	int flags;
140 {
141 	register int unit = UNIT(dev);
142 	register struct par_softc *sc = par_cd.cd_devs[unit];
143 	int s;
144 	char mask;
145 
146 	if (unit >= NPAR || !(sc->sc_flags & PARF_ALIVE))
147 		return(ENXIO);
148 	if (sc->sc_flags & PARF_OPEN)
149 		return(EBUSY);
150 	/* X680x0 can't read */
151 	if ((flags & FREAD) == FREAD)
152 		return (EINVAL);
153 
154 	sc->sc_flags |= PARF_OPEN;
155 
156 	sc->sc_flags |= PARF_OWRITE;
157 
158 	sc->sc_burst = PAR_BURST;
159 	sc->sc_timo = parmstohz(PAR_TIMO);
160 	sc->sc_delay = parmstohz(PAR_DELAY);
161 	return(0);
162 }
163 
164 int
165 parclose(dev, flags)
166 	dev_t dev;
167 	int flags;
168 {
169 	int unit = UNIT(dev);
170 	int s;
171 	struct par_softc *sc = par_cd.cd_devs[unit];
172 
173 	sc->sc_flags &= ~(PARF_OPEN|PARF_OWRITE);
174 
175 	/* don't allow interrupts any longer */
176 	s = spl1();
177 	ioctlr.intr &= (~PRTI_EN);
178 	splx(s);
179 
180 	return (0);
181 }
182 
183 void
184 parstart(unit)
185 	int unit;
186 {
187 	struct par_softc *sc = par_cd.cd_devs[unit];
188 #ifdef DEBUG
189 	if (pardebug & PDB_FOLLOW)
190 		printf("parstart(%x)\n", unit);
191 #endif
192 	sc->sc_flags &= ~PARF_DELAY;
193 	wakeup(sc);
194 }
195 
196 void
197 partimo(unit)
198 	int unit;
199 {
200 	struct par_softc *sc = par_cd.cd_devs[unit];
201 #ifdef DEBUG
202 	if (pardebug & PDB_FOLLOW)
203 		printf("partimo(%x)\n", unit);
204 #endif
205 	sc->sc_flags &= ~(PARF_UIO|PARF_TIMO);
206 	wakeup(sc);
207 }
208 
209 int
210 parwrite(dev, uio)
211 	dev_t dev;
212 	struct uio *uio;
213 {
214 
215 #ifdef DEBUG
216 	if (pardebug & PDB_FOLLOW)
217 		printf("parwrite(%x, %x)\n", dev, uio);
218 #endif
219 	return (parrw(dev, uio));
220 }
221 
222 int
223 parrw(dev, uio)
224 	dev_t dev;
225 	register struct uio *uio;
226 {
227 	int unit = UNIT(dev);
228 	register struct par_softc *sc = par_cd.cd_devs[unit];
229 	register int s, len, cnt;
230 	register char *cp;
231 	int error = 0, gotdata = 0;
232 	int buflen;
233 	char *buf;
234 
235 	if (!!(sc->sc_flags & PARF_OREAD) ^ (uio->uio_rw == UIO_READ))
236 		return EINVAL;
237 
238 	if (uio->uio_resid == 0)
239 		return(0);
240 
241 	buflen = min(sc->sc_burst, uio->uio_resid);
242 	buf = (char *)malloc(buflen, M_DEVBUF, M_WAITOK);
243 	sc->sc_flags |= PARF_UIO;
244 	if (sc->sc_timo > 0) {
245 		sc->sc_flags |= PARF_TIMO;
246 		timeout(partimo, (void *) unit, sc->sc_timo);
247 	}
248 	while (uio->uio_resid > 0) {
249 		len = min(buflen, uio->uio_resid);
250 		cp = buf;
251 		if (uio->uio_rw == UIO_WRITE) {
252 			error = uiomove(cp, len, uio);
253 			if (error)
254 				break;
255 		}
256 	      again:
257 		s = spl1();
258 		/*
259 		 * Check if we timed out during sleep or uiomove
260 		 */
261 		(void) splsoftclock();
262 		if ((sc->sc_flags & PARF_UIO) == 0) {
263 #ifdef DEBUG
264 			if (pardebug & PDB_IO)
265 				printf("parrw: uiomove/sleep timo, flags %x\n",
266 				       sc->sc_flags);
267 #endif
268 			if (sc->sc_flags & PARF_TIMO) {
269 				untimeout(partimo, (void *) unit);
270 				sc->sc_flags &= ~PARF_TIMO;
271 			}
272 			splx(s);
273 			break;
274 		}
275 		splx(s);
276 		/*
277 		 * Perform the operation
278 		 */
279 		cnt = parsend (cp, len);
280 		if (cnt < 0) {
281 			error = -cnt;
282 			break;
283 		}
284 
285 		s = splsoftclock();
286 		/*
287 		 * Operation timeout (or non-blocking), quit now.
288 		 */
289 		if ((sc->sc_flags & PARF_UIO) == 0) {
290 #ifdef DEBUG
291 			if (pardebug & PDB_IO)
292 				printf("parrw: timeout/done\n");
293 #endif
294 			splx(s);
295 			break;
296 		}
297 		/*
298 		 * Implement inter-read delay
299 		 */
300 		if (sc->sc_delay > 0) {
301 			sc->sc_flags |= PARF_DELAY;
302 			timeout(parstart, (void *) unit, sc->sc_delay);
303 			error = tsleep(sc, PCATCH|PZERO-1, "par-cdelay", 0);
304 			if (error) {
305 				splx(s);
306 				break;
307 			}
308 		}
309 		splx(s);
310 		/*
311 		 * Must not call uiomove again til we've used all data
312 		 * that we already grabbed.
313 		 */
314 		if (uio->uio_rw == UIO_WRITE && cnt != len) {
315 			cp += cnt;
316 			len -= cnt;
317 			cnt = 0;
318 			goto again;
319 		}
320 	}
321 	s = splsoftclock();
322 	if (sc->sc_flags & PARF_TIMO) {
323 		untimeout(partimo, (void *) unit);
324 		sc->sc_flags &= ~PARF_TIMO;
325 	}
326 	if (sc->sc_flags & PARF_DELAY)	{
327 		untimeout(parstart, (void *) unit);
328 		sc->sc_flags &= ~PARF_DELAY;
329 	}
330 	splx(s);
331 	/*
332 	 * Adjust for those chars that we uiomove'ed but never wrote
333 	 */
334 	if (uio->uio_rw == UIO_WRITE && cnt != len) {
335 		uio->uio_resid += (len - cnt);
336 #ifdef DEBUG
337 			if (pardebug & PDB_IO)
338 				printf("parrw: short write, adjust by %d\n",
339 				       len-cnt);
340 #endif
341 	}
342 	free(buf, M_DEVBUF);
343 #ifdef DEBUG
344 	if (pardebug & (PDB_FOLLOW|PDB_IO))
345 		printf("parrw: return %d, resid %d\n", error, uio->uio_resid);
346 #endif
347 	return (error);
348 }
349 
350 int
351 parioctl(dev, cmd, data, flag, p)
352 	dev_t dev;
353 	u_long cmd;
354 	caddr_t data;
355 	int flag;
356 	struct proc *p;
357 {
358 	struct par_softc *sc = par_cd.cd_devs[UNIT(dev)];
359 	struct parparam *pp, *upp;
360 	int error = 0;
361 
362 	switch (cmd) {
363 	      case PARIOCGPARAM:
364 		pp = &sc->sc_param;
365 		upp = (struct parparam *)data;
366 		upp->burst = pp->burst;
367 		upp->timo = parhztoms(pp->timo);
368 		upp->delay = parhztoms(pp->delay);
369 		break;
370 
371 	      case PARIOCSPARAM:
372 		pp = &sc->sc_param;
373 		upp = (struct parparam *)data;
374 		if (upp->burst < PAR_BURST_MIN || upp->burst > PAR_BURST_MAX ||
375 		    upp->delay < PAR_DELAY_MIN || upp->delay > PAR_DELAY_MAX)
376 			return(EINVAL);
377 		pp->burst = upp->burst;
378 		pp->timo = parmstohz(upp->timo);
379 		pp->delay = parmstohz(upp->delay);
380 		break;
381 
382 	      default:
383 		return(EINVAL);
384 	}
385 	return (error);
386 }
387 
388 int
389 parhztoms(h)
390 	int h;
391 {
392 	extern int hz;
393 	register int m = h;
394 
395 	if (m > 0)
396 		m = m * 1000 / hz;
397 	return(m);
398 }
399 
400 int
401 parmstohz(m)
402 	int m;
403 {
404 	extern int hz;
405 	register int h = m;
406 
407 	if (h > 0) {
408 		h = h * hz / 1000;
409 		if (h == 0)
410 			h = 1000 / hz;
411 	}
412 	return(h);
413 }
414 
415 /* stuff below here if for interrupt driven output of data thru
416    the parallel port. */
417 
418 int partimeout_pending;
419 int parsend_pending;
420 
421 void
422 parintr(arg)
423 	void *arg;
424 {
425 	int s, mask;
426 
427 	mask = (int)arg;
428 	s = splclock();
429 
430 	ioctlr.intr &= (~PRTI_EN);
431 
432 #ifdef DEBUG
433 	if (pardebug & PDB_INTERRUPT)
434 		printf ("parintr %d(%s)\n", mask, mask ? "FLG" : "tout");
435 #endif
436 	/* if invoked from timeout handler, mask will be 0,
437 	 * if from interrupt, it will contain the cia-icr mask,
438 	 * which is != 0
439 	 */
440 	if (mask) {
441 		if (partimeout_pending)
442 			untimeout (parintr, 0);
443 		if (parsend_pending)
444 			parsend_pending = 0;
445 	}
446 
447 	/* either way, there won't be a timeout pending any longer */
448 	partimeout_pending = 0;
449 
450 	wakeup(parintr);
451 	splx (s);
452 }
453 
454 int
455 parsendch (ch)
456 	u_char ch;
457 {
458 	int error = 0;
459 	int s;
460 
461 	/* if either offline, busy or out of paper, wait for that
462 	   condition to clear */
463 	s = spl1();
464 	while (!error
465 	       && (parsend_pending
466 		   || !(ioctlr.intr & PRT_INT)))
467 		{
468 			extern int hz;
469 
470 			/* wait a second, and try again */
471 			timeout (parintr, 0, hz);
472 			partimeout_pending = 1;
473 			/* this is essentially a flipflop to have us wait for the
474 			   first character being transmitted when trying to transmit
475 			   the second, etc. */
476 			parsend_pending = 0;
477 			/* it's quite important that a parallel putc can be
478 			   interrupted, given the possibility to lock a printer
479 			   in an offline condition.. */
480 			if (error = tsleep (parintr, PCATCH|PZERO-1, "parsendch", 0)) {
481 #ifdef DEBUG
482 				if (pardebug & PDB_INTERRUPT)
483 					printf ("parsendch interrupted, error = %d\n", error);
484 #endif
485 				if (partimeout_pending)
486 					untimeout (parintr, 0);
487 
488 				partimeout_pending = 0;
489 			}
490 		}
491 
492 	if (!error) {
493 #ifdef DEBUG
494 		if (pardebug & PDB_INTERRUPT)
495 			printf ("#%d", ch);
496 #endif
497 		printer.data = ch;
498 		DELAY(1);	/* (DELAY(1) == 1us) > 0.5us */
499 		printer.strobe = 0x00;
500 		ioctlr.intr |= PRTI_EN;
501 		DELAY(1);
502 		printer.strobe = 0x01;
503 		parsend_pending = 1;
504 	}
505 
506 	splx (s);
507 
508 	return error;
509 }
510 
511 
512 int
513 parsend (buf, len)
514 	u_char *buf;
515 	int len;
516 {
517 	int err, orig_len = len;
518 
519 	for (; len; len--, buf++)
520 		if (err = parsendch (*buf))
521 			return err < 0 ? -EINTR : -err;
522 
523 	/* either all or nothing.. */
524 	return orig_len;
525 }
526 
527 #endif
528