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