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