xref: /kvmtool/hw/serial.c (revision 6d54df7482d9df411bd092830acbb81d4985ba15)
1899fe063SPekka Enberg #include "kvm/8250-serial.h"
213a7760fSPekka Enberg 
313a7760fSPekka Enberg #include "kvm/ioport.h"
446aa8d69SPekka Enberg #include "kvm/util.h"
5e557eef9SPekka Enberg #include "kvm/kvm.h"
613a7760fSPekka Enberg 
746aa8d69SPekka Enberg #include <stdbool.h>
876b4a122SPekka Enberg #include <poll.h>
913a7760fSPekka Enberg 
1046aa8d69SPekka Enberg /* Transmitter holding register */
1146aa8d69SPekka Enberg #define THR             0
123f55f3acSPekka Enberg 
1346aa8d69SPekka Enberg /* Receive buffer register */
1446aa8d69SPekka Enberg #define RBR             0
153f55f3acSPekka Enberg 
1646aa8d69SPekka Enberg /* Divisor latch low byte */
1746aa8d69SPekka Enberg #define DLL		0
183f55f3acSPekka Enberg 
1946aa8d69SPekka Enberg /* Divisor latch high byte */
2046aa8d69SPekka Enberg #define DLM		1
2146aa8d69SPekka Enberg 
2246aa8d69SPekka Enberg /* Interrupt enable register */
2346aa8d69SPekka Enberg #define IER		1
2446aa8d69SPekka Enberg 
2576b4a122SPekka Enberg #define UART_IER_RDI		0x01
26e557eef9SPekka Enberg #define UART_IER_THRI		0x02
27e557eef9SPekka Enberg 
2846aa8d69SPekka Enberg /* Interrupt identification register */
2946aa8d69SPekka Enberg #define IIR		2
3046aa8d69SPekka Enberg 
319a7428ddSPekka Enberg #define UART_IIR_NO_INT		0x01
32900d32c1SPekka Enberg #define UART_IIR_THRI		0x02
339a7428ddSPekka Enberg 
3446aa8d69SPekka Enberg /* 16550 FIFO Control Register */
3546aa8d69SPekka Enberg #define FCR		2
3646aa8d69SPekka Enberg 
3746aa8d69SPekka Enberg /* Line control register */
3846aa8d69SPekka Enberg #define LCR		3
399a7428ddSPekka Enberg 
409a7428ddSPekka Enberg #define UART_LCR_DLAB		0x80
4146aa8d69SPekka Enberg 
4246aa8d69SPekka Enberg /* Modem control register */
4346aa8d69SPekka Enberg #define MCR		4
4446aa8d69SPekka Enberg 
4546aa8d69SPekka Enberg /* Line status register */
4646aa8d69SPekka Enberg #define LSR		5
4746aa8d69SPekka Enberg 
4876b4a122SPekka Enberg #define UART_LSR_DR		0x01
499a7428ddSPekka Enberg #define UART_LSR_THRE		0x20
509a7428ddSPekka Enberg 
5146aa8d69SPekka Enberg /* Modem status register */
5246aa8d69SPekka Enberg #define MSR		6
5346aa8d69SPekka Enberg 
549a7428ddSPekka Enberg #define UART_MSR_CTS		0x10
559a7428ddSPekka Enberg 
5646aa8d69SPekka Enberg /* Scratch register */
5746aa8d69SPekka Enberg #define SCR		7
5846aa8d69SPekka Enberg 
5946aa8d69SPekka Enberg struct serial8250_device {
6046aa8d69SPekka Enberg 	uint16_t		iobase;
61e557eef9SPekka Enberg 	uint8_t			irq;
6276b4a122SPekka Enberg 
6376b4a122SPekka Enberg 	uint8_t			thr;
6446aa8d69SPekka Enberg 	uint8_t			dll;
6546aa8d69SPekka Enberg 	uint8_t			dlm;
66e557eef9SPekka Enberg 	uint8_t			iir;
6746aa8d69SPekka Enberg 	uint8_t			ier;
6846aa8d69SPekka Enberg 	uint8_t			fcr;
6946aa8d69SPekka Enberg 	uint8_t			lcr;
7046aa8d69SPekka Enberg 	uint8_t			mcr;
7176b4a122SPekka Enberg 	uint8_t			lsr;
7246aa8d69SPekka Enberg 	uint8_t			scr;
7346aa8d69SPekka Enberg };
7446aa8d69SPekka Enberg 
7546aa8d69SPekka Enberg static struct serial8250_device device = {
7646aa8d69SPekka Enberg 	.iobase			= 0x3f8,	/* ttyS0 */
77e557eef9SPekka Enberg 	.irq			= 4,
78e557eef9SPekka Enberg 
7976b4a122SPekka Enberg 	.iir			= UART_IIR_NO_INT,
8076b4a122SPekka Enberg 	.lsr			= UART_LSR_THRE,
8146aa8d69SPekka Enberg };
8246aa8d69SPekka Enberg 
8376b4a122SPekka Enberg static int read_char(int fd)
8476b4a122SPekka Enberg {
8576b4a122SPekka Enberg 	int c;
8676b4a122SPekka Enberg 
8776b4a122SPekka Enberg 	if (read(fd, &c, 1) < 0)
8876b4a122SPekka Enberg 		return -1;
8976b4a122SPekka Enberg 
9076b4a122SPekka Enberg 	return c;
9176b4a122SPekka Enberg }
9276b4a122SPekka Enberg 
9376b4a122SPekka Enberg static bool is_readable(int fd)
9476b4a122SPekka Enberg {
95*6d54df74SCyrill Gorcunov 	struct pollfd pollfd = (struct pollfd) {
9676b4a122SPekka Enberg 		.fd	= fd,
9776b4a122SPekka Enberg 		.events	= POLLIN,
9876b4a122SPekka Enberg 	};
9976b4a122SPekka Enberg 
100*6d54df74SCyrill Gorcunov 	return poll(&pollfd, 1, 0) > 0;
10176b4a122SPekka Enberg }
10276b4a122SPekka Enberg 
1038bb34e0dSPekka Enberg void serial8250__interrupt(struct kvm *self)
1048bb34e0dSPekka Enberg {
10576b4a122SPekka Enberg 	if (!(device.lsr & UART_LSR_DR) && is_readable(fileno(stdin))) {
10676b4a122SPekka Enberg 		int c;
10776b4a122SPekka Enberg 
10876b4a122SPekka Enberg 		c			= read_char(fileno(stdin));
10976b4a122SPekka Enberg 		if (c >= 0) {
11076b4a122SPekka Enberg 			device.thr		= c;
11176b4a122SPekka Enberg 			device.lsr		|= UART_LSR_DR;
11276b4a122SPekka Enberg 		}
11376b4a122SPekka Enberg 	}
11476b4a122SPekka Enberg 
11576b4a122SPekka Enberg 	if (device.ier & UART_IER_THRI || device.lsr & UART_LSR_DR) {
116e557eef9SPekka Enberg 		device.iir		&= ~UART_IIR_NO_INT;
117e557eef9SPekka Enberg 		kvm__irq_line(self, device.irq, 1);
118e557eef9SPekka Enberg 	}
1198bb34e0dSPekka Enberg }
1208bb34e0dSPekka Enberg 
12146aa8d69SPekka Enberg static bool serial8250_out(struct kvm *self, uint16_t port, void *data, int size, uint32_t count)
12213a7760fSPekka Enberg {
12346aa8d69SPekka Enberg 	uint16_t offset = port - device.iobase;
12446aa8d69SPekka Enberg 
1259a7428ddSPekka Enberg 	if (device.lcr & UART_LCR_DLAB) {
12646aa8d69SPekka Enberg 		switch (offset) {
12746aa8d69SPekka Enberg 		case DLL:
12846aa8d69SPekka Enberg 			device.dll		= ioport__read8(data);
12946aa8d69SPekka Enberg 			break;
13046aa8d69SPekka Enberg 		case DLM:
13146aa8d69SPekka Enberg 			device.dlm		= ioport__read8(data);
13246aa8d69SPekka Enberg 			break;
13346aa8d69SPekka Enberg 		case FCR:
13446aa8d69SPekka Enberg 			device.fcr		= ioport__read8(data);
13546aa8d69SPekka Enberg 			break;
13646aa8d69SPekka Enberg 		case LCR:
13746aa8d69SPekka Enberg 			device.lcr		= ioport__read8(data);
13846aa8d69SPekka Enberg 			break;
13946aa8d69SPekka Enberg 		default:
14046aa8d69SPekka Enberg 			return false;
14146aa8d69SPekka Enberg 		}
14246aa8d69SPekka Enberg 	} else {
14346aa8d69SPekka Enberg 		switch (offset) {
14446aa8d69SPekka Enberg 		case THR: {
14513a7760fSPekka Enberg 			char *p = data;
146f2d8dc88SCyrill Gorcunov 			int i;
14713a7760fSPekka Enberg 
148f2d8dc88SCyrill Gorcunov 			while (count--) {
149f2d8dc88SCyrill Gorcunov 				for (i = 0; i < size; i++)
150b7475544SPekka Enberg 					fprintf(stdout, "%c", *p++);
1515b9d0b58SAsias He 			}
152b7475544SPekka Enberg 			fflush(stdout);
153e557eef9SPekka Enberg 
154e557eef9SPekka Enberg 			device.iir		|= UART_IIR_NO_INT;
155e557eef9SPekka Enberg 			kvm__irq_line(self, device.irq, 0);
156e557eef9SPekka Enberg 
15746aa8d69SPekka Enberg 			break;
15846aa8d69SPekka Enberg 		}
15946aa8d69SPekka Enberg 		case IER:
16046aa8d69SPekka Enberg 			device.ier		= ioport__read8(data);
16146aa8d69SPekka Enberg 			break;
16246aa8d69SPekka Enberg 		case FCR:
16346aa8d69SPekka Enberg 			device.fcr		= ioport__read8(data);
16446aa8d69SPekka Enberg 			break;
16546aa8d69SPekka Enberg 		case LCR:
16646aa8d69SPekka Enberg 			device.lcr		= ioport__read8(data);
16746aa8d69SPekka Enberg 			break;
16846aa8d69SPekka Enberg 		case MCR:
16946aa8d69SPekka Enberg 			device.mcr		= ioport__read8(data);
17046aa8d69SPekka Enberg 			break;
17146aa8d69SPekka Enberg 		case SCR:
17246aa8d69SPekka Enberg 			device.scr		= ioport__read8(data);
17346aa8d69SPekka Enberg 			break;
17446aa8d69SPekka Enberg 		default:
17546aa8d69SPekka Enberg 			return false;
17646aa8d69SPekka Enberg 		}
17746aa8d69SPekka Enberg 	}
17813a7760fSPekka Enberg 
17913a7760fSPekka Enberg 	return true;
18013a7760fSPekka Enberg }
18113a7760fSPekka Enberg 
18246aa8d69SPekka Enberg static bool serial8250_in(struct kvm *self, uint16_t port, void *data, int size, uint32_t count)
18325af6674SPekka Enberg {
18446aa8d69SPekka Enberg 	uint16_t offset = port - device.iobase;
18546aa8d69SPekka Enberg 
1869a7428ddSPekka Enberg 	if (device.lcr & UART_LCR_DLAB)
18746aa8d69SPekka Enberg 		return false;
18846aa8d69SPekka Enberg 
18946aa8d69SPekka Enberg 	switch (offset) {
19046aa8d69SPekka Enberg 	case THR:
19176b4a122SPekka Enberg 		if (device.lsr & UART_LSR_DR) {
19276b4a122SPekka Enberg 			device.lsr		&= ~UART_LSR_DR;
19376b4a122SPekka Enberg 			ioport__write8(data, device.thr);
19476b4a122SPekka Enberg 
19576b4a122SPekka Enberg 			device.iir		|= UART_IIR_NO_INT;
19676b4a122SPekka Enberg 			kvm__irq_line(self, device.irq, 0);
19776b4a122SPekka Enberg 		}
19846aa8d69SPekka Enberg 		break;
19946aa8d69SPekka Enberg 	case IER:
20046aa8d69SPekka Enberg 		ioport__write8(data, device.ier);
20146aa8d69SPekka Enberg 		break;
20246aa8d69SPekka Enberg 	case IIR:
203e557eef9SPekka Enberg 		ioport__write8(data, device.iir);
20446aa8d69SPekka Enberg 		break;
20546aa8d69SPekka Enberg 	case LCR:
20646aa8d69SPekka Enberg 		ioport__write8(data, device.lcr);
20746aa8d69SPekka Enberg 		break;
20846aa8d69SPekka Enberg 	case MCR:
20946aa8d69SPekka Enberg 		ioport__write8(data, device.mcr);
21046aa8d69SPekka Enberg 		break;
21146aa8d69SPekka Enberg 	case LSR:
21276b4a122SPekka Enberg 		ioport__write8(data, device.lsr);
21346aa8d69SPekka Enberg 		break;
21446aa8d69SPekka Enberg 	case MSR:
2159a7428ddSPekka Enberg 		ioport__write8(data, UART_MSR_CTS);
21646aa8d69SPekka Enberg 		break;
21746aa8d69SPekka Enberg 	case SCR:
21846aa8d69SPekka Enberg 		ioport__write8(data, device.scr);
21946aa8d69SPekka Enberg 		break;
22046aa8d69SPekka Enberg 	default:
22146aa8d69SPekka Enberg 		return false;
22225af6674SPekka Enberg 	}
22325af6674SPekka Enberg 
22413a7760fSPekka Enberg 	return true;
22513a7760fSPekka Enberg }
22613a7760fSPekka Enberg 
22746aa8d69SPekka Enberg static struct ioport_operations serial8250_ops = {
22846aa8d69SPekka Enberg 	.io_in		= serial8250_in,
22946aa8d69SPekka Enberg 	.io_out		= serial8250_out,
230a93ec68bSPekka Enberg };
231a93ec68bSPekka Enberg 
232899fe063SPekka Enberg void serial8250__init(void)
23313a7760fSPekka Enberg {
23446aa8d69SPekka Enberg 	ioport__register(device.iobase, &serial8250_ops, 8);
23513a7760fSPekka Enberg }
236