uart_emul.c revision 295124
131921Sbrian/*- 231921Sbrian * Copyright (c) 2012 NetApp, Inc. 331921Sbrian * Copyright (c) 2013 Neel Natu <neel@freebsd.org> 431921Sbrian * All rights reserved. 531921Sbrian * 631921Sbrian * Redistribution and use in source and binary forms, with or without 731921Sbrian * modification, are permitted provided that the following conditions 831921Sbrian * are met: 931921Sbrian * 1. Redistributions of source code must retain the above copyright 1031921Sbrian * notice, this list of conditions and the following disclaimer. 1131921Sbrian * 2. Redistributions in binary form must reproduce the above copyright 1231921Sbrian * notice, this list of conditions and the following disclaimer in the 1331921Sbrian * documentation and/or other materials provided with the distribution. 1431921Sbrian * 1531921Sbrian * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND 1631921Sbrian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1731921Sbrian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1831921Sbrian * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE 1931921Sbrian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2031921Sbrian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2131921Sbrian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2231921Sbrian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2331921Sbrian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2431921Sbrian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2531921Sbrian * SUCH DAMAGE. 2650479Speter * 2730715Sbrian * $FreeBSD: stable/10/usr.sbin/bhyve/uart_emul.c 295124 2016-02-01 14:56:11Z grehan $ 2830715Sbrian */ 2971657Sbrian 3071657Sbrian#include <sys/cdefs.h> 3126940Sbrian__FBSDID("$FreeBSD: stable/10/usr.sbin/bhyve/uart_emul.c 295124 2016-02-01 14:56:11Z grehan $"); 3226940Sbrian 3336285Sbrian#include <sys/types.h> 3430715Sbrian#include <dev/ic/ns16550.h> 3530715Sbrian 36102500Sbrian#include <stdio.h> 3730715Sbrian#include <stdlib.h> 3826940Sbrian#include <assert.h> 3930715Sbrian#include <fcntl.h> 4036285Sbrian#include <termios.h> 4126940Sbrian#include <unistd.h> 4230715Sbrian#include <stdbool.h> 4326940Sbrian#include <string.h> 4436285Sbrian#include <pthread.h> 4526940Sbrian 4636285Sbrian#include "mevent.h" 4781634Sbrian#include "uart_emul.h" 4881900Sbrian 4926940Sbrian#define COM1_BASE 0x3F8 5036285Sbrian#define COM1_IRQ 4 5158028Sbrian#define COM2_BASE 0x2F8 5236285Sbrian#define COM2_IRQ 3 5336285Sbrian 5436314Sbrian#define DEFAULT_RCLK 1843200 5536314Sbrian#define DEFAULT_BAUD 9600 5630715Sbrian 5736314Sbrian#define FCR_RX_MASK 0xC0 5836285Sbrian 5936285Sbrian#define MCR_OUT1 0x04 6036285Sbrian#define MCR_OUT2 0x08 6136285Sbrian 6236285Sbrian#define MSR_DELTA_MASK 0x0f 6336314Sbrian 6436285Sbrian#ifndef REG_SCR 6536314Sbrian#define REG_SCR com_scr 6636314Sbrian#endif 6736314Sbrian 6836314Sbrian#define FIFOSZ 16 6936314Sbrian 7036285Sbrianstatic bool uart_stdio; /* stdio in use for i/o */ 7126940Sbrianstatic struct termios tio_stdio_orig; 7236285Sbrian 7358028Sbrianstatic struct { 7436285Sbrian int baseaddr; 7536285Sbrian int irq; 7636314Sbrian bool inuse; 7736314Sbrian} uart_lres[] = { 7836314Sbrian { COM1_BASE, COM1_IRQ, false}, 7936314Sbrian { COM2_BASE, COM2_IRQ, false}, 8036314Sbrian}; 8136314Sbrian 8236314Sbrian#define UART_NLDEVS (sizeof(uart_lres) / sizeof(uart_lres[0])) 8336314Sbrian 8436314Sbrianstruct fifo { 8536314Sbrian uint8_t buf[FIFOSZ]; 8636285Sbrian int rindex; /* index to read from */ 8736285Sbrian int windex; /* index to write to */ 8836285Sbrian int num; /* number of characters in the fifo */ 8958028Sbrian int size; /* size of the fifo */ 9036285Sbrian}; 9136285Sbrian 9281634Sbrianstruct ttyfd { 9381634Sbrian bool opened; 9481634Sbrian int fd; /* tty device file descriptor */ 9581634Sbrian struct termios tio_orig, tio_new; /* I/O Terminals */ 9681634Sbrian}; 9781634Sbrian 9881634Sbrianstruct uart_softc { 9936285Sbrian pthread_mutex_t mtx; /* protects all softc elements */ 10081634Sbrian uint8_t data; /* Data register (R/W) */ 10136285Sbrian uint8_t ier; /* Interrupt enable register (R/W) */ 10236314Sbrian uint8_t lcr; /* Line control register (R/W) */ 10336314Sbrian uint8_t mcr; /* Modem control register (R/W) */ 10436314Sbrian uint8_t lsr; /* Line status register (R/W) */ 10536314Sbrian uint8_t msr; /* Modem status register (R/W) */ 10672436Sbrian uint8_t fcr; /* FIFO control register (W) */ 10772436Sbrian uint8_t scr; /* Scratch register (R/W) */ 10872436Sbrian 10972436Sbrian uint8_t dll; /* Baudrate divisor latch LSB */ 11036314Sbrian uint8_t dlh; /* Baudrate divisor latch MSB */ 11136314Sbrian 11236285Sbrian struct fifo rxfifo; 11336314Sbrian struct mevent *mev; 11436285Sbrian 11536285Sbrian struct ttyfd tty; 11636314Sbrian bool thre_int_pending; /* THRE interrupt pending */ 11736285Sbrian 11836314Sbrian void *arg; 11936285Sbrian uart_intr_func_t intr_assert; 12081634Sbrian uart_intr_func_t intr_deassert; 12181634Sbrian}; 12236314Sbrian 12336314Sbrianstatic void uart_drain(int fd, enum ev_type ev, void *arg); 12481634Sbrian 12536314Sbrianstatic void 12636314Sbrianttyclose(void) 12736314Sbrian{ 12836314Sbrian 12936314Sbrian tcsetattr(STDIN_FILENO, TCSANOW, &tio_stdio_orig); 13081634Sbrian} 13136285Sbrian 13236314Sbrianstatic void 13381634Sbrianttyopen(struct ttyfd *tf) 13481634Sbrian{ 13581634Sbrian 13681634Sbrian tcgetattr(tf->fd, &tf->tio_orig); 13781634Sbrian 13881634Sbrian tf->tio_new = tf->tio_orig; 13981634Sbrian cfmakeraw(&tf->tio_new); 14081634Sbrian tf->tio_new.c_cflag |= CLOCAL; 14181634Sbrian tcsetattr(tf->fd, TCSANOW, &tf->tio_new); 14281634Sbrian 14381634Sbrian if (tf->fd == STDIN_FILENO) { 14481634Sbrian tio_stdio_orig = tf->tio_orig; 14581634Sbrian atexit(ttyclose); 14681634Sbrian } 14781634Sbrian} 14881634Sbrian 14936314Sbrianstatic int 15036314Sbrianttyread(struct ttyfd *tf) 15136314Sbrian{ 15236314Sbrian unsigned char rb; 15336314Sbrian 15436285Sbrian if (read(tf->fd, &rb, 1) == 1) 15536314Sbrian return (rb); 15636314Sbrian else 15736314Sbrian return (-1); 15836314Sbrian} 15936314Sbrian 16036314Sbrianstatic void 16136314Sbrianttywrite(struct ttyfd *tf, unsigned char wb) 16236314Sbrian{ 16336314Sbrian 16471657Sbrian (void)write(tf->fd, &wb, 1); 16536314Sbrian} 16636314Sbrian 16736314Sbrianstatic void 16881634Sbrianrxfifo_reset(struct uart_softc *sc, int size) 16936314Sbrian{ 17081634Sbrian char flushbuf[32]; 17136314Sbrian struct fifo *fifo; 17281634Sbrian ssize_t nread; 17381634Sbrian int error; 17481634Sbrian 17581634Sbrian fifo = &sc->rxfifo; 17681634Sbrian bzero(fifo, sizeof(struct fifo)); 17781634Sbrian fifo->size = size; 17881634Sbrian 17936314Sbrian if (sc->tty.opened) { 18036314Sbrian /* 18136314Sbrian * Flush any unread input from the tty buffer. 18236314Sbrian */ 18336285Sbrian while (1) { 18436314Sbrian nread = read(sc->tty.fd, flushbuf, sizeof(flushbuf)); 18538013Sbrian if (nread != sizeof(flushbuf)) 18636314Sbrian break; 18738013Sbrian } 18836314Sbrian 18938013Sbrian /* 19038013Sbrian * Enable mevent to trigger when new characters are available 19138013Sbrian * on the tty fd. 19236285Sbrian */ 19336285Sbrian error = mevent_enable(sc->mev); 19437141Sbrian assert(error == 0); 195134789Sbrian } 196134789Sbrian} 19736285Sbrian 19836285Sbrianstatic int 19937019Sbrianrxfifo_available(struct uart_softc *sc) 20037141Sbrian{ 20136285Sbrian struct fifo *fifo; 20236285Sbrian 20336285Sbrian fifo = &sc->rxfifo; 20436285Sbrian return (fifo->num < fifo->size); 20536285Sbrian} 20636285Sbrian 20736285Sbrianstatic int 20836285Sbrianrxfifo_putchar(struct uart_softc *sc, uint8_t ch) 20936285Sbrian{ 21036285Sbrian struct fifo *fifo; 211134789Sbrian int error; 212134789Sbrian 21336285Sbrian fifo = &sc->rxfifo; 21436285Sbrian 21571657Sbrian if (fifo->num < fifo->size) { 21671657Sbrian fifo->buf[fifo->windex] = ch; 21771657Sbrian fifo->windex = (fifo->windex + 1) % fifo->size; 21871657Sbrian fifo->num++; 21971764Sbrian if (!rxfifo_available(sc)) { 22071657Sbrian if (sc->tty.opened) { 22171657Sbrian /* 22271657Sbrian * Disable mevent callback if the FIFO is full. 22371657Sbrian */ 22471657Sbrian error = mevent_disable(sc->mev); 22571657Sbrian assert(error == 0); 22671657Sbrian } 22771657Sbrian } 22871764Sbrian return (0); 22971764Sbrian } else 23071764Sbrian return (-1); 23171657Sbrian} 23271657Sbrian 23371657Sbrianstatic int 23471657Sbrianrxfifo_getchar(struct uart_softc *sc) 23571657Sbrian{ 23671657Sbrian struct fifo *fifo; 23771657Sbrian int c, error, wasfull; 23871657Sbrian 23971657Sbrian wasfull = 0; 24071657Sbrian fifo = &sc->rxfifo; 24171657Sbrian if (fifo->num > 0) { 24271657Sbrian if (!rxfifo_available(sc)) 24336285Sbrian wasfull = 1; 24426940Sbrian c = fifo->buf[fifo->rindex]; 24571657Sbrian fifo->rindex = (fifo->rindex + 1) % fifo->size; 24671657Sbrian fifo->num--; 24728679Sbrian if (wasfull) { 24826940Sbrian if (sc->tty.opened) { 24971657Sbrian error = mevent_enable(sc->mev); 25029083Sbrian assert(error == 0); 25171657Sbrian } 25271657Sbrian } 25371657Sbrian return (c); 25471657Sbrian } else 25571657Sbrian return (-1); 25671657Sbrian} 25736285Sbrian 25871657Sbrianstatic int 25928679Sbrianrxfifo_numchars(struct uart_softc *sc) 26071657Sbrian{ 26171657Sbrian struct fifo *fifo = &sc->rxfifo; 26226940Sbrian 26389422Sbrian return (fifo->num); 26428679Sbrian} 26536285Sbrian 26671657Sbrianstatic void 26728679Sbrianuart_opentty(struct uart_softc *sc) 26828679Sbrian{ 26931081Sbrian 27071657Sbrian ttyopen(&sc->tty); 27171657Sbrian sc->mev = mevent_add(sc->tty.fd, EVF_READ, uart_drain, sc); 27231081Sbrian assert(sc->mev != NULL); 27371657Sbrian} 27436285Sbrian 27528679Sbrianstatic uint8_t 27671657Sbrianmodem_status(uint8_t mcr) 27728679Sbrian{ 27831081Sbrian uint8_t msr; 27971657Sbrian 28028679Sbrian if (mcr & MCR_LOOPBACK) { 28167912Sbrian /* 28267912Sbrian * In the loopback mode certain bits from the MCR are 28328679Sbrian * reflected back into MSR. 28471657Sbrian */ 28571657Sbrian msr = 0; 28628679Sbrian if (mcr & MCR_RTS) 28744588Sbrian msr |= MSR_CTS; 28836285Sbrian if (mcr & MCR_DTR) 28971657Sbrian msr |= MSR_DSR; 29071657Sbrian if (mcr & MCR_OUT1) 29171657Sbrian msr |= MSR_RI; 29271657Sbrian if (mcr & MCR_OUT2) 29336285Sbrian msr |= MSR_DCD; 29471657Sbrian } else { 29571657Sbrian /* 29671657Sbrian * Always assert DCD and DSR so tty open doesn't block 29771657Sbrian * even if CLOCAL is turned off. 29871657Sbrian */ 29971657Sbrian msr = MSR_DCD | MSR_DSR; 30071657Sbrian } 30171657Sbrian assert((msr & MSR_DELTA_MASK) == 0); 30271657Sbrian 30371657Sbrian return (msr); 30471657Sbrian} 30571657Sbrian 30671657Sbrian/* 30726940Sbrian * The IIR returns a prioritized interrupt reason: 30826940Sbrian * - receive data available 30971657Sbrian * - transmit holding register empty 31071657Sbrian * - modem status change 31126940Sbrian * 31281634Sbrian * Return an interrupt reason if one is available. 31381634Sbrian */ 31481900Sbrianstatic int 31581634Sbrianuart_intr_reason(struct uart_softc *sc) 31681634Sbrian{ 31781634Sbrian 31826940Sbrian if ((sc->lsr & LSR_OE) != 0 && (sc->ier & IER_ERLS) != 0) 31971657Sbrian return (IIR_RLS); 32071657Sbrian else if (rxfifo_numchars(sc) > 0 && (sc->ier & IER_ERXRDY) != 0) 32129252Sbrian return (IIR_RXTOUT); 32271657Sbrian else if (sc->thre_int_pending && (sc->ier & IER_ETXRDY) != 0) 32371657Sbrian return (IIR_TXRDY); 32471657Sbrian else if ((sc->msr & MSR_DELTA_MASK) != 0 && (sc->ier & IER_EMSC) != 0) 32581634Sbrian return (IIR_MLSC); 32681900Sbrian else 32781900Sbrian return (IIR_NOPEND); 32881900Sbrian} 32981900Sbrian 33081924Sbrianstatic void 33181900Sbrianuart_reset(struct uart_softc *sc) 33289422Sbrian{ 33381900Sbrian uint16_t divisor; 33481634Sbrian 33581900Sbrian divisor = DEFAULT_RCLK / DEFAULT_BAUD / 16; 33681900Sbrian sc->dll = divisor; 33781900Sbrian sc->dlh = divisor >> 16; 33881924Sbrian sc->msr = modem_status(sc->mcr); 33981900Sbrian 34081900Sbrian rxfifo_reset(sc, 1); /* no fifo until enabled by software */ 34189422Sbrian} 34281900Sbrian 34381900Sbrian/* 34428679Sbrian * Toggle the COM port's intr pin depending on whether or not we have an 34536285Sbrian * interrupt condition to report to the processor. 34671657Sbrian */ 34728679Sbrianstatic void 34881634Sbrianuart_toggle_intr(struct uart_softc *sc) 349162389Sume{ 350162389Sume uint8_t intr_reason; 351162389Sume 352162389Sume intr_reason = uart_intr_reason(sc); 353162389Sume 354162389Sume if (intr_reason == IIR_NOPEND) 355162389Sume (*sc->intr_deassert)(sc->arg); 35628679Sbrian else 35781634Sbrian (*sc->intr_assert)(sc->arg); 35836285Sbrian} 35928679Sbrian 36071657Sbrianstatic void 36128679Sbrianuart_drain(int fd, enum ev_type ev, void *arg) 36228679Sbrian{ 36344588Sbrian struct uart_softc *sc; 36444588Sbrian int ch; 36528679Sbrian 36671657Sbrian sc = arg; 36728679Sbrian 36836285Sbrian assert(fd == sc->tty.fd); 36936285Sbrian assert(ev == EVF_READ); 37071657Sbrian 37171657Sbrian /* 37271657Sbrian * This routine is called in the context of the mevent thread 37336285Sbrian * to take out the softc lock to protect against concurrent 37471657Sbrian * access from a vCPU i/o exit 37571657Sbrian */ 37671657Sbrian pthread_mutex_lock(&sc->mtx); 37771657Sbrian 37871657Sbrian if ((sc->mcr & MCR_LOOPBACK) != 0) { 37971657Sbrian (void) ttyread(&sc->tty); 38071657Sbrian } else { 38171657Sbrian while (rxfifo_available(sc) && 38271657Sbrian ((ch = ttyread(&sc->tty)) != -1)) { 38371657Sbrian rxfifo_putchar(sc, ch); 38426940Sbrian } 38526940Sbrian uart_toggle_intr(sc); 38636285Sbrian } 387134789Sbrian 38826940Sbrian pthread_mutex_unlock(&sc->mtx); 38936285Sbrian} 39071657Sbrian 39144588Sbrianvoid 39244588Sbrianuart_write(struct uart_softc *sc, int offset, uint8_t value) 39344588Sbrian{ 39444588Sbrian int fifosz; 39544588Sbrian uint8_t msr; 39671657Sbrian 39726940Sbrian pthread_mutex_lock(&sc->mtx); 39844588Sbrian 39936285Sbrian /* 40036285Sbrian * Take care of the special case DLAB accesses first 40136314Sbrian */ 40271657Sbrian if ((sc->lcr & LCR_DLAB) != 0) { 40336285Sbrian if (offset == REG_DLL) { 40426940Sbrian sc->dll = value; 40571657Sbrian goto done; 40636285Sbrian } 40726940Sbrian 40871657Sbrian if (offset == REG_DLH) { 40971657Sbrian sc->dlh = value; 41071657Sbrian goto done; 41171657Sbrian } 41271657Sbrian } 41371657Sbrian 41471657Sbrian switch (offset) { 41571657Sbrian case REG_DATA: 41671657Sbrian if (sc->mcr & MCR_LOOPBACK) { 41771657Sbrian if (rxfifo_putchar(sc, value) != 0) 41871657Sbrian sc->lsr |= LSR_OE; 41971657Sbrian } else if (sc->tty.opened) { 42071657Sbrian ttywrite(&sc->tty, value); 42171657Sbrian } /* else drop on floor */ 42271657Sbrian sc->thre_int_pending = true; 423 break; 424 case REG_IER: 425 /* 426 * Apply mask so that bits 4-7 are 0 427 * Also enables bits 0-3 only if they're 1 428 */ 429 sc->ier = value & 0x0F; 430 break; 431 case REG_FCR: 432 /* 433 * When moving from FIFO and 16450 mode and vice versa, 434 * the FIFO contents are reset. 435 */ 436 if ((sc->fcr & FCR_ENABLE) ^ (value & FCR_ENABLE)) { 437 fifosz = (value & FCR_ENABLE) ? FIFOSZ : 1; 438 rxfifo_reset(sc, fifosz); 439 } 440 441 /* 442 * The FCR_ENABLE bit must be '1' for the programming 443 * of other FCR bits to be effective. 444 */ 445 if ((value & FCR_ENABLE) == 0) { 446 sc->fcr = 0; 447 } else { 448 if ((value & FCR_RCV_RST) != 0) 449 rxfifo_reset(sc, FIFOSZ); 450 451 sc->fcr = value & 452 (FCR_ENABLE | FCR_DMA | FCR_RX_MASK); 453 } 454 break; 455 case REG_LCR: 456 sc->lcr = value; 457 break; 458 case REG_MCR: 459 /* Apply mask so that bits 5-7 are 0 */ 460 sc->mcr = value & 0x1F; 461 msr = modem_status(sc->mcr); 462 463 /* 464 * Detect if there has been any change between the 465 * previous and the new value of MSR. If there is 466 * then assert the appropriate MSR delta bit. 467 */ 468 if ((msr & MSR_CTS) ^ (sc->msr & MSR_CTS)) 469 sc->msr |= MSR_DCTS; 470 if ((msr & MSR_DSR) ^ (sc->msr & MSR_DSR)) 471 sc->msr |= MSR_DDSR; 472 if ((msr & MSR_DCD) ^ (sc->msr & MSR_DCD)) 473 sc->msr |= MSR_DDCD; 474 if ((sc->msr & MSR_RI) != 0 && (msr & MSR_RI) == 0) 475 sc->msr |= MSR_TERI; 476 477 /* 478 * Update the value of MSR while retaining the delta 479 * bits. 480 */ 481 sc->msr &= MSR_DELTA_MASK; 482 sc->msr |= msr; 483 break; 484 case REG_LSR: 485 /* 486 * Line status register is not meant to be written to 487 * during normal operation. 488 */ 489 break; 490 case REG_MSR: 491 /* 492 * As far as I can tell MSR is a read-only register. 493 */ 494 break; 495 case REG_SCR: 496 sc->scr = value; 497 break; 498 default: 499 break; 500 } 501 502done: 503 uart_toggle_intr(sc); 504 pthread_mutex_unlock(&sc->mtx); 505} 506 507uint8_t 508uart_read(struct uart_softc *sc, int offset) 509{ 510 uint8_t iir, intr_reason, reg; 511 512 pthread_mutex_lock(&sc->mtx); 513 514 /* 515 * Take care of the special case DLAB accesses first 516 */ 517 if ((sc->lcr & LCR_DLAB) != 0) { 518 if (offset == REG_DLL) { 519 reg = sc->dll; 520 goto done; 521 } 522 523 if (offset == REG_DLH) { 524 reg = sc->dlh; 525 goto done; 526 } 527 } 528 529 switch (offset) { 530 case REG_DATA: 531 reg = rxfifo_getchar(sc); 532 break; 533 case REG_IER: 534 reg = sc->ier; 535 break; 536 case REG_IIR: 537 iir = (sc->fcr & FCR_ENABLE) ? IIR_FIFO_MASK : 0; 538 539 intr_reason = uart_intr_reason(sc); 540 541 /* 542 * Deal with side effects of reading the IIR register 543 */ 544 if (intr_reason == IIR_TXRDY) 545 sc->thre_int_pending = false; 546 547 iir |= intr_reason; 548 549 reg = iir; 550 break; 551 case REG_LCR: 552 reg = sc->lcr; 553 break; 554 case REG_MCR: 555 reg = sc->mcr; 556 break; 557 case REG_LSR: 558 /* Transmitter is always ready for more data */ 559 sc->lsr |= LSR_TEMT | LSR_THRE; 560 561 /* Check for new receive data */ 562 if (rxfifo_numchars(sc) > 0) 563 sc->lsr |= LSR_RXRDY; 564 else 565 sc->lsr &= ~LSR_RXRDY; 566 567 reg = sc->lsr; 568 569 /* The LSR_OE bit is cleared on LSR read */ 570 sc->lsr &= ~LSR_OE; 571 break; 572 case REG_MSR: 573 /* 574 * MSR delta bits are cleared on read 575 */ 576 reg = sc->msr; 577 sc->msr &= ~MSR_DELTA_MASK; 578 break; 579 case REG_SCR: 580 reg = sc->scr; 581 break; 582 default: 583 reg = 0xFF; 584 break; 585 } 586 587done: 588 uart_toggle_intr(sc); 589 pthread_mutex_unlock(&sc->mtx); 590 591 return (reg); 592} 593 594int 595uart_legacy_alloc(int which, int *baseaddr, int *irq) 596{ 597 598 if (which < 0 || which >= UART_NLDEVS || uart_lres[which].inuse) 599 return (-1); 600 601 uart_lres[which].inuse = true; 602 *baseaddr = uart_lres[which].baseaddr; 603 *irq = uart_lres[which].irq; 604 605 return (0); 606} 607 608struct uart_softc * 609uart_init(uart_intr_func_t intr_assert, uart_intr_func_t intr_deassert, 610 void *arg) 611{ 612 struct uart_softc *sc; 613 614 sc = calloc(1, sizeof(struct uart_softc)); 615 616 sc->arg = arg; 617 sc->intr_assert = intr_assert; 618 sc->intr_deassert = intr_deassert; 619 620 pthread_mutex_init(&sc->mtx, NULL); 621 622 uart_reset(sc); 623 624 return (sc); 625} 626 627static int 628uart_tty_backend(struct uart_softc *sc, const char *opts) 629{ 630 int fd; 631 int retval; 632 633 retval = -1; 634 635 fd = open(opts, O_RDWR | O_NONBLOCK); 636 if (fd > 0 && isatty(fd)) { 637 sc->tty.fd = fd; 638 sc->tty.opened = true; 639 retval = 0; 640 } 641 642 return (retval); 643} 644 645int 646uart_set_backend(struct uart_softc *sc, const char *opts) 647{ 648 int retval; 649 650 retval = -1; 651 652 if (opts == NULL) 653 return (0); 654 655 if (strcmp("stdio", opts) == 0) { 656 if (!uart_stdio) { 657 sc->tty.fd = STDIN_FILENO; 658 sc->tty.opened = true; 659 uart_stdio = true; 660 retval = 0; 661 } 662 } else if (uart_tty_backend(sc, opts) == 0) { 663 retval = 0; 664 } 665 666 /* Make the backend file descriptor non-blocking */ 667 if (retval == 0) 668 retval = fcntl(sc->tty.fd, F_SETFL, O_NONBLOCK); 669 670 if (retval == 0) 671 uart_opentty(sc); 672 673 return (retval); 674} 675