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