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