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