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