xref: /netbsd/sys/arch/amiga/dev/par.c (revision bf9ec67e)
1 /*	$NetBSD: par.c,v 1.25 2002/01/28 09:57:01 aymeric 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 #include <sys/cdefs.h>
39 __KERNEL_RCSID(0, "$NetBSD: par.c,v 1.25 2002/01/28 09:57:01 aymeric Exp $");
40 
41 /*
42  * parallel port interface
43  */
44 
45 #include "par.h"
46 #if NPAR > 0
47 
48 #include <sys/param.h>
49 #include <sys/errno.h>
50 #include <sys/uio.h>
51 #include <sys/device.h>
52 #include <sys/malloc.h>
53 #include <sys/file.h>
54 #include <sys/systm.h>
55 #include <sys/callout.h>
56 #include <sys/proc.h>
57 
58 #include <amiga/amiga/device.h>
59 #include <amiga/amiga/cia.h>
60 #include <amiga/dev/parioctl.h>
61 
62 #include <sys/conf.h>
63 #include <machine/conf.h>
64 
65 struct	par_softc {
66 	struct device sc_dev;
67 
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 	struct callout sc_timo_ch;
75 	struct callout sc_start_ch;
76 } *par_softcp;
77 
78 #define getparsp(x)	(x > 0 ? NULL : par_softcp)
79 
80 /* sc_flags values */
81 #define	PARF_ALIVE	0x01
82 #define	PARF_OPEN	0x02
83 #define PARF_UIO	0x04
84 #define PARF_TIMO	0x08
85 #define PARF_DELAY	0x10
86 #define PARF_OREAD	0x40
87 #define PARF_OWRITE	0x80
88 
89 #define UNIT(x)		minor(x)
90 
91 #ifdef DEBUG
92 int	pardebug = 0;
93 #define PDB_FOLLOW	0x01
94 #define PDB_IO		0x02
95 #define PDB_INTERRUPT   0x04
96 #define PDB_NOCHECK	0x80
97 #endif
98 
99 int parrw(dev_t, struct uio *);
100 int parhztoms(int);
101 int parmstohz(int);
102 int parsend(u_char *, int);
103 int parreceive(u_char *, int);
104 int parsendch(u_char);
105 
106 void partimo(void *);
107 void parstart(void *);
108 void parintr(void *);
109 
110 void parattach(struct device *, struct device *, void *);
111 int parmatch(struct device *, struct cfdata *, void *);
112 
113 struct cfattach par_ca = {
114 	sizeof(struct par_softc), parmatch, parattach
115 };
116 
117 /*ARGSUSED*/
118 int
119 parmatch(struct device *pdp, struct cfdata *cfp, void *auxp)
120 {
121 	static int par_found = 0;
122 
123 	if (!matchname((char *)auxp, "par") || par_found)
124 		return(0);
125 
126 	par_found = 1;
127 	return(1);
128 }
129 
130 void
131 parattach(struct device *pdp, struct device *dp, void *auxp)
132 {
133 	par_softcp = (struct par_softc *)dp;
134 
135 #ifdef DEBUG
136 	if ((pardebug & PDB_NOCHECK) == 0)
137 #endif
138 		par_softcp->sc_flags = PARF_ALIVE;
139 	printf("\n");
140 
141 	callout_init(&par_softcp->sc_timo_ch);
142 	callout_init(&par_softcp->sc_start_ch);
143 }
144 
145 int
146 paropen(dev_t dev, int flags, int mode, struct proc *p)
147 {
148 	int unit = UNIT(dev);
149 	struct par_softc *sc = getparsp(unit);
150 
151 	if (unit >= NPAR || (sc->sc_flags & PARF_ALIVE) == 0)
152 		return(ENXIO);
153 #ifdef DEBUG
154 	if (pardebug & PDB_FOLLOW) {
155 		printf("paropen(%x, %x): flags %x, ",
156 		    dev, flags, sc->sc_flags);
157 		printf ("port = $%x\n", ((ciab.pra ^ CIAB_PRA_SEL)
158 		    & (CIAB_PRA_SEL|CIAB_PRA_BUSY|CIAB_PRA_POUT)));
159 	}
160 #endif
161 	if (sc->sc_flags & PARF_OPEN)
162 		return(EBUSY);
163 	/* can either read or write, but not both */
164 	if ((flags & (FREAD|FWRITE)) == (FREAD|FWRITE))
165 		return EINVAL;
166 
167 	sc->sc_flags |= PARF_OPEN;
168 
169 	if (flags & FREAD)
170 		sc->sc_flags |= PARF_OREAD;
171 	else
172 		sc->sc_flags |= PARF_OWRITE;
173 
174 	sc->sc_burst = PAR_BURST;
175 	sc->sc_timo = parmstohz(PAR_TIMO);
176 	sc->sc_delay = parmstohz(PAR_DELAY);
177 	/* enable interrupts for CIAA-FLG */
178 	ciaa.icr = CIA_ICR_IR_SC | CIA_ICR_FLG;
179 	return(0);
180 }
181 
182 int
183 parclose(dev_t dev, int flags, int mode, struct proc *p)
184 {
185   int unit = UNIT(dev);
186   struct par_softc *sc = getparsp(unit);
187 
188 #ifdef DEBUG
189   if (pardebug & PDB_FOLLOW)
190     printf("parclose(%x, %x): flags %x\n",
191 	   dev, flags, sc->sc_flags);
192 #endif
193   sc->sc_flags &= ~(PARF_OPEN|PARF_OREAD|PARF_OWRITE);
194   /* don't allow interrupts for CIAA-FLG any longer */
195   ciaa.icr = CIA_ICR_FLG;
196   return(0);
197 }
198 
199 void
200 parstart(void *arg)
201 {
202 	struct par_softc *sc = arg;
203 
204 #ifdef DEBUG
205 	if (pardebug & PDB_FOLLOW)
206 		printf("parstart(%x)\n", sc->sc_dev.dv_unit);
207 #endif
208 	sc->sc_flags &= ~PARF_DELAY;
209 	wakeup(sc);
210 }
211 
212 void
213 partimo(void *arg)
214 {
215 	struct par_softc *sc = arg;
216 
217 #ifdef DEBUG
218 	if (pardebug & PDB_FOLLOW)
219 		printf("partimo(%x)\n", sc->sc_dev.dv_unit);
220 #endif
221 	sc->sc_flags &= ~(PARF_UIO|PARF_TIMO);
222 	wakeup(sc);
223 }
224 
225 int
226 parread(dev_t dev, struct uio *uio, int flags)
227 {
228 
229 #ifdef DEBUG
230 	if (pardebug & PDB_FOLLOW)
231 		printf("parread(%x, %p)\n", dev, uio);
232 #endif
233 	return (parrw(dev, uio));
234 }
235 
236 
237 int
238 parwrite(dev_t dev, struct uio *uio, int flags)
239 {
240 
241 #ifdef DEBUG
242 	if (pardebug & PDB_FOLLOW)
243 		printf("parwrite(%x, %p)\n", dev, uio);
244 #endif
245 	return (parrw(dev, uio));
246 }
247 
248 
249 int
250 parrw(dev_t dev, register struct uio *uio)
251 {
252   int unit = UNIT(dev);
253   register struct par_softc *sc = getparsp(unit);
254   register int s, len, cnt;
255   register char *cp;
256   int error = 0, gotdata = 0;
257   int buflen;
258   char *buf;
259 
260   len = 0;
261   cnt = 0;
262   if (!!(sc->sc_flags & PARF_OREAD) ^ (uio->uio_rw == UIO_READ))
263     return EINVAL;
264 
265   if (uio->uio_resid == 0)
266     return(0);
267 
268 #ifdef DEBUG
269   if (pardebug & (PDB_FOLLOW|PDB_IO))
270     printf("parrw(%x, %p, %c): burst %d, timo %d, resid %x\n",
271 	   dev, uio, uio->uio_rw == UIO_READ ? 'R' : 'W',
272 	   sc->sc_burst, sc->sc_timo, uio->uio_resid);
273 #endif
274   buflen = min(sc->sc_burst, uio->uio_resid);
275   buf = (char *)malloc(buflen, M_DEVBUF, M_WAITOK);
276   sc->sc_flags |= PARF_UIO;
277   if (sc->sc_timo > 0)
278     {
279       sc->sc_flags |= PARF_TIMO;
280       callout_reset(&sc->sc_timo_ch, sc->sc_timo, partimo, sc);
281     }
282   while (uio->uio_resid > 0)
283     {
284       len = min(buflen, uio->uio_resid);
285       cp = buf;
286       if (uio->uio_rw == UIO_WRITE)
287 	{
288 	  error = uiomove(cp, len, uio);
289 	  if (error)
290 	    break;
291 	}
292 again:
293       s = splbio();
294 #if 0
295       if ((sc->sc_flags & PARF_UIO) && hpibreq(&sc->sc_dq) == 0)
296 	sleep(sc, PRIBIO+1);
297 #endif
298       /*
299        * Check if we timed out during sleep or uiomove
300        */
301       (void) spllowersoftclock();
302       if ((sc->sc_flags & PARF_UIO) == 0)
303 	{
304 #ifdef DEBUG
305 	  if (pardebug & PDB_IO)
306 	    printf("parrw: uiomove/sleep timo, flags %x\n",
307 		   sc->sc_flags);
308 #endif
309 	  if (sc->sc_flags & PARF_TIMO)
310 	    {
311 	      callout_stop(&sc->sc_timo_ch);
312 	      sc->sc_flags &= ~PARF_TIMO;
313 	    }
314 	  splx(s);
315 	  break;
316 	}
317       splx(s);
318       /*
319        * Perform the operation
320        */
321       if (uio->uio_rw == UIO_WRITE)
322 	cnt = parsend (cp, len);
323       else
324 	cnt = parreceive (cp, len);
325 
326       if (cnt < 0)
327 	{
328 	  error = -cnt;
329 	  break;
330 	}
331 
332       s = splbio();
333 #if 0
334       hpibfree(&sc->sc_dq);
335 #endif
336 #ifdef DEBUG
337       if (pardebug & PDB_IO)
338 	printf("parrw: %s(%p, %d) -> %d\n",
339 	       uio->uio_rw == UIO_READ ? "recv" : "send", cp, len, cnt);
340 #endif
341       splx(s);
342       if (uio->uio_rw == UIO_READ)
343 	{
344 	  if (cnt)
345 	    {
346 	      error = uiomove(cp, cnt, uio);
347 	      if (error)
348 		break;
349 	      gotdata++;
350 	    }
351 	  /*
352 	   * Didn't get anything this time, but did in the past.
353 	   * Consider us done.
354 	   */
355 	  else if (gotdata)
356 	    break;
357 	}
358       s = splsoftclock();
359       /*
360        * Operation timeout (or non-blocking), quit now.
361        */
362       if ((sc->sc_flags & PARF_UIO) == 0)
363 	{
364 #ifdef DEBUG
365 	  if (pardebug & PDB_IO)
366 	    printf("parrw: timeout/done\n");
367 #endif
368 	  splx(s);
369 	  break;
370 	}
371       /*
372        * Implement inter-read delay
373        */
374       if (sc->sc_delay > 0)
375 	{
376 	  sc->sc_flags |= PARF_DELAY;
377 	  callout_reset(&sc->sc_start_ch, sc->sc_delay, parstart, sc);
378 	  error = tsleep(sc, PCATCH | (PZERO - 1), "par-cdelay", 0);
379 	  if (error)
380 	    {
381 	      splx(s);
382 	      break;
383 	    }
384 	}
385       splx(s);
386       /*
387        * Must not call uiomove again til we've used all data
388        * that we already grabbed.
389        */
390       if (uio->uio_rw == UIO_WRITE && cnt != len)
391 	{
392 	  cp += cnt;
393 	  len -= cnt;
394 	  cnt = 0;
395 	  goto again;
396 	}
397     }
398   s = splsoftclock();
399   if (sc->sc_flags & PARF_TIMO)
400     {
401       callout_stop(&sc->sc_timo_ch);
402       sc->sc_flags &= ~PARF_TIMO;
403     }
404   if (sc->sc_flags & PARF_DELAY)
405     {
406       callout_stop(&sc->sc_start_ch);
407       sc->sc_flags &= ~PARF_DELAY;
408     }
409   splx(s);
410   /*
411    * Adjust for those chars that we uiomove'ed but never wrote
412    */
413   if (uio->uio_rw == UIO_WRITE && cnt != len)
414     {
415       uio->uio_resid += (len - cnt);
416 #ifdef DEBUG
417       if (pardebug & PDB_IO)
418 	printf("parrw: short write, adjust by %d\n",
419 	       len-cnt);
420 #endif
421     }
422   free(buf, M_DEVBUF);
423 #ifdef DEBUG
424   if (pardebug & (PDB_FOLLOW|PDB_IO))
425     printf("parrw: return %d, resid %d\n", error, uio->uio_resid);
426 #endif
427   return (error);
428 }
429 
430 int
431 parioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
432 {
433   struct par_softc *sc = getparsp(UNIT(dev));
434   struct parparam *pp, *upp;
435   int error = 0;
436 
437   switch (cmd)
438     {
439     case PARIOCGPARAM:
440       pp = &sc->sc_param;
441       upp = (struct parparam *)data;
442       upp->burst = pp->burst;
443       upp->timo = parhztoms(pp->timo);
444       upp->delay = parhztoms(pp->delay);
445       break;
446 
447     case PARIOCSPARAM:
448       pp = &sc->sc_param;
449       upp = (struct parparam *)data;
450       if (upp->burst < PAR_BURST_MIN || upp->burst > PAR_BURST_MAX ||
451 	  upp->delay < PAR_DELAY_MIN || upp->delay > PAR_DELAY_MAX)
452 	return(EINVAL);
453       pp->burst = upp->burst;
454       pp->timo = parmstohz(upp->timo);
455       pp->delay = parmstohz(upp->delay);
456       break;
457 
458     default:
459       return(EINVAL);
460     }
461   return (error);
462 }
463 
464 int
465 parhztoms(int h)
466 {
467   extern int hz;
468   register int m = h;
469 
470   if (m > 0)
471     m = m * 1000 / hz;
472   return(m);
473 }
474 
475 int
476 parmstohz(int m)
477 {
478   extern int hz;
479   register int h = m;
480 
481   if (h > 0) {
482     h = h * hz / 1000;
483     if (h == 0)
484       h = 1000 / hz;
485   }
486   return(h);
487 }
488 
489 /* stuff below here if for interrupt driven output of data thru
490    the parallel port. */
491 
492 int parsend_pending;
493 
494 void
495 parintr(void *arg)
496 {
497 	int s;
498 
499 	s = splclock();
500 
501 #ifdef DEBUG
502 	if (pardebug & PDB_INTERRUPT)
503 		printf("parintr\n");
504 #endif
505 	parsend_pending = 0;
506 
507 	wakeup(parintr);
508 	splx(s);
509 }
510 
511 int
512 parsendch (u_char ch)
513 {
514   int error = 0;
515   int s;
516 
517   /* if either offline, busy or out of paper, wait for that
518      condition to clear */
519   s = splclock();
520   while (!error
521 	 && (parsend_pending
522 	     || ((ciab.pra ^ CIAB_PRA_SEL)
523 		 & (CIAB_PRA_SEL|CIAB_PRA_BUSY|CIAB_PRA_POUT))))
524     {
525       extern int hz;
526 
527 #ifdef DEBUG
528       if (pardebug & PDB_INTERRUPT)
529 	printf ("parsendch, port = $%x\n",
530 		((ciab.pra ^ CIAB_PRA_SEL)
531 		 & (CIAB_PRA_SEL|CIAB_PRA_BUSY|CIAB_PRA_POUT)));
532 #endif
533       /* this is essentially a flipflop to have us wait for the
534 	 first character being transmitted when trying to transmit
535 	 the second, etc. */
536       parsend_pending = 0;
537       /* it's quite important that a parallel putc can be
538 	 interrupted, given the possibility to lock a printer
539 	 in an offline condition.. */
540       error = tsleep(parintr, PCATCH | (PZERO - 1), "parsendch", hz);
541       if (error == EWOULDBLOCK)
542 	error = 0;
543       if (error > 0)
544 	{
545 #ifdef DEBUG
546 	  if (pardebug & PDB_INTERRUPT)
547 	    printf ("parsendch interrupted, error = %d\n", error);
548 #endif
549 	}
550     }
551 
552   if (! error)
553     {
554 #ifdef DEBUG
555       if (pardebug & PDB_INTERRUPT)
556 	printf ("#%d", ch);
557 #endif
558       ciaa.prb = ch;
559       parsend_pending = 1;
560     }
561 
562   splx (s);
563 
564   return error;
565 }
566 
567 
568 int
569 parsend (u_char *buf, int len)
570 {
571   int err, orig_len = len;
572 
573   /* make sure I/O lines are setup right for output */
574 
575   /* control lines set to input */
576   ciab.ddra &= ~(CIAB_PRA_SEL|CIAB_PRA_POUT|CIAB_PRA_BUSY);
577   /* data lines to output */
578   ciaa.ddrb = 0xff;
579 
580   for (; len; len--, buf++)
581     if ((err = parsendch (*buf)) != 0)
582       return err < 0 ? -EINTR : -err;
583 
584   /* either all or nothing.. */
585   return orig_len;
586 }
587 
588 
589 
590 int
591 parreceive (u_char *buf, int len)
592 {
593   /* oh deary me, something's gotta be left to be implemented
594      later... */
595   return 0;
596 }
597 
598 
599 #endif
600