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