git: d1c5d0cfcc17 - main - bhyve: Move device model-independent UART code into a separate file
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Thu, 21 Mar 2024 05:06:21 UTC
The branch main has been updated by markj: URL: https://cgit.FreeBSD.org/src/commit/?id=d1c5d0cfcc1733c243d87f20477b115db4cf24b6 commit d1c5d0cfcc1733c243d87f20477b115db4cf24b6 Author: Mark Johnston <markj@FreeBSD.org> AuthorDate: 2024-03-21 04:20:37 +0000 Commit: Mark Johnston <markj@FreeBSD.org> CommitDate: 2024-03-21 05:04:48 +0000 bhyve: Move device model-independent UART code into a separate file Currently bhyve implements a ns16550-compatible UART in uart_emul.c. This file also contains generic code to manage RX FIFOs and to handle reading from and writing to a TTY. bhyve instantiates UARTs to implement COM devices (via pci_lpc.c) and PCI UART devices. The arm64 port will bring with it a PL011 device model which is used as the default console (i.e., no COM ports). To simplify its integration, add a UART "backend" layer which lets UART device models allocate an RX FIFO and interact with TTYs without duplicating code. In particular, code in uart_backend.* is to be shared among device models, and the namespace for uart_emul.* is changed to uart_ns16550_*. This is based on andrew@'s work in https://github.com/zxombie/freebsd/tree/bhyvearm64 but I've made a number of changes, particularly with respect to naming and source code organization. No functional change intended. Reviewed by: corvink, jhb MFC after: 1 week Sponsored by: Innovate UK Differential Revision: https://reviews.freebsd.org/D40993 --- usr.sbin/bhyve/Makefile | 1 + usr.sbin/bhyve/amd64/pci_lpc.c | 31 ++-- usr.sbin/bhyve/pci_uart.c | 13 +- usr.sbin/bhyve/uart_backend.c | 348 +++++++++++++++++++++++++++++++++++++++++ usr.sbin/bhyve/uart_backend.h | 55 +++++++ usr.sbin/bhyve/uart_emul.c | 344 +++++----------------------------------- usr.sbin/bhyve/uart_emul.h | 20 ++- 7 files changed, 478 insertions(+), 334 deletions(-) diff --git a/usr.sbin/bhyve/Makefile b/usr.sbin/bhyve/Makefile index c9d571daebbc..03ea77769754 100644 --- a/usr.sbin/bhyve/Makefile +++ b/usr.sbin/bhyve/Makefile @@ -56,6 +56,7 @@ SRCS= \ tpm_emul_passthru.c \ tpm_intf_crb.c \ tpm_ppi_qemu.c \ + uart_backend.c \ uart_emul.c \ usb_emul.c \ usb_mouse.c \ diff --git a/usr.sbin/bhyve/amd64/pci_lpc.c b/usr.sbin/bhyve/amd64/pci_lpc.c index d6e7689ccce1..57d2333edcc6 100644 --- a/usr.sbin/bhyve/amd64/pci_lpc.c +++ b/usr.sbin/bhyve/amd64/pci_lpc.c @@ -69,7 +69,7 @@ static struct pci_devinst *lpc_bridge; #define LPC_UART_NUM 4 static struct lpc_uart_softc { - struct uart_softc *uart_softc; + struct uart_ns16550_softc *uart_softc; int iobase; int irq; int enabled; @@ -226,17 +226,19 @@ lpc_uart_io_handler(struct vmctx *ctx __unused, int in, switch (bytes) { case 1: if (in) - *eax = uart_read(sc->uart_softc, offset); + *eax = uart_ns16550_read(sc->uart_softc, offset); else - uart_write(sc->uart_softc, offset, *eax); + uart_ns16550_write(sc->uart_softc, offset, *eax); break; case 2: if (in) { - *eax = uart_read(sc->uart_softc, offset); - *eax |= uart_read(sc->uart_softc, offset + 1) << 8; + *eax = uart_ns16550_read(sc->uart_softc, offset); + *eax |= + uart_ns16550_read(sc->uart_softc, offset + 1) << 8; } else { - uart_write(sc->uart_softc, offset, *eax); - uart_write(sc->uart_softc, offset + 1, *eax >> 8); + uart_ns16550_write(sc->uart_softc, offset, *eax); + uart_ns16550_write(sc->uart_softc, offset + 1, + *eax >> 8); } break; default: @@ -275,13 +277,14 @@ lpc_init(struct vmctx *ctx) } pci_irq_reserve(sc->irq); - sc->uart_softc = uart_init(lpc_uart_intr_assert, - lpc_uart_intr_deassert, sc); + sc->uart_softc = uart_ns16550_init(lpc_uart_intr_assert, + lpc_uart_intr_deassert, sc); asprintf(&node_name, "lpc.%s.path", name); backend = get_config_value(node_name); free(node_name); - if (uart_set_backend(sc->uart_softc, backend) != 0) { + if (backend != NULL && + uart_ns16550_tty_open(sc->uart_softc, backend) != 0) { EPRINTLN("Unable to initialize backend '%s' " "for LPC device %s", backend, name); return (-1); @@ -290,7 +293,7 @@ lpc_init(struct vmctx *ctx) bzero(&iop, sizeof(struct inout_port)); iop.name = name; iop.port = sc->iobase; - iop.size = UART_IO_BAR_SIZE; + iop.size = UART_NS16550_IO_BAR_SIZE; iop.flags = IOPORT_F_INOUT; iop.handler = lpc_uart_io_handler; iop.arg = sc; @@ -423,7 +426,7 @@ pci_lpc_uart_dsdt(void) dsdt_line(" Name (_CRS, ResourceTemplate ()"); dsdt_line(" {"); dsdt_indent(2); - dsdt_fixed_ioport(sc->iobase, UART_IO_BAR_SIZE); + dsdt_fixed_ioport(sc->iobase, UART_NS16550_IO_BAR_SIZE); dsdt_fixed_irq(sc->irq); dsdt_unindent(2); dsdt_line(" })"); @@ -588,12 +591,12 @@ static int pci_lpc_snapshot(struct vm_snapshot_meta *meta) { int unit, ret; - struct uart_softc *sc; + struct uart_ns16550_softc *sc; for (unit = 0; unit < LPC_UART_NUM; unit++) { sc = lpc_uart_softc[unit].uart_softc; - ret = uart_snapshot(sc, meta); + ret = uart_ns16550_snapshot(sc, meta); if (ret != 0) goto done; } diff --git a/usr.sbin/bhyve/pci_uart.c b/usr.sbin/bhyve/pci_uart.c index 054f9af0f5cc..f0f05c731a18 100644 --- a/usr.sbin/bhyve/pci_uart.c +++ b/usr.sbin/bhyve/pci_uart.c @@ -67,7 +67,7 @@ pci_uart_write(struct pci_devinst *pi, int baridx, uint64_t offset, int size, assert(baridx == 0); assert(size == 1); - uart_write(pi->pi_arg, offset, value); + uart_ns16550_write(pi->pi_arg, offset, value); } static uint64_t @@ -78,7 +78,7 @@ pci_uart_read(struct pci_devinst *pi, int baridx, uint64_t offset, int size) assert(baridx == 0); assert(size == 1); - val = uart_read(pi->pi_arg, offset); + val = uart_ns16550_read(pi->pi_arg, offset); return (val); } @@ -94,10 +94,10 @@ pci_uart_legacy_config(nvlist_t *nvl, const char *opts) static int pci_uart_init(struct pci_devinst *pi, nvlist_t *nvl) { - struct uart_softc *sc; + struct uart_ns16550_softc *sc; const char *device; - pci_emul_alloc_bar(pi, 0, PCIBAR_IO, UART_IO_BAR_SIZE); + pci_emul_alloc_bar(pi, 0, PCIBAR_IO, UART_NS16550_IO_BAR_SIZE); pci_lintr_request(pi); /* initialize config space */ @@ -105,11 +105,12 @@ pci_uart_init(struct pci_devinst *pi, nvlist_t *nvl) pci_set_cfgdata16(pi, PCIR_VENDOR, COM_VENDOR); pci_set_cfgdata8(pi, PCIR_CLASS, PCIC_SIMPLECOMM); - sc = uart_init(pci_uart_intr_assert, pci_uart_intr_deassert, pi); + sc = uart_ns16550_init(pci_uart_intr_assert, pci_uart_intr_deassert, + pi); pi->pi_arg = sc; device = get_config_value_node(nvl, "path"); - if (uart_set_backend(sc, device) != 0) { + if (device != NULL && uart_ns16550_tty_open(sc, device) != 0) { EPRINTLN("Unable to initialize backend '%s' for " "pci uart at %d:%d", device, pi->pi_slot, pi->pi_func); return (-1); diff --git a/usr.sbin/bhyve/uart_backend.c b/usr.sbin/bhyve/uart_backend.c new file mode 100644 index 000000000000..8d91f4f671e1 --- /dev/null +++ b/usr.sbin/bhyve/uart_backend.c @@ -0,0 +1,348 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2012 NetApp, Inc. + * Copyright (c) 2013 Neel Natu <neel@freebsd.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/types.h> + +#include <machine/vmm.h> +#include <machine/vmm_snapshot.h> + +#include <assert.h> +#include <capsicum_helpers.h> +#include <err.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> +#include <sysexits.h> +#include <termios.h> +#include <unistd.h> + +#include "debug.h" +#include "mevent.h" +#include "uart_backend.h" + +struct ttyfd { + bool opened; + int rfd; /* fd for reading */ + int wfd; /* fd for writing, may be == rfd */ +}; + +#define FIFOSZ 16 + +struct fifo { + uint8_t buf[FIFOSZ]; + int rindex; /* index to read from */ + int windex; /* index to write to */ + int num; /* number of characters in the fifo */ + int size; /* size of the fifo */ +}; + +struct uart_softc { + struct ttyfd tty; + struct fifo rxfifo; + struct mevent *mev; +}; + +static bool uart_stdio; /* stdio in use for i/o */ +static struct termios tio_stdio_orig; + +static void +ttyclose(void) +{ + tcsetattr(STDIN_FILENO, TCSANOW, &tio_stdio_orig); +} + +static void +ttyopen(struct ttyfd *tf) +{ + struct termios orig, new; + + tcgetattr(tf->rfd, &orig); + new = orig; + cfmakeraw(&new); + new.c_cflag |= CLOCAL; + tcsetattr(tf->rfd, TCSANOW, &new); + if (uart_stdio) { + tio_stdio_orig = orig; + atexit(ttyclose); + } + raw_stdio = 1; +} + +static int +ttyread(struct ttyfd *tf) +{ + unsigned char rb; + + if (read(tf->rfd, &rb, 1) == 1) + return (rb); + else + return (-1); +} + +static void +ttywrite(struct ttyfd *tf, unsigned char wb) +{ + (void)write(tf->wfd, &wb, 1); +} + +static bool +rxfifo_available(struct uart_softc *sc) +{ + return (sc->rxfifo.num < sc->rxfifo.size); +} + +int +uart_rxfifo_getchar(struct uart_softc *sc) +{ + struct fifo *fifo; + int c, error, wasfull; + + wasfull = 0; + fifo = &sc->rxfifo; + if (fifo->num > 0) { + if (!rxfifo_available(sc)) + wasfull = 1; + c = fifo->buf[fifo->rindex]; + fifo->rindex = (fifo->rindex + 1) % fifo->size; + fifo->num--; + if (wasfull) { + if (sc->tty.opened) { + error = mevent_enable(sc->mev); + assert(error == 0); + } + } + return (c); + } else + return (-1); +} + +int +uart_rxfifo_numchars(struct uart_softc *sc) +{ + return (sc->rxfifo.num); +} + +static int +rxfifo_putchar(struct uart_softc *sc, uint8_t ch) +{ + struct fifo *fifo; + int error; + + fifo = &sc->rxfifo; + + if (fifo->num < fifo->size) { + fifo->buf[fifo->windex] = ch; + fifo->windex = (fifo->windex + 1) % fifo->size; + fifo->num++; + if (!rxfifo_available(sc)) { + if (sc->tty.opened) { + /* + * Disable mevent callback if the FIFO is full. + */ + error = mevent_disable(sc->mev); + assert(error == 0); + } + } + return (0); + } else + return (-1); +} + +void +uart_rxfifo_drain(struct uart_softc *sc, bool loopback) +{ + int ch; + + if (loopback) { + (void)ttyread(&sc->tty); + } else { + while (rxfifo_available(sc) && + ((ch = ttyread(&sc->tty)) != -1)) + rxfifo_putchar(sc, ch); + } +} + +int +uart_rxfifo_putchar(struct uart_softc *sc, uint8_t ch, bool loopback) +{ + if (loopback) { + return (rxfifo_putchar(sc, ch)); + } else if (sc->tty.opened) { + ttywrite(&sc->tty, ch); + return (0); + } else { + /* Drop on the floor. */ + return (0); + } +} + +void +uart_rxfifo_reset(struct uart_softc *sc, int size) +{ + char flushbuf[32]; + struct fifo *fifo; + ssize_t nread; + int error; + + fifo = &sc->rxfifo; + bzero(fifo, sizeof(struct fifo)); + fifo->size = size; + + if (sc->tty.opened) { + /* + * Flush any unread input from the tty buffer. + */ + while (1) { + nread = read(sc->tty.rfd, flushbuf, sizeof(flushbuf)); + if (nread != sizeof(flushbuf)) + break; + } + + /* + * Enable mevent to trigger when new characters are available + * on the tty fd. + */ + error = mevent_enable(sc->mev); + assert(error == 0); + } +} + +int +uart_rxfifo_size(struct uart_softc *sc __unused) +{ + return (FIFOSZ); +} + +#ifdef BHYVE_SNAPSHOT +int +uart_rxfifo_snapshot(struct uart_softc *sc, struct vm_snapshot_meta *meta) +{ + int ret; + + SNAPSHOT_VAR_OR_LEAVE(sc->rxfifo.rindex, meta, ret, done); + SNAPSHOT_VAR_OR_LEAVE(sc->rxfifo.windex, meta, ret, done); + SNAPSHOT_VAR_OR_LEAVE(sc->rxfifo.num, meta, ret, done); + SNAPSHOT_VAR_OR_LEAVE(sc->rxfifo.size, meta, ret, done); + SNAPSHOT_BUF_OR_LEAVE(sc->rxfifo.buf, sizeof(sc->rxfifo.buf), + meta, ret, done); + +done: + return (ret); +} +#endif + +static int +uart_stdio_backend(struct uart_softc *sc) +{ +#ifndef WITHOUT_CAPSICUM + cap_rights_t rights; + cap_ioctl_t cmds[] = { TIOCGETA, TIOCSETA, TIOCGWINSZ }; +#endif + + if (uart_stdio) + return (-1); + + sc->tty.rfd = STDIN_FILENO; + sc->tty.wfd = STDOUT_FILENO; + sc->tty.opened = true; + + if (fcntl(sc->tty.rfd, F_SETFL, O_NONBLOCK) != 0) + return (-1); + if (fcntl(sc->tty.wfd, F_SETFL, O_NONBLOCK) != 0) + return (-1); + +#ifndef WITHOUT_CAPSICUM + cap_rights_init(&rights, CAP_EVENT, CAP_IOCTL, CAP_READ); + if (caph_rights_limit(sc->tty.rfd, &rights) == -1) + errx(EX_OSERR, "Unable to apply rights for sandbox"); + if (caph_ioctls_limit(sc->tty.rfd, cmds, nitems(cmds)) == -1) + errx(EX_OSERR, "Unable to apply rights for sandbox"); +#endif + + uart_stdio = true; + + return (0); +} + +static int +uart_tty_backend(struct uart_softc *sc, const char *path) +{ +#ifndef WITHOUT_CAPSICUM + cap_rights_t rights; + cap_ioctl_t cmds[] = { TIOCGETA, TIOCSETA, TIOCGWINSZ }; +#endif + int fd; + + fd = open(path, O_RDWR | O_NONBLOCK); + if (fd < 0) + return (-1); + + if (!isatty(fd)) { + close(fd); + return (-1); + } + + sc->tty.rfd = sc->tty.wfd = fd; + sc->tty.opened = true; + +#ifndef WITHOUT_CAPSICUM + cap_rights_init(&rights, CAP_EVENT, CAP_IOCTL, CAP_READ, CAP_WRITE); + if (caph_rights_limit(fd, &rights) == -1) + errx(EX_OSERR, "Unable to apply rights for sandbox"); + if (caph_ioctls_limit(fd, cmds, nitems(cmds)) == -1) + errx(EX_OSERR, "Unable to apply rights for sandbox"); +#endif + + return (0); +} + +struct uart_softc * +uart_init(void) +{ + return (calloc(1, sizeof(struct uart_softc))); +} + +int +uart_tty_open(struct uart_softc *sc, const char *path, + void (*drain)(int, enum ev_type, void *), void *arg) +{ + int retval; + + if (strcmp("stdio", path) == 0) + retval = uart_stdio_backend(sc); + else + retval = uart_tty_backend(sc, path); + if (retval == 0) { + ttyopen(&sc->tty); + sc->mev = mevent_add(sc->tty.rfd, EVF_READ, drain, arg); + assert(sc->mev != NULL); + } + + return (retval); +} diff --git a/usr.sbin/bhyve/uart_backend.h b/usr.sbin/bhyve/uart_backend.h new file mode 100644 index 000000000000..fa7949ad6d1c --- /dev/null +++ b/usr.sbin/bhyve/uart_backend.h @@ -0,0 +1,55 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2012 NetApp, Inc. + * Copyright (c) 2013 Neel Natu <neel@freebsd.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _UART_BACKEND_H_ +#define _UART_BACKEND_H_ + +#include <stdbool.h> + +#include "mevent.h" + +struct uart_softc; +struct vm_snapshot_meta; + +void uart_rxfifo_drain(struct uart_softc *sc, bool loopback); +int uart_rxfifo_getchar(struct uart_softc *sc); +int uart_rxfifo_numchars(struct uart_softc *sc); +int uart_rxfifo_putchar(struct uart_softc *sc, uint8_t ch, bool loopback); +void uart_rxfifo_reset(struct uart_softc *sc, int size); +int uart_rxfifo_size(struct uart_softc *sc); +#ifdef BHYVE_SNAPSHOT +int uart_rxfifo_snapshot(struct uart_softc *sc, + struct vm_snapshot_meta *meta); +#endif + +struct uart_softc *uart_init(void); +int uart_tty_open(struct uart_softc *sc, const char *path, + void (*drain)(int, enum ev_type, void *), void *arg); + +#endif /* _UART_BACKEND_H_ */ diff --git a/usr.sbin/bhyve/uart_emul.c b/usr.sbin/bhyve/uart_emul.c index a8c28fb5f230..58d6697e4fea 100644 --- a/usr.sbin/bhyve/uart_emul.c +++ b/usr.sbin/bhyve/uart_emul.c @@ -29,29 +29,20 @@ #include <sys/types.h> #include <dev/ic/ns16550.h> -#ifndef WITHOUT_CAPSICUM -#include <sys/capsicum.h> -#include <capsicum_helpers.h> -#endif #include <machine/vmm_snapshot.h> +#include <assert.h> #include <stdio.h> #include <stdlib.h> -#include <assert.h> -#include <err.h> #include <errno.h> -#include <fcntl.h> -#include <termios.h> #include <unistd.h> #include <stdbool.h> #include <string.h> #include <pthread.h> -#include <sysexits.h> -#include "mevent.h" +#include "uart_backend.h" #include "uart_emul.h" -#include "debug.h" #define COM1_BASE 0x3F8 #define COM1_IRQ 4 @@ -76,11 +67,6 @@ #define REG_SCR com_scr #endif -#define FIFOSZ 16 - -static bool uart_stdio; /* stdio in use for i/o */ -static struct termios tio_stdio_orig; - static struct { int baseaddr; int irq; @@ -94,21 +80,9 @@ static struct { #define UART_NLDEVS (sizeof(uart_lres) / sizeof(uart_lres[0])) -struct fifo { - uint8_t buf[FIFOSZ]; - int rindex; /* index to read from */ - int windex; /* index to write to */ - int num; /* number of characters in the fifo */ - int size; /* size of the fifo */ -}; - -struct ttyfd { - bool opened; - int rfd; /* fd for reading */ - int wfd; /* fd for writing, may be == rfd */ -}; +struct uart_ns16550_softc { + struct uart_softc *backend; -struct uart_softc { pthread_mutex_t mtx; /* protects all softc elements */ uint8_t data; /* Data register (R/W) */ uint8_t ier; /* Interrupt enable register (R/W) */ @@ -122,10 +96,6 @@ struct uart_softc { uint8_t dll; /* Baudrate divisor latch LSB */ uint8_t dlh; /* Baudrate divisor latch MSB */ - struct fifo rxfifo; - struct mevent *mev; - - struct ttyfd tty; bool thre_int_pending; /* THRE interrupt pending */ void *arg; @@ -133,158 +103,6 @@ struct uart_softc { uart_intr_func_t intr_deassert; }; -static void uart_drain(int fd, enum ev_type ev, void *arg); - -static void -ttyclose(void) -{ - - tcsetattr(STDIN_FILENO, TCSANOW, &tio_stdio_orig); -} - -static void -ttyopen(struct ttyfd *tf) -{ - struct termios orig, new; - - tcgetattr(tf->rfd, &orig); - new = orig; - cfmakeraw(&new); - new.c_cflag |= CLOCAL; - tcsetattr(tf->rfd, TCSANOW, &new); - if (uart_stdio) { - tio_stdio_orig = orig; - atexit(ttyclose); - } - raw_stdio = 1; -} - -static int -ttyread(struct ttyfd *tf) -{ - unsigned char rb; - - if (read(tf->rfd, &rb, 1) == 1) - return (rb); - else - return (-1); -} - -static void -ttywrite(struct ttyfd *tf, unsigned char wb) -{ - - (void)write(tf->wfd, &wb, 1); -} - -static void -rxfifo_reset(struct uart_softc *sc, int size) -{ - char flushbuf[32]; - struct fifo *fifo; - ssize_t nread; - int error; - - fifo = &sc->rxfifo; - bzero(fifo, sizeof(struct fifo)); - fifo->size = size; - - if (sc->tty.opened) { - /* - * Flush any unread input from the tty buffer. - */ - while (1) { - nread = read(sc->tty.rfd, flushbuf, sizeof(flushbuf)); - if (nread != sizeof(flushbuf)) - break; - } - - /* - * Enable mevent to trigger when new characters are available - * on the tty fd. - */ - error = mevent_enable(sc->mev); - assert(error == 0); - } -} - -static int -rxfifo_available(struct uart_softc *sc) -{ - struct fifo *fifo; - - fifo = &sc->rxfifo; - return (fifo->num < fifo->size); -} - -static int -rxfifo_putchar(struct uart_softc *sc, uint8_t ch) -{ - struct fifo *fifo; - int error; - - fifo = &sc->rxfifo; - - if (fifo->num < fifo->size) { - fifo->buf[fifo->windex] = ch; - fifo->windex = (fifo->windex + 1) % fifo->size; - fifo->num++; - if (!rxfifo_available(sc)) { - if (sc->tty.opened) { - /* - * Disable mevent callback if the FIFO is full. - */ - error = mevent_disable(sc->mev); - assert(error == 0); - } - } - return (0); - } else - return (-1); -} - -static int -rxfifo_getchar(struct uart_softc *sc) -{ - struct fifo *fifo; - int c, error, wasfull; - - wasfull = 0; - fifo = &sc->rxfifo; - if (fifo->num > 0) { - if (!rxfifo_available(sc)) - wasfull = 1; - c = fifo->buf[fifo->rindex]; - fifo->rindex = (fifo->rindex + 1) % fifo->size; - fifo->num--; - if (wasfull) { - if (sc->tty.opened) { - error = mevent_enable(sc->mev); - assert(error == 0); - } - } - return (c); - } else - return (-1); -} - -static int -rxfifo_numchars(struct uart_softc *sc) -{ - struct fifo *fifo = &sc->rxfifo; - - return (fifo->num); -} - -static void -uart_opentty(struct uart_softc *sc) -{ - - ttyopen(&sc->tty); - sc->mev = mevent_add(sc->tty.rfd, EVF_READ, uart_drain, sc); - assert(sc->mev != NULL); -} - static uint8_t modem_status(uint8_t mcr) { @@ -325,12 +143,13 @@ modem_status(uint8_t mcr) * Return an interrupt reason if one is available. */ static int -uart_intr_reason(struct uart_softc *sc) +uart_intr_reason(struct uart_ns16550_softc *sc) { if ((sc->lsr & LSR_OE) != 0 && (sc->ier & IER_ERLS) != 0) return (IIR_RLS); - else if (rxfifo_numchars(sc) > 0 && (sc->ier & IER_ERXRDY) != 0) + else if (uart_rxfifo_numchars(sc->backend) > 0 && + (sc->ier & IER_ERXRDY) != 0) return (IIR_RXTOUT); else if (sc->thre_int_pending && (sc->ier & IER_ETXRDY) != 0) return (IIR_TXRDY); @@ -341,7 +160,7 @@ uart_intr_reason(struct uart_softc *sc) } static void -uart_reset(struct uart_softc *sc) +uart_reset(struct uart_ns16550_softc *sc) { uint16_t divisor; @@ -350,7 +169,7 @@ uart_reset(struct uart_softc *sc) sc->dlh = divisor >> 16; sc->msr = modem_status(sc->mcr); - rxfifo_reset(sc, 1); /* no fifo until enabled by software */ + uart_rxfifo_reset(sc->backend, 1); } /* @@ -358,7 +177,7 @@ uart_reset(struct uart_softc *sc) * interrupt condition to report to the processor. */ static void -uart_toggle_intr(struct uart_softc *sc) +uart_toggle_intr(struct uart_ns16550_softc *sc) { uint8_t intr_reason; @@ -371,14 +190,13 @@ uart_toggle_intr(struct uart_softc *sc) } static void -uart_drain(int fd, enum ev_type ev, void *arg) +uart_drain(int fd __unused, enum ev_type ev, void *arg) { - struct uart_softc *sc; - int ch; + struct uart_ns16550_softc *sc; + bool loopback; sc = arg; - assert(fd == sc->tty.rfd); assert(ev == EVF_READ); /* @@ -388,21 +206,16 @@ uart_drain(int fd, enum ev_type ev, void *arg) */ pthread_mutex_lock(&sc->mtx); - if ((sc->mcr & MCR_LOOPBACK) != 0) { - (void) ttyread(&sc->tty); - } else { - while (rxfifo_available(sc) && - ((ch = ttyread(&sc->tty)) != -1)) { - rxfifo_putchar(sc, ch); - } + loopback = (sc->mcr & MCR_LOOPBACK) != 0; + uart_rxfifo_drain(sc->backend, loopback); + if (!loopback) uart_toggle_intr(sc); - } pthread_mutex_unlock(&sc->mtx); } void -uart_write(struct uart_softc *sc, int offset, uint8_t value) +uart_ns16550_write(struct uart_ns16550_softc *sc, int offset, uint8_t value) { int fifosz; uint8_t msr; @@ -426,12 +239,9 @@ uart_write(struct uart_softc *sc, int offset, uint8_t value) switch (offset) { case REG_DATA: - if (sc->mcr & MCR_LOOPBACK) { - if (rxfifo_putchar(sc, value) != 0) - sc->lsr |= LSR_OE; - } else if (sc->tty.opened) { - ttywrite(&sc->tty, value); - } /* else drop on floor */ + if (uart_rxfifo_putchar(sc->backend, value, + (sc->mcr & MCR_LOOPBACK) != 0)) + sc->lsr |= LSR_OE; sc->thre_int_pending = true; break; case REG_IER: @@ -450,8 +260,9 @@ uart_write(struct uart_softc *sc, int offset, uint8_t value) * the FIFO contents are reset. */ if ((sc->fcr & FCR_ENABLE) ^ (value & FCR_ENABLE)) { - fifosz = (value & FCR_ENABLE) ? FIFOSZ : 1; - rxfifo_reset(sc, fifosz); + fifosz = (value & FCR_ENABLE) ? + uart_rxfifo_size(sc->backend) : 1; + uart_rxfifo_reset(sc->backend, fifosz); } /* @@ -462,7 +273,8 @@ uart_write(struct uart_softc *sc, int offset, uint8_t value) sc->fcr = 0; } else { if ((value & FCR_RCV_RST) != 0) - rxfifo_reset(sc, FIFOSZ); + uart_rxfifo_reset(sc->backend, + uart_rxfifo_size(sc->backend)); sc->fcr = value & (FCR_ENABLE | FCR_DMA | FCR_RX_MASK); @@ -521,7 +333,7 @@ done: } uint8_t -uart_read(struct uart_softc *sc, int offset) +uart_ns16550_read(struct uart_ns16550_softc *sc, int offset) { *** 191 LINES SKIPPED ***