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