xref: /netbsd-src/sys/arch/amiga/dev/par.c (revision b1c86f5f087524e68db12794ee9c3e3da1ab17a0)
1 /*	$NetBSD: par.c,v 1.37 2009/05/31 23:07:18 phx 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.37 2009/05/31 23:07:18 phx 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, 0);
146 	callout_init(&par_softcp->sc_start_ch, 0);
147 }
148 
149 int
150 paropen(dev_t dev, int flags, int mode, struct lwp *l)
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(%llx, %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 lwp *l)
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(%llx, %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", device_unit(&sc->sc_dev));
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", device_unit(&sc->sc_dev));
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(%llx, %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(%llx, %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(%llx, %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 #if 0
298       if ((sc->sc_flags & PARF_UIO) && hpibreq(&sc->sc_dq) == 0)
299 	sleep(sc, PRIBIO+1);
300 #endif
301       /*
302        * Check if we timed out during sleep or uiomove
303        */
304       s = splsoftclock();
305       if ((sc->sc_flags & PARF_UIO) == 0)
306 	{
307 #ifdef DEBUG
308 	  if (pardebug & PDB_IO)
309 	    printf("parrw: uiomove/sleep timo, flags %x\n",
310 		   sc->sc_flags);
311 #endif
312 	  if (sc->sc_flags & PARF_TIMO)
313 	    {
314 	      callout_stop(&sc->sc_timo_ch);
315 	      sc->sc_flags &= ~PARF_TIMO;
316 	    }
317 	  splx(s);
318 	  break;
319 	}
320       splx(s);
321       /*
322        * Perform the operation
323        */
324       if (uio->uio_rw == UIO_WRITE)
325 	cnt = parsend (cp, len);
326       else
327 	cnt = parreceive (cp, len);
328 
329       if (cnt < 0)
330 	{
331 	  error = -cnt;
332 	  break;
333 	}
334 
335       s = splbio();
336 #if 0
337       hpibfree(&sc->sc_dq);
338 #endif
339 #ifdef DEBUG
340       if (pardebug & PDB_IO)
341 	printf("parrw: %s(%p, %d) -> %d\n",
342 	       uio->uio_rw == UIO_READ ? "recv" : "send", cp, len, cnt);
343 #endif
344       splx(s);
345       if (uio->uio_rw == UIO_READ)
346 	{
347 	  if (cnt)
348 	    {
349 	      error = uiomove(cp, cnt, uio);
350 	      if (error)
351 		break;
352 	      gotdata++;
353 	    }
354 	  /*
355 	   * Didn't get anything this time, but did in the past.
356 	   * Consider us done.
357 	   */
358 	  else if (gotdata)
359 	    break;
360 	}
361       s = splsoftclock();
362       /*
363        * Operation timeout (or non-blocking), quit now.
364        */
365       if ((sc->sc_flags & PARF_UIO) == 0)
366 	{
367 #ifdef DEBUG
368 	  if (pardebug & PDB_IO)
369 	    printf("parrw: timeout/done\n");
370 #endif
371 	  splx(s);
372 	  break;
373 	}
374       /*
375        * Implement inter-read delay
376        */
377       if (sc->sc_delay > 0)
378 	{
379 	  sc->sc_flags |= PARF_DELAY;
380 	  callout_reset(&sc->sc_start_ch, sc->sc_delay, parstart, sc);
381 	  error = tsleep(sc, PCATCH | (PZERO - 1), "par-cdelay", 0);
382 	  if (error)
383 	    {
384 	      splx(s);
385 	      break;
386 	    }
387 	}
388       splx(s);
389       /*
390        * Must not call uiomove again til we've used all data
391        * that we already grabbed.
392        */
393       if (uio->uio_rw == UIO_WRITE && cnt != len)
394 	{
395 	  cp += cnt;
396 	  len -= cnt;
397 	  cnt = 0;
398 	  goto again;
399 	}
400     }
401   s = splsoftclock();
402   if (sc->sc_flags & PARF_TIMO)
403     {
404       callout_stop(&sc->sc_timo_ch);
405       sc->sc_flags &= ~PARF_TIMO;
406     }
407   if (sc->sc_flags & PARF_DELAY)
408     {
409       callout_stop(&sc->sc_start_ch);
410       sc->sc_flags &= ~PARF_DELAY;
411     }
412   splx(s);
413   /*
414    * Adjust for those chars that we uiomove'ed but never wrote
415    */
416   if (uio->uio_rw == UIO_WRITE && cnt != len)
417     {
418       uio->uio_resid += (len - cnt);
419 #ifdef DEBUG
420       if (pardebug & PDB_IO)
421 	printf("parrw: short write, adjust by %d\n",
422 	       len-cnt);
423 #endif
424     }
425   free(buf, M_DEVBUF);
426 #ifdef DEBUG
427   if (pardebug & (PDB_FOLLOW|PDB_IO))
428     printf("parrw: return %d, resid %d\n", error, uio->uio_resid);
429 #endif
430   return (error);
431 }
432 
433 int
434 parioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
435 {
436   struct par_softc *sc = getparsp(UNIT(dev));
437   struct parparam *pp, *upp;
438   int error = 0;
439 
440   switch (cmd)
441     {
442     case PARIOCGPARAM:
443       pp = &sc->sc_param;
444       upp = (struct parparam *)data;
445       upp->burst = pp->burst;
446       upp->timo = parhztoms(pp->timo);
447       upp->delay = parhztoms(pp->delay);
448       break;
449 
450     case PARIOCSPARAM:
451       pp = &sc->sc_param;
452       upp = (struct parparam *)data;
453       if (upp->burst < PAR_BURST_MIN || upp->burst > PAR_BURST_MAX ||
454 	  upp->delay < PAR_DELAY_MIN || upp->delay > PAR_DELAY_MAX)
455 	return(EINVAL);
456       pp->burst = upp->burst;
457       pp->timo = parmstohz(upp->timo);
458       pp->delay = parmstohz(upp->delay);
459       break;
460 
461     default:
462       return(EINVAL);
463     }
464   return (error);
465 }
466 
467 int
468 parhztoms(int h)
469 {
470   extern int hz;
471   register int m = h;
472 
473   if (m > 0)
474     m = m * 1000 / hz;
475   return(m);
476 }
477 
478 int
479 parmstohz(int m)
480 {
481   extern int hz;
482   register int h = m;
483 
484   if (h > 0) {
485     h = h * hz / 1000;
486     if (h == 0)
487       h = 1000 / hz;
488   }
489   return(h);
490 }
491 
492 /* stuff below here if for interrupt driven output of data thru
493    the parallel port. */
494 
495 int parsend_pending;
496 
497 void
498 parintr(void *arg)
499 {
500 	int s;
501 
502 	s = splclock();
503 
504 #ifdef DEBUG
505 	if (pardebug & PDB_INTERRUPT)
506 		printf("parintr\n");
507 #endif
508 	parsend_pending = 0;
509 
510 	wakeup(parintr);
511 	splx(s);
512 }
513 
514 int
515 parsendch (u_char ch)
516 {
517   int error = 0;
518   int s;
519 
520   /* if either offline, busy or out of paper, wait for that
521      condition to clear */
522   s = splclock();
523   while (!error
524 	 && (parsend_pending
525 	     || ((ciab.pra ^ CIAB_PRA_SEL)
526 		 & (CIAB_PRA_SEL|CIAB_PRA_BUSY|CIAB_PRA_POUT))))
527     {
528       extern int hz;
529 
530 #ifdef DEBUG
531       if (pardebug & PDB_INTERRUPT)
532 	printf ("parsendch, port = $%x\n",
533 		((ciab.pra ^ CIAB_PRA_SEL)
534 		 & (CIAB_PRA_SEL|CIAB_PRA_BUSY|CIAB_PRA_POUT)));
535 #endif
536       /* this is essentially a flipflop to have us wait for the
537 	 first character being transmitted when trying to transmit
538 	 the second, etc. */
539       parsend_pending = 0;
540       /* it's quite important that a parallel putc can be
541 	 interrupted, given the possibility to lock a printer
542 	 in an offline condition.. */
543       error = tsleep(parintr, PCATCH | (PZERO - 1), "parsendch", hz);
544       if (error == EWOULDBLOCK)
545 	error = 0;
546       if (error > 0)
547 	{
548 #ifdef DEBUG
549 	  if (pardebug & PDB_INTERRUPT)
550 	    printf ("parsendch interrupted, error = %d\n", error);
551 #endif
552 	}
553     }
554 
555   if (! error)
556     {
557 #ifdef DEBUG
558       if (pardebug & PDB_INTERRUPT)
559 	printf ("#%d", ch);
560 #endif
561       ciaa.prb = ch;
562       parsend_pending = 1;
563     }
564 
565   splx (s);
566 
567   return error;
568 }
569 
570 
571 int
572 parsend (u_char *buf, int len)
573 {
574   int err, orig_len = len;
575 
576   /* make sure I/O lines are setup right for output */
577 
578   /* control lines set to input */
579   ciab.ddra &= ~(CIAB_PRA_SEL|CIAB_PRA_POUT|CIAB_PRA_BUSY);
580   /* data lines to output */
581   ciaa.ddrb = 0xff;
582 
583   for (; len; len--, buf++)
584     if ((err = parsendch (*buf)) != 0)
585       return err < 0 ? -EINTR : -err;
586 
587   /* either all or nothing.. */
588   return orig_len;
589 }
590 
591 
592 
593 int
594 parreceive (u_char *buf, int len)
595 {
596   /* oh deary me, something's gotta be left to be implemented
597      later... */
598   return 0;
599 }
600 
601 
602 #endif
603