1ea7f1c8cSNeel Natu /*- 21de7b4b8SPedro F. Giffuni * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 31de7b4b8SPedro F. Giffuni * 4ea7f1c8cSNeel Natu * Copyright (c) 2012 NetApp, Inc. 5ea7f1c8cSNeel Natu * Copyright (c) 2013 Neel Natu <neel@freebsd.org> 6ea7f1c8cSNeel Natu * All rights reserved. 7ea7f1c8cSNeel Natu * 8ea7f1c8cSNeel Natu * Redistribution and use in source and binary forms, with or without 9ea7f1c8cSNeel Natu * modification, are permitted provided that the following conditions 10ea7f1c8cSNeel Natu * are met: 11ea7f1c8cSNeel Natu * 1. Redistributions of source code must retain the above copyright 12ea7f1c8cSNeel Natu * notice, this list of conditions and the following disclaimer. 13ea7f1c8cSNeel Natu * 2. Redistributions in binary form must reproduce the above copyright 14ea7f1c8cSNeel Natu * notice, this list of conditions and the following disclaimer in the 15ea7f1c8cSNeel Natu * documentation and/or other materials provided with the distribution. 16ea7f1c8cSNeel Natu * 17ea7f1c8cSNeel Natu * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND 18ea7f1c8cSNeel Natu * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19ea7f1c8cSNeel Natu * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20ea7f1c8cSNeel Natu * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE 21ea7f1c8cSNeel Natu * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22ea7f1c8cSNeel Natu * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23ea7f1c8cSNeel Natu * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24ea7f1c8cSNeel Natu * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25ea7f1c8cSNeel Natu * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26ea7f1c8cSNeel Natu * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27ea7f1c8cSNeel Natu * SUCH DAMAGE. 28ea7f1c8cSNeel Natu * 29ea7f1c8cSNeel Natu * $FreeBSD$ 30ea7f1c8cSNeel Natu */ 31ea7f1c8cSNeel Natu 32ea7f1c8cSNeel Natu #include <sys/cdefs.h> 33ea7f1c8cSNeel Natu __FBSDID("$FreeBSD$"); 34ea7f1c8cSNeel Natu 35ea7f1c8cSNeel Natu #include <sys/types.h> 36ea7f1c8cSNeel Natu #include <dev/ic/ns16550.h> 3700ef17beSBartek Rutkowski #ifndef WITHOUT_CAPSICUM 3800ef17beSBartek Rutkowski #include <sys/capsicum.h> 3900ef17beSBartek Rutkowski #include <capsicum_helpers.h> 4000ef17beSBartek Rutkowski #endif 41ea7f1c8cSNeel Natu 42483d953aSJohn Baldwin #include <machine/vmm_snapshot.h> 43483d953aSJohn Baldwin 44ea7f1c8cSNeel Natu #include <stdio.h> 45ea7f1c8cSNeel Natu #include <stdlib.h> 46ea7f1c8cSNeel Natu #include <assert.h> 4700ef17beSBartek Rutkowski #include <err.h> 4800ef17beSBartek Rutkowski #include <errno.h> 496380102cSPeter Grehan #include <fcntl.h> 50ea7f1c8cSNeel Natu #include <termios.h> 51ea7f1c8cSNeel Natu #include <unistd.h> 52ea7f1c8cSNeel Natu #include <stdbool.h> 53ea7f1c8cSNeel Natu #include <string.h> 54ea7f1c8cSNeel Natu #include <pthread.h> 5500ef17beSBartek Rutkowski #include <sysexits.h> 56ea7f1c8cSNeel Natu 57ea7f1c8cSNeel Natu #include "mevent.h" 58ea7f1c8cSNeel Natu #include "uart_emul.h" 59332eff95SVincenzo Maffione #include "debug.h" 60ea7f1c8cSNeel Natu 61ea7f1c8cSNeel Natu #define COM1_BASE 0x3F8 62ea7f1c8cSNeel Natu #define COM1_IRQ 4 63ea7f1c8cSNeel Natu #define COM2_BASE 0x2F8 64ea7f1c8cSNeel Natu #define COM2_IRQ 3 65*eed1cc6cSPeter Grehan #define COM3_BASE 0x3E8 66*eed1cc6cSPeter Grehan #define COM3_IRQ 4 67*eed1cc6cSPeter Grehan #define COM4_BASE 0x2E8 68*eed1cc6cSPeter Grehan #define COM4_IRQ 3 69ea7f1c8cSNeel Natu 70ea7f1c8cSNeel Natu #define DEFAULT_RCLK 1843200 71ea7f1c8cSNeel Natu #define DEFAULT_BAUD 9600 72ea7f1c8cSNeel Natu 73ea7f1c8cSNeel Natu #define FCR_RX_MASK 0xC0 74ea7f1c8cSNeel Natu 75ea7f1c8cSNeel Natu #define MCR_OUT1 0x04 76ea7f1c8cSNeel Natu #define MCR_OUT2 0x08 77ea7f1c8cSNeel Natu 78ea7f1c8cSNeel Natu #define MSR_DELTA_MASK 0x0f 79ea7f1c8cSNeel Natu 80ea7f1c8cSNeel Natu #ifndef REG_SCR 81ea7f1c8cSNeel Natu #define REG_SCR com_scr 82ea7f1c8cSNeel Natu #endif 83ea7f1c8cSNeel Natu 84ea7f1c8cSNeel Natu #define FIFOSZ 16 85ea7f1c8cSNeel Natu 86ea7f1c8cSNeel Natu static bool uart_stdio; /* stdio in use for i/o */ 876380102cSPeter Grehan static struct termios tio_stdio_orig; 88ea7f1c8cSNeel Natu 89ea7f1c8cSNeel Natu static struct { 90ea7f1c8cSNeel Natu int baseaddr; 91ea7f1c8cSNeel Natu int irq; 92ea7f1c8cSNeel Natu bool inuse; 93ea7f1c8cSNeel Natu } uart_lres[] = { 94ea7f1c8cSNeel Natu { COM1_BASE, COM1_IRQ, false}, 95ea7f1c8cSNeel Natu { COM2_BASE, COM2_IRQ, false}, 96*eed1cc6cSPeter Grehan { COM3_BASE, COM3_IRQ, false}, 97*eed1cc6cSPeter Grehan { COM4_BASE, COM4_IRQ, false}, 98ea7f1c8cSNeel Natu }; 99ea7f1c8cSNeel Natu 100ea7f1c8cSNeel Natu #define UART_NLDEVS (sizeof(uart_lres) / sizeof(uart_lres[0])) 101ea7f1c8cSNeel Natu 102ea7f1c8cSNeel Natu struct fifo { 103ea7f1c8cSNeel Natu uint8_t buf[FIFOSZ]; 104ea7f1c8cSNeel Natu int rindex; /* index to read from */ 105ea7f1c8cSNeel Natu int windex; /* index to write to */ 106ea7f1c8cSNeel Natu int num; /* number of characters in the fifo */ 107ea7f1c8cSNeel Natu int size; /* size of the fifo */ 108ea7f1c8cSNeel Natu }; 109ea7f1c8cSNeel Natu 1106380102cSPeter Grehan struct ttyfd { 1116380102cSPeter Grehan bool opened; 112d6ef759eSMark Johnston int rfd; /* fd for reading */ 113d6ef759eSMark Johnston int wfd; /* fd for writing, may be == rfd */ 1146380102cSPeter Grehan }; 1156380102cSPeter Grehan 116ea7f1c8cSNeel Natu struct uart_softc { 117ea7f1c8cSNeel Natu pthread_mutex_t mtx; /* protects all softc elements */ 118ea7f1c8cSNeel Natu uint8_t data; /* Data register (R/W) */ 119ea7f1c8cSNeel Natu uint8_t ier; /* Interrupt enable register (R/W) */ 120ea7f1c8cSNeel Natu uint8_t lcr; /* Line control register (R/W) */ 121ea7f1c8cSNeel Natu uint8_t mcr; /* Modem control register (R/W) */ 122ea7f1c8cSNeel Natu uint8_t lsr; /* Line status register (R/W) */ 123ea7f1c8cSNeel Natu uint8_t msr; /* Modem status register (R/W) */ 124ea7f1c8cSNeel Natu uint8_t fcr; /* FIFO control register (W) */ 125ea7f1c8cSNeel Natu uint8_t scr; /* Scratch register (R/W) */ 126ea7f1c8cSNeel Natu 127ea7f1c8cSNeel Natu uint8_t dll; /* Baudrate divisor latch LSB */ 128ea7f1c8cSNeel Natu uint8_t dlh; /* Baudrate divisor latch MSB */ 129ea7f1c8cSNeel Natu 130ea7f1c8cSNeel Natu struct fifo rxfifo; 1312bd073e1SNeel Natu struct mevent *mev; 132ea7f1c8cSNeel Natu 1336380102cSPeter Grehan struct ttyfd tty; 134ea7f1c8cSNeel Natu bool thre_int_pending; /* THRE interrupt pending */ 135ea7f1c8cSNeel Natu 136ea7f1c8cSNeel Natu void *arg; 137ea7f1c8cSNeel Natu uart_intr_func_t intr_assert; 138ea7f1c8cSNeel Natu uart_intr_func_t intr_deassert; 139ea7f1c8cSNeel Natu }; 140ea7f1c8cSNeel Natu 141ea7f1c8cSNeel Natu static void uart_drain(int fd, enum ev_type ev, void *arg); 142ea7f1c8cSNeel Natu 143ea7f1c8cSNeel Natu static void 144ea7f1c8cSNeel Natu ttyclose(void) 145ea7f1c8cSNeel Natu { 146ea7f1c8cSNeel Natu 1476380102cSPeter Grehan tcsetattr(STDIN_FILENO, TCSANOW, &tio_stdio_orig); 148ea7f1c8cSNeel Natu } 149ea7f1c8cSNeel Natu 150ea7f1c8cSNeel Natu static void 1516380102cSPeter Grehan ttyopen(struct ttyfd *tf) 152ea7f1c8cSNeel Natu { 153d6ef759eSMark Johnston struct termios orig, new; 154ea7f1c8cSNeel Natu 155d6ef759eSMark Johnston tcgetattr(tf->rfd, &orig); 156d6ef759eSMark Johnston new = orig; 157d6ef759eSMark Johnston cfmakeraw(&new); 158d6ef759eSMark Johnston new.c_cflag |= CLOCAL; 159d6ef759eSMark Johnston tcsetattr(tf->rfd, TCSANOW, &new); 160d6ef759eSMark Johnston if (uart_stdio) { 161d6ef759eSMark Johnston tio_stdio_orig = orig; 162ea7f1c8cSNeel Natu atexit(ttyclose); 163ea7f1c8cSNeel Natu } 164332eff95SVincenzo Maffione raw_stdio = 1; 1656380102cSPeter Grehan } 166ea7f1c8cSNeel Natu 167ea7f1c8cSNeel Natu static int 1686380102cSPeter Grehan ttyread(struct ttyfd *tf) 169ea7f1c8cSNeel Natu { 1702bd073e1SNeel Natu unsigned char rb; 171ea7f1c8cSNeel Natu 172d6ef759eSMark Johnston if (read(tf->rfd, &rb, 1) == 1) 1732bd073e1SNeel Natu return (rb); 1742bd073e1SNeel Natu else 175ea7f1c8cSNeel Natu return (-1); 176ea7f1c8cSNeel Natu } 177ea7f1c8cSNeel Natu 178ea7f1c8cSNeel Natu static void 1796380102cSPeter Grehan ttywrite(struct ttyfd *tf, unsigned char wb) 180ea7f1c8cSNeel Natu { 181ea7f1c8cSNeel Natu 182d6ef759eSMark Johnston (void)write(tf->wfd, &wb, 1); 183ea7f1c8cSNeel Natu } 184ea7f1c8cSNeel Natu 185ea7f1c8cSNeel Natu static void 1862bd073e1SNeel Natu rxfifo_reset(struct uart_softc *sc, int size) 187ea7f1c8cSNeel Natu { 1882bd073e1SNeel Natu char flushbuf[32]; 1892bd073e1SNeel Natu struct fifo *fifo; 1902bd073e1SNeel Natu ssize_t nread; 1912bd073e1SNeel Natu int error; 192ea7f1c8cSNeel Natu 1932bd073e1SNeel Natu fifo = &sc->rxfifo; 194ea7f1c8cSNeel Natu bzero(fifo, sizeof(struct fifo)); 195ea7f1c8cSNeel Natu fifo->size = size; 1962bd073e1SNeel Natu 1972bd073e1SNeel Natu if (sc->tty.opened) { 1982bd073e1SNeel Natu /* 1992bd073e1SNeel Natu * Flush any unread input from the tty buffer. 2002bd073e1SNeel Natu */ 2012bd073e1SNeel Natu while (1) { 202d6ef759eSMark Johnston nread = read(sc->tty.rfd, flushbuf, sizeof(flushbuf)); 2032bd073e1SNeel Natu if (nread != sizeof(flushbuf)) 2042bd073e1SNeel Natu break; 2052bd073e1SNeel Natu } 2062bd073e1SNeel Natu 2072bd073e1SNeel Natu /* 2082bd073e1SNeel Natu * Enable mevent to trigger when new characters are available 2092bd073e1SNeel Natu * on the tty fd. 2102bd073e1SNeel Natu */ 2112bd073e1SNeel Natu error = mevent_enable(sc->mev); 2122bd073e1SNeel Natu assert(error == 0); 2132bd073e1SNeel Natu } 214ea7f1c8cSNeel Natu } 215ea7f1c8cSNeel Natu 216ea7f1c8cSNeel Natu static int 2172bd073e1SNeel Natu rxfifo_available(struct uart_softc *sc) 218ea7f1c8cSNeel Natu { 2192bd073e1SNeel Natu struct fifo *fifo; 2202bd073e1SNeel Natu 2212bd073e1SNeel Natu fifo = &sc->rxfifo; 2222bd073e1SNeel Natu return (fifo->num < fifo->size); 2232bd073e1SNeel Natu } 2242bd073e1SNeel Natu 2252bd073e1SNeel Natu static int 2262bd073e1SNeel Natu rxfifo_putchar(struct uart_softc *sc, uint8_t ch) 2272bd073e1SNeel Natu { 2282bd073e1SNeel Natu struct fifo *fifo; 2292bd073e1SNeel Natu int error; 2302bd073e1SNeel Natu 2312bd073e1SNeel Natu fifo = &sc->rxfifo; 232ea7f1c8cSNeel Natu 233ea7f1c8cSNeel Natu if (fifo->num < fifo->size) { 234ea7f1c8cSNeel Natu fifo->buf[fifo->windex] = ch; 235ea7f1c8cSNeel Natu fifo->windex = (fifo->windex + 1) % fifo->size; 236ea7f1c8cSNeel Natu fifo->num++; 2372bd073e1SNeel Natu if (!rxfifo_available(sc)) { 2382bd073e1SNeel Natu if (sc->tty.opened) { 2392bd073e1SNeel Natu /* 2402bd073e1SNeel Natu * Disable mevent callback if the FIFO is full. 2412bd073e1SNeel Natu */ 2422bd073e1SNeel Natu error = mevent_disable(sc->mev); 2432bd073e1SNeel Natu assert(error == 0); 2442bd073e1SNeel Natu } 2452bd073e1SNeel Natu } 246ea7f1c8cSNeel Natu return (0); 247ea7f1c8cSNeel Natu } else 248ea7f1c8cSNeel Natu return (-1); 249ea7f1c8cSNeel Natu } 250ea7f1c8cSNeel Natu 251ea7f1c8cSNeel Natu static int 2522bd073e1SNeel Natu rxfifo_getchar(struct uart_softc *sc) 253ea7f1c8cSNeel Natu { 2542bd073e1SNeel Natu struct fifo *fifo; 2552bd073e1SNeel Natu int c, error, wasfull; 256ea7f1c8cSNeel Natu 2572bd073e1SNeel Natu wasfull = 0; 2582bd073e1SNeel Natu fifo = &sc->rxfifo; 259ea7f1c8cSNeel Natu if (fifo->num > 0) { 2602bd073e1SNeel Natu if (!rxfifo_available(sc)) 2612bd073e1SNeel Natu wasfull = 1; 262ea7f1c8cSNeel Natu c = fifo->buf[fifo->rindex]; 263ea7f1c8cSNeel Natu fifo->rindex = (fifo->rindex + 1) % fifo->size; 264ea7f1c8cSNeel Natu fifo->num--; 2652bd073e1SNeel Natu if (wasfull) { 2662bd073e1SNeel Natu if (sc->tty.opened) { 2672bd073e1SNeel Natu error = mevent_enable(sc->mev); 2682bd073e1SNeel Natu assert(error == 0); 2692bd073e1SNeel Natu } 2702bd073e1SNeel Natu } 271ea7f1c8cSNeel Natu return (c); 272ea7f1c8cSNeel Natu } else 273ea7f1c8cSNeel Natu return (-1); 274ea7f1c8cSNeel Natu } 275ea7f1c8cSNeel Natu 276ea7f1c8cSNeel Natu static int 2772bd073e1SNeel Natu rxfifo_numchars(struct uart_softc *sc) 278ea7f1c8cSNeel Natu { 2792bd073e1SNeel Natu struct fifo *fifo = &sc->rxfifo; 280ea7f1c8cSNeel Natu 281ea7f1c8cSNeel Natu return (fifo->num); 282ea7f1c8cSNeel Natu } 283ea7f1c8cSNeel Natu 284ea7f1c8cSNeel Natu static void 285ea7f1c8cSNeel Natu uart_opentty(struct uart_softc *sc) 286ea7f1c8cSNeel Natu { 287ea7f1c8cSNeel Natu 2886380102cSPeter Grehan ttyopen(&sc->tty); 289d6ef759eSMark Johnston sc->mev = mevent_add(sc->tty.rfd, EVF_READ, uart_drain, sc); 2902bd073e1SNeel Natu assert(sc->mev != NULL); 291ea7f1c8cSNeel Natu } 292ea7f1c8cSNeel Natu 293ccfe4c3fSNeel Natu static uint8_t 294ccfe4c3fSNeel Natu modem_status(uint8_t mcr) 295ccfe4c3fSNeel Natu { 296ccfe4c3fSNeel Natu uint8_t msr; 297ccfe4c3fSNeel Natu 298ccfe4c3fSNeel Natu if (mcr & MCR_LOOPBACK) { 299ccfe4c3fSNeel Natu /* 300ccfe4c3fSNeel Natu * In the loopback mode certain bits from the MCR are 301ccfe4c3fSNeel Natu * reflected back into MSR. 302ccfe4c3fSNeel Natu */ 303ccfe4c3fSNeel Natu msr = 0; 304ccfe4c3fSNeel Natu if (mcr & MCR_RTS) 305ccfe4c3fSNeel Natu msr |= MSR_CTS; 306ccfe4c3fSNeel Natu if (mcr & MCR_DTR) 307ccfe4c3fSNeel Natu msr |= MSR_DSR; 308ccfe4c3fSNeel Natu if (mcr & MCR_OUT1) 309ccfe4c3fSNeel Natu msr |= MSR_RI; 310ccfe4c3fSNeel Natu if (mcr & MCR_OUT2) 311ccfe4c3fSNeel Natu msr |= MSR_DCD; 312ccfe4c3fSNeel Natu } else { 313ccfe4c3fSNeel Natu /* 314ccfe4c3fSNeel Natu * Always assert DCD and DSR so tty open doesn't block 315ccfe4c3fSNeel Natu * even if CLOCAL is turned off. 316ccfe4c3fSNeel Natu */ 317ccfe4c3fSNeel Natu msr = MSR_DCD | MSR_DSR; 318ccfe4c3fSNeel Natu } 319ccfe4c3fSNeel Natu assert((msr & MSR_DELTA_MASK) == 0); 320ccfe4c3fSNeel Natu 321ccfe4c3fSNeel Natu return (msr); 322ccfe4c3fSNeel Natu } 323ccfe4c3fSNeel Natu 324ea7f1c8cSNeel Natu /* 325ea7f1c8cSNeel Natu * The IIR returns a prioritized interrupt reason: 326ea7f1c8cSNeel Natu * - receive data available 327ea7f1c8cSNeel Natu * - transmit holding register empty 328ea7f1c8cSNeel Natu * - modem status change 329ea7f1c8cSNeel Natu * 330ea7f1c8cSNeel Natu * Return an interrupt reason if one is available. 331ea7f1c8cSNeel Natu */ 332ea7f1c8cSNeel Natu static int 333ea7f1c8cSNeel Natu uart_intr_reason(struct uart_softc *sc) 334ea7f1c8cSNeel Natu { 335ea7f1c8cSNeel Natu 336ea7f1c8cSNeel Natu if ((sc->lsr & LSR_OE) != 0 && (sc->ier & IER_ERLS) != 0) 337ea7f1c8cSNeel Natu return (IIR_RLS); 3382bd073e1SNeel Natu else if (rxfifo_numchars(sc) > 0 && (sc->ier & IER_ERXRDY) != 0) 339ea7f1c8cSNeel Natu return (IIR_RXTOUT); 340ea7f1c8cSNeel Natu else if (sc->thre_int_pending && (sc->ier & IER_ETXRDY) != 0) 341ea7f1c8cSNeel Natu return (IIR_TXRDY); 342ea7f1c8cSNeel Natu else if ((sc->msr & MSR_DELTA_MASK) != 0 && (sc->ier & IER_EMSC) != 0) 343ea7f1c8cSNeel Natu return (IIR_MLSC); 344ea7f1c8cSNeel Natu else 345ea7f1c8cSNeel Natu return (IIR_NOPEND); 346ea7f1c8cSNeel Natu } 347ea7f1c8cSNeel Natu 348ea7f1c8cSNeel Natu static void 349ea7f1c8cSNeel Natu uart_reset(struct uart_softc *sc) 350ea7f1c8cSNeel Natu { 351ea7f1c8cSNeel Natu uint16_t divisor; 352ea7f1c8cSNeel Natu 353ea7f1c8cSNeel Natu divisor = DEFAULT_RCLK / DEFAULT_BAUD / 16; 354ea7f1c8cSNeel Natu sc->dll = divisor; 355ea7f1c8cSNeel Natu sc->dlh = divisor >> 16; 356ccfe4c3fSNeel Natu sc->msr = modem_status(sc->mcr); 357ea7f1c8cSNeel Natu 3582bd073e1SNeel Natu rxfifo_reset(sc, 1); /* no fifo until enabled by software */ 359ea7f1c8cSNeel Natu } 360ea7f1c8cSNeel Natu 361ea7f1c8cSNeel Natu /* 362ea7f1c8cSNeel Natu * Toggle the COM port's intr pin depending on whether or not we have an 363ea7f1c8cSNeel Natu * interrupt condition to report to the processor. 364ea7f1c8cSNeel Natu */ 365ea7f1c8cSNeel Natu static void 366ea7f1c8cSNeel Natu uart_toggle_intr(struct uart_softc *sc) 367ea7f1c8cSNeel Natu { 368ea7f1c8cSNeel Natu uint8_t intr_reason; 369ea7f1c8cSNeel Natu 370ea7f1c8cSNeel Natu intr_reason = uart_intr_reason(sc); 371ea7f1c8cSNeel Natu 372ea7f1c8cSNeel Natu if (intr_reason == IIR_NOPEND) 373ea7f1c8cSNeel Natu (*sc->intr_deassert)(sc->arg); 374ea7f1c8cSNeel Natu else 375ea7f1c8cSNeel Natu (*sc->intr_assert)(sc->arg); 376ea7f1c8cSNeel Natu } 377ea7f1c8cSNeel Natu 378ea7f1c8cSNeel Natu static void 379ea7f1c8cSNeel Natu uart_drain(int fd, enum ev_type ev, void *arg) 380ea7f1c8cSNeel Natu { 381ea7f1c8cSNeel Natu struct uart_softc *sc; 382ea7f1c8cSNeel Natu int ch; 383ea7f1c8cSNeel Natu 384ea7f1c8cSNeel Natu sc = arg; 385ea7f1c8cSNeel Natu 386d6ef759eSMark Johnston assert(fd == sc->tty.rfd); 387ea7f1c8cSNeel Natu assert(ev == EVF_READ); 388ea7f1c8cSNeel Natu 389ea7f1c8cSNeel Natu /* 390ea7f1c8cSNeel Natu * This routine is called in the context of the mevent thread 391ea7f1c8cSNeel Natu * to take out the softc lock to protect against concurrent 392ea7f1c8cSNeel Natu * access from a vCPU i/o exit 393ea7f1c8cSNeel Natu */ 394ea7f1c8cSNeel Natu pthread_mutex_lock(&sc->mtx); 395ea7f1c8cSNeel Natu 396ea7f1c8cSNeel Natu if ((sc->mcr & MCR_LOOPBACK) != 0) { 3976380102cSPeter Grehan (void) ttyread(&sc->tty); 398ea7f1c8cSNeel Natu } else { 3992bd073e1SNeel Natu while (rxfifo_available(sc) && 4006380102cSPeter Grehan ((ch = ttyread(&sc->tty)) != -1)) { 4012bd073e1SNeel Natu rxfifo_putchar(sc, ch); 402ea7f1c8cSNeel Natu } 403ea7f1c8cSNeel Natu uart_toggle_intr(sc); 404ea7f1c8cSNeel Natu } 405ea7f1c8cSNeel Natu 406ea7f1c8cSNeel Natu pthread_mutex_unlock(&sc->mtx); 407ea7f1c8cSNeel Natu } 408ea7f1c8cSNeel Natu 409ea7f1c8cSNeel Natu void 410ea7f1c8cSNeel Natu uart_write(struct uart_softc *sc, int offset, uint8_t value) 411ea7f1c8cSNeel Natu { 412ea7f1c8cSNeel Natu int fifosz; 413ea7f1c8cSNeel Natu uint8_t msr; 414ea7f1c8cSNeel Natu 415ea7f1c8cSNeel Natu pthread_mutex_lock(&sc->mtx); 416ea7f1c8cSNeel Natu 417ea7f1c8cSNeel Natu /* 418ea7f1c8cSNeel Natu * Take care of the special case DLAB accesses first 419ea7f1c8cSNeel Natu */ 420ea7f1c8cSNeel Natu if ((sc->lcr & LCR_DLAB) != 0) { 421ea7f1c8cSNeel Natu if (offset == REG_DLL) { 422ea7f1c8cSNeel Natu sc->dll = value; 423ea7f1c8cSNeel Natu goto done; 424ea7f1c8cSNeel Natu } 425ea7f1c8cSNeel Natu 426ea7f1c8cSNeel Natu if (offset == REG_DLH) { 427ea7f1c8cSNeel Natu sc->dlh = value; 428ea7f1c8cSNeel Natu goto done; 429ea7f1c8cSNeel Natu } 430ea7f1c8cSNeel Natu } 431ea7f1c8cSNeel Natu 432ea7f1c8cSNeel Natu switch (offset) { 433ea7f1c8cSNeel Natu case REG_DATA: 434ea7f1c8cSNeel Natu if (sc->mcr & MCR_LOOPBACK) { 4352bd073e1SNeel Natu if (rxfifo_putchar(sc, value) != 0) 436ea7f1c8cSNeel Natu sc->lsr |= LSR_OE; 4376380102cSPeter Grehan } else if (sc->tty.opened) { 4386380102cSPeter Grehan ttywrite(&sc->tty, value); 439ea7f1c8cSNeel Natu } /* else drop on floor */ 440ea7f1c8cSNeel Natu sc->thre_int_pending = true; 441ea7f1c8cSNeel Natu break; 442ea7f1c8cSNeel Natu case REG_IER: 44355792380SConrad Meyer /* Set pending when IER_ETXRDY is raised (edge-triggered). */ 44455792380SConrad Meyer if ((sc->ier & IER_ETXRDY) == 0 && (value & IER_ETXRDY) != 0) 44555792380SConrad Meyer sc->thre_int_pending = true; 446ea7f1c8cSNeel Natu /* 447ea7f1c8cSNeel Natu * Apply mask so that bits 4-7 are 0 448ea7f1c8cSNeel Natu * Also enables bits 0-3 only if they're 1 449ea7f1c8cSNeel Natu */ 450ea7f1c8cSNeel Natu sc->ier = value & 0x0F; 451ea7f1c8cSNeel Natu break; 452ea7f1c8cSNeel Natu case REG_FCR: 453ea7f1c8cSNeel Natu /* 454ea7f1c8cSNeel Natu * When moving from FIFO and 16450 mode and vice versa, 455ea7f1c8cSNeel Natu * the FIFO contents are reset. 456ea7f1c8cSNeel Natu */ 457ea7f1c8cSNeel Natu if ((sc->fcr & FCR_ENABLE) ^ (value & FCR_ENABLE)) { 458ea7f1c8cSNeel Natu fifosz = (value & FCR_ENABLE) ? FIFOSZ : 1; 4592bd073e1SNeel Natu rxfifo_reset(sc, fifosz); 460ea7f1c8cSNeel Natu } 461ea7f1c8cSNeel Natu 462ea7f1c8cSNeel Natu /* 463ea7f1c8cSNeel Natu * The FCR_ENABLE bit must be '1' for the programming 464ea7f1c8cSNeel Natu * of other FCR bits to be effective. 465ea7f1c8cSNeel Natu */ 466ea7f1c8cSNeel Natu if ((value & FCR_ENABLE) == 0) { 467ea7f1c8cSNeel Natu sc->fcr = 0; 468ea7f1c8cSNeel Natu } else { 469ea7f1c8cSNeel Natu if ((value & FCR_RCV_RST) != 0) 4702bd073e1SNeel Natu rxfifo_reset(sc, FIFOSZ); 471ea7f1c8cSNeel Natu 472ea7f1c8cSNeel Natu sc->fcr = value & 473ea7f1c8cSNeel Natu (FCR_ENABLE | FCR_DMA | FCR_RX_MASK); 474ea7f1c8cSNeel Natu } 475ea7f1c8cSNeel Natu break; 476ea7f1c8cSNeel Natu case REG_LCR: 477ea7f1c8cSNeel Natu sc->lcr = value; 478ea7f1c8cSNeel Natu break; 479ea7f1c8cSNeel Natu case REG_MCR: 480ea7f1c8cSNeel Natu /* Apply mask so that bits 5-7 are 0 */ 481ea7f1c8cSNeel Natu sc->mcr = value & 0x1F; 482ccfe4c3fSNeel Natu msr = modem_status(sc->mcr); 483ea7f1c8cSNeel Natu 484ea7f1c8cSNeel Natu /* 485ea7f1c8cSNeel Natu * Detect if there has been any change between the 486ea7f1c8cSNeel Natu * previous and the new value of MSR. If there is 487ea7f1c8cSNeel Natu * then assert the appropriate MSR delta bit. 488ea7f1c8cSNeel Natu */ 489ea7f1c8cSNeel Natu if ((msr & MSR_CTS) ^ (sc->msr & MSR_CTS)) 490ea7f1c8cSNeel Natu sc->msr |= MSR_DCTS; 491ea7f1c8cSNeel Natu if ((msr & MSR_DSR) ^ (sc->msr & MSR_DSR)) 492ea7f1c8cSNeel Natu sc->msr |= MSR_DDSR; 493ea7f1c8cSNeel Natu if ((msr & MSR_DCD) ^ (sc->msr & MSR_DCD)) 494ea7f1c8cSNeel Natu sc->msr |= MSR_DDCD; 495ea7f1c8cSNeel Natu if ((sc->msr & MSR_RI) != 0 && (msr & MSR_RI) == 0) 496ea7f1c8cSNeel Natu sc->msr |= MSR_TERI; 497ea7f1c8cSNeel Natu 498ea7f1c8cSNeel Natu /* 499ea7f1c8cSNeel Natu * Update the value of MSR while retaining the delta 500ea7f1c8cSNeel Natu * bits. 501ea7f1c8cSNeel Natu */ 502ea7f1c8cSNeel Natu sc->msr &= MSR_DELTA_MASK; 503ea7f1c8cSNeel Natu sc->msr |= msr; 504ea7f1c8cSNeel Natu break; 505ea7f1c8cSNeel Natu case REG_LSR: 506ea7f1c8cSNeel Natu /* 507ea7f1c8cSNeel Natu * Line status register is not meant to be written to 508ea7f1c8cSNeel Natu * during normal operation. 509ea7f1c8cSNeel Natu */ 510ea7f1c8cSNeel Natu break; 511ea7f1c8cSNeel Natu case REG_MSR: 512ea7f1c8cSNeel Natu /* 513ea7f1c8cSNeel Natu * As far as I can tell MSR is a read-only register. 514ea7f1c8cSNeel Natu */ 515ea7f1c8cSNeel Natu break; 516ea7f1c8cSNeel Natu case REG_SCR: 517ea7f1c8cSNeel Natu sc->scr = value; 518ea7f1c8cSNeel Natu break; 519ea7f1c8cSNeel Natu default: 520ea7f1c8cSNeel Natu break; 521ea7f1c8cSNeel Natu } 522ea7f1c8cSNeel Natu 523ea7f1c8cSNeel Natu done: 524ea7f1c8cSNeel Natu uart_toggle_intr(sc); 525ea7f1c8cSNeel Natu pthread_mutex_unlock(&sc->mtx); 526ea7f1c8cSNeel Natu } 527ea7f1c8cSNeel Natu 528ea7f1c8cSNeel Natu uint8_t 529ea7f1c8cSNeel Natu uart_read(struct uart_softc *sc, int offset) 530ea7f1c8cSNeel Natu { 531ea7f1c8cSNeel Natu uint8_t iir, intr_reason, reg; 532ea7f1c8cSNeel Natu 533ea7f1c8cSNeel Natu pthread_mutex_lock(&sc->mtx); 534ea7f1c8cSNeel Natu 535ea7f1c8cSNeel Natu /* 536ea7f1c8cSNeel Natu * Take care of the special case DLAB accesses first 537ea7f1c8cSNeel Natu */ 538ea7f1c8cSNeel Natu if ((sc->lcr & LCR_DLAB) != 0) { 539ea7f1c8cSNeel Natu if (offset == REG_DLL) { 540ea7f1c8cSNeel Natu reg = sc->dll; 541ea7f1c8cSNeel Natu goto done; 542ea7f1c8cSNeel Natu } 543ea7f1c8cSNeel Natu 544ea7f1c8cSNeel Natu if (offset == REG_DLH) { 545ea7f1c8cSNeel Natu reg = sc->dlh; 546ea7f1c8cSNeel Natu goto done; 547ea7f1c8cSNeel Natu } 548ea7f1c8cSNeel Natu } 549ea7f1c8cSNeel Natu 550ea7f1c8cSNeel Natu switch (offset) { 551ea7f1c8cSNeel Natu case REG_DATA: 5522bd073e1SNeel Natu reg = rxfifo_getchar(sc); 553ea7f1c8cSNeel Natu break; 554ea7f1c8cSNeel Natu case REG_IER: 555ea7f1c8cSNeel Natu reg = sc->ier; 556ea7f1c8cSNeel Natu break; 557ea7f1c8cSNeel Natu case REG_IIR: 558ea7f1c8cSNeel Natu iir = (sc->fcr & FCR_ENABLE) ? IIR_FIFO_MASK : 0; 559ea7f1c8cSNeel Natu 560ea7f1c8cSNeel Natu intr_reason = uart_intr_reason(sc); 561ea7f1c8cSNeel Natu 562ea7f1c8cSNeel Natu /* 563ea7f1c8cSNeel Natu * Deal with side effects of reading the IIR register 564ea7f1c8cSNeel Natu */ 565ea7f1c8cSNeel Natu if (intr_reason == IIR_TXRDY) 566ea7f1c8cSNeel Natu sc->thre_int_pending = false; 567ea7f1c8cSNeel Natu 568ea7f1c8cSNeel Natu iir |= intr_reason; 569ea7f1c8cSNeel Natu 570ea7f1c8cSNeel Natu reg = iir; 571ea7f1c8cSNeel Natu break; 572ea7f1c8cSNeel Natu case REG_LCR: 573ea7f1c8cSNeel Natu reg = sc->lcr; 574ea7f1c8cSNeel Natu break; 575ea7f1c8cSNeel Natu case REG_MCR: 576ea7f1c8cSNeel Natu reg = sc->mcr; 577ea7f1c8cSNeel Natu break; 578ea7f1c8cSNeel Natu case REG_LSR: 579ea7f1c8cSNeel Natu /* Transmitter is always ready for more data */ 580ea7f1c8cSNeel Natu sc->lsr |= LSR_TEMT | LSR_THRE; 581ea7f1c8cSNeel Natu 582ea7f1c8cSNeel Natu /* Check for new receive data */ 5832bd073e1SNeel Natu if (rxfifo_numchars(sc) > 0) 584ea7f1c8cSNeel Natu sc->lsr |= LSR_RXRDY; 585ea7f1c8cSNeel Natu else 586ea7f1c8cSNeel Natu sc->lsr &= ~LSR_RXRDY; 587ea7f1c8cSNeel Natu 588ea7f1c8cSNeel Natu reg = sc->lsr; 589ea7f1c8cSNeel Natu 590ea7f1c8cSNeel Natu /* The LSR_OE bit is cleared on LSR read */ 591ea7f1c8cSNeel Natu sc->lsr &= ~LSR_OE; 592ea7f1c8cSNeel Natu break; 593ea7f1c8cSNeel Natu case REG_MSR: 594ea7f1c8cSNeel Natu /* 595ea7f1c8cSNeel Natu * MSR delta bits are cleared on read 596ea7f1c8cSNeel Natu */ 597ea7f1c8cSNeel Natu reg = sc->msr; 598ea7f1c8cSNeel Natu sc->msr &= ~MSR_DELTA_MASK; 599ea7f1c8cSNeel Natu break; 600ea7f1c8cSNeel Natu case REG_SCR: 601ea7f1c8cSNeel Natu reg = sc->scr; 602ea7f1c8cSNeel Natu break; 603ea7f1c8cSNeel Natu default: 604ea7f1c8cSNeel Natu reg = 0xFF; 605ea7f1c8cSNeel Natu break; 606ea7f1c8cSNeel Natu } 607ea7f1c8cSNeel Natu 608ea7f1c8cSNeel Natu done: 609ea7f1c8cSNeel Natu uart_toggle_intr(sc); 610ea7f1c8cSNeel Natu pthread_mutex_unlock(&sc->mtx); 611ea7f1c8cSNeel Natu 612ea7f1c8cSNeel Natu return (reg); 613ea7f1c8cSNeel Natu } 614ea7f1c8cSNeel Natu 615ea7f1c8cSNeel Natu int 616ea7f1c8cSNeel Natu uart_legacy_alloc(int which, int *baseaddr, int *irq) 617ea7f1c8cSNeel Natu { 618ea7f1c8cSNeel Natu 619ea7f1c8cSNeel Natu if (which < 0 || which >= UART_NLDEVS || uart_lres[which].inuse) 620ea7f1c8cSNeel Natu return (-1); 621ea7f1c8cSNeel Natu 622ea7f1c8cSNeel Natu uart_lres[which].inuse = true; 623ea7f1c8cSNeel Natu *baseaddr = uart_lres[which].baseaddr; 624ea7f1c8cSNeel Natu *irq = uart_lres[which].irq; 625ea7f1c8cSNeel Natu 626ea7f1c8cSNeel Natu return (0); 627ea7f1c8cSNeel Natu } 628ea7f1c8cSNeel Natu 629ea7f1c8cSNeel Natu struct uart_softc * 630ea7f1c8cSNeel Natu uart_init(uart_intr_func_t intr_assert, uart_intr_func_t intr_deassert, 631ea7f1c8cSNeel Natu void *arg) 632ea7f1c8cSNeel Natu { 633ea7f1c8cSNeel Natu struct uart_softc *sc; 634ea7f1c8cSNeel Natu 635994f858aSXin LI sc = calloc(1, sizeof(struct uart_softc)); 636ea7f1c8cSNeel Natu 637ea7f1c8cSNeel Natu sc->arg = arg; 638ea7f1c8cSNeel Natu sc->intr_assert = intr_assert; 639ea7f1c8cSNeel Natu sc->intr_deassert = intr_deassert; 640ea7f1c8cSNeel Natu 641ea7f1c8cSNeel Natu pthread_mutex_init(&sc->mtx, NULL); 642ea7f1c8cSNeel Natu 643ea7f1c8cSNeel Natu uart_reset(sc); 644ea7f1c8cSNeel Natu 645ea7f1c8cSNeel Natu return (sc); 646ea7f1c8cSNeel Natu } 647ea7f1c8cSNeel Natu 6486380102cSPeter Grehan static int 649d6ef759eSMark Johnston uart_stdio_backend(struct uart_softc *sc) 6506380102cSPeter Grehan { 651d6ef759eSMark Johnston #ifndef WITHOUT_CAPSICUM 652d6ef759eSMark Johnston cap_rights_t rights; 653d6ef759eSMark Johnston cap_ioctl_t cmds[] = { TIOCGETA, TIOCSETA, TIOCGWINSZ }; 654d6ef759eSMark Johnston #endif 6556380102cSPeter Grehan 656d6ef759eSMark Johnston if (uart_stdio) 657d6ef759eSMark Johnston return (-1); 6586380102cSPeter Grehan 659d6ef759eSMark Johnston sc->tty.rfd = STDIN_FILENO; 660d6ef759eSMark Johnston sc->tty.wfd = STDOUT_FILENO; 6616380102cSPeter Grehan sc->tty.opened = true; 662d6ef759eSMark Johnston 663d6ef759eSMark Johnston if (fcntl(sc->tty.rfd, F_SETFL, O_NONBLOCK) != 0) 664d6ef759eSMark Johnston return (-1); 665d6ef759eSMark Johnston if (fcntl(sc->tty.wfd, F_SETFL, O_NONBLOCK) != 0) 666d6ef759eSMark Johnston return (-1); 667d6ef759eSMark Johnston 668d6ef759eSMark Johnston #ifndef WITHOUT_CAPSICUM 669d6ef759eSMark Johnston cap_rights_init(&rights, CAP_EVENT, CAP_IOCTL, CAP_READ); 670d6ef759eSMark Johnston if (caph_rights_limit(sc->tty.rfd, &rights) == -1) 671d6ef759eSMark Johnston errx(EX_OSERR, "Unable to apply rights for sandbox"); 672d6ef759eSMark Johnston if (caph_ioctls_limit(sc->tty.rfd, cmds, nitems(cmds)) == -1) 673d6ef759eSMark Johnston errx(EX_OSERR, "Unable to apply rights for sandbox"); 674d6ef759eSMark Johnston #endif 675d6ef759eSMark Johnston 676d6ef759eSMark Johnston uart_stdio = true; 677d6ef759eSMark Johnston 678d6ef759eSMark Johnston return (0); 6796380102cSPeter Grehan } 6806380102cSPeter Grehan 681d6ef759eSMark Johnston static int 682d6ef759eSMark Johnston uart_tty_backend(struct uart_softc *sc, const char *opts) 683d6ef759eSMark Johnston { 684d6ef759eSMark Johnston #ifndef WITHOUT_CAPSICUM 685d6ef759eSMark Johnston cap_rights_t rights; 686d6ef759eSMark Johnston cap_ioctl_t cmds[] = { TIOCGETA, TIOCSETA, TIOCGWINSZ }; 687d6ef759eSMark Johnston #endif 688d6ef759eSMark Johnston int fd; 689d6ef759eSMark Johnston 690d6ef759eSMark Johnston fd = open(opts, O_RDWR | O_NONBLOCK); 691ae2c5fe3SSean Chittenden if (fd < 0) 692d6ef759eSMark Johnston return (-1); 693d6ef759eSMark Johnston 694ae2c5fe3SSean Chittenden if (!isatty(fd)) { 695ae2c5fe3SSean Chittenden close(fd); 696ae2c5fe3SSean Chittenden return (-1); 697ae2c5fe3SSean Chittenden } 698ae2c5fe3SSean Chittenden 699d6ef759eSMark Johnston sc->tty.rfd = sc->tty.wfd = fd; 700d6ef759eSMark Johnston sc->tty.opened = true; 701d6ef759eSMark Johnston 702d6ef759eSMark Johnston #ifndef WITHOUT_CAPSICUM 703d6ef759eSMark Johnston cap_rights_init(&rights, CAP_EVENT, CAP_IOCTL, CAP_READ, CAP_WRITE); 704d6ef759eSMark Johnston if (caph_rights_limit(fd, &rights) == -1) 705d6ef759eSMark Johnston errx(EX_OSERR, "Unable to apply rights for sandbox"); 706d6ef759eSMark Johnston if (caph_ioctls_limit(fd, cmds, nitems(cmds)) == -1) 707d6ef759eSMark Johnston errx(EX_OSERR, "Unable to apply rights for sandbox"); 708d6ef759eSMark Johnston #endif 709d6ef759eSMark Johnston 710d6ef759eSMark Johnston return (0); 7116380102cSPeter Grehan } 7126380102cSPeter Grehan 713ea7f1c8cSNeel Natu int 714ea7f1c8cSNeel Natu uart_set_backend(struct uart_softc *sc, const char *opts) 715ea7f1c8cSNeel Natu { 7166380102cSPeter Grehan int retval; 7176380102cSPeter Grehan 718ea7f1c8cSNeel Natu if (opts == NULL) 719ea7f1c8cSNeel Natu return (0); 720ea7f1c8cSNeel Natu 721d6ef759eSMark Johnston if (strcmp("stdio", opts) == 0) 722d6ef759eSMark Johnston retval = uart_stdio_backend(sc); 723d6ef759eSMark Johnston else 724d6ef759eSMark Johnston retval = uart_tty_backend(sc, opts); 7252bd073e1SNeel Natu if (retval == 0) 7266380102cSPeter Grehan uart_opentty(sc); 7276380102cSPeter Grehan 7286380102cSPeter Grehan return (retval); 729ea7f1c8cSNeel Natu } 730483d953aSJohn Baldwin 731483d953aSJohn Baldwin #ifdef BHYVE_SNAPSHOT 732483d953aSJohn Baldwin int 733483d953aSJohn Baldwin uart_snapshot(struct uart_softc *sc, struct vm_snapshot_meta *meta) 734483d953aSJohn Baldwin { 735483d953aSJohn Baldwin int ret; 736483d953aSJohn Baldwin 737483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(sc->data, meta, ret, done); 738483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(sc->ier, meta, ret, done); 739483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(sc->lcr, meta, ret, done); 740483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(sc->mcr, meta, ret, done); 741483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(sc->lsr, meta, ret, done); 742483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(sc->msr, meta, ret, done); 743483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(sc->fcr, meta, ret, done); 744483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(sc->scr, meta, ret, done); 745483d953aSJohn Baldwin 746483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(sc->dll, meta, ret, done); 747483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(sc->dlh, meta, ret, done); 748483d953aSJohn Baldwin 749483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(sc->rxfifo.rindex, meta, ret, done); 750483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(sc->rxfifo.windex, meta, ret, done); 751483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(sc->rxfifo.num, meta, ret, done); 752483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(sc->rxfifo.size, meta, ret, done); 753483d953aSJohn Baldwin SNAPSHOT_BUF_OR_LEAVE(sc->rxfifo.buf, sizeof(sc->rxfifo.buf), 754483d953aSJohn Baldwin meta, ret, done); 755483d953aSJohn Baldwin 756483d953aSJohn Baldwin sc->thre_int_pending = 1; 757483d953aSJohn Baldwin 758483d953aSJohn Baldwin done: 759483d953aSJohn Baldwin return (ret); 760483d953aSJohn Baldwin } 761483d953aSJohn Baldwin #endif 762