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