par.c revision 1.2 1 /* $NetBSD: par.c,v 1.2 1996/05/21 15:32:42 oki 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 /*
39 * parallel port interface
40 */
41
42 #include "par.h"
43 #if NPAR > 0
44
45 #if NPAR > 1
46 #undef NPAR
47 #define NPAR 1
48 #endif
49
50 #include <sys/param.h>
51 #include <sys/errno.h>
52 #include <sys/uio.h>
53 #include <sys/device.h>
54 #include <sys/malloc.h>
55 #include <sys/file.h>
56 #include <sys/systm.h>
57 #include <sys/proc.h>
58
59 #include <x68k/x68k/iodevice.h>
60 #include <x68k/dev/parioctl.h>
61
62 void partimo();
63 void parstart();
64 void parintr();
65
66 struct par_softc {
67 struct device sc_dev;
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
75 /* sc_flags values */
76 #define PARF_ALIVE 0x01
77 #define PARF_OPEN 0x02
78 #define PARF_UIO 0x04
79 #define PARF_TIMO 0x08
80 #define PARF_DELAY 0x10
81 #define PARF_OREAD 0x40 /* no support */
82 #define PARF_OWRITE 0x80
83
84 #define UNIT(x) minor(x)
85
86 #ifdef DEBUG
87 #define PDB_FOLLOW 0x01
88 #define PDB_IO 0x02
89 #define PDB_INTERRUPT 0x04
90 #define PDB_NOCHECK 0x80
91 #if 0
92 int pardebug = PDB_FOLLOW | PDB_IO | PDB_INTERRUPT;
93 #else
94 int pardebug = 0;
95 #endif
96 #endif
97
98 #define PRTI_EN 0x01
99 #define PRT_INT 0x20
100
101 int parmatch __P((struct device *, struct cfdata *, void *));
102 void parattach __P((struct device *, struct device *, void *));
103
104 struct cfattach par_ca = {
105 sizeof(struct par_softc), (void *)parmatch, parattach
106 };
107
108 struct cfdriver par_cd = {
109 NULL, "par", DV_DULL
110 };
111
112 int
113 parmatch(pdp, cfp, aux)
114 struct device *pdp;
115 struct cfdata *cfp;
116 void *aux;
117 {
118 /* X680x0 has only one parallel port */
119 if (strcmp(aux, "par") || cfp->cf_unit > 0)
120 return 0;
121 return 1;
122 }
123
124 void
125 parattach(pdp, dp, aux)
126 struct device *pdp, *dp;
127 void *aux;
128 {
129 register struct par_softc *sc = (struct par_softc *)dp;
130
131 sc->sc_flags = PARF_ALIVE;
132 printf(": parallel port (write only, interrupt)\n");
133 ioctlr.intr &= (~PRTI_EN);
134 }
135
136 int
137 paropen(dev, flags)
138 dev_t dev;
139 int flags;
140 {
141 register int unit = UNIT(dev);
142 register struct par_softc *sc = par_cd.cd_devs[unit];
143 int s;
144 char mask;
145
146 if (unit >= NPAR || !(sc->sc_flags & PARF_ALIVE))
147 return(ENXIO);
148 if (sc->sc_flags & PARF_OPEN)
149 return(EBUSY);
150 /* X680x0 can't read */
151 if ((flags & FREAD) == FREAD)
152 return (EINVAL);
153
154 sc->sc_flags |= PARF_OPEN;
155
156 sc->sc_flags |= PARF_OWRITE;
157
158 sc->sc_burst = PAR_BURST;
159 sc->sc_timo = parmstohz(PAR_TIMO);
160 sc->sc_delay = parmstohz(PAR_DELAY);
161 return(0);
162 }
163
164 int
165 parclose(dev, flags)
166 dev_t dev;
167 int flags;
168 {
169 int unit = UNIT(dev);
170 int s;
171 struct par_softc *sc = par_cd.cd_devs[unit];
172
173 sc->sc_flags &= ~(PARF_OPEN|PARF_OWRITE);
174
175 /* don't allow interrupts any longer */
176 s = spl1();
177 ioctlr.intr &= (~PRTI_EN);
178 splx(s);
179
180 return (0);
181 }
182
183 void
184 parstart(unit)
185 int unit;
186 {
187 struct par_softc *sc = par_cd.cd_devs[unit];
188 #ifdef DEBUG
189 if (pardebug & PDB_FOLLOW)
190 printf("parstart(%x)\n", unit);
191 #endif
192 sc->sc_flags &= ~PARF_DELAY;
193 wakeup(sc);
194 }
195
196 void
197 partimo(unit)
198 int unit;
199 {
200 struct par_softc *sc = par_cd.cd_devs[unit];
201 #ifdef DEBUG
202 if (pardebug & PDB_FOLLOW)
203 printf("partimo(%x)\n", unit);
204 #endif
205 sc->sc_flags &= ~(PARF_UIO|PARF_TIMO);
206 wakeup(sc);
207 }
208
209 int
210 parwrite(dev, uio)
211 dev_t dev;
212 struct uio *uio;
213 {
214
215 #ifdef DEBUG
216 if (pardebug & PDB_FOLLOW)
217 printf("parwrite(%x, %x)\n", dev, uio);
218 #endif
219 return (parrw(dev, uio));
220 }
221
222 int
223 parrw(dev, uio)
224 dev_t dev;
225 register struct uio *uio;
226 {
227 int unit = UNIT(dev);
228 register struct par_softc *sc = par_cd.cd_devs[unit];
229 register int s, len, cnt;
230 register char *cp;
231 int error = 0, gotdata = 0;
232 int buflen;
233 char *buf;
234
235 if (!!(sc->sc_flags & PARF_OREAD) ^ (uio->uio_rw == UIO_READ))
236 return EINVAL;
237
238 if (uio->uio_resid == 0)
239 return(0);
240
241 buflen = min(sc->sc_burst, uio->uio_resid);
242 buf = (char *)malloc(buflen, M_DEVBUF, M_WAITOK);
243 sc->sc_flags |= PARF_UIO;
244 if (sc->sc_timo > 0) {
245 sc->sc_flags |= PARF_TIMO;
246 timeout(partimo, (void *) unit, sc->sc_timo);
247 }
248 while (uio->uio_resid > 0) {
249 len = min(buflen, uio->uio_resid);
250 cp = buf;
251 if (uio->uio_rw == UIO_WRITE) {
252 error = uiomove(cp, len, uio);
253 if (error)
254 break;
255 }
256 again:
257 s = spl1();
258 /*
259 * Check if we timed out during sleep or uiomove
260 */
261 (void) splsoftclock();
262 if ((sc->sc_flags & PARF_UIO) == 0) {
263 #ifdef DEBUG
264 if (pardebug & PDB_IO)
265 printf("parrw: uiomove/sleep timo, flags %x\n",
266 sc->sc_flags);
267 #endif
268 if (sc->sc_flags & PARF_TIMO) {
269 untimeout(partimo, (void *) unit);
270 sc->sc_flags &= ~PARF_TIMO;
271 }
272 splx(s);
273 break;
274 }
275 splx(s);
276 /*
277 * Perform the operation
278 */
279 cnt = parsend (cp, len);
280 if (cnt < 0) {
281 error = -cnt;
282 break;
283 }
284
285 s = splsoftclock();
286 /*
287 * Operation timeout (or non-blocking), quit now.
288 */
289 if ((sc->sc_flags & PARF_UIO) == 0) {
290 #ifdef DEBUG
291 if (pardebug & PDB_IO)
292 printf("parrw: timeout/done\n");
293 #endif
294 splx(s);
295 break;
296 }
297 /*
298 * Implement inter-read delay
299 */
300 if (sc->sc_delay > 0) {
301 sc->sc_flags |= PARF_DELAY;
302 timeout(parstart, (void *) unit, sc->sc_delay);
303 error = tsleep(sc, PCATCH|PZERO-1, "par-cdelay", 0);
304 if (error) {
305 splx(s);
306 break;
307 }
308 }
309 splx(s);
310 /*
311 * Must not call uiomove again til we've used all data
312 * that we already grabbed.
313 */
314 if (uio->uio_rw == UIO_WRITE && cnt != len) {
315 cp += cnt;
316 len -= cnt;
317 cnt = 0;
318 goto again;
319 }
320 }
321 s = splsoftclock();
322 if (sc->sc_flags & PARF_TIMO) {
323 untimeout(partimo, (void *) unit);
324 sc->sc_flags &= ~PARF_TIMO;
325 }
326 if (sc->sc_flags & PARF_DELAY) {
327 untimeout(parstart, (void *) unit);
328 sc->sc_flags &= ~PARF_DELAY;
329 }
330 splx(s);
331 /*
332 * Adjust for those chars that we uiomove'ed but never wrote
333 */
334 if (uio->uio_rw == UIO_WRITE && cnt != len) {
335 uio->uio_resid += (len - cnt);
336 #ifdef DEBUG
337 if (pardebug & PDB_IO)
338 printf("parrw: short write, adjust by %d\n",
339 len-cnt);
340 #endif
341 }
342 free(buf, M_DEVBUF);
343 #ifdef DEBUG
344 if (pardebug & (PDB_FOLLOW|PDB_IO))
345 printf("parrw: return %d, resid %d\n", error, uio->uio_resid);
346 #endif
347 return (error);
348 }
349
350 int
351 parioctl(dev, cmd, data, flag, p)
352 dev_t dev;
353 u_long cmd;
354 caddr_t data;
355 int flag;
356 struct proc *p;
357 {
358 struct par_softc *sc = par_cd.cd_devs[UNIT(dev)];
359 struct parparam *pp, *upp;
360 int error = 0;
361
362 switch (cmd) {
363 case PARIOCGPARAM:
364 pp = &sc->sc_param;
365 upp = (struct parparam *)data;
366 upp->burst = pp->burst;
367 upp->timo = parhztoms(pp->timo);
368 upp->delay = parhztoms(pp->delay);
369 break;
370
371 case PARIOCSPARAM:
372 pp = &sc->sc_param;
373 upp = (struct parparam *)data;
374 if (upp->burst < PAR_BURST_MIN || upp->burst > PAR_BURST_MAX ||
375 upp->delay < PAR_DELAY_MIN || upp->delay > PAR_DELAY_MAX)
376 return(EINVAL);
377 pp->burst = upp->burst;
378 pp->timo = parmstohz(upp->timo);
379 pp->delay = parmstohz(upp->delay);
380 break;
381
382 default:
383 return(EINVAL);
384 }
385 return (error);
386 }
387
388 int
389 parhztoms(h)
390 int h;
391 {
392 extern int hz;
393 register int m = h;
394
395 if (m > 0)
396 m = m * 1000 / hz;
397 return(m);
398 }
399
400 int
401 parmstohz(m)
402 int m;
403 {
404 extern int hz;
405 register int h = m;
406
407 if (h > 0) {
408 h = h * hz / 1000;
409 if (h == 0)
410 h = 1000 / hz;
411 }
412 return(h);
413 }
414
415 /* stuff below here if for interrupt driven output of data thru
416 the parallel port. */
417
418 int partimeout_pending;
419 int parsend_pending;
420
421 void
422 parintr(arg)
423 void *arg;
424 {
425 int s, mask;
426
427 mask = (int)arg;
428 s = splclock();
429
430 ioctlr.intr &= (~PRTI_EN);
431
432 #ifdef DEBUG
433 if (pardebug & PDB_INTERRUPT)
434 printf ("parintr %d(%s)\n", mask, mask ? "FLG" : "tout");
435 #endif
436 /* if invoked from timeout handler, mask will be 0,
437 * if from interrupt, it will contain the cia-icr mask,
438 * which is != 0
439 */
440 if (mask) {
441 if (partimeout_pending)
442 untimeout (parintr, 0);
443 if (parsend_pending)
444 parsend_pending = 0;
445 }
446
447 /* either way, there won't be a timeout pending any longer */
448 partimeout_pending = 0;
449
450 wakeup(parintr);
451 splx (s);
452 }
453
454 int
455 parsendch (ch)
456 u_char ch;
457 {
458 int error = 0;
459 int s;
460
461 /* if either offline, busy or out of paper, wait for that
462 condition to clear */
463 s = spl1();
464 while (!error
465 && (parsend_pending
466 || !(ioctlr.intr & PRT_INT)))
467 {
468 extern int hz;
469
470 /* wait a second, and try again */
471 timeout (parintr, 0, hz);
472 partimeout_pending = 1;
473 /* this is essentially a flipflop to have us wait for the
474 first character being transmitted when trying to transmit
475 the second, etc. */
476 parsend_pending = 0;
477 /* it's quite important that a parallel putc can be
478 interrupted, given the possibility to lock a printer
479 in an offline condition.. */
480 if (error = tsleep (parintr, PCATCH|PZERO-1, "parsendch", 0)) {
481 #ifdef DEBUG
482 if (pardebug & PDB_INTERRUPT)
483 printf ("parsendch interrupted, error = %d\n", error);
484 #endif
485 if (partimeout_pending)
486 untimeout (parintr, 0);
487
488 partimeout_pending = 0;
489 }
490 }
491
492 if (!error) {
493 #ifdef DEBUG
494 if (pardebug & PDB_INTERRUPT)
495 printf ("#%d", ch);
496 #endif
497 printer.data = ch;
498 DELAY(1); /* (DELAY(1) == 1us) > 0.5us */
499 printer.strobe = 0x00;
500 ioctlr.intr |= PRTI_EN;
501 DELAY(1);
502 printer.strobe = 0x01;
503 parsend_pending = 1;
504 }
505
506 splx (s);
507
508 return error;
509 }
510
511
512 int
513 parsend (buf, len)
514 u_char *buf;
515 int len;
516 {
517 int err, orig_len = len;
518
519 for (; len; len--, buf++)
520 if (err = parsendch (*buf))
521 return err < 0 ? -EINTR : -err;
522
523 /* either all or nothing.. */
524 return orig_len;
525 }
526
527 #endif
528