1 /* $NetBSD: gftty.c,v 1.9 2026/11/10 03:05:01 thorpej Exp $ */ 2 3 /*- 4 * Copyright (c) 2023, 2024 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Jason R. Thorpe. 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 /* 33 * Support for the Goldfish virtual TTY. 34 */ 35 36 #include <sys/cdefs.h> 37 __KERNEL_RCSID(0, "$NetBSD: gftty.c,v 1.9 2026/11/10 03:05:01 thorpej Exp $"); 38 39 #include <sys/param.h> 40 #include <sys/conf.h> 41 #include <sys/fcntl.h> 42 #include <sys/systm.h> 43 #include <sys/bus.h> 44 #include <sys/device.h> 45 #include <sys/kauth.h> 46 #include <sys/kmem.h> 47 #include <sys/tty.h> 48 49 #include <uvm/uvm_extern.h> 50 51 #include <dev/cons.h> 52 53 #include <dev/goldfish/gfttyvar.h> 54 55 #include "ioconf.h" 56 57 /* 58 * Goldfish TTY registers. 59 */ 60 #define GFTTY_PUT_CHAR 0x00 /* 8 bit output value */ 61 #define GFTTY_BYTES_READY 0x04 /* number of input bytes available */ 62 #define GFTTY_CMD 0x08 /* command */ 63 #define GFTTY_DATA_PTR 0x10 /* DMA pointer */ 64 #define GFTTY_DATA_LEN 0x14 /* DMA length */ 65 #define GFTTY_DATA_PTR_HIGH 0x18 /* DMA pointer (64-bit) */ 66 #define GFTTY_VERSION 0x20 /* TTY version */ 67 68 #define CMD_INT_DISABLE 0x00 69 #define CMD_INT_ENABLE 0x01 70 #define CMD_WRITE_BUFFER 0x02 71 #define CMD_READ_BUFFER 0x03 72 73 #define REG_READ0(c, r) \ 74 bus_space_read_4((c)->c_bst, (c)->c_bsh, (r)) 75 #define REG_WRITE0(c, r, v) \ 76 bus_space_write_4((c)->c_bst, (c)->c_bsh, (r), (v)) 77 78 #define REG_READ(sc, r) REG_READ0((sc)->sc_config, (r)) 79 #define REG_WRITE(sc, r, v) REG_WRITE0((sc)->sc_config, (r), (v)) 80 81 static int gftty_cngetc(dev_t); 82 static void gftty_cnputc(dev_t, int); 83 static void gftty_cnpollc(dev_t, int); 84 85 static struct gftty_config gftty_cnconfig; 86 static struct cnm_state gftty_cnmagic_state; 87 static struct consdev gftty_consdev = { 88 .cn_getc = gftty_cngetc, 89 .cn_putc = gftty_cnputc, 90 .cn_pollc = gftty_cnpollc, 91 .cn_dev = NODEV, 92 .cn_pri = CN_NORMAL, 93 }; 94 95 static dev_type_open(gftty_open); 96 static dev_type_close(gftty_close); 97 static dev_type_read(gftty_read); 98 static dev_type_write(gftty_write); 99 static dev_type_ioctl(gftty_ioctl); 100 static dev_type_stop(gftty_stop); 101 static dev_type_tty(gftty_tty); 102 static dev_type_poll(gftty_poll); 103 104 const struct cdevsw gftty_cdevsw = { 105 .d_open = gftty_open, 106 .d_close = gftty_close, 107 .d_read = gftty_read, 108 .d_write = gftty_write, 109 .d_ioctl = gftty_ioctl, 110 .d_stop = gftty_stop, 111 .d_tty = gftty_tty, 112 .d_poll = gftty_poll, 113 .d_mmap = nommap, 114 .d_kqfilter = ttykqfilter, 115 .d_discard = nodiscard, 116 .d_flag = D_TTY, 117 }; 118 119 static void gftty_start(struct tty *); 120 static int gftty_param_locked(struct tty *, struct termios *); 121 static int gftty_param(struct tty *, struct termios *); 122 123 static void gftty_softrx(void *); 124 125 #define GFTTY_UNIT(x) minor(x) 126 #define GFTTY_DMASIZE (64 * 1024) /* XXX TTY_MAXQSIZE */ 127 #define GFTTY_MAXSEGS ((GFTTY_DMASIZE / PAGE_SIZE) + 1) 128 #define GFTTY_RXBUFSIZE 128 129 #define GFTTY_RXBUFALLOC (128 << 1) 130 131 static void 132 gftty_reset_rxptrs(struct gftty_softc *sc) 133 { 134 sc->sc_rxpos = 0; 135 sc->sc_rxcur = 0; 136 sc->sc_rxbuf = sc->sc_rxbufs[sc->sc_rxcur]; 137 sc->sc_rxaddr = sc->sc_rxaddrs[sc->sc_rxcur]; 138 } 139 140 /* 141 * gftty_attach -- 142 * Attach a Goldfish virtual TTY. 143 */ 144 void 145 gftty_attach(struct gftty_softc *sc) 146 { 147 device_t self = sc->sc_dev; 148 int error; 149 bool is_console; 150 151 aprint_naive("\n"); 152 aprint_normal(": Google Goldfish TTY\n"); 153 154 /* If we got here without a config, we're the console. */ 155 if ((is_console = (sc->sc_config == NULL))) { 156 KASSERT(gftty_is_console(sc)); 157 sc->sc_config = &gftty_cnconfig; 158 aprint_normal_dev(sc->sc_dev, "console\n"); 159 } 160 161 if (sc->sc_config->c_version == 0) { 162 aprint_normal_dev(self, 163 "WARNING: version 0 device -- uncharted territory!\n"); 164 } 165 166 /* Register our Rx soft interrupt. */ 167 sc->sc_rx_si = softint_establish(SOFTINT_SERIAL, gftty_softrx, sc); 168 if (sc->sc_rx_si == NULL) { 169 aprint_error_dev(self, 170 "Unable to register software interrupt.\n"); 171 return; 172 } 173 174 error = bus_dmamap_create(sc->sc_dmat, GFTTY_DMASIZE, 175 GFTTY_MAXSEGS, GFTTY_DMASIZE, 0, BUS_DMA_WAITOK, 176 &sc->sc_tx_dma); 177 if (error != 0) { 178 aprint_error_dev(self, 179 "unable to create Tx DMA map, error %d.\n", error); 180 return; 181 } 182 error = bus_dmamap_create(sc->sc_dmat, GFTTY_RXBUFALLOC, 183 1, GFTTY_RXBUFALLOC, 0, BUS_DMA_WAITOK, 184 &sc->sc_rx_dma); 185 if (error != 0) { 186 aprint_error_dev(self, 187 "unable to create Rx DMA map, error %d.\n", error); 188 bus_dmamap_destroy(sc->sc_dmat, sc->sc_tx_dma); 189 sc->sc_tx_dma = NULL; 190 return; 191 } 192 193 sc->sc_rxbuf = kmem_zalloc(GFTTY_RXBUFALLOC, KM_SLEEP); 194 error = bus_dmamap_load(sc->sc_dmat, sc->sc_rx_dma, 195 sc->sc_rxbuf, GFTTY_RXBUFALLOC, NULL, BUS_DMA_WAITOK); 196 if (error != 0) { 197 aprint_error_dev(self, 198 "unable to load Rx DMA map, error %d.\n", error); 199 kmem_free(sc->sc_rxbuf, GFTTY_RXBUFALLOC); 200 bus_dmamap_destroy(sc->sc_dmat, sc->sc_rx_dma); 201 sc->sc_rx_dma = NULL; 202 bus_dmamap_destroy(sc->sc_dmat, sc->sc_tx_dma); 203 sc->sc_tx_dma = NULL; 204 return; 205 } 206 sc->sc_rxbufs[0] = sc->sc_rxbuf; 207 sc->sc_rxbufs[1] = sc->sc_rxbufs[0] + GFTTY_RXBUFSIZE; 208 if (sc->sc_config->c_version == 0) { 209 sc->sc_rxaddrs[0] = (bus_addr_t)sc->sc_rxbufs[0]; 210 } else { 211 sc->sc_rxaddrs[0] = sc->sc_rx_dma->dm_segs[0].ds_addr; 212 } 213 sc->sc_rxaddrs[1] = sc->sc_rxaddrs[0] + GFTTY_RXBUFSIZE; 214 gftty_reset_rxptrs(sc); 215 216 struct tty *tp = tty_alloc(); 217 tp->t_oproc = gftty_start; 218 tp->t_param = gftty_param; 219 tp->t_softc = sc; 220 221 mutex_init(&sc->sc_hwlock, MUTEX_DEFAULT, IPL_TTY); 222 223 if (is_console) { 224 /* Locate the major number. */ 225 int maj = cdevsw_lookup_major(&gftty_cdevsw); 226 tp->t_dev = cn_tab->cn_dev = makedev(maj, device_unit(self)); 227 } 228 229 mutex_spin_enter(&tty_lock); 230 sc->sc_tty = tp; 231 mutex_spin_exit(&tty_lock); 232 233 tty_attach(tp); 234 } 235 236 /* 237 * gftty_is_console -- 238 * Returns true if the specified gftty instance is currently 239 * the console. 240 */ 241 bool 242 gftty_is_console(struct gftty_softc *sc) 243 { 244 if (cn_tab == &gftty_consdev) { 245 return device_getprop_bool(sc->sc_dev, "is-console"); 246 } 247 return false; 248 } 249 250 /* 251 * gftty_init_config -- 252 * Initialize a config structure. 253 */ 254 static void 255 gftty_init_config(struct gftty_config *c, bus_space_tag_t bst, 256 bus_space_handle_t bsh) 257 { 258 c->c_bst = bst; 259 c->c_bsh = bsh; 260 c->c_version = REG_READ0(c, GFTTY_VERSION); 261 } 262 263 /* 264 * gftty_alloc_config -- 265 * Allocate a config structure, initialize it, and assign 266 * it to this device. 267 */ 268 void 269 gftty_alloc_config(struct gftty_softc *sc, bus_space_tag_t bst, 270 bus_space_handle_t bsh) 271 { 272 struct gftty_config *c = kmem_zalloc(sizeof(*c), KM_SLEEP); 273 274 gftty_init_config(c, bst, bsh); 275 sc->sc_config = c; 276 } 277 278 /* 279 * gftty_set_buffer -- 280 * Set the buffer address / length for an I/O operation. 281 */ 282 static void 283 gftty_set_buffer(struct gftty_config *c, bus_addr_t addr, bus_size_t size) 284 { 285 REG_WRITE0(c, GFTTY_DATA_PTR, BUS_ADDR_LO32(addr)); 286 if (sizeof(bus_addr_t) == 8) { 287 REG_WRITE0(c, GFTTY_DATA_PTR_HIGH, BUS_ADDR_HI32(addr)); 288 } 289 REG_WRITE0(c, GFTTY_DATA_LEN, (uint32_t)size); 290 } 291 292 /* 293 * gftty_flush -- 294 * Flush input bytes. 295 */ 296 static bool 297 gftty_flush(struct gftty_softc *sc) 298 { 299 uint32_t count; 300 bool claimed = false; 301 302 KASSERT(ttylocked(sc->sc_tty)); 303 304 mutex_spin_enter(&sc->sc_hwlock); 305 306 while ((count = REG_READ(sc, GFTTY_BYTES_READY)) != 0) { 307 claimed = true; 308 if (count > GFTTY_RXBUFALLOC) { 309 count = GFTTY_RXBUFALLOC; 310 } 311 gftty_set_buffer(sc->sc_config, 312 sc->sc_rx_dma->dm_segs[0].ds_addr, count); 313 REG_WRITE(sc, GFTTY_CMD, CMD_READ_BUFFER); 314 } 315 316 mutex_spin_exit(&sc->sc_hwlock); 317 318 gftty_reset_rxptrs(sc); 319 320 return claimed; 321 } 322 323 /* 324 * gftty_rx -- 325 * Receive from the virtual TTY. 326 */ 327 static bool 328 gftty_rx(struct gftty_softc *sc) 329 { 330 uint32_t count, avail; 331 bool claimed = false; 332 333 KASSERT(ttylocked(sc->sc_tty)); 334 335 mutex_spin_enter(&sc->sc_hwlock); 336 337 count = REG_READ(sc, GFTTY_BYTES_READY); 338 if (count != 0) { 339 claimed = true; 340 avail = GFTTY_RXBUFSIZE - sc->sc_rxpos; 341 if (count > avail) { 342 /* 343 * Receive what we can, but disable the interrupt 344 * until the buffer can be drained. 345 */ 346 REG_WRITE(sc, GFTTY_CMD, CMD_INT_DISABLE); 347 count = avail; 348 } 349 if (count != 0) { 350 bus_addr_t syncoff = 351 (sc->sc_rxaddr - sc->sc_rxaddrs[0]) + sc->sc_rxpos; 352 353 bus_dmamap_sync(sc->sc_dmat, sc->sc_rx_dma, 354 syncoff, count, BUS_DMASYNC_PREREAD); 355 gftty_set_buffer(sc->sc_config, 356 sc->sc_rxaddr + sc->sc_rxpos, count); 357 REG_WRITE(sc, GFTTY_CMD, CMD_READ_BUFFER); 358 sc->sc_rxpos += count; 359 bus_dmamap_sync(sc->sc_dmat, sc->sc_rx_dma, 360 syncoff, count, BUS_DMASYNC_POSTREAD); 361 } 362 softint_schedule(sc->sc_rx_si); 363 } 364 365 mutex_spin_exit(&sc->sc_hwlock); 366 367 return claimed; 368 } 369 370 /* 371 * gftty_softrx -- 372 * Software interrupt to complete Rx processing. 373 */ 374 static void 375 gftty_softrx(void *v) 376 { 377 struct gftty_softc *sc = v; 378 struct tty *tp = sc->sc_tty; 379 int i, len; 380 unsigned char *cp, ch; 381 382 ttylock(tp); 383 cp = sc->sc_rxbuf; 384 len = sc->sc_rxpos; 385 sc->sc_rxcur ^= 1; 386 sc->sc_rxbuf = sc->sc_rxbufs[sc->sc_rxcur]; 387 sc->sc_rxaddr = sc->sc_rxaddrs[sc->sc_rxcur]; 388 sc->sc_rxpos = 0; 389 if (ISSET(tp->t_state, TS_ISOPEN)) { 390 REG_WRITE(sc, GFTTY_CMD, CMD_INT_ENABLE); 391 } 392 ttyunlock(tp); 393 394 for (i = 0; i < len; i++) { 395 ch = *cp++; 396 if (cn_check_magic(tp->t_dev, ch, &gftty_cnmagic_state)) { 397 continue; 398 } 399 (*tp->t_linesw->l_rint)(ch, tp); 400 } 401 } 402 403 /* 404 * gftty_intr -- 405 * Interrupt service routine. 406 */ 407 int 408 gftty_intr(void *v) 409 { 410 struct gftty_softc *sc = v; 411 struct tty *tp = sc->sc_tty; 412 bool claimed; 413 414 ttylock(tp); 415 if (ISSET(tp->t_state, TS_ISOPEN)) { 416 claimed = gftty_rx(sc); 417 } else { 418 claimed = gftty_flush(sc); 419 } 420 ttyunlock(tp); 421 422 return claimed; 423 } 424 425 /* 426 * gftty_open -- 427 * cdevsw open routine. 428 */ 429 static int 430 gftty_open(dev_t dev, int flag, int mode, struct lwp *l) 431 { 432 struct gftty_softc *sc = 433 device_lookup_private(&gftty_cd, GFTTY_UNIT(dev)); 434 struct tty *tp; 435 436 if (sc == NULL) { 437 return ENXIO; 438 } 439 440 mutex_spin_enter(&tty_lock); 441 tp = sc->sc_tty; 442 mutex_spin_exit(&tty_lock); 443 if (tp == NULL) { 444 return ENXIO; 445 } 446 447 if (kauth_authorize_device_tty(l->l_cred, KAUTH_DEVICE_TTY_OPEN, tp)) { 448 return EBUSY; 449 } 450 451 ttylock(tp); 452 453 if (ISSET(tp->t_state, TS_KERN_ONLY)) { 454 ttyunlock(tp); 455 return EBUSY; 456 } 457 458 tp->t_oproc = gftty_start; 459 tp->t_param = gftty_param; 460 tp->t_dev = dev; 461 462 if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) { 463 struct termios t; 464 465 ttychars(tp); 466 tp->t_iflag = TTYDEF_IFLAG; 467 tp->t_oflag = TTYDEF_OFLAG; 468 tp->t_lflag = TTYDEF_LFLAG; 469 t.c_cflag = TTYDEF_CFLAG; 470 t.c_ispeed = t.c_ospeed = TTYDEF_SPEED; 471 (void) gftty_param_locked(tp, &t); 472 ttsetwater(tp); 473 474 gftty_flush(sc); 475 REG_WRITE(sc, GFTTY_CMD, CMD_INT_ENABLE); 476 } 477 SET(tp->t_state, TS_CARR_ON); 478 479 ttyunlock(tp); 480 481 int error = ttyopen(tp, 0, ISSET(flag, O_NONBLOCK)); 482 if (error == 0) { 483 error = (*tp->t_linesw->l_open)(dev, tp); 484 if (error != 0) { 485 ttyclose(tp); 486 } 487 } 488 489 if (error != 0 && 490 !ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) { 491 REG_WRITE(sc, GFTTY_CMD, CMD_INT_DISABLE); 492 } 493 494 return error; 495 } 496 497 /* 498 * gftty_close -- 499 * cdevsw close routine. 500 */ 501 static int 502 gftty_close(dev_t dev, int flag, int mode, struct lwp *l) 503 { 504 struct gftty_softc *sc = 505 device_lookup_private(&gftty_cd, GFTTY_UNIT(dev)); 506 507 KASSERT(sc != NULL); 508 509 struct tty *tp = sc->sc_tty; 510 511 ttylock(tp); 512 513 /* XXX This is for cons.c. */ 514 if (!ISSET(tp->t_state, TS_ISOPEN)) { 515 ttyunlock(tp); 516 return 0; 517 } 518 519 if (ISSET(tp->t_state, TS_KERN_ONLY)) { 520 ttyunlock(tp); 521 return 0; 522 } 523 524 ttyunlock(tp); 525 526 (*tp->t_linesw->l_close)(tp, flag); 527 ttyclose(tp); 528 529 ttylock(tp); 530 if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) { 531 REG_WRITE(sc, GFTTY_CMD, CMD_INT_DISABLE); 532 } 533 ttyunlock(tp); 534 535 return 0; 536 } 537 538 /* 539 * gftty_read -- 540 * cdevsw read routine. 541 */ 542 static int 543 gftty_read(dev_t dev, struct uio *uio, int flag) 544 { 545 struct gftty_softc *sc = 546 device_lookup_private(&gftty_cd, GFTTY_UNIT(dev)); 547 548 KASSERT(sc != NULL); 549 550 struct tty *tp = sc->sc_tty; 551 return (*tp->t_linesw->l_read)(tp, uio, flag); 552 } 553 554 /* 555 * gftty_write -- 556 * cdevsw write routine. 557 */ 558 static int 559 gftty_write(dev_t dev, struct uio *uio, int flag) 560 { 561 struct gftty_softc *sc = 562 device_lookup_private(&gftty_cd, GFTTY_UNIT(dev)); 563 564 KASSERT(sc != NULL); 565 566 struct tty *tp = sc->sc_tty; 567 return (*tp->t_linesw->l_write)(tp, uio, flag); 568 } 569 570 /* 571 * gftty_poll -- 572 * cdevsw poll routine. 573 */ 574 static int 575 gftty_poll(dev_t dev, int events, struct lwp *l) 576 { 577 struct gftty_softc *sc = 578 device_lookup_private(&gftty_cd, GFTTY_UNIT(dev)); 579 580 KASSERT(sc != NULL); 581 582 struct tty *tp = sc->sc_tty; 583 return (*tp->t_linesw->l_poll)(tp, events, l); 584 } 585 586 /* 587 * gftty_tty -- 588 * cdevsw tty routine. 589 */ 590 static struct tty * 591 gftty_tty(dev_t dev) 592 { 593 struct gftty_softc *sc = 594 device_lookup_private(&gftty_cd, GFTTY_UNIT(dev)); 595 596 KASSERT(sc != NULL); 597 598 return sc->sc_tty; 599 } 600 601 /* 602 * gftty_ioctl -- 603 * cdevsw ioctl routine. 604 */ 605 static int 606 gftty_ioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l) 607 { 608 struct gftty_softc *sc = 609 device_lookup_private(&gftty_cd, GFTTY_UNIT(dev)); 610 611 KASSERT(sc != NULL); 612 613 struct tty *tp = sc->sc_tty; 614 int error; 615 616 /* Do the line discipline ioctls first. */ 617 error = (*tp->t_linesw->l_ioctl)(tp, cmd, data, flag, l); 618 if (error != EPASSTHROUGH) { 619 return error; 620 } 621 622 /* Next, the TTY ioctls. */ 623 error = ttioctl(tp, cmd, data, flag, l); 624 if (error != EPASSTHROUGH) { 625 return error; 626 } 627 628 /* None at this layer. */ 629 return EPASSTHROUGH; 630 } 631 632 /* 633 * gftty_tx -- 634 * Transmit a buffer on the virtual TTY using DMA. 635 */ 636 static void 637 gftty_tx(struct gftty_softc *sc, void *buf, size_t len) 638 { 639 int error, i; 640 641 KASSERT(len <= GFTTY_DMASIZE); 642 643 error = bus_dmamap_load(sc->sc_dmat, sc->sc_tx_dma, buf, len, 644 NULL, BUS_DMA_NOWAIT); 645 if (error) { 646 /* XXX report error */ 647 return; 648 } 649 bus_dmamap_sync(sc->sc_dmat, sc->sc_tx_dma, 0, len, 650 BUS_DMASYNC_PREWRITE); 651 652 mutex_spin_enter(&sc->sc_hwlock); 653 for (i = 0; i < sc->sc_tx_dma->dm_nsegs; i++) { 654 gftty_set_buffer(sc->sc_config, 655 sc->sc_tx_dma->dm_segs[i].ds_addr, 656 sc->sc_tx_dma->dm_segs[i].ds_len); 657 REG_WRITE(sc, GFTTY_CMD, CMD_WRITE_BUFFER); 658 } 659 mutex_spin_exit(&sc->sc_hwlock); 660 661 bus_dmamap_sync(sc->sc_dmat, sc->sc_tx_dma, 0, len, 662 BUS_DMASYNC_POSTWRITE); 663 bus_dmamap_unload(sc->sc_dmat, sc->sc_tx_dma); 664 } 665 666 /* 667 * gftty_start -- 668 * TTY oproc routine. 669 */ 670 static void 671 gftty_start(struct tty *tp) 672 { 673 struct gftty_softc *sc = tp->t_softc; 674 u_char *tbuf; 675 int n; 676 677 KASSERT(ttylocked(tp)); 678 679 if (ISSET(tp->t_state, TS_BUSY | TS_TIMEOUT | TS_TTSTOP) || 680 ttypull(tp) == 0) { 681 return; 682 } 683 SET(tp->t_state, TS_BUSY); 684 685 /* 686 * Drain the output from the ring buffer. This will normally 687 * be one contiguous chunk, but we have to do it in two pieces 688 * when the ring wraps. 689 */ 690 691 n = ndqb(&tp->t_outq, 0); 692 tbuf = tp->t_outq.c_cf; 693 gftty_tx(sc, tbuf, n); 694 ndflush(&tp->t_outq, n); 695 696 if ((n = ndqb(&tp->t_outq, 0)) > 0) { 697 tbuf = tp->t_outq.c_cf; 698 gftty_tx(sc, tbuf, n); 699 ndflush(&tp->t_outq, n); 700 } 701 702 CLR(tp->t_state, TS_BUSY); 703 /* Come back if there's more to do. */ 704 if (ttypull(tp)) { 705 SET(tp->t_state, TS_TIMEOUT); 706 callout_schedule(&tp->t_rstrt_ch, (hz > 128) ? (hz / 128) : 1); 707 } 708 } 709 710 /* 711 * gftty_stop -- 712 * cdevsw stop routine. 713 */ 714 static void 715 gftty_stop(struct tty *tp, int flag) 716 { 717 KASSERT(ttylocked(tp)); 718 719 if (ISSET(tp->t_state, TS_BUSY)) { 720 if (!ISSET(tp->t_state, TS_TTSTOP)) { 721 SET(tp->t_state, TS_FLUSH); 722 } 723 } 724 } 725 726 /* 727 * gftty_param_locked -- 728 * Set TTY parameters. TTY must be locked. 729 */ 730 static int 731 gftty_param_locked(struct tty *tp, struct termios *t) 732 { 733 734 KASSERT(ttylocked(tp)); 735 736 tp->t_ispeed = t->c_ispeed; 737 tp->t_ospeed = t->c_ospeed; 738 tp->t_cflag = t->c_cflag; 739 740 return 0; 741 } 742 743 /* 744 * gftty_param -- 745 * TTY param routine. 746 */ 747 static int 748 gftty_param(struct tty *tp, struct termios *t) 749 { 750 int rv; 751 752 ttylock(tp); 753 rv = gftty_param_locked(tp, t); 754 ttyunlock(tp); 755 756 return rv; 757 } 758 759 /* 760 * gftty console routines. 761 */ 762 static int 763 gftty_cngetc(dev_t dev) 764 { 765 struct gftty_config * const c = &gftty_cnconfig; 766 767 if (REG_READ0(c, GFTTY_BYTES_READY) == 0) { 768 return -1; 769 } 770 771 /* 772 * XXX This is all terrible and should burn to the ground. 773 * XXX This device desperately needs to be improved with 774 * XXX a GET_CHAR register. 775 */ 776 bus_addr_t addr; 777 uint8_t buf[1]; 778 779 if (c->c_version == 0) { 780 addr = (bus_addr_t)buf; 781 } else { 782 addr = vtophys((vaddr_t)buf); 783 } 784 785 gftty_set_buffer(c, addr, sizeof(buf)); 786 REG_WRITE0(c, GFTTY_CMD, CMD_READ_BUFFER); 787 788 cn_check_magic(dev, buf[0], &gftty_cnmagic_state); 789 790 return buf[0]; 791 } 792 793 static void 794 gftty_cnputc(dev_t dev, int ch) 795 { 796 REG_WRITE0(&gftty_cnconfig, GFTTY_PUT_CHAR, (unsigned char)ch); 797 } 798 799 static void 800 gftty_cnpollc(dev_t dev, int on) 801 { 802 /* XXX */ 803 } 804 805 /* 806 * gftty_cnattach -- 807 * Attach a Goldfish virtual TTY console. 808 */ 809 void 810 gftty_cnattach(bus_space_tag_t bst, bus_space_handle_t bsh) 811 { 812 gftty_init_config(&gftty_cnconfig, bst, bsh); 813 814 cn_tab = &gftty_consdev; 815 cn_init_magic(&gftty_cnmagic_state); 816 cn_set_magic("+++++"); 817 } 818