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