1 /* $OpenBSD: siotty.c,v 1.11 2010/04/12 12:57:52 tedu Exp $ */ 2 /* $NetBSD: siotty.c,v 1.9 2002/03/17 19:40:43 atatat Exp $ */ 3 4 /*- 5 * Copyright (c) 2000 The NetBSD Foundation, Inc. 6 * All rights reserved. 7 * 8 * This code is derived from software contributed to The NetBSD Foundation 9 * by Tohru Nishimura. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 * POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 #include <sys/param.h> 34 #include <sys/systm.h> 35 #include <sys/device.h> 36 #include <sys/conf.h> 37 #include <sys/ioctl.h> 38 #include <sys/proc.h> 39 #include <sys/user.h> 40 #include <sys/tty.h> 41 #include <sys/uio.h> 42 #include <sys/fcntl.h> 43 #include <dev/cons.h> 44 45 #include <machine/cpu.h> 46 47 #include <luna88k/dev/sioreg.h> 48 #include <luna88k/dev/siovar.h> 49 50 #define TIOCM_BREAK 01000 /* non standard use */ 51 52 static const u_int8_t ch0_regs[6] = { 53 WR0_RSTINT, /* reset E/S interrupt */ 54 WR1_RXALLS | WR1_TXENBL, /* Rx per char, Tx */ 55 0, /* */ 56 WR3_RX8BIT | WR3_RXENBL, /* Rx */ 57 WR4_BAUD96 | WR4_STOP1, /* Tx/Rx */ 58 WR5_TX8BIT | WR5_TXENBL | WR5_DTR | WR5_RTS, /* Tx */ 59 }; 60 61 static const struct speedtab siospeedtab[] = { 62 { 2400, WR4_BAUD24, }, 63 { 4800, WR4_BAUD48, }, 64 { 9600, WR4_BAUD96, }, 65 { -1, 0, }, 66 }; 67 68 struct siotty_softc { 69 struct device sc_dev; 70 struct tty *sc_tty; 71 struct sioreg *sc_ctl; 72 u_int sc_flags; 73 u_int8_t sc_wr[6]; 74 }; 75 76 cdev_decl(sio); 77 void siostart(struct tty *); 78 int sioparam(struct tty *, struct termios *); 79 void siottyintr(int); 80 int siomctl(struct siotty_softc *, int, int); 81 82 int siotty_match(struct device *, void *, void *); 83 void siotty_attach(struct device *, struct device *, void *); 84 85 const struct cfattach siotty_ca = { 86 sizeof(struct siotty_softc), siotty_match, siotty_attach 87 }; 88 89 struct cfdriver siotty_cd = { 90 NULL, "siotty", DV_TTY 91 }; 92 93 int 94 siotty_match(parent, cf, aux) 95 struct device *parent; 96 void *cf, *aux; 97 { 98 struct sio_attach_args *args = aux; 99 100 if (args->channel != 0) /* XXX allow tty on Ch.B XXX */ 101 return 0; 102 return 1; 103 } 104 105 void 106 siotty_attach(parent, self, aux) 107 struct device *parent, *self; 108 void *aux; 109 { 110 struct sio_softc *scp = (void *)parent; 111 struct siotty_softc *sc = (void *)self; 112 struct sio_attach_args *args = aux; 113 114 sc->sc_ctl = (struct sioreg *)scp->scp_ctl + args->channel; 115 bcopy(ch0_regs, sc->sc_wr, sizeof(ch0_regs)); 116 scp->scp_intr[args->channel] = siottyintr; 117 118 if (args->hwflags == 1) { 119 printf(" (console)"); 120 sc->sc_flags = TIOCFLAG_SOFTCAR; 121 } 122 else { 123 setsioreg(sc->sc_ctl, WR0, WR0_CHANRST); 124 setsioreg(sc->sc_ctl, WR2A, WR2_VEC86 | WR2_INTR_1); 125 setsioreg(sc->sc_ctl, WR2B, 0); 126 setsioreg(sc->sc_ctl, WR0, sc->sc_wr[WR0]); 127 setsioreg(sc->sc_ctl, WR4, sc->sc_wr[WR4]); 128 setsioreg(sc->sc_ctl, WR3, sc->sc_wr[WR3]); 129 setsioreg(sc->sc_ctl, WR5, sc->sc_wr[WR5]); 130 setsioreg(sc->sc_ctl, WR0, sc->sc_wr[WR0]); 131 } 132 setsioreg(sc->sc_ctl, WR1, sc->sc_wr[WR1]); /* now interrupt driven */ 133 134 printf("\n"); 135 } 136 137 /*-------------------- low level routine --------------------*/ 138 139 void 140 siottyintr(chan) 141 int chan; 142 { 143 struct siotty_softc *sc; 144 struct sioreg *sio; 145 struct tty *tp; 146 unsigned int code; 147 int rr; 148 149 if (chan >= siotty_cd.cd_ndevs) 150 return; 151 sc = siotty_cd.cd_devs[chan]; 152 tp = sc->sc_tty; 153 sio = sc->sc_ctl; 154 rr = getsiocsr(sio); 155 if (rr & RR_RXRDY) { 156 do { 157 code = sio->sio_data; 158 if (rr & (RR_FRAMING | RR_OVERRUN | RR_PARITY)) { 159 sio->sio_cmd = WR0_ERRRST; 160 if (sio->sio_stat & RR_FRAMING) 161 code |= TTY_FE; 162 else if (sio->sio_stat & RR_PARITY) 163 code |= TTY_PE; 164 } 165 if (tp == NULL || (tp->t_state & TS_ISOPEN) == 0) 166 continue; 167 #if 0 && defined(DDB) /* ?!?! fails to resume ?!?! */ 168 if ((rr & RR_BREAK) && tp->t_dev == cn_tab->cn_dev) { 169 if (db_console) 170 Debugger(); 171 return; 172 } 173 #endif 174 /* 175 (*tp->t_linesw->l_rint)(code, tp); 176 */ 177 (*linesw[tp->t_line].l_rint)(code, tp); 178 } while ((rr = getsiocsr(sio)) & RR_RXRDY); 179 } 180 if (rr & RR_TXRDY) { 181 sio->sio_cmd = WR0_RSTPEND; 182 if (tp != NULL) { 183 tp->t_state &= ~(TS_BUSY|TS_FLUSH); 184 /* 185 (*tp->t_linesw->l_start)(tp); 186 */ 187 (*linesw[tp->t_line].l_start)(tp); 188 } 189 } 190 } 191 192 void 193 siostart(tp) 194 struct tty *tp; 195 { 196 struct siotty_softc *sc = siotty_cd.cd_devs[minor(tp->t_dev)]; 197 int s, c; 198 199 s = spltty(); 200 if (tp->t_state & (TS_BUSY|TS_TIMEOUT|TS_TTSTOP)) 201 goto out; 202 if (tp->t_outq.c_cc <= tp->t_lowat) { 203 if (tp->t_state & TS_ASLEEP) { 204 tp->t_state &= ~TS_ASLEEP; 205 wakeup((caddr_t)&tp->t_outq); 206 } 207 selwakeup(&tp->t_wsel); 208 } 209 if (tp->t_outq.c_cc == 0) 210 goto out; 211 212 tp->t_state |= TS_BUSY; 213 while (getsiocsr(sc->sc_ctl) & RR_TXRDY) { 214 if ((c = getc(&tp->t_outq)) == -1) 215 break; 216 sc->sc_ctl->sio_data = c; 217 } 218 out: 219 splx(s); 220 } 221 222 int 223 siostop(tp, flag) 224 struct tty *tp; 225 int flag; 226 { 227 int s; 228 229 s = spltty(); 230 if (TS_BUSY == (tp->t_state & (TS_BUSY|TS_TTSTOP))) { 231 /* 232 * Device is transmitting; must stop it. 233 */ 234 tp->t_state |= TS_FLUSH; 235 } 236 splx(s); 237 return (0); 238 } 239 240 int 241 sioparam(tp, t) 242 struct tty *tp; 243 struct termios *t; 244 { 245 struct siotty_softc *sc = siotty_cd.cd_devs[minor(tp->t_dev)]; 246 int wr4, s; 247 248 if (t->c_ispeed && t->c_ispeed != t->c_ospeed) 249 return EINVAL; 250 wr4 = ttspeedtab(t->c_ospeed, siospeedtab); 251 if (wr4 < 0) 252 return EINVAL; 253 254 if (sc->sc_flags & TIOCFLAG_SOFTCAR) { 255 t->c_cflag |= CLOCAL; 256 t->c_cflag &= ~HUPCL; 257 } 258 if (sc->sc_flags & TIOCFLAG_CLOCAL) 259 t->c_cflag |= CLOCAL; 260 261 /* 262 * If there were no changes, don't do anything. This avoids dropping 263 * input and improves performance when all we did was frob things like 264 * VMIN and VTIME. 265 */ 266 if (tp->t_ospeed == t->c_ospeed && tp->t_cflag == t->c_cflag) 267 return 0; 268 269 tp->t_ispeed = t->c_ispeed; 270 tp->t_ospeed = t->c_ospeed; 271 tp->t_cflag = t->c_cflag; 272 273 sc->sc_wr[WR3] &= 0x3f; 274 sc->sc_wr[WR5] &= 0x9f; 275 switch (tp->t_cflag & CSIZE) { 276 case CS7: 277 sc->sc_wr[WR3] |= WR3_RX7BIT; sc->sc_wr[WR5] |= WR5_TX7BIT; 278 break; 279 case CS8: 280 sc->sc_wr[WR3] |= WR3_RX8BIT; sc->sc_wr[WR5] |= WR5_TX8BIT; 281 break; 282 } 283 if (tp->t_cflag & PARENB) { 284 wr4 |= WR4_PARENAB; 285 if ((tp->t_cflag & PARODD) == 0) 286 wr4 |= WR4_EPARITY; 287 } 288 wr4 |= (tp->t_cflag & CSTOPB) ? WR4_STOP2 : WR4_STOP1; 289 sc->sc_wr[WR4] = wr4; 290 291 s = spltty(); 292 setsioreg(sc->sc_ctl, WR4, sc->sc_wr[WR4]); 293 setsioreg(sc->sc_ctl, WR3, sc->sc_wr[WR3]); 294 setsioreg(sc->sc_ctl, WR5, sc->sc_wr[WR5]); 295 splx(s); 296 297 return 0; 298 } 299 300 int 301 siomctl(sc, control, op) 302 struct siotty_softc *sc; 303 int control, op; 304 { 305 int val, s, wr5, rr; 306 307 val = 0; 308 if (control & TIOCM_BREAK) 309 val |= WR5_BREAK; 310 if (control & TIOCM_DTR) 311 val |= WR5_DTR; 312 if (control & TIOCM_RTS) 313 val |= WR5_RTS; 314 s = spltty(); 315 wr5 = sc->sc_wr[WR5]; 316 switch (op) { 317 case DMSET: 318 wr5 &= ~(WR5_BREAK|WR5_DTR|WR5_RTS); 319 /* FALLTHROUGH */ 320 case DMBIS: 321 wr5 |= val; 322 break; 323 case DMBIC: 324 wr5 &= ~val; 325 break; 326 case DMGET: 327 val = 0; 328 rr = getsiocsr(sc->sc_ctl); 329 if (wr5 & WR5_DTR) 330 val |= TIOCM_DTR; 331 if (wr5 & WR5_RTS) 332 val |= TIOCM_RTS; 333 if (rr & RR_CTS) 334 val |= TIOCM_CTS; 335 if (rr & RR_DCD) 336 val |= TIOCM_CD; 337 goto done; 338 } 339 sc->sc_wr[WR5] = wr5; 340 setsioreg(sc->sc_ctl, WR5, wr5); 341 val = 0; 342 done: 343 splx(s); 344 return val; 345 } 346 347 /*-------------------- cdevsw[] interface --------------------*/ 348 349 int 350 sioopen(dev, flag, mode, p) 351 dev_t dev; 352 int flag, mode; 353 struct proc *p; 354 { 355 struct siotty_softc *sc; 356 struct tty *tp; 357 int error; 358 359 if ((sc = siotty_cd.cd_devs[minor(dev)]) == NULL) 360 return ENXIO; 361 if ((tp = sc->sc_tty) == NULL) { 362 tp = sc->sc_tty = ttymalloc(); 363 } 364 else if ((tp->t_state & TS_ISOPEN) && (tp->t_state & TS_XCLUDE) 365 && suser(p, 0) != 0) 366 return EBUSY; 367 368 tp->t_oproc = siostart; 369 tp->t_param = sioparam; 370 tp->t_hwiflow = NULL /* XXX siohwiflow XXX */; 371 tp->t_dev = dev; 372 if ((tp->t_state & TS_ISOPEN) == 0) { 373 struct termios t; 374 375 t.c_ispeed = t.c_ospeed = TTYDEF_SPEED; 376 t.c_cflag = TTYDEF_CFLAG; 377 tp->t_ospeed = 0; /* force register update */ 378 (void)sioparam(tp, &t); 379 tp->t_iflag = TTYDEF_IFLAG; 380 tp->t_oflag = TTYDEF_OFLAG; 381 tp->t_lflag = TTYDEF_LFLAG; 382 ttychars(tp); 383 ttsetwater(tp); 384 /* raise RTS and DTR here; but, DTR lead is not wired */ 385 /* then check DCD condition; but, DCD lead is not wired */ 386 tp->t_state |= TS_CARR_ON; /* assume detected all the time */ 387 #if 0 388 if ((sc->sc_flags & TIOCFLAG_SOFTCAR) 389 || (tp->t_cflag & MDMBUF) 390 || (getsiocsr(sc->sc_ctl) & RR_DCD)) 391 tp->t_state |= TS_CARR_ON; 392 else 393 tp->t_state &= ~TS_CARR_ON; 394 #endif 395 } 396 397 error = ttyopen(dev, tp); 398 if (error > 0) 399 return error; 400 /* 401 return (*tp->t_linesw->l_open)(dev, tp); 402 */ 403 return (*linesw[tp->t_line].l_open)(dev, tp, p); 404 } 405 406 int 407 sioclose(dev, flag, mode, p) 408 dev_t dev; 409 int flag, mode; 410 struct proc *p; 411 { 412 struct siotty_softc *sc = siotty_cd.cd_devs[minor(dev)]; 413 struct tty *tp = sc->sc_tty; 414 int s; 415 416 /* 417 (*tp->t_linesw->l_close)(tp, flag); 418 */ 419 (*linesw[tp->t_line].l_close)(tp, flag, p); 420 421 s = spltty(); 422 siomctl(sc, TIOCM_BREAK, DMBIC); 423 #if 0 /* because unable to feed DTR signal */ 424 if ((tp->t_cflag & HUPCL) 425 || tp->t_wopen || (tp->t_state & TS_ISOPEN) == 0) { 426 siomctl(sc, TIOCM_DTR, DMBIC); 427 /* Yield CPU time to others for 1 second, then ... */ 428 siomctl(sc, TIOCM_DTR, DMBIS); 429 } 430 #endif 431 splx(s); 432 return ttyclose(tp); 433 } 434 435 int 436 sioread(dev, uio, flag) 437 dev_t dev; 438 struct uio *uio; 439 int flag; 440 { 441 struct siotty_softc *sc = siotty_cd.cd_devs[minor(dev)]; 442 struct tty *tp = sc->sc_tty; 443 444 /* 445 return (*tp->t_linesw->l_read)(tp, uio, flag); 446 */ 447 return (*linesw[tp->t_line].l_read)(tp, uio, flag); 448 } 449 450 int 451 siowrite(dev, uio, flag) 452 dev_t dev; 453 struct uio *uio; 454 int flag; 455 { 456 struct siotty_softc *sc = siotty_cd.cd_devs[minor(dev)]; 457 struct tty *tp = sc->sc_tty; 458 459 /* 460 return (*tp->t_linesw->l_write)(tp, uio, flag); 461 */ 462 return (*linesw[tp->t_line].l_write)(tp, uio, flag); 463 } 464 465 #if 0 466 int 467 sioselect(dev, events, p) 468 dev_t dev; 469 int events; 470 struct proc *p; 471 { 472 struct siotty_softc *sc = siotty_cd.cd_devs[minor(dev)]; 473 struct tty *tp = sc->sc_tty; 474 475 /* 476 return ((*tp->t_linesw->l_poll)(tp, events, p)); 477 */ 478 return ((*linesw[tp->t_line].l_select)(tp, events, p)); 479 480 } 481 #endif 482 483 int 484 sioioctl(dev, cmd, data, flag, p) 485 dev_t dev; 486 u_long cmd; 487 caddr_t data; 488 int flag; 489 struct proc *p; 490 { 491 struct siotty_softc *sc = siotty_cd.cd_devs[minor(dev)]; 492 struct tty *tp = sc->sc_tty; 493 int error; 494 495 /* 496 error = (*tp->t_linesw->l_ioctl)(tp, cmd, data, flag, p); 497 */ 498 error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p); 499 if (error >= 0) 500 return error; 501 502 error = ttioctl(tp, cmd, data, flag, p); 503 if (error >= 0) 504 return error; 505 506 /* the last resort for TIOC ioctl tranversing */ 507 switch (cmd) { 508 case TIOCSBRK: /* Set the hardware into BREAK condition */ 509 siomctl(sc, TIOCM_BREAK, DMBIS); 510 break; 511 case TIOCCBRK: /* Clear the hardware BREAK condition */ 512 siomctl(sc, TIOCM_BREAK, DMBIC); 513 break; 514 case TIOCSDTR: /* Assert DTR signal */ 515 siomctl(sc, TIOCM_DTR|TIOCM_RTS, DMBIS); 516 break; 517 case TIOCCDTR: /* Clear DTR signal */ 518 siomctl(sc, TIOCM_DTR|TIOCM_RTS, DMBIC); 519 break; 520 case TIOCMSET: /* Set modem state replacing current one */ 521 siomctl(sc, *(int *)data, DMSET); 522 break; 523 case TIOCMGET: /* Return current modem state */ 524 *(int *)data = siomctl(sc, 0, DMGET); 525 break; 526 case TIOCMBIS: /* Set individual bits of modem state */ 527 siomctl(sc, *(int *)data, DMBIS); 528 break; 529 case TIOCMBIC: /* Clear individual bits of modem state */ 530 siomctl(sc, *(int *)data, DMBIC); 531 break; 532 case TIOCSFLAGS: /* Instruct how serial port behaves */ 533 error = suser(p, 0); 534 if (error != 0) 535 return EPERM; 536 sc->sc_flags = *(int *)data; 537 break; 538 case TIOCGFLAGS: /* Return current serial port state */ 539 *(int *)data = sc->sc_flags; 540 break; 541 default: 542 /* 543 return EPASSTHROUGH; 544 */ 545 return ENOTTY; 546 } 547 return 0; 548 } 549 550 /* ARSGUSED */ 551 struct tty * 552 siotty(dev) 553 dev_t dev; 554 { 555 struct siotty_softc *sc = siotty_cd.cd_devs[minor(dev)]; 556 557 return sc->sc_tty; 558 } 559 560 /*-------------------- miscellaneous routines --------------------*/ 561 562 /* EXPORT */ void 563 setsioreg(sio, regno, val) 564 struct sioreg *sio; 565 int regno, val; 566 { 567 if (regno != 0) 568 sio->sio_cmd = regno; /* DELAY(); */ 569 sio->sio_cmd = val; /* DELAY(); */ 570 } 571 572 /* EXPORT */ int 573 getsiocsr(sio) 574 struct sioreg *sio; 575 { 576 int val; 577 578 val = sio->sio_stat << 8; /* DELAY(); */ 579 sio->sio_cmd = 1; /* DELAY(); */ 580 val |= sio->sio_stat; /* DELAY(); */ 581 return val; 582 } 583 584 /*--------------------- console interface ----------------------*/ 585 586 void syscnattach(int); 587 int syscngetc(dev_t); 588 void syscnputc(dev_t, int); 589 590 struct consdev syscons = { 591 NULL, 592 NULL, 593 syscngetc, 594 syscnputc, 595 nullcnpollc, 596 NULL, 597 NODEV, 598 CN_HIGHPRI, 599 }; 600 601 /* EXPORT */ void 602 syscnattach(channel) 603 int channel; 604 { 605 /* 606 * Channel A is immediately initialized with 9600N1 right after cold 607 * boot/reset/poweron. ROM monitor emits one line message on CH.A. 608 */ 609 struct sioreg *sio; 610 sio = (struct sioreg *)0x51000000 + channel; 611 612 /* syscons.cn_dev = makedev(7, channel); */ 613 syscons.cn_dev = makedev(12, channel); 614 cn_tab = &syscons; 615 616 #if 0 617 setsioreg(sio, WR0, WR0_CHANRST); 618 setsioreg(sio, WR2A, WR2_VEC86 | WR2_INTR_1); 619 setsioreg(sio, WR2B, 0); 620 setsioreg(sio, WR0, ch0_regs[WR0]); 621 setsioreg(sio, WR4, ch0_regs[WR4]); 622 setsioreg(sio, WR3, ch0_regs[WR3]); 623 setsioreg(sio, WR5, ch0_regs[WR5]); 624 setsioreg(sio, WR0, ch0_regs[WR0]); 625 #endif 626 } 627 628 /* EXPORT */ int 629 syscngetc(dev) 630 dev_t dev; 631 { 632 struct sioreg *sio; 633 int s, c; 634 635 sio = (struct sioreg *)0x51000000 + ((int)dev & 0x1); 636 s = splhigh(); 637 while ((getsiocsr(sio) & RR_RXRDY) == 0) 638 ; 639 c = sio->sio_data; 640 splx(s); 641 642 return c; 643 } 644 645 /* EXPORT */ void 646 syscnputc(dev, c) 647 dev_t dev; 648 int c; 649 { 650 struct sioreg *sio; 651 int s; 652 653 sio = (struct sioreg *)0x51000000 + ((int)dev & 0x1); 654 s = splhigh(); 655 while ((getsiocsr(sio) & RR_TXRDY) == 0) 656 ; 657 sio->sio_cmd = WR0_RSTPEND; 658 sio->sio_data = c; 659 splx(s); 660 } 661