Home | History | Annotate | Line # | Download | only in goldfish
      1 /*	$NetBSD: gftty.c,v 1.3 2024/01/06 17:52:43 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.3 2024/01/06 17:52:43 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 virual 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 		bool val;
    246 
    247 		if (prop_dictionary_get_bool(device_properties(sc->sc_dev),
    248 					     "is-console", &val)) {
    249 			return val;
    250 		}
    251 	}
    252 	return false;
    253 }
    254 
    255 /*
    256  * gftty_init_config --
    257  *	Initialize a config structure.
    258  */
    259 static void
    260 gftty_init_config(struct gftty_config *c, bus_space_tag_t bst,
    261     bus_space_handle_t bsh)
    262 {
    263 	c->c_bst = bst;
    264 	c->c_bsh = bsh;
    265 	c->c_version = REG_READ0(c, GFTTY_VERSION);
    266 }
    267 
    268 /*
    269  * gftty_alloc_config --
    270  *	Allocate a config structure, initialize it, and assign
    271  *	it to this device.
    272  */
    273 void
    274 gftty_alloc_config(struct gftty_softc *sc, bus_space_tag_t bst,
    275     bus_space_handle_t bsh)
    276 {
    277 	struct gftty_config *c = kmem_zalloc(sizeof(*c), KM_SLEEP);
    278 
    279 	gftty_init_config(c, bst, bsh);
    280 	sc->sc_config = c;
    281 }
    282 
    283 /*
    284  * gftty_set_buffer --
    285  *	Set the buffer address / length for an I/O operation.
    286  */
    287 static void
    288 gftty_set_buffer(struct gftty_config *c, bus_addr_t addr, bus_size_t size)
    289 {
    290 	REG_WRITE0(c, GFTTY_DATA_PTR, BUS_ADDR_LO32(addr));
    291 	if (sizeof(bus_addr_t) == 8) {
    292 		REG_WRITE0(c, GFTTY_DATA_PTR_HIGH, BUS_ADDR_HI32(addr));
    293 	}
    294 	REG_WRITE0(c, GFTTY_DATA_LEN, (uint32_t)size);
    295 }
    296 
    297 /*
    298  * gftty_flush --
    299  *	Flush input bytes.
    300  */
    301 static bool
    302 gftty_flush(struct gftty_softc *sc)
    303 {
    304 	uint32_t count;
    305 	bool claimed = false;
    306 
    307 	KASSERT(ttylocked(sc->sc_tty));
    308 
    309 	mutex_spin_enter(&sc->sc_hwlock);
    310 
    311 	while ((count = REG_READ(sc, GFTTY_BYTES_READY)) != 0) {
    312 		claimed = true;
    313 		if (count > GFTTY_RXBUFALLOC) {
    314 			count = GFTTY_RXBUFALLOC;
    315 		}
    316 		gftty_set_buffer(sc->sc_config,
    317 		    sc->sc_rx_dma->dm_segs[0].ds_addr, count);
    318 		REG_WRITE(sc, GFTTY_CMD, CMD_READ_BUFFER);
    319 	}
    320 
    321 	mutex_spin_exit(&sc->sc_hwlock);
    322 
    323 	gftty_reset_rxptrs(sc);
    324 
    325 	return claimed;
    326 }
    327 
    328 /*
    329  * gftty_rx --
    330  *	Receive from the virtual TTY.
    331  */
    332 static bool
    333 gftty_rx(struct gftty_softc *sc)
    334 {
    335 	uint32_t count, avail;
    336 	bool claimed = false;
    337 
    338 	KASSERT(ttylocked(sc->sc_tty));
    339 
    340 	mutex_spin_enter(&sc->sc_hwlock);
    341 
    342 	count = REG_READ(sc, GFTTY_BYTES_READY);
    343 	if (count != 0) {
    344 		claimed = true;
    345 		avail = GFTTY_RXBUFSIZE - sc->sc_rxpos;
    346 		if (count > avail) {
    347 			/*
    348 			 * Receive what we can, but disable the interrupt
    349 			 * until the buffer can be drained.
    350 			 */
    351 			REG_WRITE(sc, GFTTY_CMD, CMD_INT_DISABLE);
    352 			count = avail;
    353 		}
    354 		if (count != 0) {
    355 			bus_addr_t syncoff =
    356 			    (sc->sc_rxaddr - sc->sc_rxaddrs[0]) + sc->sc_rxpos;
    357 
    358 			bus_dmamap_sync(sc->sc_dmat, sc->sc_rx_dma,
    359 			    syncoff, count, BUS_DMASYNC_PREREAD);
    360 			gftty_set_buffer(sc->sc_config,
    361 			    sc->sc_rxaddr + sc->sc_rxpos, count);
    362 			REG_WRITE(sc, GFTTY_CMD, CMD_READ_BUFFER);
    363 			sc->sc_rxpos += count;
    364 			bus_dmamap_sync(sc->sc_dmat, sc->sc_rx_dma,
    365 			    syncoff, count, BUS_DMASYNC_POSTREAD);
    366 		}
    367 		softint_schedule(sc->sc_rx_si);
    368 	}
    369 
    370 	mutex_spin_exit(&sc->sc_hwlock);
    371 
    372 	return claimed;
    373 }
    374 
    375 /*
    376  * gftty_softrx --
    377  *	Software interrupt to comple Rx processing.
    378  */
    379 static void
    380 gftty_softrx(void *v)
    381 {
    382 	struct gftty_softc *sc = v;
    383 	struct tty *tp = sc->sc_tty;
    384 	int i, len;
    385 	char *cp;
    386 
    387 	ttylock(tp);
    388 	cp = sc->sc_rxbuf;
    389 	len = sc->sc_rxpos;
    390 	sc->sc_rxcur ^= 1;
    391 	sc->sc_rxbuf = sc->sc_rxbufs[sc->sc_rxcur];
    392 	sc->sc_rxaddr = sc->sc_rxaddrs[sc->sc_rxcur];
    393 	sc->sc_rxpos = 0;
    394 	if (ISSET(tp->t_state, TS_ISOPEN)) {
    395 		REG_WRITE(sc, GFTTY_CMD, CMD_INT_ENABLE);
    396 	}
    397 	ttyunlock(tp);
    398 
    399 	for (i = 0; i < len; i++) {
    400 		(*tp->t_linesw->l_rint)(*cp++, tp);
    401 	}
    402 }
    403 
    404 /*
    405  * gftty_intr --
    406  *	Interrupt service routine.
    407  */
    408 int
    409 gftty_intr(void *v)
    410 {
    411 	struct gftty_softc *sc = v;
    412 	struct tty *tp = sc->sc_tty;
    413 	bool claimed;
    414 
    415 	ttylock(tp);
    416 	if (ISSET(tp->t_state, TS_ISOPEN)) {
    417 		claimed = gftty_rx(sc);
    418 	} else {
    419 		claimed = gftty_flush(sc);
    420 	}
    421 	ttyunlock(tp);
    422 
    423 	return claimed;
    424 }
    425 
    426 /*
    427  * gftty_open --
    428  *	cdevsw open routine.
    429  */
    430 static int
    431 gftty_open(dev_t dev, int flag, int mode, struct lwp *l)
    432 {
    433 	struct gftty_softc *sc =
    434 	    device_lookup_private(&gftty_cd, GFTTY_UNIT(dev));
    435 	struct tty *tp;
    436 
    437 	if (sc == NULL) {
    438 		return ENXIO;
    439 	}
    440 
    441 	mutex_spin_enter(&tty_lock);
    442 	tp = sc->sc_tty;
    443 	mutex_spin_exit(&tty_lock);
    444 	if (tp == NULL) {
    445 		return ENXIO;
    446 	}
    447 
    448 	if (kauth_authorize_device_tty(l->l_cred, KAUTH_DEVICE_TTY_OPEN, tp)) {
    449 		return EBUSY;
    450 	}
    451 
    452 	ttylock(tp);
    453 
    454 	if (ISSET(tp->t_state, TS_KERN_ONLY)) {
    455 		ttyunlock(tp);
    456 		return EBUSY;
    457 	}
    458 
    459 	tp->t_oproc = gftty_start;
    460 	tp->t_param = gftty_param;
    461 	tp->t_dev = dev;
    462 
    463 	if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) {
    464 		struct termios t;
    465 
    466 		ttychars(tp);
    467 		tp->t_iflag = TTYDEF_IFLAG;
    468 		tp->t_oflag = TTYDEF_OFLAG;
    469 		tp->t_lflag = TTYDEF_LFLAG;
    470 		t.c_cflag = TTYDEF_CFLAG;
    471 		t.c_ispeed = t.c_ospeed = TTYDEF_SPEED;
    472 		(void) gftty_param_locked(tp, &t);
    473 		ttsetwater(tp);
    474 
    475 		gftty_flush(sc);
    476 		REG_WRITE(sc, GFTTY_CMD, CMD_INT_ENABLE);
    477 	}
    478 	SET(tp->t_state, TS_CARR_ON);
    479 
    480 	ttyunlock(tp);
    481 
    482 	int error = ttyopen(tp, 0, ISSET(flag, O_NONBLOCK));
    483 	if (error == 0) {
    484 		error = (*tp->t_linesw->l_open)(dev, tp);
    485 		if (error != 0) {
    486 			ttyclose(tp);
    487 		}
    488 	}
    489 
    490 	if (error != 0 &&
    491 	    !ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) {
    492 		REG_WRITE(sc, GFTTY_CMD, CMD_INT_DISABLE);
    493 	}
    494 
    495 	return error;
    496 }
    497 
    498 /*
    499  * gftty_close --
    500  *	cdevsw close routine.
    501  */
    502 static int
    503 gftty_close(dev_t dev, int flag, int mode, struct lwp *l)
    504 {
    505 	struct gftty_softc *sc =
    506 	    device_lookup_private(&gftty_cd, GFTTY_UNIT(dev));
    507 
    508 	KASSERT(sc != NULL);
    509 
    510 	struct tty *tp = sc->sc_tty;
    511 
    512 	ttylock(tp);
    513 
    514 	/* XXX This is for cons.c. */
    515 	if (!ISSET(tp->t_state, TS_ISOPEN)) {
    516 		ttyunlock(tp);
    517 		return 0;
    518 	}
    519 
    520 	if (ISSET(tp->t_state, TS_KERN_ONLY)) {
    521 		ttyunlock(tp);
    522 		return 0;
    523 	}
    524 
    525 	ttyunlock(tp);
    526 
    527 	(*tp->t_linesw->l_close)(tp, flag);
    528 	ttyclose(tp);
    529 
    530 	ttylock(tp);
    531 	if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) {
    532 		REG_WRITE(sc, GFTTY_CMD, CMD_INT_DISABLE);
    533 	}
    534 	ttyunlock(tp);
    535 
    536 	return 0;
    537 }
    538 
    539 /*
    540  * gftty_read --
    541  *	cdevsw read routine.
    542  */
    543 static int
    544 gftty_read(dev_t dev, struct uio *uio, int flag)
    545 {
    546 	struct gftty_softc *sc =
    547 	    device_lookup_private(&gftty_cd, GFTTY_UNIT(dev));
    548 
    549 	KASSERT(sc != NULL);
    550 
    551 	struct tty *tp = sc->sc_tty;
    552 	return (*tp->t_linesw->l_read)(tp, uio, flag);
    553 }
    554 
    555 /*
    556  * gftty_write --
    557  *	cdevsw write routine.
    558  */
    559 static int
    560 gftty_write(dev_t dev, struct uio *uio, int flag)
    561 {
    562 	struct gftty_softc *sc =
    563 	    device_lookup_private(&gftty_cd, GFTTY_UNIT(dev));
    564 
    565 	KASSERT(sc != NULL);
    566 
    567 	struct tty *tp = sc->sc_tty;
    568 	return (*tp->t_linesw->l_write)(tp, uio, flag);
    569 }
    570 
    571 /*
    572  * gftty_poll --
    573  *	cdevsw poll routine.
    574  */
    575 static int
    576 gftty_poll(dev_t dev, int events, struct lwp *l)
    577 {
    578 	struct gftty_softc *sc =
    579 	    device_lookup_private(&gftty_cd, GFTTY_UNIT(dev));
    580 
    581 	KASSERT(sc != NULL);
    582 
    583 	struct tty *tp = sc->sc_tty;
    584 	return (*tp->t_linesw->l_poll)(tp, events, l);
    585 }
    586 
    587 /*
    588  * gftty_tty --
    589  *	cdevsw tty routine.
    590  */
    591 static struct tty *
    592 gftty_tty(dev_t dev)
    593 {
    594 	struct gftty_softc *sc =
    595 	    device_lookup_private(&gftty_cd, GFTTY_UNIT(dev));
    596 
    597 	KASSERT(sc != NULL);
    598 
    599 	return sc->sc_tty;
    600 }
    601 
    602 /*
    603  * gftty_ioctl --
    604  *	cdevsw ioctl routine.
    605  */
    606 static int
    607 gftty_ioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
    608 {
    609 	struct gftty_softc *sc =
    610 	    device_lookup_private(&gftty_cd, GFTTY_UNIT(dev));
    611 
    612 	KASSERT(sc != NULL);
    613 
    614 	struct tty *tp = sc->sc_tty;
    615 	int error;
    616 
    617 	/* Do the line discipline ioctls first. */
    618 	error = (*tp->t_linesw->l_ioctl)(tp, cmd, data, flag, l);
    619 	if (error != EPASSTHROUGH) {
    620 		return error;
    621 	}
    622 
    623 	/* Next, the TTY ioctls. */
    624 	error = ttioctl(tp, cmd, data, flag, l);
    625 	if (error != EPASSTHROUGH) {
    626 		return error;
    627 	}
    628 
    629 	/* None at this layer. */
    630 	return EPASSTHROUGH;
    631 }
    632 
    633 /*
    634  * gftty_tx --
    635  *	Transmit a buffer on the virtual TTY using DMA.
    636  */
    637 static void
    638 gftty_tx(struct gftty_softc *sc, void *buf, size_t len)
    639 {
    640 	int error, i;
    641 
    642 	KASSERT(len <= GFTTY_DMASIZE);
    643 
    644 	error = bus_dmamap_load(sc->sc_dmat, sc->sc_tx_dma, buf, len,
    645 	    NULL, BUS_DMA_NOWAIT);
    646 	if (error) {
    647 		/* XXX report error */
    648 		return;
    649 	}
    650 	bus_dmamap_sync(sc->sc_dmat, sc->sc_tx_dma, 0, len,
    651 	    BUS_DMASYNC_PREWRITE);
    652 
    653 	mutex_spin_enter(&sc->sc_hwlock);
    654 	for (i = 0; i < sc->sc_tx_dma->dm_nsegs; i++) {
    655 		gftty_set_buffer(sc->sc_config,
    656 		    sc->sc_tx_dma->dm_segs[i].ds_addr,
    657 		    sc->sc_tx_dma->dm_segs[i].ds_len);
    658 		REG_WRITE(sc, GFTTY_CMD, CMD_WRITE_BUFFER);
    659 	}
    660 	mutex_spin_exit(&sc->sc_hwlock);
    661 
    662 	bus_dmamap_sync(sc->sc_dmat, sc->sc_tx_dma, 0, len,
    663 	    BUS_DMASYNC_POSTWRITE);
    664 	bus_dmamap_unload(sc->sc_dmat, sc->sc_tx_dma);
    665 }
    666 
    667 /*
    668  * gftty_start --
    669  *	TTY oproc routine.
    670  */
    671 static void
    672 gftty_start(struct tty *tp)
    673 {
    674 	struct gftty_softc *sc = tp->t_softc;
    675 	u_char *tbuf;
    676 	int n;
    677 
    678 	KASSERT(ttylocked(tp));
    679 
    680 	if (ISSET(tp->t_state, TS_BUSY | TS_TIMEOUT | TS_TTSTOP) ||
    681 	    ttypull(tp) == 0) {
    682 		return;
    683 	}
    684 	SET(tp->t_state, TS_BUSY);
    685 
    686 	/*
    687 	 * Drain the output from the ring buffer.  This will normally
    688 	 * be one contiguous chunk, but we have to do it in two pieces
    689 	 * when the ring wraps.
    690 	 */
    691 
    692 	n = ndqb(&tp->t_outq, 0);
    693 	tbuf = tp->t_outq.c_cf;
    694 	gftty_tx(sc, tbuf, n);
    695 	ndflush(&tp->t_outq, n);
    696 
    697 	if ((n = ndqb(&tp->t_outq, 0)) > 0) {
    698 		tbuf = tp->t_outq.c_cf;
    699 		gftty_tx(sc, tbuf, n);
    700 		ndflush(&tp->t_outq, n);
    701 	}
    702 
    703 	CLR(tp->t_state, TS_BUSY);
    704 	/* Come back if there's more to do. */
    705 	if (ttypull(tp)) {
    706 		SET(tp->t_state, TS_TIMEOUT);
    707 		callout_schedule(&tp->t_rstrt_ch, (hz > 128) ? (hz / 128) : 1);
    708 	}
    709 }
    710 
    711 /*
    712  * gftty_stop --
    713  *	cdevsw stop routine.
    714  */
    715 static void
    716 gftty_stop(struct tty *tp, int flag)
    717 {
    718 	KASSERT(ttylocked(tp));
    719 
    720 	if (ISSET(tp->t_state, TS_BUSY)) {
    721 		if (!ISSET(tp->t_state, TS_TTSTOP)) {
    722 			SET(tp->t_state, TS_FLUSH);
    723 		}
    724 	}
    725 }
    726 
    727 /*
    728  * gftty_param_locked --
    729  *	Set TTY parameters.  TTY must be locked.
    730  */
    731 static int
    732 gftty_param_locked(struct tty *tp, struct termios *t)
    733 {
    734 
    735 	KASSERT(ttylocked(tp));
    736 
    737 	tp->t_ispeed = t->c_ispeed;
    738 	tp->t_ospeed = t->c_ospeed;
    739 	tp->t_cflag = t->c_cflag;
    740 
    741 	return 0;
    742 }
    743 
    744 /*
    745  * gftty_param --
    746  *	TTY param routine.
    747  */
    748 static int
    749 gftty_param(struct tty *tp, struct termios *t)
    750 {
    751 	int rv;
    752 
    753 	ttylock(tp);
    754 	rv = gftty_param_locked(tp, t);
    755 	ttyunlock(tp);
    756 
    757 	return rv;
    758 }
    759 
    760 /*
    761  * gftty console routines.
    762  */
    763 static int
    764 gftty_cngetc(dev_t dev)
    765 {
    766 	struct gftty_config * const c = &gftty_cnconfig;
    767 
    768 	if (REG_READ0(c, GFTTY_BYTES_READY) == 0) {
    769 		return -1;
    770 	}
    771 
    772 	/*
    773 	 * XXX This is all terrible and should burn to the ground.
    774 	 * XXX This device desperately needs to be improved with
    775 	 * XXX a GET_CHAR register.
    776 	 */
    777 	bus_addr_t addr;
    778 	uint8_t buf[1];
    779 
    780 	if (c->c_version == 0) {
    781 		addr = (bus_addr_t)buf;
    782 	} else {
    783 		addr = vtophys((vaddr_t)buf);
    784 	}
    785 
    786 	gftty_set_buffer(c, addr, sizeof(buf));
    787 	REG_WRITE0(c, GFTTY_CMD, CMD_READ_BUFFER);
    788 
    789 	return buf[0];
    790 }
    791 
    792 static void
    793 gftty_cnputc(dev_t dev, int ch)
    794 {
    795 	REG_WRITE0(&gftty_cnconfig, GFTTY_PUT_CHAR, (unsigned char)ch);
    796 }
    797 
    798 static void
    799 gftty_cnpollc(dev_t dev, int on)
    800 {
    801 	/* XXX */
    802 }
    803 
    804 /*
    805  * gftty_cnattach --
    806  *	Attach a Goldfish virtual TTY console.
    807  */
    808 void
    809 gftty_cnattach(bus_space_tag_t bst, bus_space_handle_t bsh)
    810 {
    811 	gftty_init_config(&gftty_cnconfig, bst, bsh);
    812 
    813 	cn_tab = &gftty_consdev;
    814 	cn_init_magic(&gftty_cnmagic_state);
    815 	cn_set_magic("+++++");
    816 }
    817