1257293Sneel/*- 2257293Sneel * Copyright (c) 2012 NetApp, Inc. 3257293Sneel * Copyright (c) 2013 Neel Natu <neel@freebsd.org> 4257293Sneel * All rights reserved. 5257293Sneel * 6257293Sneel * Redistribution and use in source and binary forms, with or without 7257293Sneel * modification, are permitted provided that the following conditions 8257293Sneel * are met: 9257293Sneel * 1. Redistributions of source code must retain the above copyright 10257293Sneel * notice, this list of conditions and the following disclaimer. 11257293Sneel * 2. Redistributions in binary form must reproduce the above copyright 12257293Sneel * notice, this list of conditions and the following disclaimer in the 13257293Sneel * documentation and/or other materials provided with the distribution. 14257293Sneel * 15257293Sneel * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND 16257293Sneel * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17257293Sneel * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18257293Sneel * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE 19257293Sneel * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20257293Sneel * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21257293Sneel * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22257293Sneel * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23257293Sneel * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24257293Sneel * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25257293Sneel * SUCH DAMAGE. 26257293Sneel * 27257293Sneel * $FreeBSD$ 28257293Sneel */ 29257293Sneel 30257293Sneel#include <sys/cdefs.h> 31257293Sneel__FBSDID("$FreeBSD$"); 32257293Sneel 33257293Sneel#include <sys/types.h> 34257293Sneel#include <dev/ic/ns16550.h> 35257293Sneel 36257293Sneel#include <stdio.h> 37257293Sneel#include <stdlib.h> 38257293Sneel#include <assert.h> 39259301Sgrehan#include <fcntl.h> 40257293Sneel#include <termios.h> 41257293Sneel#include <unistd.h> 42257293Sneel#include <stdbool.h> 43257293Sneel#include <string.h> 44257293Sneel#include <pthread.h> 45257293Sneel 46257293Sneel#include "mevent.h" 47257293Sneel#include "uart_emul.h" 48257293Sneel 49257293Sneel#define COM1_BASE 0x3F8 50257293Sneel#define COM1_IRQ 4 51257293Sneel#define COM2_BASE 0x2F8 52257293Sneel#define COM2_IRQ 3 53257293Sneel 54257293Sneel#define DEFAULT_RCLK 1843200 55257293Sneel#define DEFAULT_BAUD 9600 56257293Sneel 57257293Sneel#define FCR_RX_MASK 0xC0 58257293Sneel 59257293Sneel#define MCR_OUT1 0x04 60257293Sneel#define MCR_OUT2 0x08 61257293Sneel 62257293Sneel#define MSR_DELTA_MASK 0x0f 63257293Sneel 64257293Sneel#ifndef REG_SCR 65257293Sneel#define REG_SCR com_scr 66257293Sneel#endif 67257293Sneel 68257293Sneel#define FIFOSZ 16 69257293Sneel 70257293Sneelstatic bool uart_stdio; /* stdio in use for i/o */ 71259301Sgrehanstatic struct termios tio_stdio_orig; 72257293Sneel 73257293Sneelstatic struct { 74257293Sneel int baseaddr; 75257293Sneel int irq; 76257293Sneel bool inuse; 77257293Sneel} uart_lres[] = { 78257293Sneel { COM1_BASE, COM1_IRQ, false}, 79257293Sneel { COM2_BASE, COM2_IRQ, false}, 80257293Sneel}; 81257293Sneel 82257293Sneel#define UART_NLDEVS (sizeof(uart_lres) / sizeof(uart_lres[0])) 83257293Sneel 84257293Sneelstruct fifo { 85257293Sneel uint8_t buf[FIFOSZ]; 86257293Sneel int rindex; /* index to read from */ 87257293Sneel int windex; /* index to write to */ 88257293Sneel int num; /* number of characters in the fifo */ 89257293Sneel int size; /* size of the fifo */ 90257293Sneel}; 91257293Sneel 92259301Sgrehanstruct ttyfd { 93259301Sgrehan bool opened; 94259301Sgrehan int fd; /* tty device file descriptor */ 95259301Sgrehan struct termios tio_orig, tio_new; /* I/O Terminals */ 96259301Sgrehan}; 97259301Sgrehan 98257293Sneelstruct uart_softc { 99257293Sneel pthread_mutex_t mtx; /* protects all softc elements */ 100257293Sneel uint8_t data; /* Data register (R/W) */ 101257293Sneel uint8_t ier; /* Interrupt enable register (R/W) */ 102257293Sneel uint8_t lcr; /* Line control register (R/W) */ 103257293Sneel uint8_t mcr; /* Modem control register (R/W) */ 104257293Sneel uint8_t lsr; /* Line status register (R/W) */ 105257293Sneel uint8_t msr; /* Modem status register (R/W) */ 106257293Sneel uint8_t fcr; /* FIFO control register (W) */ 107257293Sneel uint8_t scr; /* Scratch register (R/W) */ 108257293Sneel 109257293Sneel uint8_t dll; /* Baudrate divisor latch LSB */ 110257293Sneel uint8_t dlh; /* Baudrate divisor latch MSB */ 111257293Sneel 112257293Sneel struct fifo rxfifo; 113268892Sjhb struct mevent *mev; 114257293Sneel 115259301Sgrehan struct ttyfd tty; 116257293Sneel bool thre_int_pending; /* THRE interrupt pending */ 117257293Sneel 118257293Sneel void *arg; 119257293Sneel uart_intr_func_t intr_assert; 120257293Sneel uart_intr_func_t intr_deassert; 121257293Sneel}; 122257293Sneel 123257293Sneelstatic void uart_drain(int fd, enum ev_type ev, void *arg); 124257293Sneel 125257293Sneelstatic void 126257293Sneelttyclose(void) 127257293Sneel{ 128257293Sneel 129259301Sgrehan tcsetattr(STDIN_FILENO, TCSANOW, &tio_stdio_orig); 130257293Sneel} 131257293Sneel 132257293Sneelstatic void 133259301Sgrehanttyopen(struct ttyfd *tf) 134257293Sneel{ 135257293Sneel 136259301Sgrehan tcgetattr(tf->fd, &tf->tio_orig); 137257293Sneel 138259301Sgrehan tf->tio_new = tf->tio_orig; 139259301Sgrehan cfmakeraw(&tf->tio_new); 140259301Sgrehan tf->tio_new.c_cflag |= CLOCAL; 141259301Sgrehan tcsetattr(tf->fd, TCSANOW, &tf->tio_new); 142257293Sneel 143259301Sgrehan if (tf->fd == STDIN_FILENO) { 144259301Sgrehan tio_stdio_orig = tf->tio_orig; 145259301Sgrehan atexit(ttyclose); 146259301Sgrehan } 147257293Sneel} 148257293Sneel 149257293Sneelstatic int 150259301Sgrehanttyread(struct ttyfd *tf) 151257293Sneel{ 152268892Sjhb unsigned char rb; 153257293Sneel 154268892Sjhb if (read(tf->fd, &rb, 1) == 1) 155268892Sjhb return (rb); 156268892Sjhb else 157257293Sneel return (-1); 158257293Sneel} 159257293Sneel 160257293Sneelstatic void 161259301Sgrehanttywrite(struct ttyfd *tf, unsigned char wb) 162257293Sneel{ 163257293Sneel 164259301Sgrehan (void)write(tf->fd, &wb, 1); 165257293Sneel} 166257293Sneel 167257293Sneelstatic void 168268892Sjhbrxfifo_reset(struct uart_softc *sc, int size) 169257293Sneel{ 170268892Sjhb char flushbuf[32]; 171268892Sjhb struct fifo *fifo; 172268892Sjhb ssize_t nread; 173268892Sjhb int error; 174257293Sneel 175268892Sjhb fifo = &sc->rxfifo; 176257293Sneel bzero(fifo, sizeof(struct fifo)); 177257293Sneel fifo->size = size; 178268892Sjhb 179268892Sjhb if (sc->tty.opened) { 180268892Sjhb /* 181268892Sjhb * Flush any unread input from the tty buffer. 182268892Sjhb */ 183268892Sjhb while (1) { 184268892Sjhb nread = read(sc->tty.fd, flushbuf, sizeof(flushbuf)); 185268892Sjhb if (nread != sizeof(flushbuf)) 186268892Sjhb break; 187268892Sjhb } 188268892Sjhb 189268892Sjhb /* 190268892Sjhb * Enable mevent to trigger when new characters are available 191268892Sjhb * on the tty fd. 192268892Sjhb */ 193268892Sjhb error = mevent_enable(sc->mev); 194268892Sjhb assert(error == 0); 195268892Sjhb } 196257293Sneel} 197257293Sneel 198257293Sneelstatic int 199268892Sjhbrxfifo_available(struct uart_softc *sc) 200257293Sneel{ 201268892Sjhb struct fifo *fifo; 202257293Sneel 203268892Sjhb fifo = &sc->rxfifo; 204268892Sjhb return (fifo->num < fifo->size); 205268892Sjhb} 206268892Sjhb 207268892Sjhbstatic int 208268892Sjhbrxfifo_putchar(struct uart_softc *sc, uint8_t ch) 209268892Sjhb{ 210268892Sjhb struct fifo *fifo; 211268892Sjhb int error; 212268892Sjhb 213268892Sjhb fifo = &sc->rxfifo; 214268892Sjhb 215257293Sneel if (fifo->num < fifo->size) { 216257293Sneel fifo->buf[fifo->windex] = ch; 217257293Sneel fifo->windex = (fifo->windex + 1) % fifo->size; 218257293Sneel fifo->num++; 219268892Sjhb if (!rxfifo_available(sc)) { 220268892Sjhb if (sc->tty.opened) { 221268892Sjhb /* 222268892Sjhb * Disable mevent callback if the FIFO is full. 223268892Sjhb */ 224268892Sjhb error = mevent_disable(sc->mev); 225268892Sjhb assert(error == 0); 226268892Sjhb } 227268892Sjhb } 228257293Sneel return (0); 229257293Sneel } else 230257293Sneel return (-1); 231257293Sneel} 232257293Sneel 233257293Sneelstatic int 234268892Sjhbrxfifo_getchar(struct uart_softc *sc) 235257293Sneel{ 236268892Sjhb struct fifo *fifo; 237268892Sjhb int c, error, wasfull; 238257293Sneel 239268892Sjhb wasfull = 0; 240268892Sjhb fifo = &sc->rxfifo; 241257293Sneel if (fifo->num > 0) { 242268892Sjhb if (!rxfifo_available(sc)) 243268892Sjhb wasfull = 1; 244257293Sneel c = fifo->buf[fifo->rindex]; 245257293Sneel fifo->rindex = (fifo->rindex + 1) % fifo->size; 246257293Sneel fifo->num--; 247268892Sjhb if (wasfull) { 248268892Sjhb if (sc->tty.opened) { 249268892Sjhb error = mevent_enable(sc->mev); 250268892Sjhb assert(error == 0); 251268892Sjhb } 252268892Sjhb } 253257293Sneel return (c); 254257293Sneel } else 255257293Sneel return (-1); 256257293Sneel} 257257293Sneel 258257293Sneelstatic int 259268892Sjhbrxfifo_numchars(struct uart_softc *sc) 260257293Sneel{ 261268892Sjhb struct fifo *fifo = &sc->rxfifo; 262257293Sneel 263257293Sneel return (fifo->num); 264257293Sneel} 265257293Sneel 266257293Sneelstatic void 267257293Sneeluart_opentty(struct uart_softc *sc) 268257293Sneel{ 269257293Sneel 270259301Sgrehan ttyopen(&sc->tty); 271268892Sjhb sc->mev = mevent_add(sc->tty.fd, EVF_READ, uart_drain, sc); 272268892Sjhb assert(sc->mev != NULL); 273257293Sneel} 274257293Sneel 275257293Sneel/* 276257293Sneel * The IIR returns a prioritized interrupt reason: 277257293Sneel * - receive data available 278257293Sneel * - transmit holding register empty 279257293Sneel * - modem status change 280257293Sneel * 281257293Sneel * Return an interrupt reason if one is available. 282257293Sneel */ 283257293Sneelstatic int 284257293Sneeluart_intr_reason(struct uart_softc *sc) 285257293Sneel{ 286257293Sneel 287257293Sneel if ((sc->lsr & LSR_OE) != 0 && (sc->ier & IER_ERLS) != 0) 288257293Sneel return (IIR_RLS); 289268892Sjhb else if (rxfifo_numchars(sc) > 0 && (sc->ier & IER_ERXRDY) != 0) 290257293Sneel return (IIR_RXTOUT); 291257293Sneel else if (sc->thre_int_pending && (sc->ier & IER_ETXRDY) != 0) 292257293Sneel return (IIR_TXRDY); 293257293Sneel else if ((sc->msr & MSR_DELTA_MASK) != 0 && (sc->ier & IER_EMSC) != 0) 294257293Sneel return (IIR_MLSC); 295257293Sneel else 296257293Sneel return (IIR_NOPEND); 297257293Sneel} 298257293Sneel 299257293Sneelstatic void 300257293Sneeluart_reset(struct uart_softc *sc) 301257293Sneel{ 302257293Sneel uint16_t divisor; 303257293Sneel 304257293Sneel divisor = DEFAULT_RCLK / DEFAULT_BAUD / 16; 305257293Sneel sc->dll = divisor; 306257293Sneel sc->dlh = divisor >> 16; 307257293Sneel 308268892Sjhb rxfifo_reset(sc, 1); /* no fifo until enabled by software */ 309257293Sneel} 310257293Sneel 311257293Sneel/* 312257293Sneel * Toggle the COM port's intr pin depending on whether or not we have an 313257293Sneel * interrupt condition to report to the processor. 314257293Sneel */ 315257293Sneelstatic void 316257293Sneeluart_toggle_intr(struct uart_softc *sc) 317257293Sneel{ 318257293Sneel uint8_t intr_reason; 319257293Sneel 320257293Sneel intr_reason = uart_intr_reason(sc); 321257293Sneel 322257293Sneel if (intr_reason == IIR_NOPEND) 323257293Sneel (*sc->intr_deassert)(sc->arg); 324257293Sneel else 325257293Sneel (*sc->intr_assert)(sc->arg); 326257293Sneel} 327257293Sneel 328257293Sneelstatic void 329257293Sneeluart_drain(int fd, enum ev_type ev, void *arg) 330257293Sneel{ 331257293Sneel struct uart_softc *sc; 332257293Sneel int ch; 333257293Sneel 334257293Sneel sc = arg; 335257293Sneel 336259301Sgrehan assert(fd == sc->tty.fd); 337257293Sneel assert(ev == EVF_READ); 338257293Sneel 339257293Sneel /* 340257293Sneel * This routine is called in the context of the mevent thread 341257293Sneel * to take out the softc lock to protect against concurrent 342257293Sneel * access from a vCPU i/o exit 343257293Sneel */ 344257293Sneel pthread_mutex_lock(&sc->mtx); 345257293Sneel 346257293Sneel if ((sc->mcr & MCR_LOOPBACK) != 0) { 347259301Sgrehan (void) ttyread(&sc->tty); 348257293Sneel } else { 349268892Sjhb while (rxfifo_available(sc) && 350259301Sgrehan ((ch = ttyread(&sc->tty)) != -1)) { 351268892Sjhb rxfifo_putchar(sc, ch); 352257293Sneel } 353257293Sneel uart_toggle_intr(sc); 354257293Sneel } 355257293Sneel 356257293Sneel pthread_mutex_unlock(&sc->mtx); 357257293Sneel} 358257293Sneel 359257293Sneelvoid 360257293Sneeluart_write(struct uart_softc *sc, int offset, uint8_t value) 361257293Sneel{ 362257293Sneel int fifosz; 363257293Sneel uint8_t msr; 364257293Sneel 365257293Sneel pthread_mutex_lock(&sc->mtx); 366257293Sneel 367257293Sneel /* 368257293Sneel * Take care of the special case DLAB accesses first 369257293Sneel */ 370257293Sneel if ((sc->lcr & LCR_DLAB) != 0) { 371257293Sneel if (offset == REG_DLL) { 372257293Sneel sc->dll = value; 373257293Sneel goto done; 374257293Sneel } 375257293Sneel 376257293Sneel if (offset == REG_DLH) { 377257293Sneel sc->dlh = value; 378257293Sneel goto done; 379257293Sneel } 380257293Sneel } 381257293Sneel 382257293Sneel switch (offset) { 383257293Sneel case REG_DATA: 384257293Sneel if (sc->mcr & MCR_LOOPBACK) { 385268892Sjhb if (rxfifo_putchar(sc, value) != 0) 386257293Sneel sc->lsr |= LSR_OE; 387259301Sgrehan } else if (sc->tty.opened) { 388259301Sgrehan ttywrite(&sc->tty, value); 389257293Sneel } /* else drop on floor */ 390257293Sneel sc->thre_int_pending = true; 391257293Sneel break; 392257293Sneel case REG_IER: 393257293Sneel /* 394257293Sneel * Apply mask so that bits 4-7 are 0 395257293Sneel * Also enables bits 0-3 only if they're 1 396257293Sneel */ 397257293Sneel sc->ier = value & 0x0F; 398257293Sneel break; 399257293Sneel case REG_FCR: 400257293Sneel /* 401257293Sneel * When moving from FIFO and 16450 mode and vice versa, 402257293Sneel * the FIFO contents are reset. 403257293Sneel */ 404257293Sneel if ((sc->fcr & FCR_ENABLE) ^ (value & FCR_ENABLE)) { 405257293Sneel fifosz = (value & FCR_ENABLE) ? FIFOSZ : 1; 406268892Sjhb rxfifo_reset(sc, fifosz); 407257293Sneel } 408257293Sneel 409257293Sneel /* 410257293Sneel * The FCR_ENABLE bit must be '1' for the programming 411257293Sneel * of other FCR bits to be effective. 412257293Sneel */ 413257293Sneel if ((value & FCR_ENABLE) == 0) { 414257293Sneel sc->fcr = 0; 415257293Sneel } else { 416257293Sneel if ((value & FCR_RCV_RST) != 0) 417268892Sjhb rxfifo_reset(sc, FIFOSZ); 418257293Sneel 419257293Sneel sc->fcr = value & 420257293Sneel (FCR_ENABLE | FCR_DMA | FCR_RX_MASK); 421257293Sneel } 422257293Sneel break; 423257293Sneel case REG_LCR: 424257293Sneel sc->lcr = value; 425257293Sneel break; 426257293Sneel case REG_MCR: 427257293Sneel /* Apply mask so that bits 5-7 are 0 */ 428257293Sneel sc->mcr = value & 0x1F; 429257293Sneel 430257293Sneel msr = 0; 431257293Sneel if (sc->mcr & MCR_LOOPBACK) { 432257293Sneel /* 433257293Sneel * In the loopback mode certain bits from the 434257293Sneel * MCR are reflected back into MSR 435257293Sneel */ 436257293Sneel if (sc->mcr & MCR_RTS) 437257293Sneel msr |= MSR_CTS; 438257293Sneel if (sc->mcr & MCR_DTR) 439257293Sneel msr |= MSR_DSR; 440257293Sneel if (sc->mcr & MCR_OUT1) 441257293Sneel msr |= MSR_RI; 442257293Sneel if (sc->mcr & MCR_OUT2) 443257293Sneel msr |= MSR_DCD; 444257293Sneel } 445257293Sneel 446257293Sneel /* 447257293Sneel * Detect if there has been any change between the 448257293Sneel * previous and the new value of MSR. If there is 449257293Sneel * then assert the appropriate MSR delta bit. 450257293Sneel */ 451257293Sneel if ((msr & MSR_CTS) ^ (sc->msr & MSR_CTS)) 452257293Sneel sc->msr |= MSR_DCTS; 453257293Sneel if ((msr & MSR_DSR) ^ (sc->msr & MSR_DSR)) 454257293Sneel sc->msr |= MSR_DDSR; 455257293Sneel if ((msr & MSR_DCD) ^ (sc->msr & MSR_DCD)) 456257293Sneel sc->msr |= MSR_DDCD; 457257293Sneel if ((sc->msr & MSR_RI) != 0 && (msr & MSR_RI) == 0) 458257293Sneel sc->msr |= MSR_TERI; 459257293Sneel 460257293Sneel /* 461257293Sneel * Update the value of MSR while retaining the delta 462257293Sneel * bits. 463257293Sneel */ 464257293Sneel sc->msr &= MSR_DELTA_MASK; 465257293Sneel sc->msr |= msr; 466257293Sneel break; 467257293Sneel case REG_LSR: 468257293Sneel /* 469257293Sneel * Line status register is not meant to be written to 470257293Sneel * during normal operation. 471257293Sneel */ 472257293Sneel break; 473257293Sneel case REG_MSR: 474257293Sneel /* 475257293Sneel * As far as I can tell MSR is a read-only register. 476257293Sneel */ 477257293Sneel break; 478257293Sneel case REG_SCR: 479257293Sneel sc->scr = value; 480257293Sneel break; 481257293Sneel default: 482257293Sneel break; 483257293Sneel } 484257293Sneel 485257293Sneeldone: 486257293Sneel uart_toggle_intr(sc); 487257293Sneel pthread_mutex_unlock(&sc->mtx); 488257293Sneel} 489257293Sneel 490257293Sneeluint8_t 491257293Sneeluart_read(struct uart_softc *sc, int offset) 492257293Sneel{ 493257293Sneel uint8_t iir, intr_reason, reg; 494257293Sneel 495257293Sneel pthread_mutex_lock(&sc->mtx); 496257293Sneel 497257293Sneel /* 498257293Sneel * Take care of the special case DLAB accesses first 499257293Sneel */ 500257293Sneel if ((sc->lcr & LCR_DLAB) != 0) { 501257293Sneel if (offset == REG_DLL) { 502257293Sneel reg = sc->dll; 503257293Sneel goto done; 504257293Sneel } 505257293Sneel 506257293Sneel if (offset == REG_DLH) { 507257293Sneel reg = sc->dlh; 508257293Sneel goto done; 509257293Sneel } 510257293Sneel } 511257293Sneel 512257293Sneel switch (offset) { 513257293Sneel case REG_DATA: 514268892Sjhb reg = rxfifo_getchar(sc); 515257293Sneel break; 516257293Sneel case REG_IER: 517257293Sneel reg = sc->ier; 518257293Sneel break; 519257293Sneel case REG_IIR: 520257293Sneel iir = (sc->fcr & FCR_ENABLE) ? IIR_FIFO_MASK : 0; 521257293Sneel 522257293Sneel intr_reason = uart_intr_reason(sc); 523257293Sneel 524257293Sneel /* 525257293Sneel * Deal with side effects of reading the IIR register 526257293Sneel */ 527257293Sneel if (intr_reason == IIR_TXRDY) 528257293Sneel sc->thre_int_pending = false; 529257293Sneel 530257293Sneel iir |= intr_reason; 531257293Sneel 532257293Sneel reg = iir; 533257293Sneel break; 534257293Sneel case REG_LCR: 535257293Sneel reg = sc->lcr; 536257293Sneel break; 537257293Sneel case REG_MCR: 538257293Sneel reg = sc->mcr; 539257293Sneel break; 540257293Sneel case REG_LSR: 541257293Sneel /* Transmitter is always ready for more data */ 542257293Sneel sc->lsr |= LSR_TEMT | LSR_THRE; 543257293Sneel 544257293Sneel /* Check for new receive data */ 545268892Sjhb if (rxfifo_numchars(sc) > 0) 546257293Sneel sc->lsr |= LSR_RXRDY; 547257293Sneel else 548257293Sneel sc->lsr &= ~LSR_RXRDY; 549257293Sneel 550257293Sneel reg = sc->lsr; 551257293Sneel 552257293Sneel /* The LSR_OE bit is cleared on LSR read */ 553257293Sneel sc->lsr &= ~LSR_OE; 554257293Sneel break; 555257293Sneel case REG_MSR: 556257293Sneel /* 557257293Sneel * MSR delta bits are cleared on read 558257293Sneel */ 559257293Sneel reg = sc->msr; 560257293Sneel sc->msr &= ~MSR_DELTA_MASK; 561257293Sneel break; 562257293Sneel case REG_SCR: 563257293Sneel reg = sc->scr; 564257293Sneel break; 565257293Sneel default: 566257293Sneel reg = 0xFF; 567257293Sneel break; 568257293Sneel } 569257293Sneel 570257293Sneeldone: 571257293Sneel uart_toggle_intr(sc); 572257293Sneel pthread_mutex_unlock(&sc->mtx); 573257293Sneel 574257293Sneel return (reg); 575257293Sneel} 576257293Sneel 577257293Sneelint 578257293Sneeluart_legacy_alloc(int which, int *baseaddr, int *irq) 579257293Sneel{ 580257293Sneel 581257293Sneel if (which < 0 || which >= UART_NLDEVS || uart_lres[which].inuse) 582257293Sneel return (-1); 583257293Sneel 584257293Sneel uart_lres[which].inuse = true; 585257293Sneel *baseaddr = uart_lres[which].baseaddr; 586257293Sneel *irq = uart_lres[which].irq; 587257293Sneel 588257293Sneel return (0); 589257293Sneel} 590257293Sneel 591257293Sneelstruct uart_softc * 592257293Sneeluart_init(uart_intr_func_t intr_assert, uart_intr_func_t intr_deassert, 593257293Sneel void *arg) 594257293Sneel{ 595257293Sneel struct uart_softc *sc; 596257293Sneel 597268953Sjhb sc = calloc(1, sizeof(struct uart_softc)); 598257293Sneel 599257293Sneel sc->arg = arg; 600257293Sneel sc->intr_assert = intr_assert; 601257293Sneel sc->intr_deassert = intr_deassert; 602257293Sneel 603257293Sneel pthread_mutex_init(&sc->mtx, NULL); 604257293Sneel 605257293Sneel uart_reset(sc); 606257293Sneel 607257293Sneel return (sc); 608257293Sneel} 609257293Sneel 610259301Sgrehanstatic int 611259301Sgrehanuart_tty_backend(struct uart_softc *sc, const char *opts) 612259301Sgrehan{ 613259301Sgrehan int fd; 614259301Sgrehan int retval; 615259301Sgrehan 616259301Sgrehan retval = -1; 617259301Sgrehan 618268892Sjhb fd = open(opts, O_RDWR | O_NONBLOCK); 619259301Sgrehan if (fd > 0 && isatty(fd)) { 620259301Sgrehan sc->tty.fd = fd; 621259301Sgrehan sc->tty.opened = true; 622259301Sgrehan retval = 0; 623259301Sgrehan } 624259301Sgrehan 625259301Sgrehan return (retval); 626259301Sgrehan} 627259301Sgrehan 628257293Sneelint 629257293Sneeluart_set_backend(struct uart_softc *sc, const char *opts) 630257293Sneel{ 631259301Sgrehan int retval; 632259301Sgrehan 633259301Sgrehan retval = -1; 634259301Sgrehan 635257293Sneel if (opts == NULL) 636257293Sneel return (0); 637257293Sneel 638259301Sgrehan if (strcmp("stdio", opts) == 0) { 639259301Sgrehan if (!uart_stdio) { 640259301Sgrehan sc->tty.fd = STDIN_FILENO; 641259301Sgrehan sc->tty.opened = true; 642259301Sgrehan uart_stdio = true; 643259301Sgrehan retval = 0; 644259301Sgrehan } 645259301Sgrehan } else if (uart_tty_backend(sc, opts) == 0) { 646259301Sgrehan retval = 0; 647259301Sgrehan } 648259301Sgrehan 649268892Sjhb /* Make the backend file descriptor non-blocking */ 650259301Sgrehan if (retval == 0) 651268892Sjhb retval = fcntl(sc->tty.fd, F_SETFL, O_NONBLOCK); 652268892Sjhb 653268892Sjhb if (retval == 0) 654259301Sgrehan uart_opentty(sc); 655259301Sgrehan 656259301Sgrehan return (retval); 657257293Sneel} 658