1ea7f1c8cSNeel Natu /*- 2ea7f1c8cSNeel Natu * Copyright (c) 2012 NetApp, Inc. 3ea7f1c8cSNeel Natu * Copyright (c) 2013 Neel Natu <neel@freebsd.org> 4ea7f1c8cSNeel Natu * All rights reserved. 5ea7f1c8cSNeel Natu * 6ea7f1c8cSNeel Natu * Redistribution and use in source and binary forms, with or without 7ea7f1c8cSNeel Natu * modification, are permitted provided that the following conditions 8ea7f1c8cSNeel Natu * are met: 9ea7f1c8cSNeel Natu * 1. Redistributions of source code must retain the above copyright 10ea7f1c8cSNeel Natu * notice, this list of conditions and the following disclaimer. 11ea7f1c8cSNeel Natu * 2. Redistributions in binary form must reproduce the above copyright 12ea7f1c8cSNeel Natu * notice, this list of conditions and the following disclaimer in the 13ea7f1c8cSNeel Natu * documentation and/or other materials provided with the distribution. 14ea7f1c8cSNeel Natu * 15ea7f1c8cSNeel Natu * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND 16ea7f1c8cSNeel Natu * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17ea7f1c8cSNeel Natu * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18ea7f1c8cSNeel Natu * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE 19ea7f1c8cSNeel Natu * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20ea7f1c8cSNeel Natu * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21ea7f1c8cSNeel Natu * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22ea7f1c8cSNeel Natu * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23ea7f1c8cSNeel Natu * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24ea7f1c8cSNeel Natu * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25ea7f1c8cSNeel Natu * SUCH DAMAGE. 26ea7f1c8cSNeel Natu * 27ea7f1c8cSNeel Natu * $FreeBSD$ 28ea7f1c8cSNeel Natu */ 29ea7f1c8cSNeel Natu 30ea7f1c8cSNeel Natu #include <sys/cdefs.h> 31ea7f1c8cSNeel Natu __FBSDID("$FreeBSD$"); 32ea7f1c8cSNeel Natu 33ea7f1c8cSNeel Natu #include <sys/types.h> 34ea7f1c8cSNeel Natu #include <dev/ic/ns16550.h> 35*00ef17beSBartek Rutkowski #ifndef WITHOUT_CAPSICUM 36*00ef17beSBartek Rutkowski #include <sys/capsicum.h> 37*00ef17beSBartek Rutkowski #include <capsicum_helpers.h> 38*00ef17beSBartek Rutkowski #endif 39ea7f1c8cSNeel Natu 40ea7f1c8cSNeel Natu #include <stdio.h> 41ea7f1c8cSNeel Natu #include <stdlib.h> 42ea7f1c8cSNeel Natu #include <assert.h> 43*00ef17beSBartek Rutkowski #include <err.h> 44*00ef17beSBartek Rutkowski #include <errno.h> 456380102cSPeter Grehan #include <fcntl.h> 46ea7f1c8cSNeel Natu #include <termios.h> 47ea7f1c8cSNeel Natu #include <unistd.h> 48ea7f1c8cSNeel Natu #include <stdbool.h> 49ea7f1c8cSNeel Natu #include <string.h> 50ea7f1c8cSNeel Natu #include <pthread.h> 51*00ef17beSBartek Rutkowski #include <sysexits.h> 52ea7f1c8cSNeel Natu 53ea7f1c8cSNeel Natu #include "mevent.h" 54ea7f1c8cSNeel Natu #include "uart_emul.h" 55ea7f1c8cSNeel Natu 56ea7f1c8cSNeel Natu #define COM1_BASE 0x3F8 57ea7f1c8cSNeel Natu #define COM1_IRQ 4 58ea7f1c8cSNeel Natu #define COM2_BASE 0x2F8 59ea7f1c8cSNeel Natu #define COM2_IRQ 3 60ea7f1c8cSNeel Natu 61ea7f1c8cSNeel Natu #define DEFAULT_RCLK 1843200 62ea7f1c8cSNeel Natu #define DEFAULT_BAUD 9600 63ea7f1c8cSNeel Natu 64ea7f1c8cSNeel Natu #define FCR_RX_MASK 0xC0 65ea7f1c8cSNeel Natu 66ea7f1c8cSNeel Natu #define MCR_OUT1 0x04 67ea7f1c8cSNeel Natu #define MCR_OUT2 0x08 68ea7f1c8cSNeel Natu 69ea7f1c8cSNeel Natu #define MSR_DELTA_MASK 0x0f 70ea7f1c8cSNeel Natu 71ea7f1c8cSNeel Natu #ifndef REG_SCR 72ea7f1c8cSNeel Natu #define REG_SCR com_scr 73ea7f1c8cSNeel Natu #endif 74ea7f1c8cSNeel Natu 75ea7f1c8cSNeel Natu #define FIFOSZ 16 76ea7f1c8cSNeel Natu 77ea7f1c8cSNeel Natu static bool uart_stdio; /* stdio in use for i/o */ 786380102cSPeter Grehan static struct termios tio_stdio_orig; 79ea7f1c8cSNeel Natu 80ea7f1c8cSNeel Natu static struct { 81ea7f1c8cSNeel Natu int baseaddr; 82ea7f1c8cSNeel Natu int irq; 83ea7f1c8cSNeel Natu bool inuse; 84ea7f1c8cSNeel Natu } uart_lres[] = { 85ea7f1c8cSNeel Natu { COM1_BASE, COM1_IRQ, false}, 86ea7f1c8cSNeel Natu { COM2_BASE, COM2_IRQ, false}, 87ea7f1c8cSNeel Natu }; 88ea7f1c8cSNeel Natu 89ea7f1c8cSNeel Natu #define UART_NLDEVS (sizeof(uart_lres) / sizeof(uart_lres[0])) 90ea7f1c8cSNeel Natu 91ea7f1c8cSNeel Natu struct fifo { 92ea7f1c8cSNeel Natu uint8_t buf[FIFOSZ]; 93ea7f1c8cSNeel Natu int rindex; /* index to read from */ 94ea7f1c8cSNeel Natu int windex; /* index to write to */ 95ea7f1c8cSNeel Natu int num; /* number of characters in the fifo */ 96ea7f1c8cSNeel Natu int size; /* size of the fifo */ 97ea7f1c8cSNeel Natu }; 98ea7f1c8cSNeel Natu 996380102cSPeter Grehan struct ttyfd { 1006380102cSPeter Grehan bool opened; 1016380102cSPeter Grehan int fd; /* tty device file descriptor */ 1026380102cSPeter Grehan struct termios tio_orig, tio_new; /* I/O Terminals */ 1036380102cSPeter Grehan }; 1046380102cSPeter Grehan 105ea7f1c8cSNeel Natu struct uart_softc { 106ea7f1c8cSNeel Natu pthread_mutex_t mtx; /* protects all softc elements */ 107ea7f1c8cSNeel Natu uint8_t data; /* Data register (R/W) */ 108ea7f1c8cSNeel Natu uint8_t ier; /* Interrupt enable register (R/W) */ 109ea7f1c8cSNeel Natu uint8_t lcr; /* Line control register (R/W) */ 110ea7f1c8cSNeel Natu uint8_t mcr; /* Modem control register (R/W) */ 111ea7f1c8cSNeel Natu uint8_t lsr; /* Line status register (R/W) */ 112ea7f1c8cSNeel Natu uint8_t msr; /* Modem status register (R/W) */ 113ea7f1c8cSNeel Natu uint8_t fcr; /* FIFO control register (W) */ 114ea7f1c8cSNeel Natu uint8_t scr; /* Scratch register (R/W) */ 115ea7f1c8cSNeel Natu 116ea7f1c8cSNeel Natu uint8_t dll; /* Baudrate divisor latch LSB */ 117ea7f1c8cSNeel Natu uint8_t dlh; /* Baudrate divisor latch MSB */ 118ea7f1c8cSNeel Natu 119ea7f1c8cSNeel Natu struct fifo rxfifo; 1202bd073e1SNeel Natu struct mevent *mev; 121ea7f1c8cSNeel Natu 1226380102cSPeter Grehan struct ttyfd tty; 123ea7f1c8cSNeel Natu bool thre_int_pending; /* THRE interrupt pending */ 124ea7f1c8cSNeel Natu 125ea7f1c8cSNeel Natu void *arg; 126ea7f1c8cSNeel Natu uart_intr_func_t intr_assert; 127ea7f1c8cSNeel Natu uart_intr_func_t intr_deassert; 128ea7f1c8cSNeel Natu }; 129ea7f1c8cSNeel Natu 130ea7f1c8cSNeel Natu static void uart_drain(int fd, enum ev_type ev, void *arg); 131ea7f1c8cSNeel Natu 132ea7f1c8cSNeel Natu static void 133ea7f1c8cSNeel Natu ttyclose(void) 134ea7f1c8cSNeel Natu { 135ea7f1c8cSNeel Natu 1366380102cSPeter Grehan tcsetattr(STDIN_FILENO, TCSANOW, &tio_stdio_orig); 137ea7f1c8cSNeel Natu } 138ea7f1c8cSNeel Natu 139ea7f1c8cSNeel Natu static void 1406380102cSPeter Grehan ttyopen(struct ttyfd *tf) 141ea7f1c8cSNeel Natu { 142ea7f1c8cSNeel Natu 1436380102cSPeter Grehan tcgetattr(tf->fd, &tf->tio_orig); 144ea7f1c8cSNeel Natu 1456380102cSPeter Grehan tf->tio_new = tf->tio_orig; 1466380102cSPeter Grehan cfmakeraw(&tf->tio_new); 1476380102cSPeter Grehan tf->tio_new.c_cflag |= CLOCAL; 1486380102cSPeter Grehan tcsetattr(tf->fd, TCSANOW, &tf->tio_new); 149ea7f1c8cSNeel Natu 1506380102cSPeter Grehan if (tf->fd == STDIN_FILENO) { 1516380102cSPeter Grehan tio_stdio_orig = tf->tio_orig; 152ea7f1c8cSNeel Natu atexit(ttyclose); 153ea7f1c8cSNeel Natu } 1546380102cSPeter Grehan } 155ea7f1c8cSNeel Natu 156ea7f1c8cSNeel Natu static int 1576380102cSPeter Grehan ttyread(struct ttyfd *tf) 158ea7f1c8cSNeel Natu { 1592bd073e1SNeel Natu unsigned char rb; 160ea7f1c8cSNeel Natu 1612bd073e1SNeel Natu if (read(tf->fd, &rb, 1) == 1) 1622bd073e1SNeel Natu return (rb); 1632bd073e1SNeel Natu else 164ea7f1c8cSNeel Natu return (-1); 165ea7f1c8cSNeel Natu } 166ea7f1c8cSNeel Natu 167ea7f1c8cSNeel Natu static void 1686380102cSPeter Grehan ttywrite(struct ttyfd *tf, unsigned char wb) 169ea7f1c8cSNeel Natu { 170ea7f1c8cSNeel Natu 1716380102cSPeter Grehan (void)write(tf->fd, &wb, 1); 172ea7f1c8cSNeel Natu } 173ea7f1c8cSNeel Natu 174ea7f1c8cSNeel Natu static void 1752bd073e1SNeel Natu rxfifo_reset(struct uart_softc *sc, int size) 176ea7f1c8cSNeel Natu { 1772bd073e1SNeel Natu char flushbuf[32]; 1782bd073e1SNeel Natu struct fifo *fifo; 1792bd073e1SNeel Natu ssize_t nread; 1802bd073e1SNeel Natu int error; 181ea7f1c8cSNeel Natu 1822bd073e1SNeel Natu fifo = &sc->rxfifo; 183ea7f1c8cSNeel Natu bzero(fifo, sizeof(struct fifo)); 184ea7f1c8cSNeel Natu fifo->size = size; 1852bd073e1SNeel Natu 1862bd073e1SNeel Natu if (sc->tty.opened) { 1872bd073e1SNeel Natu /* 1882bd073e1SNeel Natu * Flush any unread input from the tty buffer. 1892bd073e1SNeel Natu */ 1902bd073e1SNeel Natu while (1) { 1912bd073e1SNeel Natu nread = read(sc->tty.fd, flushbuf, sizeof(flushbuf)); 1922bd073e1SNeel Natu if (nread != sizeof(flushbuf)) 1932bd073e1SNeel Natu break; 1942bd073e1SNeel Natu } 1952bd073e1SNeel Natu 1962bd073e1SNeel Natu /* 1972bd073e1SNeel Natu * Enable mevent to trigger when new characters are available 1982bd073e1SNeel Natu * on the tty fd. 1992bd073e1SNeel Natu */ 2002bd073e1SNeel Natu error = mevent_enable(sc->mev); 2012bd073e1SNeel Natu assert(error == 0); 2022bd073e1SNeel Natu } 203ea7f1c8cSNeel Natu } 204ea7f1c8cSNeel Natu 205ea7f1c8cSNeel Natu static int 2062bd073e1SNeel Natu rxfifo_available(struct uart_softc *sc) 207ea7f1c8cSNeel Natu { 2082bd073e1SNeel Natu struct fifo *fifo; 2092bd073e1SNeel Natu 2102bd073e1SNeel Natu fifo = &sc->rxfifo; 2112bd073e1SNeel Natu return (fifo->num < fifo->size); 2122bd073e1SNeel Natu } 2132bd073e1SNeel Natu 2142bd073e1SNeel Natu static int 2152bd073e1SNeel Natu rxfifo_putchar(struct uart_softc *sc, uint8_t ch) 2162bd073e1SNeel Natu { 2172bd073e1SNeel Natu struct fifo *fifo; 2182bd073e1SNeel Natu int error; 2192bd073e1SNeel Natu 2202bd073e1SNeel Natu fifo = &sc->rxfifo; 221ea7f1c8cSNeel Natu 222ea7f1c8cSNeel Natu if (fifo->num < fifo->size) { 223ea7f1c8cSNeel Natu fifo->buf[fifo->windex] = ch; 224ea7f1c8cSNeel Natu fifo->windex = (fifo->windex + 1) % fifo->size; 225ea7f1c8cSNeel Natu fifo->num++; 2262bd073e1SNeel Natu if (!rxfifo_available(sc)) { 2272bd073e1SNeel Natu if (sc->tty.opened) { 2282bd073e1SNeel Natu /* 2292bd073e1SNeel Natu * Disable mevent callback if the FIFO is full. 2302bd073e1SNeel Natu */ 2312bd073e1SNeel Natu error = mevent_disable(sc->mev); 2322bd073e1SNeel Natu assert(error == 0); 2332bd073e1SNeel Natu } 2342bd073e1SNeel Natu } 235ea7f1c8cSNeel Natu return (0); 236ea7f1c8cSNeel Natu } else 237ea7f1c8cSNeel Natu return (-1); 238ea7f1c8cSNeel Natu } 239ea7f1c8cSNeel Natu 240ea7f1c8cSNeel Natu static int 2412bd073e1SNeel Natu rxfifo_getchar(struct uart_softc *sc) 242ea7f1c8cSNeel Natu { 2432bd073e1SNeel Natu struct fifo *fifo; 2442bd073e1SNeel Natu int c, error, wasfull; 245ea7f1c8cSNeel Natu 2462bd073e1SNeel Natu wasfull = 0; 2472bd073e1SNeel Natu fifo = &sc->rxfifo; 248ea7f1c8cSNeel Natu if (fifo->num > 0) { 2492bd073e1SNeel Natu if (!rxfifo_available(sc)) 2502bd073e1SNeel Natu wasfull = 1; 251ea7f1c8cSNeel Natu c = fifo->buf[fifo->rindex]; 252ea7f1c8cSNeel Natu fifo->rindex = (fifo->rindex + 1) % fifo->size; 253ea7f1c8cSNeel Natu fifo->num--; 2542bd073e1SNeel Natu if (wasfull) { 2552bd073e1SNeel Natu if (sc->tty.opened) { 2562bd073e1SNeel Natu error = mevent_enable(sc->mev); 2572bd073e1SNeel Natu assert(error == 0); 2582bd073e1SNeel Natu } 2592bd073e1SNeel Natu } 260ea7f1c8cSNeel Natu return (c); 261ea7f1c8cSNeel Natu } else 262ea7f1c8cSNeel Natu return (-1); 263ea7f1c8cSNeel Natu } 264ea7f1c8cSNeel Natu 265ea7f1c8cSNeel Natu static int 2662bd073e1SNeel Natu rxfifo_numchars(struct uart_softc *sc) 267ea7f1c8cSNeel Natu { 2682bd073e1SNeel Natu struct fifo *fifo = &sc->rxfifo; 269ea7f1c8cSNeel Natu 270ea7f1c8cSNeel Natu return (fifo->num); 271ea7f1c8cSNeel Natu } 272ea7f1c8cSNeel Natu 273ea7f1c8cSNeel Natu static void 274ea7f1c8cSNeel Natu uart_opentty(struct uart_softc *sc) 275ea7f1c8cSNeel Natu { 276ea7f1c8cSNeel Natu 2776380102cSPeter Grehan ttyopen(&sc->tty); 2782bd073e1SNeel Natu sc->mev = mevent_add(sc->tty.fd, EVF_READ, uart_drain, sc); 2792bd073e1SNeel Natu assert(sc->mev != NULL); 280ea7f1c8cSNeel Natu } 281ea7f1c8cSNeel Natu 282ccfe4c3fSNeel Natu static uint8_t 283ccfe4c3fSNeel Natu modem_status(uint8_t mcr) 284ccfe4c3fSNeel Natu { 285ccfe4c3fSNeel Natu uint8_t msr; 286ccfe4c3fSNeel Natu 287ccfe4c3fSNeel Natu if (mcr & MCR_LOOPBACK) { 288ccfe4c3fSNeel Natu /* 289ccfe4c3fSNeel Natu * In the loopback mode certain bits from the MCR are 290ccfe4c3fSNeel Natu * reflected back into MSR. 291ccfe4c3fSNeel Natu */ 292ccfe4c3fSNeel Natu msr = 0; 293ccfe4c3fSNeel Natu if (mcr & MCR_RTS) 294ccfe4c3fSNeel Natu msr |= MSR_CTS; 295ccfe4c3fSNeel Natu if (mcr & MCR_DTR) 296ccfe4c3fSNeel Natu msr |= MSR_DSR; 297ccfe4c3fSNeel Natu if (mcr & MCR_OUT1) 298ccfe4c3fSNeel Natu msr |= MSR_RI; 299ccfe4c3fSNeel Natu if (mcr & MCR_OUT2) 300ccfe4c3fSNeel Natu msr |= MSR_DCD; 301ccfe4c3fSNeel Natu } else { 302ccfe4c3fSNeel Natu /* 303ccfe4c3fSNeel Natu * Always assert DCD and DSR so tty open doesn't block 304ccfe4c3fSNeel Natu * even if CLOCAL is turned off. 305ccfe4c3fSNeel Natu */ 306ccfe4c3fSNeel Natu msr = MSR_DCD | MSR_DSR; 307ccfe4c3fSNeel Natu } 308ccfe4c3fSNeel Natu assert((msr & MSR_DELTA_MASK) == 0); 309ccfe4c3fSNeel Natu 310ccfe4c3fSNeel Natu return (msr); 311ccfe4c3fSNeel Natu } 312ccfe4c3fSNeel Natu 313ea7f1c8cSNeel Natu /* 314ea7f1c8cSNeel Natu * The IIR returns a prioritized interrupt reason: 315ea7f1c8cSNeel Natu * - receive data available 316ea7f1c8cSNeel Natu * - transmit holding register empty 317ea7f1c8cSNeel Natu * - modem status change 318ea7f1c8cSNeel Natu * 319ea7f1c8cSNeel Natu * Return an interrupt reason if one is available. 320ea7f1c8cSNeel Natu */ 321ea7f1c8cSNeel Natu static int 322ea7f1c8cSNeel Natu uart_intr_reason(struct uart_softc *sc) 323ea7f1c8cSNeel Natu { 324ea7f1c8cSNeel Natu 325ea7f1c8cSNeel Natu if ((sc->lsr & LSR_OE) != 0 && (sc->ier & IER_ERLS) != 0) 326ea7f1c8cSNeel Natu return (IIR_RLS); 3272bd073e1SNeel Natu else if (rxfifo_numchars(sc) > 0 && (sc->ier & IER_ERXRDY) != 0) 328ea7f1c8cSNeel Natu return (IIR_RXTOUT); 329ea7f1c8cSNeel Natu else if (sc->thre_int_pending && (sc->ier & IER_ETXRDY) != 0) 330ea7f1c8cSNeel Natu return (IIR_TXRDY); 331ea7f1c8cSNeel Natu else if ((sc->msr & MSR_DELTA_MASK) != 0 && (sc->ier & IER_EMSC) != 0) 332ea7f1c8cSNeel Natu return (IIR_MLSC); 333ea7f1c8cSNeel Natu else 334ea7f1c8cSNeel Natu return (IIR_NOPEND); 335ea7f1c8cSNeel Natu } 336ea7f1c8cSNeel Natu 337ea7f1c8cSNeel Natu static void 338ea7f1c8cSNeel Natu uart_reset(struct uart_softc *sc) 339ea7f1c8cSNeel Natu { 340ea7f1c8cSNeel Natu uint16_t divisor; 341ea7f1c8cSNeel Natu 342ea7f1c8cSNeel Natu divisor = DEFAULT_RCLK / DEFAULT_BAUD / 16; 343ea7f1c8cSNeel Natu sc->dll = divisor; 344ea7f1c8cSNeel Natu sc->dlh = divisor >> 16; 345ccfe4c3fSNeel Natu sc->msr = modem_status(sc->mcr); 346ea7f1c8cSNeel Natu 3472bd073e1SNeel Natu rxfifo_reset(sc, 1); /* no fifo until enabled by software */ 348ea7f1c8cSNeel Natu } 349ea7f1c8cSNeel Natu 350ea7f1c8cSNeel Natu /* 351ea7f1c8cSNeel Natu * Toggle the COM port's intr pin depending on whether or not we have an 352ea7f1c8cSNeel Natu * interrupt condition to report to the processor. 353ea7f1c8cSNeel Natu */ 354ea7f1c8cSNeel Natu static void 355ea7f1c8cSNeel Natu uart_toggle_intr(struct uart_softc *sc) 356ea7f1c8cSNeel Natu { 357ea7f1c8cSNeel Natu uint8_t intr_reason; 358ea7f1c8cSNeel Natu 359ea7f1c8cSNeel Natu intr_reason = uart_intr_reason(sc); 360ea7f1c8cSNeel Natu 361ea7f1c8cSNeel Natu if (intr_reason == IIR_NOPEND) 362ea7f1c8cSNeel Natu (*sc->intr_deassert)(sc->arg); 363ea7f1c8cSNeel Natu else 364ea7f1c8cSNeel Natu (*sc->intr_assert)(sc->arg); 365ea7f1c8cSNeel Natu } 366ea7f1c8cSNeel Natu 367ea7f1c8cSNeel Natu static void 368ea7f1c8cSNeel Natu uart_drain(int fd, enum ev_type ev, void *arg) 369ea7f1c8cSNeel Natu { 370ea7f1c8cSNeel Natu struct uart_softc *sc; 371ea7f1c8cSNeel Natu int ch; 372ea7f1c8cSNeel Natu 373ea7f1c8cSNeel Natu sc = arg; 374ea7f1c8cSNeel Natu 3756380102cSPeter Grehan assert(fd == sc->tty.fd); 376ea7f1c8cSNeel Natu assert(ev == EVF_READ); 377ea7f1c8cSNeel Natu 378ea7f1c8cSNeel Natu /* 379ea7f1c8cSNeel Natu * This routine is called in the context of the mevent thread 380ea7f1c8cSNeel Natu * to take out the softc lock to protect against concurrent 381ea7f1c8cSNeel Natu * access from a vCPU i/o exit 382ea7f1c8cSNeel Natu */ 383ea7f1c8cSNeel Natu pthread_mutex_lock(&sc->mtx); 384ea7f1c8cSNeel Natu 385ea7f1c8cSNeel Natu if ((sc->mcr & MCR_LOOPBACK) != 0) { 3866380102cSPeter Grehan (void) ttyread(&sc->tty); 387ea7f1c8cSNeel Natu } else { 3882bd073e1SNeel Natu while (rxfifo_available(sc) && 3896380102cSPeter Grehan ((ch = ttyread(&sc->tty)) != -1)) { 3902bd073e1SNeel Natu rxfifo_putchar(sc, ch); 391ea7f1c8cSNeel Natu } 392ea7f1c8cSNeel Natu uart_toggle_intr(sc); 393ea7f1c8cSNeel Natu } 394ea7f1c8cSNeel Natu 395ea7f1c8cSNeel Natu pthread_mutex_unlock(&sc->mtx); 396ea7f1c8cSNeel Natu } 397ea7f1c8cSNeel Natu 398ea7f1c8cSNeel Natu void 399ea7f1c8cSNeel Natu uart_write(struct uart_softc *sc, int offset, uint8_t value) 400ea7f1c8cSNeel Natu { 401ea7f1c8cSNeel Natu int fifosz; 402ea7f1c8cSNeel Natu uint8_t msr; 403ea7f1c8cSNeel Natu 404ea7f1c8cSNeel Natu pthread_mutex_lock(&sc->mtx); 405ea7f1c8cSNeel Natu 406ea7f1c8cSNeel Natu /* 407ea7f1c8cSNeel Natu * Take care of the special case DLAB accesses first 408ea7f1c8cSNeel Natu */ 409ea7f1c8cSNeel Natu if ((sc->lcr & LCR_DLAB) != 0) { 410ea7f1c8cSNeel Natu if (offset == REG_DLL) { 411ea7f1c8cSNeel Natu sc->dll = value; 412ea7f1c8cSNeel Natu goto done; 413ea7f1c8cSNeel Natu } 414ea7f1c8cSNeel Natu 415ea7f1c8cSNeel Natu if (offset == REG_DLH) { 416ea7f1c8cSNeel Natu sc->dlh = value; 417ea7f1c8cSNeel Natu goto done; 418ea7f1c8cSNeel Natu } 419ea7f1c8cSNeel Natu } 420ea7f1c8cSNeel Natu 421ea7f1c8cSNeel Natu switch (offset) { 422ea7f1c8cSNeel Natu case REG_DATA: 423ea7f1c8cSNeel Natu if (sc->mcr & MCR_LOOPBACK) { 4242bd073e1SNeel Natu if (rxfifo_putchar(sc, value) != 0) 425ea7f1c8cSNeel Natu sc->lsr |= LSR_OE; 4266380102cSPeter Grehan } else if (sc->tty.opened) { 4276380102cSPeter Grehan ttywrite(&sc->tty, value); 428ea7f1c8cSNeel Natu } /* else drop on floor */ 429ea7f1c8cSNeel Natu sc->thre_int_pending = true; 430ea7f1c8cSNeel Natu break; 431ea7f1c8cSNeel Natu case REG_IER: 432ea7f1c8cSNeel Natu /* 433ea7f1c8cSNeel Natu * Apply mask so that bits 4-7 are 0 434ea7f1c8cSNeel Natu * Also enables bits 0-3 only if they're 1 435ea7f1c8cSNeel Natu */ 436ea7f1c8cSNeel Natu sc->ier = value & 0x0F; 437ea7f1c8cSNeel Natu break; 438ea7f1c8cSNeel Natu case REG_FCR: 439ea7f1c8cSNeel Natu /* 440ea7f1c8cSNeel Natu * When moving from FIFO and 16450 mode and vice versa, 441ea7f1c8cSNeel Natu * the FIFO contents are reset. 442ea7f1c8cSNeel Natu */ 443ea7f1c8cSNeel Natu if ((sc->fcr & FCR_ENABLE) ^ (value & FCR_ENABLE)) { 444ea7f1c8cSNeel Natu fifosz = (value & FCR_ENABLE) ? FIFOSZ : 1; 4452bd073e1SNeel Natu rxfifo_reset(sc, fifosz); 446ea7f1c8cSNeel Natu } 447ea7f1c8cSNeel Natu 448ea7f1c8cSNeel Natu /* 449ea7f1c8cSNeel Natu * The FCR_ENABLE bit must be '1' for the programming 450ea7f1c8cSNeel Natu * of other FCR bits to be effective. 451ea7f1c8cSNeel Natu */ 452ea7f1c8cSNeel Natu if ((value & FCR_ENABLE) == 0) { 453ea7f1c8cSNeel Natu sc->fcr = 0; 454ea7f1c8cSNeel Natu } else { 455ea7f1c8cSNeel Natu if ((value & FCR_RCV_RST) != 0) 4562bd073e1SNeel Natu rxfifo_reset(sc, FIFOSZ); 457ea7f1c8cSNeel Natu 458ea7f1c8cSNeel Natu sc->fcr = value & 459ea7f1c8cSNeel Natu (FCR_ENABLE | FCR_DMA | FCR_RX_MASK); 460ea7f1c8cSNeel Natu } 461ea7f1c8cSNeel Natu break; 462ea7f1c8cSNeel Natu case REG_LCR: 463ea7f1c8cSNeel Natu sc->lcr = value; 464ea7f1c8cSNeel Natu break; 465ea7f1c8cSNeel Natu case REG_MCR: 466ea7f1c8cSNeel Natu /* Apply mask so that bits 5-7 are 0 */ 467ea7f1c8cSNeel Natu sc->mcr = value & 0x1F; 468ccfe4c3fSNeel Natu msr = modem_status(sc->mcr); 469ea7f1c8cSNeel Natu 470ea7f1c8cSNeel Natu /* 471ea7f1c8cSNeel Natu * Detect if there has been any change between the 472ea7f1c8cSNeel Natu * previous and the new value of MSR. If there is 473ea7f1c8cSNeel Natu * then assert the appropriate MSR delta bit. 474ea7f1c8cSNeel Natu */ 475ea7f1c8cSNeel Natu if ((msr & MSR_CTS) ^ (sc->msr & MSR_CTS)) 476ea7f1c8cSNeel Natu sc->msr |= MSR_DCTS; 477ea7f1c8cSNeel Natu if ((msr & MSR_DSR) ^ (sc->msr & MSR_DSR)) 478ea7f1c8cSNeel Natu sc->msr |= MSR_DDSR; 479ea7f1c8cSNeel Natu if ((msr & MSR_DCD) ^ (sc->msr & MSR_DCD)) 480ea7f1c8cSNeel Natu sc->msr |= MSR_DDCD; 481ea7f1c8cSNeel Natu if ((sc->msr & MSR_RI) != 0 && (msr & MSR_RI) == 0) 482ea7f1c8cSNeel Natu sc->msr |= MSR_TERI; 483ea7f1c8cSNeel Natu 484ea7f1c8cSNeel Natu /* 485ea7f1c8cSNeel Natu * Update the value of MSR while retaining the delta 486ea7f1c8cSNeel Natu * bits. 487ea7f1c8cSNeel Natu */ 488ea7f1c8cSNeel Natu sc->msr &= MSR_DELTA_MASK; 489ea7f1c8cSNeel Natu sc->msr |= msr; 490ea7f1c8cSNeel Natu break; 491ea7f1c8cSNeel Natu case REG_LSR: 492ea7f1c8cSNeel Natu /* 493ea7f1c8cSNeel Natu * Line status register is not meant to be written to 494ea7f1c8cSNeel Natu * during normal operation. 495ea7f1c8cSNeel Natu */ 496ea7f1c8cSNeel Natu break; 497ea7f1c8cSNeel Natu case REG_MSR: 498ea7f1c8cSNeel Natu /* 499ea7f1c8cSNeel Natu * As far as I can tell MSR is a read-only register. 500ea7f1c8cSNeel Natu */ 501ea7f1c8cSNeel Natu break; 502ea7f1c8cSNeel Natu case REG_SCR: 503ea7f1c8cSNeel Natu sc->scr = value; 504ea7f1c8cSNeel Natu break; 505ea7f1c8cSNeel Natu default: 506ea7f1c8cSNeel Natu break; 507ea7f1c8cSNeel Natu } 508ea7f1c8cSNeel Natu 509ea7f1c8cSNeel Natu done: 510ea7f1c8cSNeel Natu uart_toggle_intr(sc); 511ea7f1c8cSNeel Natu pthread_mutex_unlock(&sc->mtx); 512ea7f1c8cSNeel Natu } 513ea7f1c8cSNeel Natu 514ea7f1c8cSNeel Natu uint8_t 515ea7f1c8cSNeel Natu uart_read(struct uart_softc *sc, int offset) 516ea7f1c8cSNeel Natu { 517ea7f1c8cSNeel Natu uint8_t iir, intr_reason, reg; 518ea7f1c8cSNeel Natu 519ea7f1c8cSNeel Natu pthread_mutex_lock(&sc->mtx); 520ea7f1c8cSNeel Natu 521ea7f1c8cSNeel Natu /* 522ea7f1c8cSNeel Natu * Take care of the special case DLAB accesses first 523ea7f1c8cSNeel Natu */ 524ea7f1c8cSNeel Natu if ((sc->lcr & LCR_DLAB) != 0) { 525ea7f1c8cSNeel Natu if (offset == REG_DLL) { 526ea7f1c8cSNeel Natu reg = sc->dll; 527ea7f1c8cSNeel Natu goto done; 528ea7f1c8cSNeel Natu } 529ea7f1c8cSNeel Natu 530ea7f1c8cSNeel Natu if (offset == REG_DLH) { 531ea7f1c8cSNeel Natu reg = sc->dlh; 532ea7f1c8cSNeel Natu goto done; 533ea7f1c8cSNeel Natu } 534ea7f1c8cSNeel Natu } 535ea7f1c8cSNeel Natu 536ea7f1c8cSNeel Natu switch (offset) { 537ea7f1c8cSNeel Natu case REG_DATA: 5382bd073e1SNeel Natu reg = rxfifo_getchar(sc); 539ea7f1c8cSNeel Natu break; 540ea7f1c8cSNeel Natu case REG_IER: 541ea7f1c8cSNeel Natu reg = sc->ier; 542ea7f1c8cSNeel Natu break; 543ea7f1c8cSNeel Natu case REG_IIR: 544ea7f1c8cSNeel Natu iir = (sc->fcr & FCR_ENABLE) ? IIR_FIFO_MASK : 0; 545ea7f1c8cSNeel Natu 546ea7f1c8cSNeel Natu intr_reason = uart_intr_reason(sc); 547ea7f1c8cSNeel Natu 548ea7f1c8cSNeel Natu /* 549ea7f1c8cSNeel Natu * Deal with side effects of reading the IIR register 550ea7f1c8cSNeel Natu */ 551ea7f1c8cSNeel Natu if (intr_reason == IIR_TXRDY) 552ea7f1c8cSNeel Natu sc->thre_int_pending = false; 553ea7f1c8cSNeel Natu 554ea7f1c8cSNeel Natu iir |= intr_reason; 555ea7f1c8cSNeel Natu 556ea7f1c8cSNeel Natu reg = iir; 557ea7f1c8cSNeel Natu break; 558ea7f1c8cSNeel Natu case REG_LCR: 559ea7f1c8cSNeel Natu reg = sc->lcr; 560ea7f1c8cSNeel Natu break; 561ea7f1c8cSNeel Natu case REG_MCR: 562ea7f1c8cSNeel Natu reg = sc->mcr; 563ea7f1c8cSNeel Natu break; 564ea7f1c8cSNeel Natu case REG_LSR: 565ea7f1c8cSNeel Natu /* Transmitter is always ready for more data */ 566ea7f1c8cSNeel Natu sc->lsr |= LSR_TEMT | LSR_THRE; 567ea7f1c8cSNeel Natu 568ea7f1c8cSNeel Natu /* Check for new receive data */ 5692bd073e1SNeel Natu if (rxfifo_numchars(sc) > 0) 570ea7f1c8cSNeel Natu sc->lsr |= LSR_RXRDY; 571ea7f1c8cSNeel Natu else 572ea7f1c8cSNeel Natu sc->lsr &= ~LSR_RXRDY; 573ea7f1c8cSNeel Natu 574ea7f1c8cSNeel Natu reg = sc->lsr; 575ea7f1c8cSNeel Natu 576ea7f1c8cSNeel Natu /* The LSR_OE bit is cleared on LSR read */ 577ea7f1c8cSNeel Natu sc->lsr &= ~LSR_OE; 578ea7f1c8cSNeel Natu break; 579ea7f1c8cSNeel Natu case REG_MSR: 580ea7f1c8cSNeel Natu /* 581ea7f1c8cSNeel Natu * MSR delta bits are cleared on read 582ea7f1c8cSNeel Natu */ 583ea7f1c8cSNeel Natu reg = sc->msr; 584ea7f1c8cSNeel Natu sc->msr &= ~MSR_DELTA_MASK; 585ea7f1c8cSNeel Natu break; 586ea7f1c8cSNeel Natu case REG_SCR: 587ea7f1c8cSNeel Natu reg = sc->scr; 588ea7f1c8cSNeel Natu break; 589ea7f1c8cSNeel Natu default: 590ea7f1c8cSNeel Natu reg = 0xFF; 591ea7f1c8cSNeel Natu break; 592ea7f1c8cSNeel Natu } 593ea7f1c8cSNeel Natu 594ea7f1c8cSNeel Natu done: 595ea7f1c8cSNeel Natu uart_toggle_intr(sc); 596ea7f1c8cSNeel Natu pthread_mutex_unlock(&sc->mtx); 597ea7f1c8cSNeel Natu 598ea7f1c8cSNeel Natu return (reg); 599ea7f1c8cSNeel Natu } 600ea7f1c8cSNeel Natu 601ea7f1c8cSNeel Natu int 602ea7f1c8cSNeel Natu uart_legacy_alloc(int which, int *baseaddr, int *irq) 603ea7f1c8cSNeel Natu { 604ea7f1c8cSNeel Natu 605ea7f1c8cSNeel Natu if (which < 0 || which >= UART_NLDEVS || uart_lres[which].inuse) 606ea7f1c8cSNeel Natu return (-1); 607ea7f1c8cSNeel Natu 608ea7f1c8cSNeel Natu uart_lres[which].inuse = true; 609ea7f1c8cSNeel Natu *baseaddr = uart_lres[which].baseaddr; 610ea7f1c8cSNeel Natu *irq = uart_lres[which].irq; 611ea7f1c8cSNeel Natu 612ea7f1c8cSNeel Natu return (0); 613ea7f1c8cSNeel Natu } 614ea7f1c8cSNeel Natu 615ea7f1c8cSNeel Natu struct uart_softc * 616ea7f1c8cSNeel Natu uart_init(uart_intr_func_t intr_assert, uart_intr_func_t intr_deassert, 617ea7f1c8cSNeel Natu void *arg) 618ea7f1c8cSNeel Natu { 619ea7f1c8cSNeel Natu struct uart_softc *sc; 620ea7f1c8cSNeel Natu 621994f858aSXin LI sc = calloc(1, sizeof(struct uart_softc)); 622ea7f1c8cSNeel Natu 623ea7f1c8cSNeel Natu sc->arg = arg; 624ea7f1c8cSNeel Natu sc->intr_assert = intr_assert; 625ea7f1c8cSNeel Natu sc->intr_deassert = intr_deassert; 626ea7f1c8cSNeel Natu 627ea7f1c8cSNeel Natu pthread_mutex_init(&sc->mtx, NULL); 628ea7f1c8cSNeel Natu 629ea7f1c8cSNeel Natu uart_reset(sc); 630ea7f1c8cSNeel Natu 631ea7f1c8cSNeel Natu return (sc); 632ea7f1c8cSNeel Natu } 633ea7f1c8cSNeel Natu 6346380102cSPeter Grehan static int 6356380102cSPeter Grehan uart_tty_backend(struct uart_softc *sc, const char *opts) 6366380102cSPeter Grehan { 6376380102cSPeter Grehan int fd; 6386380102cSPeter Grehan int retval; 6396380102cSPeter Grehan 6406380102cSPeter Grehan retval = -1; 6416380102cSPeter Grehan 642f4959d35SPeter Grehan fd = open(opts, O_RDWR | O_NONBLOCK); 6436380102cSPeter Grehan if (fd > 0 && isatty(fd)) { 6446380102cSPeter Grehan sc->tty.fd = fd; 6456380102cSPeter Grehan sc->tty.opened = true; 6466380102cSPeter Grehan retval = 0; 6476380102cSPeter Grehan } 6486380102cSPeter Grehan 6496380102cSPeter Grehan return (retval); 6506380102cSPeter Grehan } 6516380102cSPeter Grehan 652ea7f1c8cSNeel Natu int 653ea7f1c8cSNeel Natu uart_set_backend(struct uart_softc *sc, const char *opts) 654ea7f1c8cSNeel Natu { 6556380102cSPeter Grehan int retval; 656*00ef17beSBartek Rutkowski #ifndef WITHOUT_CAPSICUM 657*00ef17beSBartek Rutkowski cap_rights_t rights; 658*00ef17beSBartek Rutkowski cap_ioctl_t cmds[] = { TIOCGETA, TIOCSETA, TIOCGWINSZ }; 659*00ef17beSBartek Rutkowski #endif 6606380102cSPeter Grehan 6616380102cSPeter Grehan retval = -1; 6626380102cSPeter Grehan 663ea7f1c8cSNeel Natu if (opts == NULL) 664ea7f1c8cSNeel Natu return (0); 665ea7f1c8cSNeel Natu 6666380102cSPeter Grehan if (strcmp("stdio", opts) == 0) { 6676380102cSPeter Grehan if (!uart_stdio) { 6686380102cSPeter Grehan sc->tty.fd = STDIN_FILENO; 6696380102cSPeter Grehan sc->tty.opened = true; 670ea7f1c8cSNeel Natu uart_stdio = true; 6716380102cSPeter Grehan retval = 0; 6726380102cSPeter Grehan } 6736380102cSPeter Grehan } else if (uart_tty_backend(sc, opts) == 0) { 6746380102cSPeter Grehan retval = 0; 6756380102cSPeter Grehan } 6766380102cSPeter Grehan 6772bd073e1SNeel Natu /* Make the backend file descriptor non-blocking */ 6782bd073e1SNeel Natu if (retval == 0) 6792bd073e1SNeel Natu retval = fcntl(sc->tty.fd, F_SETFL, O_NONBLOCK); 6802bd073e1SNeel Natu 681*00ef17beSBartek Rutkowski #ifndef WITHOUT_CAPSICUM 682*00ef17beSBartek Rutkowski cap_rights_init(&rights, CAP_EVENT, CAP_IOCTL, CAP_READ, CAP_WRITE); 683*00ef17beSBartek Rutkowski if (cap_rights_limit(sc->tty.fd, &rights) == -1 && errno != ENOSYS) 684*00ef17beSBartek Rutkowski errx(EX_OSERR, "Unable to apply rights for sandbox"); 685*00ef17beSBartek Rutkowski if (cap_ioctls_limit(sc->tty.fd, cmds, nitems(cmds)) == -1 && errno != ENOSYS) 686*00ef17beSBartek Rutkowski errx(EX_OSERR, "Unable to apply rights for sandbox"); 687*00ef17beSBartek Rutkowski if (!uart_stdio) { 688*00ef17beSBartek Rutkowski if (caph_limit_stdin() == -1 && errno != ENOSYS) 689*00ef17beSBartek Rutkowski errx(EX_OSERR, "Unable to apply rights for sandbox"); 690*00ef17beSBartek Rutkowski } 691*00ef17beSBartek Rutkowski #endif 692*00ef17beSBartek Rutkowski 6936380102cSPeter Grehan if (retval == 0) 6946380102cSPeter Grehan uart_opentty(sc); 6956380102cSPeter Grehan 6966380102cSPeter Grehan return (retval); 697ea7f1c8cSNeel Natu } 698