1/*	$NetBSD: par.c,v 1.36 2007/10/17 19:53:17 garbled 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.36 2007/10/17 19:53:17 garbled 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
59struct	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
86int	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
93int parrw(dev_t, struct uio *);
94int parhztoms(int);
95int parmstohz(int);
96int parsend(u_char *, int);
97int parreceive(u_char *, int);
98int parsendch(u_char);
99
100void partimo(void *);
101void parstart(void *);
102void parintr(void *);
103
104void parattach(struct device *, struct device *, void *);
105int parmatch(struct device *, struct cfdata *, void *);
106
107CFATTACH_DECL(par, sizeof(struct par_softc),
108    parmatch, parattach, NULL, NULL);
109
110dev_type_open(paropen);
111dev_type_close(parclose);
112dev_type_read(parread);
113dev_type_write(parwrite);
114dev_type_ioctl(parioctl);
115
116const struct cdevsw par_cdevsw = {
117	paropen, parclose, parread, parwrite, parioctl,
118	nostop, notty, nopoll, nommap, nokqfilter,
119};
120
121/*ARGSUSED*/
122int
123parmatch(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
134void
135parattach(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
149int
150paropen(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
186int
187parclose(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
203void
204parstart(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
216void
217partimo(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
229int
230parread(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
241int
242parwrite(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
253int
254parrw(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	}
296again:
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
433int
434parioctl(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
467int
468parhztoms(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
478int
479parmstohz(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
495int parsend_pending;
496
497void
498parintr(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
514int
515parsendch (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
571int
572parsend (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
593int
594parreceive (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