aboutsummaryrefslogtreecommitdiff
path: root/hw/char
diff options
context:
space:
mode:
Diffstat (limited to 'hw/char')
-rw-r--r--hw/char/Makefile.objs10
-rw-r--r--hw/char/cadence_uart.c518
-rw-r--r--hw/char/escc.c938
-rw-r--r--hw/char/ipack.c115
-rw-r--r--hw/char/ipoctal232.c605
-rw-r--r--hw/char/parallel.c614
-rw-r--r--hw/char/pl011.c330
-rw-r--r--hw/char/serial-isa.c130
-rw-r--r--hw/char/serial-pci.c252
-rw-r--r--hw/char/serial.c789
-rw-r--r--hw/char/tpci200.c671
-rw-r--r--hw/char/virtio-console.c184
-rw-r--r--hw/char/xen_console.c305
-rw-r--r--hw/char/xilinx_uartlite.c231
14 files changed, 5692 insertions, 0 deletions
diff --git a/hw/char/Makefile.objs b/hw/char/Makefile.objs
index e69de29bb2..eee23ff637 100644
--- a/hw/char/Makefile.objs
+++ b/hw/char/Makefile.objs
@@ -0,0 +1,10 @@
+common-obj-$(CONFIG_IPACK) += tpci200.o ipoctal232.o ipack.o
+common-obj-$(CONFIG_ESCC) += escc.o
+common-obj-$(CONFIG_PARALLEL) += parallel.o
+common-obj-$(CONFIG_PL011) += pl011.o
+common-obj-$(CONFIG_SERIAL) += serial.o serial-isa.o
+common-obj-$(CONFIG_SERIAL_PCI) += serial-pci.o
+common-obj-$(CONFIG_VIRTIO) += virtio-console.o
+common-obj-$(CONFIG_XILINX) += xilinx_uartlite.o
+common-obj-$(CONFIG_XEN_BACKEND) += xen_console.o
+common-obj-$(CONFIG_CADENCE) += cadence_uart.o
diff --git a/hw/char/cadence_uart.c b/hw/char/cadence_uart.c
new file mode 100644
index 0000000000..421ec998d6
--- /dev/null
+++ b/hw/char/cadence_uart.c
@@ -0,0 +1,518 @@
+/*
+ * Device model for Cadence UART
+ *
+ * Copyright (c) 2010 Xilinx Inc.
+ * Copyright (c) 2012 Peter A.G. Crosthwaite (peter.crosthwaite@petalogix.com)
+ * Copyright (c) 2012 PetaLogix Pty Ltd.
+ * Written by Haibing Ma
+ * M.Habib
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "hw/sysbus.h"
+#include "char/char.h"
+#include "qemu/timer.h"
+
+#ifdef CADENCE_UART_ERR_DEBUG
+#define DB_PRINT(...) do { \
+ fprintf(stderr, ": %s: ", __func__); \
+ fprintf(stderr, ## __VA_ARGS__); \
+ } while (0);
+#else
+ #define DB_PRINT(...)
+#endif
+
+#define UART_SR_INTR_RTRIG 0x00000001
+#define UART_SR_INTR_REMPTY 0x00000002
+#define UART_SR_INTR_RFUL 0x00000004
+#define UART_SR_INTR_TEMPTY 0x00000008
+#define UART_SR_INTR_TFUL 0x00000010
+/* bits fields in CSR that correlate to CISR. If any of these bits are set in
+ * SR, then the same bit in CISR is set high too */
+#define UART_SR_TO_CISR_MASK 0x0000001F
+
+#define UART_INTR_ROVR 0x00000020
+#define UART_INTR_FRAME 0x00000040
+#define UART_INTR_PARE 0x00000080
+#define UART_INTR_TIMEOUT 0x00000100
+#define UART_INTR_DMSI 0x00000200
+
+#define UART_SR_RACTIVE 0x00000400
+#define UART_SR_TACTIVE 0x00000800
+#define UART_SR_FDELT 0x00001000
+
+#define UART_CR_RXRST 0x00000001
+#define UART_CR_TXRST 0x00000002
+#define UART_CR_RX_EN 0x00000004
+#define UART_CR_RX_DIS 0x00000008
+#define UART_CR_TX_EN 0x00000010
+#define UART_CR_TX_DIS 0x00000020
+#define UART_CR_RST_TO 0x00000040
+#define UART_CR_STARTBRK 0x00000080
+#define UART_CR_STOPBRK 0x00000100
+
+#define UART_MR_CLKS 0x00000001
+#define UART_MR_CHRL 0x00000006
+#define UART_MR_CHRL_SH 1
+#define UART_MR_PAR 0x00000038
+#define UART_MR_PAR_SH 3
+#define UART_MR_NBSTOP 0x000000C0
+#define UART_MR_NBSTOP_SH 6
+#define UART_MR_CHMODE 0x00000300
+#define UART_MR_CHMODE_SH 8
+#define UART_MR_UCLKEN 0x00000400
+#define UART_MR_IRMODE 0x00000800
+
+#define UART_DATA_BITS_6 (0x3 << UART_MR_CHRL_SH)
+#define UART_DATA_BITS_7 (0x2 << UART_MR_CHRL_SH)
+#define UART_PARITY_ODD (0x1 << UART_MR_PAR_SH)
+#define UART_PARITY_EVEN (0x0 << UART_MR_PAR_SH)
+#define UART_STOP_BITS_1 (0x3 << UART_MR_NBSTOP_SH)
+#define UART_STOP_BITS_2 (0x2 << UART_MR_NBSTOP_SH)
+#define NORMAL_MODE (0x0 << UART_MR_CHMODE_SH)
+#define ECHO_MODE (0x1 << UART_MR_CHMODE_SH)
+#define LOCAL_LOOPBACK (0x2 << UART_MR_CHMODE_SH)
+#define REMOTE_LOOPBACK (0x3 << UART_MR_CHMODE_SH)
+
+#define RX_FIFO_SIZE 16
+#define TX_FIFO_SIZE 16
+#define UART_INPUT_CLK 50000000
+
+#define R_CR (0x00/4)
+#define R_MR (0x04/4)
+#define R_IER (0x08/4)
+#define R_IDR (0x0C/4)
+#define R_IMR (0x10/4)
+#define R_CISR (0x14/4)
+#define R_BRGR (0x18/4)
+#define R_RTOR (0x1C/4)
+#define R_RTRIG (0x20/4)
+#define R_MCR (0x24/4)
+#define R_MSR (0x28/4)
+#define R_SR (0x2C/4)
+#define R_TX_RX (0x30/4)
+#define R_BDIV (0x34/4)
+#define R_FDEL (0x38/4)
+#define R_PMIN (0x3C/4)
+#define R_PWID (0x40/4)
+#define R_TTRIG (0x44/4)
+
+#define R_MAX (R_TTRIG + 1)
+
+typedef struct {
+ SysBusDevice busdev;
+ MemoryRegion iomem;
+ uint32_t r[R_MAX];
+ uint8_t r_fifo[RX_FIFO_SIZE];
+ uint32_t rx_wpos;
+ uint32_t rx_count;
+ uint64_t char_tx_time;
+ CharDriverState *chr;
+ qemu_irq irq;
+ struct QEMUTimer *fifo_trigger_handle;
+ struct QEMUTimer *tx_time_handle;
+} UartState;
+
+static void uart_update_status(UartState *s)
+{
+ s->r[R_CISR] |= s->r[R_SR] & UART_SR_TO_CISR_MASK;
+ qemu_set_irq(s->irq, !!(s->r[R_IMR] & s->r[R_CISR]));
+}
+
+static void fifo_trigger_update(void *opaque)
+{
+ UartState *s = (UartState *)opaque;
+
+ s->r[R_CISR] |= UART_INTR_TIMEOUT;
+
+ uart_update_status(s);
+}
+
+static void uart_tx_redo(UartState *s)
+{
+ uint64_t new_tx_time = qemu_get_clock_ns(vm_clock);
+
+ qemu_mod_timer(s->tx_time_handle, new_tx_time + s->char_tx_time);
+
+ s->r[R_SR] |= UART_SR_INTR_TEMPTY;
+
+ uart_update_status(s);
+}
+
+static void uart_tx_write(void *opaque)
+{
+ UartState *s = (UartState *)opaque;
+
+ uart_tx_redo(s);
+}
+
+static void uart_rx_reset(UartState *s)
+{
+ s->rx_wpos = 0;
+ s->rx_count = 0;
+ qemu_chr_accept_input(s->chr);
+
+ s->r[R_SR] |= UART_SR_INTR_REMPTY;
+ s->r[R_SR] &= ~UART_SR_INTR_RFUL;
+}
+
+static void uart_tx_reset(UartState *s)
+{
+ s->r[R_SR] |= UART_SR_INTR_TEMPTY;
+ s->r[R_SR] &= ~UART_SR_INTR_TFUL;
+}
+
+static void uart_send_breaks(UartState *s)
+{
+ int break_enabled = 1;
+
+ qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_SERIAL_SET_BREAK,
+ &break_enabled);
+}
+
+static void uart_parameters_setup(UartState *s)
+{
+ QEMUSerialSetParams ssp;
+ unsigned int baud_rate, packet_size;
+
+ baud_rate = (s->r[R_MR] & UART_MR_CLKS) ?
+ UART_INPUT_CLK / 8 : UART_INPUT_CLK;
+
+ ssp.speed = baud_rate / (s->r[R_BRGR] * (s->r[R_BDIV] + 1));
+ packet_size = 1;
+
+ switch (s->r[R_MR] & UART_MR_PAR) {
+ case UART_PARITY_EVEN:
+ ssp.parity = 'E';
+ packet_size++;
+ break;
+ case UART_PARITY_ODD:
+ ssp.parity = 'O';
+ packet_size++;
+ break;
+ default:
+ ssp.parity = 'N';
+ break;
+ }
+
+ switch (s->r[R_MR] & UART_MR_CHRL) {
+ case UART_DATA_BITS_6:
+ ssp.data_bits = 6;
+ break;
+ case UART_DATA_BITS_7:
+ ssp.data_bits = 7;
+ break;
+ default:
+ ssp.data_bits = 8;
+ break;
+ }
+
+ switch (s->r[R_MR] & UART_MR_NBSTOP) {
+ case UART_STOP_BITS_1:
+ ssp.stop_bits = 1;
+ break;
+ default:
+ ssp.stop_bits = 2;
+ break;
+ }
+
+ packet_size += ssp.data_bits + ssp.stop_bits;
+ s->char_tx_time = (get_ticks_per_sec() / ssp.speed) * packet_size;
+ qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp);
+}
+
+static int uart_can_receive(void *opaque)
+{
+ UartState *s = (UartState *)opaque;
+
+ return RX_FIFO_SIZE - s->rx_count;
+}
+
+static void uart_ctrl_update(UartState *s)
+{
+ if (s->r[R_CR] & UART_CR_TXRST) {
+ uart_tx_reset(s);
+ }
+
+ if (s->r[R_CR] & UART_CR_RXRST) {
+ uart_rx_reset(s);
+ }
+
+ s->r[R_CR] &= ~(UART_CR_TXRST | UART_CR_RXRST);
+
+ if ((s->r[R_CR] & UART_CR_TX_EN) && !(s->r[R_CR] & UART_CR_TX_DIS)) {
+ uart_tx_redo(s);
+ }
+
+ if (s->r[R_CR] & UART_CR_STARTBRK && !(s->r[R_CR] & UART_CR_STOPBRK)) {
+ uart_send_breaks(s);
+ }
+}
+
+static void uart_write_rx_fifo(void *opaque, const uint8_t *buf, int size)
+{
+ UartState *s = (UartState *)opaque;
+ uint64_t new_rx_time = qemu_get_clock_ns(vm_clock);
+ int i;
+
+ if ((s->r[R_CR] & UART_CR_RX_DIS) || !(s->r[R_CR] & UART_CR_RX_EN)) {
+ return;
+ }
+
+ s->r[R_SR] &= ~UART_SR_INTR_REMPTY;
+
+ if (s->rx_count == RX_FIFO_SIZE) {
+ s->r[R_CISR] |= UART_INTR_ROVR;
+ } else {
+ for (i = 0; i < size; i++) {
+ s->r_fifo[s->rx_wpos] = buf[i];
+ s->rx_wpos = (s->rx_wpos + 1) % RX_FIFO_SIZE;
+ s->rx_count++;
+
+ if (s->rx_count == RX_FIFO_SIZE) {
+ s->r[R_SR] |= UART_SR_INTR_RFUL;
+ break;
+ }
+
+ if (s->rx_count >= s->r[R_RTRIG]) {
+ s->r[R_SR] |= UART_SR_INTR_RTRIG;
+ }
+ }
+ qemu_mod_timer(s->fifo_trigger_handle, new_rx_time +
+ (s->char_tx_time * 4));
+ }
+ uart_update_status(s);
+}
+
+static void uart_write_tx_fifo(UartState *s, const uint8_t *buf, int size)
+{
+ if ((s->r[R_CR] & UART_CR_TX_DIS) || !(s->r[R_CR] & UART_CR_TX_EN)) {
+ return;
+ }
+
+ while (size) {
+ size -= qemu_chr_fe_write(s->chr, buf, size);
+ }
+}
+
+static void uart_receive(void *opaque, const uint8_t *buf, int size)
+{
+ UartState *s = (UartState *)opaque;
+ uint32_t ch_mode = s->r[R_MR] & UART_MR_CHMODE;
+
+ if (ch_mode == NORMAL_MODE || ch_mode == ECHO_MODE) {
+ uart_write_rx_fifo(opaque, buf, size);
+ }
+ if (ch_mode == REMOTE_LOOPBACK || ch_mode == ECHO_MODE) {
+ uart_write_tx_fifo(s, buf, size);
+ }
+}
+
+static void uart_event(void *opaque, int event)
+{
+ UartState *s = (UartState *)opaque;
+ uint8_t buf = '\0';
+
+ if (event == CHR_EVENT_BREAK) {
+ uart_write_rx_fifo(opaque, &buf, 1);
+ }
+
+ uart_update_status(s);
+}
+
+static void uart_read_rx_fifo(UartState *s, uint32_t *c)
+{
+ if ((s->r[R_CR] & UART_CR_RX_DIS) || !(s->r[R_CR] & UART_CR_RX_EN)) {
+ return;
+ }
+
+ s->r[R_SR] &= ~UART_SR_INTR_RFUL;
+
+ if (s->rx_count) {
+ uint32_t rx_rpos =
+ (RX_FIFO_SIZE + s->rx_wpos - s->rx_count) % RX_FIFO_SIZE;
+ *c = s->r_fifo[rx_rpos];
+ s->rx_count--;
+
+ if (!s->rx_count) {
+ s->r[R_SR] |= UART_SR_INTR_REMPTY;
+ }
+ qemu_chr_accept_input(s->chr);
+ } else {
+ *c = 0;
+ s->r[R_SR] |= UART_SR_INTR_REMPTY;
+ }
+
+ if (s->rx_count < s->r[R_RTRIG]) {
+ s->r[R_SR] &= ~UART_SR_INTR_RTRIG;
+ }
+ uart_update_status(s);
+}
+
+static void uart_write(void *opaque, hwaddr offset,
+ uint64_t value, unsigned size)
+{
+ UartState *s = (UartState *)opaque;
+
+ DB_PRINT(" offset:%x data:%08x\n", (unsigned)offset, (unsigned)value);
+ offset >>= 2;
+ switch (offset) {
+ case R_IER: /* ier (wts imr) */
+ s->r[R_IMR] |= value;
+ break;
+ case R_IDR: /* idr (wtc imr) */
+ s->r[R_IMR] &= ~value;
+ break;
+ case R_IMR: /* imr (read only) */
+ break;
+ case R_CISR: /* cisr (wtc) */
+ s->r[R_CISR] &= ~value;
+ break;
+ case R_TX_RX: /* UARTDR */
+ switch (s->r[R_MR] & UART_MR_CHMODE) {
+ case NORMAL_MODE:
+ uart_write_tx_fifo(s, (uint8_t *) &value, 1);
+ break;
+ case LOCAL_LOOPBACK:
+ uart_write_rx_fifo(opaque, (uint8_t *) &value, 1);
+ break;
+ }
+ break;
+ default:
+ s->r[offset] = value;
+ }
+
+ switch (offset) {
+ case R_CR:
+ uart_ctrl_update(s);
+ break;
+ case R_MR:
+ uart_parameters_setup(s);
+ break;
+ }
+}
+
+static uint64_t uart_read(void *opaque, hwaddr offset,
+ unsigned size)
+{
+ UartState *s = (UartState *)opaque;
+ uint32_t c = 0;
+
+ offset >>= 2;
+ if (offset >= R_MAX) {
+ c = 0;
+ } else if (offset == R_TX_RX) {
+ uart_read_rx_fifo(s, &c);
+ } else {
+ c = s->r[offset];
+ }
+
+ DB_PRINT(" offset:%x data:%08x\n", (unsigned)(offset << 2), (unsigned)c);
+ return c;
+}
+
+static const MemoryRegionOps uart_ops = {
+ .read = uart_read,
+ .write = uart_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void cadence_uart_reset(UartState *s)
+{
+ s->r[R_CR] = 0x00000128;
+ s->r[R_IMR] = 0;
+ s->r[R_CISR] = 0;
+ s->r[R_RTRIG] = 0x00000020;
+ s->r[R_BRGR] = 0x0000000F;
+ s->r[R_TTRIG] = 0x00000020;
+
+ uart_rx_reset(s);
+ uart_tx_reset(s);
+
+ s->rx_count = 0;
+ s->rx_wpos = 0;
+}
+
+static int cadence_uart_init(SysBusDevice *dev)
+{
+ UartState *s = FROM_SYSBUS(UartState, dev);
+
+ memory_region_init_io(&s->iomem, &uart_ops, s, "uart", 0x1000);
+ sysbus_init_mmio(dev, &s->iomem);
+ sysbus_init_irq(dev, &s->irq);
+
+ s->fifo_trigger_handle = qemu_new_timer_ns(vm_clock,
+ (QEMUTimerCB *)fifo_trigger_update, s);
+
+ s->tx_time_handle = qemu_new_timer_ns(vm_clock,
+ (QEMUTimerCB *)uart_tx_write, s);
+
+ s->char_tx_time = (get_ticks_per_sec() / 9600) * 10;
+
+ s->chr = qemu_char_get_next_serial();
+
+ cadence_uart_reset(s);
+
+ if (s->chr) {
+ qemu_chr_add_handlers(s->chr, uart_can_receive, uart_receive,
+ uart_event, s);
+ }
+
+ return 0;
+}
+
+static int cadence_uart_post_load(void *opaque, int version_id)
+{
+ UartState *s = opaque;
+
+ uart_parameters_setup(s);
+ uart_update_status(s);
+ return 0;
+}
+
+static const VMStateDescription vmstate_cadence_uart = {
+ .name = "cadence_uart",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .post_load = cadence_uart_post_load,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32_ARRAY(r, UartState, R_MAX),
+ VMSTATE_UINT8_ARRAY(r_fifo, UartState, RX_FIFO_SIZE),
+ VMSTATE_UINT32(rx_count, UartState),
+ VMSTATE_UINT32(rx_wpos, UartState),
+ VMSTATE_TIMER(fifo_trigger_handle, UartState),
+ VMSTATE_TIMER(tx_time_handle, UartState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void cadence_uart_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
+
+ sdc->init = cadence_uart_init;
+ dc->vmsd = &vmstate_cadence_uart;
+}
+
+static const TypeInfo cadence_uart_info = {
+ .name = "cadence_uart",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(UartState),
+ .class_init = cadence_uart_class_init,
+};
+
+static void cadence_uart_register_types(void)
+{
+ type_register_static(&cadence_uart_info);
+}
+
+type_init(cadence_uart_register_types)
diff --git a/hw/char/escc.c b/hw/char/escc.c
new file mode 100644
index 0000000000..067b055fee
--- /dev/null
+++ b/hw/char/escc.c
@@ -0,0 +1,938 @@
+/*
+ * QEMU ESCC (Z8030/Z8530/Z85C30/SCC/ESCC) serial port emulation
+ *
+ * Copyright (c) 2003-2005 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "hw/hw.h"
+#include "hw/sysbus.h"
+#include "hw/char/escc.h"
+#include "char/char.h"
+#include "ui/console.h"
+#include "trace.h"
+
+/*
+ * Chipset docs:
+ * "Z80C30/Z85C30/Z80230/Z85230/Z85233 SCC/ESCC User Manual",
+ * http://www.zilog.com/docs/serial/scc_escc_um.pdf
+ *
+ * On Sparc32 this is the serial port, mouse and keyboard part of chip STP2001
+ * (Slave I/O), also produced as NCR89C105. See
+ * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR89C105.txt
+ *
+ * The serial ports implement full AMD AM8530 or Zilog Z8530 chips,
+ * mouse and keyboard ports don't implement all functions and they are
+ * only asynchronous. There is no DMA.
+ *
+ * Z85C30 is also used on PowerMacs. There are some small differences
+ * between Sparc version (sunzilog) and PowerMac (pmac):
+ * Offset between control and data registers
+ * There is some kind of lockup bug, but we can ignore it
+ * CTS is inverted
+ * DMA on pmac using DBDMA chip
+ * pmac can do IRDA and faster rates, sunzilog can only do 38400
+ * pmac baud rate generator clock is 3.6864 MHz, sunzilog 4.9152 MHz
+ */
+
+/*
+ * Modifications:
+ * 2006-Aug-10 Igor Kovalenko : Renamed KBDQueue to SERIOQueue, implemented
+ * serial mouse queue.
+ * Implemented serial mouse protocol.
+ *
+ * 2010-May-23 Artyom Tarasenko: Reworked IUS logic
+ */
+
+typedef enum {
+ chn_a, chn_b,
+} ChnID;
+
+#define CHN_C(s) ((s)->chn == chn_b? 'b' : 'a')
+
+typedef enum {
+ ser, kbd, mouse,
+} ChnType;
+
+#define SERIO_QUEUE_SIZE 256
+
+typedef struct {
+ uint8_t data[SERIO_QUEUE_SIZE];
+ int rptr, wptr, count;
+} SERIOQueue;
+
+#define SERIAL_REGS 16
+typedef struct ChannelState {
+ qemu_irq irq;
+ uint32_t rxint, txint, rxint_under_svc, txint_under_svc;
+ struct ChannelState *otherchn;
+ uint32_t reg;
+ uint8_t wregs[SERIAL_REGS], rregs[SERIAL_REGS];
+ SERIOQueue queue;
+ CharDriverState *chr;
+ int e0_mode, led_mode, caps_lock_mode, num_lock_mode;
+ int disabled;
+ int clock;
+ uint32_t vmstate_dummy;
+ ChnID chn; // this channel, A (base+4) or B (base+0)
+ ChnType type;
+ uint8_t rx, tx;
+} ChannelState;
+
+struct SerialState {
+ SysBusDevice busdev;
+ struct ChannelState chn[2];
+ uint32_t it_shift;
+ MemoryRegion mmio;
+ uint32_t disabled;
+ uint32_t frequency;
+};
+
+#define SERIAL_CTRL 0
+#define SERIAL_DATA 1
+
+#define W_CMD 0
+#define CMD_PTR_MASK 0x07
+#define CMD_CMD_MASK 0x38
+#define CMD_HI 0x08
+#define CMD_CLR_TXINT 0x28
+#define CMD_CLR_IUS 0x38
+#define W_INTR 1
+#define INTR_INTALL 0x01
+#define INTR_TXINT 0x02
+#define INTR_RXMODEMSK 0x18
+#define INTR_RXINT1ST 0x08
+#define INTR_RXINTALL 0x10
+#define W_IVEC 2
+#define W_RXCTRL 3
+#define RXCTRL_RXEN 0x01
+#define W_TXCTRL1 4
+#define TXCTRL1_PAREN 0x01
+#define TXCTRL1_PAREV 0x02
+#define TXCTRL1_1STOP 0x04
+#define TXCTRL1_1HSTOP 0x08
+#define TXCTRL1_2STOP 0x0c
+#define TXCTRL1_STPMSK 0x0c
+#define TXCTRL1_CLK1X 0x00
+#define TXCTRL1_CLK16X 0x40
+#define TXCTRL1_CLK32X 0x80
+#define TXCTRL1_CLK64X 0xc0
+#define TXCTRL1_CLKMSK 0xc0
+#define W_TXCTRL2 5
+#define TXCTRL2_TXEN 0x08
+#define TXCTRL2_BITMSK 0x60
+#define TXCTRL2_5BITS 0x00
+#define TXCTRL2_7BITS 0x20
+#define TXCTRL2_6BITS 0x40
+#define TXCTRL2_8BITS 0x60
+#define W_SYNC1 6
+#define W_SYNC2 7
+#define W_TXBUF 8
+#define W_MINTR 9
+#define MINTR_STATUSHI 0x10
+#define MINTR_RST_MASK 0xc0
+#define MINTR_RST_B 0x40
+#define MINTR_RST_A 0x80
+#define MINTR_RST_ALL 0xc0
+#define W_MISC1 10
+#define W_CLOCK 11
+#define CLOCK_TRXC 0x08
+#define W_BRGLO 12
+#define W_BRGHI 13
+#define W_MISC2 14
+#define MISC2_PLLDIS 0x30
+#define W_EXTINT 15
+#define EXTINT_DCD 0x08
+#define EXTINT_SYNCINT 0x10
+#define EXTINT_CTSINT 0x20
+#define EXTINT_TXUNDRN 0x40
+#define EXTINT_BRKINT 0x80
+
+#define R_STATUS 0
+#define STATUS_RXAV 0x01
+#define STATUS_ZERO 0x02
+#define STATUS_TXEMPTY 0x04
+#define STATUS_DCD 0x08
+#define STATUS_SYNC 0x10
+#define STATUS_CTS 0x20
+#define STATUS_TXUNDRN 0x40
+#define STATUS_BRK 0x80
+#define R_SPEC 1
+#define SPEC_ALLSENT 0x01
+#define SPEC_BITS8 0x06
+#define R_IVEC 2
+#define IVEC_TXINTB 0x00
+#define IVEC_LONOINT 0x06
+#define IVEC_LORXINTA 0x0c
+#define IVEC_LORXINTB 0x04
+#define IVEC_LOTXINTA 0x08
+#define IVEC_HINOINT 0x60
+#define IVEC_HIRXINTA 0x30
+#define IVEC_HIRXINTB 0x20
+#define IVEC_HITXINTA 0x10
+#define R_INTR 3
+#define INTR_EXTINTB 0x01
+#define INTR_TXINTB 0x02
+#define INTR_RXINTB 0x04
+#define INTR_EXTINTA 0x08
+#define INTR_TXINTA 0x10
+#define INTR_RXINTA 0x20
+#define R_IPEN 4
+#define R_TXCTRL1 5
+#define R_TXCTRL2 6
+#define R_BC 7
+#define R_RXBUF 8
+#define R_RXCTRL 9
+#define R_MISC 10
+#define R_MISC1 11
+#define R_BRGLO 12
+#define R_BRGHI 13
+#define R_MISC1I 14
+#define R_EXTINT 15
+
+static void handle_kbd_command(ChannelState *s, int val);
+static int serial_can_receive(void *opaque);
+static void serial_receive_byte(ChannelState *s, int ch);
+
+static void clear_queue(void *opaque)
+{
+ ChannelState *s = opaque;
+ SERIOQueue *q = &s->queue;
+ q->rptr = q->wptr = q->count = 0;
+}
+
+static void put_queue(void *opaque, int b)
+{
+ ChannelState *s = opaque;
+ SERIOQueue *q = &s->queue;
+
+ trace_escc_put_queue(CHN_C(s), b);
+ if (q->count >= SERIO_QUEUE_SIZE)
+ return;
+ q->data[q->wptr] = b;
+ if (++q->wptr == SERIO_QUEUE_SIZE)
+ q->wptr = 0;
+ q->count++;
+ serial_receive_byte(s, 0);
+}
+
+static uint32_t get_queue(void *opaque)
+{
+ ChannelState *s = opaque;
+ SERIOQueue *q = &s->queue;
+ int val;
+
+ if (q->count == 0) {
+ return 0;
+ } else {
+ val = q->data[q->rptr];
+ if (++q->rptr == SERIO_QUEUE_SIZE)
+ q->rptr = 0;
+ q->count--;
+ }
+ trace_escc_get_queue(CHN_C(s), val);
+ if (q->count > 0)
+ serial_receive_byte(s, 0);
+ return val;
+}
+
+static int escc_update_irq_chn(ChannelState *s)
+{
+ if ((((s->wregs[W_INTR] & INTR_TXINT) && (s->txint == 1)) ||
+ // tx ints enabled, pending
+ ((((s->wregs[W_INTR] & INTR_RXMODEMSK) == INTR_RXINT1ST) ||
+ ((s->wregs[W_INTR] & INTR_RXMODEMSK) == INTR_RXINTALL)) &&
+ s->rxint == 1) || // rx ints enabled, pending
+ ((s->wregs[W_EXTINT] & EXTINT_BRKINT) &&
+ (s->rregs[R_STATUS] & STATUS_BRK)))) { // break int e&p
+ return 1;
+ }
+ return 0;
+}
+
+static void escc_update_irq(ChannelState *s)
+{
+ int irq;
+
+ irq = escc_update_irq_chn(s);
+ irq |= escc_update_irq_chn(s->otherchn);
+
+ trace_escc_update_irq(irq);
+ qemu_set_irq(s->irq, irq);
+}
+
+static void escc_reset_chn(ChannelState *s)
+{
+ int i;
+
+ s->reg = 0;
+ for (i = 0; i < SERIAL_REGS; i++) {
+ s->rregs[i] = 0;
+ s->wregs[i] = 0;
+ }
+ s->wregs[W_TXCTRL1] = TXCTRL1_1STOP; // 1X divisor, 1 stop bit, no parity
+ s->wregs[W_MINTR] = MINTR_RST_ALL;
+ s->wregs[W_CLOCK] = CLOCK_TRXC; // Synch mode tx clock = TRxC
+ s->wregs[W_MISC2] = MISC2_PLLDIS; // PLL disabled
+ s->wregs[W_EXTINT] = EXTINT_DCD | EXTINT_SYNCINT | EXTINT_CTSINT |
+ EXTINT_TXUNDRN | EXTINT_BRKINT; // Enable most interrupts
+ if (s->disabled)
+ s->rregs[R_STATUS] = STATUS_TXEMPTY | STATUS_DCD | STATUS_SYNC |
+ STATUS_CTS | STATUS_TXUNDRN;
+ else
+ s->rregs[R_STATUS] = STATUS_TXEMPTY | STATUS_TXUNDRN;
+ s->rregs[R_SPEC] = SPEC_BITS8 | SPEC_ALLSENT;
+
+ s->rx = s->tx = 0;
+ s->rxint = s->txint = 0;
+ s->rxint_under_svc = s->txint_under_svc = 0;
+ s->e0_mode = s->led_mode = s->caps_lock_mode = s->num_lock_mode = 0;
+ clear_queue(s);
+}
+
+static void escc_reset(DeviceState *d)
+{
+ SerialState *s = container_of(d, SerialState, busdev.qdev);
+
+ escc_reset_chn(&s->chn[0]);
+ escc_reset_chn(&s->chn[1]);
+}
+
+static inline void set_rxint(ChannelState *s)
+{
+ s->rxint = 1;
+ /* XXX: missing daisy chainnig: chn_b rx should have a lower priority
+ than chn_a rx/tx/special_condition service*/
+ s->rxint_under_svc = 1;
+ if (s->chn == chn_a) {
+ s->rregs[R_INTR] |= INTR_RXINTA;
+ if (s->wregs[W_MINTR] & MINTR_STATUSHI)
+ s->otherchn->rregs[R_IVEC] = IVEC_HIRXINTA;
+ else
+ s->otherchn->rregs[R_IVEC] = IVEC_LORXINTA;
+ } else {
+ s->otherchn->rregs[R_INTR] |= INTR_RXINTB;
+ if (s->wregs[W_MINTR] & MINTR_STATUSHI)
+ s->rregs[R_IVEC] = IVEC_HIRXINTB;
+ else
+ s->rregs[R_IVEC] = IVEC_LORXINTB;
+ }
+ escc_update_irq(s);
+}
+
+static inline void set_txint(ChannelState *s)
+{
+ s->txint = 1;
+ if (!s->rxint_under_svc) {
+ s->txint_under_svc = 1;
+ if (s->chn == chn_a) {
+ if (s->wregs[W_INTR] & INTR_TXINT) {
+ s->rregs[R_INTR] |= INTR_TXINTA;
+ }
+ if (s->wregs[W_MINTR] & MINTR_STATUSHI)
+ s->otherchn->rregs[R_IVEC] = IVEC_HITXINTA;
+ else
+ s->otherchn->rregs[R_IVEC] = IVEC_LOTXINTA;
+ } else {
+ s->rregs[R_IVEC] = IVEC_TXINTB;
+ if (s->wregs[W_INTR] & INTR_TXINT) {
+ s->otherchn->rregs[R_INTR] |= INTR_TXINTB;
+ }
+ }
+ escc_update_irq(s);
+ }
+}
+
+static inline void clr_rxint(ChannelState *s)
+{
+ s->rxint = 0;
+ s->rxint_under_svc = 0;
+ if (s->chn == chn_a) {
+ if (s->wregs[W_MINTR] & MINTR_STATUSHI)
+ s->otherchn->rregs[R_IVEC] = IVEC_HINOINT;
+ else
+ s->otherchn->rregs[R_IVEC] = IVEC_LONOINT;
+ s->rregs[R_INTR] &= ~INTR_RXINTA;
+ } else {
+ if (s->wregs[W_MINTR] & MINTR_STATUSHI)
+ s->rregs[R_IVEC] = IVEC_HINOINT;
+ else
+ s->rregs[R_IVEC] = IVEC_LONOINT;
+ s->otherchn->rregs[R_INTR] &= ~INTR_RXINTB;
+ }
+ if (s->txint)
+ set_txint(s);
+ escc_update_irq(s);
+}
+
+static inline void clr_txint(ChannelState *s)
+{
+ s->txint = 0;
+ s->txint_under_svc = 0;
+ if (s->chn == chn_a) {
+ if (s->wregs[W_MINTR] & MINTR_STATUSHI)
+ s->otherchn->rregs[R_IVEC] = IVEC_HINOINT;
+ else
+ s->otherchn->rregs[R_IVEC] = IVEC_LONOINT;
+ s->rregs[R_INTR] &= ~INTR_TXINTA;
+ } else {
+ s->otherchn->rregs[R_INTR] &= ~INTR_TXINTB;
+ if (s->wregs[W_MINTR] & MINTR_STATUSHI)
+ s->rregs[R_IVEC] = IVEC_HINOINT;
+ else
+ s->rregs[R_IVEC] = IVEC_LONOINT;
+ s->otherchn->rregs[R_INTR] &= ~INTR_TXINTB;
+ }
+ if (s->rxint)
+ set_rxint(s);
+ escc_update_irq(s);
+}
+
+static void escc_update_parameters(ChannelState *s)
+{
+ int speed, parity, data_bits, stop_bits;
+ QEMUSerialSetParams ssp;
+
+ if (!s->chr || s->type != ser)
+ return;
+
+ if (s->wregs[W_TXCTRL1] & TXCTRL1_PAREN) {
+ if (s->wregs[W_TXCTRL1] & TXCTRL1_PAREV)
+ parity = 'E';
+ else
+ parity = 'O';
+ } else {
+ parity = 'N';
+ }
+ if ((s->wregs[W_TXCTRL1] & TXCTRL1_STPMSK) == TXCTRL1_2STOP)
+ stop_bits = 2;
+ else
+ stop_bits = 1;
+ switch (s->wregs[W_TXCTRL2] & TXCTRL2_BITMSK) {
+ case TXCTRL2_5BITS:
+ data_bits = 5;
+ break;
+ case TXCTRL2_7BITS:
+ data_bits = 7;
+ break;
+ case TXCTRL2_6BITS:
+ data_bits = 6;
+ break;
+ default:
+ case TXCTRL2_8BITS:
+ data_bits = 8;
+ break;
+ }
+ speed = s->clock / ((s->wregs[W_BRGLO] | (s->wregs[W_BRGHI] << 8)) + 2);
+ switch (s->wregs[W_TXCTRL1] & TXCTRL1_CLKMSK) {
+ case TXCTRL1_CLK1X:
+ break;
+ case TXCTRL1_CLK16X:
+ speed /= 16;
+ break;
+ case TXCTRL1_CLK32X:
+ speed /= 32;
+ break;
+ default:
+ case TXCTRL1_CLK64X:
+ speed /= 64;
+ break;
+ }
+ ssp.speed = speed;
+ ssp.parity = parity;
+ ssp.data_bits = data_bits;
+ ssp.stop_bits = stop_bits;
+ trace_escc_update_parameters(CHN_C(s), speed, parity, data_bits, stop_bits);
+ qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp);
+}
+
+static void escc_mem_write(void *opaque, hwaddr addr,
+ uint64_t val, unsigned size)
+{
+ SerialState *serial = opaque;
+ ChannelState *s;
+ uint32_t saddr;
+ int newreg, channel;
+
+ val &= 0xff;
+ saddr = (addr >> serial->it_shift) & 1;
+ channel = (addr >> (serial->it_shift + 1)) & 1;
+ s = &serial->chn[channel];
+ switch (saddr) {
+ case SERIAL_CTRL:
+ trace_escc_mem_writeb_ctrl(CHN_C(s), s->reg, val & 0xff);
+ newreg = 0;
+ switch (s->reg) {
+ case W_CMD:
+ newreg = val & CMD_PTR_MASK;
+ val &= CMD_CMD_MASK;
+ switch (val) {
+ case CMD_HI:
+ newreg |= CMD_HI;
+ break;
+ case CMD_CLR_TXINT:
+ clr_txint(s);
+ break;
+ case CMD_CLR_IUS:
+ if (s->rxint_under_svc) {
+ s->rxint_under_svc = 0;
+ if (s->txint) {
+ set_txint(s);
+ }
+ } else if (s->txint_under_svc) {
+ s->txint_under_svc = 0;
+ }
+ escc_update_irq(s);
+ break;
+ default:
+ break;
+ }
+ break;
+ case W_INTR ... W_RXCTRL:
+ case W_SYNC1 ... W_TXBUF:
+ case W_MISC1 ... W_CLOCK:
+ case W_MISC2 ... W_EXTINT:
+ s->wregs[s->reg] = val;
+ break;
+ case W_TXCTRL1:
+ case W_TXCTRL2:
+ s->wregs[s->reg] = val;
+ escc_update_parameters(s);
+ break;
+ case W_BRGLO:
+ case W_BRGHI:
+ s->wregs[s->reg] = val;
+ s->rregs[s->reg] = val;
+ escc_update_parameters(s);
+ break;
+ case W_MINTR:
+ switch (val & MINTR_RST_MASK) {
+ case 0:
+ default:
+ break;
+ case MINTR_RST_B:
+ escc_reset_chn(&serial->chn[0]);
+ return;
+ case MINTR_RST_A:
+ escc_reset_chn(&serial->chn[1]);
+ return;
+ case MINTR_RST_ALL:
+ escc_reset(&serial->busdev.qdev);
+ return;
+ }
+ break;
+ default:
+ break;
+ }
+ if (s->reg == 0)
+ s->reg = newreg;
+ else
+ s->reg = 0;
+ break;
+ case SERIAL_DATA:
+ trace_escc_mem_writeb_data(CHN_C(s), val);
+ s->tx = val;
+ if (s->wregs[W_TXCTRL2] & TXCTRL2_TXEN) { // tx enabled
+ if (s->chr)
+ qemu_chr_fe_write(s->chr, &s->tx, 1);
+ else if (s->type == kbd && !s->disabled) {
+ handle_kbd_command(s, val);
+ }
+ }
+ s->rregs[R_STATUS] |= STATUS_TXEMPTY; // Tx buffer empty
+ s->rregs[R_SPEC] |= SPEC_ALLSENT; // All sent
+ set_txint(s);
+ break;
+ default:
+ break;
+ }
+}
+
+static uint64_t escc_mem_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ SerialState *serial = opaque;
+ ChannelState *s;
+ uint32_t saddr;
+ uint32_t ret;
+ int channel;
+
+ saddr = (addr >> serial->it_shift) & 1;
+ channel = (addr >> (serial->it_shift + 1)) & 1;
+ s = &serial->chn[channel];
+ switch (saddr) {
+ case SERIAL_CTRL:
+ trace_escc_mem_readb_ctrl(CHN_C(s), s->reg, s->rregs[s->reg]);
+ ret = s->rregs[s->reg];
+ s->reg = 0;
+ return ret;
+ case SERIAL_DATA:
+ s->rregs[R_STATUS] &= ~STATUS_RXAV;
+ clr_rxint(s);
+ if (s->type == kbd || s->type == mouse)
+ ret = get_queue(s);
+ else
+ ret = s->rx;
+ trace_escc_mem_readb_data(CHN_C(s), ret);
+ if (s->chr)
+ qemu_chr_accept_input(s->chr);
+ return ret;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static const MemoryRegionOps escc_mem_ops = {
+ .read = escc_mem_read,
+ .write = escc_mem_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid = {
+ .min_access_size = 1,
+ .max_access_size = 1,
+ },
+};
+
+static int serial_can_receive(void *opaque)
+{
+ ChannelState *s = opaque;
+ int ret;
+
+ if (((s->wregs[W_RXCTRL] & RXCTRL_RXEN) == 0) // Rx not enabled
+ || ((s->rregs[R_STATUS] & STATUS_RXAV) == STATUS_RXAV))
+ // char already available
+ ret = 0;
+ else
+ ret = 1;
+ return ret;
+}
+
+static void serial_receive_byte(ChannelState *s, int ch)
+{
+ trace_escc_serial_receive_byte(CHN_C(s), ch);
+ s->rregs[R_STATUS] |= STATUS_RXAV;
+ s->rx = ch;
+ set_rxint(s);
+}
+
+static void serial_receive_break(ChannelState *s)
+{
+ s->rregs[R_STATUS] |= STATUS_BRK;
+ escc_update_irq(s);
+}
+
+static void serial_receive1(void *opaque, const uint8_t *buf, int size)
+{
+ ChannelState *s = opaque;
+ serial_receive_byte(s, buf[0]);
+}
+
+static void serial_event(void *opaque, int event)
+{
+ ChannelState *s = opaque;
+ if (event == CHR_EVENT_BREAK)
+ serial_receive_break(s);
+}
+
+static const VMStateDescription vmstate_escc_chn = {
+ .name ="escc_chn",
+ .version_id = 2,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField []) {
+ VMSTATE_UINT32(vmstate_dummy, ChannelState),
+ VMSTATE_UINT32(reg, ChannelState),
+ VMSTATE_UINT32(rxint, ChannelState),
+ VMSTATE_UINT32(txint, ChannelState),
+ VMSTATE_UINT32(rxint_under_svc, ChannelState),
+ VMSTATE_UINT32(txint_under_svc, ChannelState),
+ VMSTATE_UINT8(rx, ChannelState),
+ VMSTATE_UINT8(tx, ChannelState),
+ VMSTATE_BUFFER(wregs, ChannelState),
+ VMSTATE_BUFFER(rregs, ChannelState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_escc = {
+ .name ="escc",
+ .version_id = 2,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField []) {
+ VMSTATE_STRUCT_ARRAY(chn, SerialState, 2, 2, vmstate_escc_chn,
+ ChannelState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+MemoryRegion *escc_init(hwaddr base, qemu_irq irqA, qemu_irq irqB,
+ CharDriverState *chrA, CharDriverState *chrB,
+ int clock, int it_shift)
+{
+ DeviceState *dev;
+ SysBusDevice *s;
+ SerialState *d;
+
+ dev = qdev_create(NULL, "escc");
+ qdev_prop_set_uint32(dev, "disabled", 0);
+ qdev_prop_set_uint32(dev, "frequency", clock);
+ qdev_prop_set_uint32(dev, "it_shift", it_shift);
+ qdev_prop_set_chr(dev, "chrB", chrB);
+ qdev_prop_set_chr(dev, "chrA", chrA);
+ qdev_prop_set_uint32(dev, "chnBtype", ser);
+ qdev_prop_set_uint32(dev, "chnAtype", ser);
+ qdev_init_nofail(dev);
+ s = SYS_BUS_DEVICE(dev);
+ sysbus_connect_irq(s, 0, irqB);
+ sysbus_connect_irq(s, 1, irqA);
+ if (base) {
+ sysbus_mmio_map(s, 0, base);
+ }
+
+ d = FROM_SYSBUS(SerialState, s);
+ return &d->mmio;
+}
+
+static const uint8_t keycodes[128] = {
+ 127, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 43, 53,
+ 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 89, 76, 77, 78,
+ 79, 80, 81, 82, 83, 84, 85, 86, 87, 42, 99, 88, 100, 101, 102, 103,
+ 104, 105, 106, 107, 108, 109, 110, 47, 19, 121, 119, 5, 6, 8, 10, 12,
+ 14, 16, 17, 18, 7, 98, 23, 68, 69, 70, 71, 91, 92, 93, 125, 112,
+ 113, 114, 94, 50, 0, 0, 124, 9, 11, 0, 0, 0, 0, 0, 0, 0,
+ 90, 0, 46, 22, 13, 111, 52, 20, 96, 24, 28, 74, 27, 123, 44, 66,
+ 0, 45, 2, 4, 48, 0, 0, 21, 0, 0, 0, 0, 0, 120, 122, 67,
+};
+
+static const uint8_t e0_keycodes[128] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 90, 76, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 109, 0, 0, 13, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 68, 69, 70, 0, 91, 0, 93, 0, 112,
+ 113, 114, 94, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 3, 25, 26, 49, 52, 72, 73, 97, 99, 111, 118, 120, 122, 67, 0,
+};
+
+static void sunkbd_event(void *opaque, int ch)
+{
+ ChannelState *s = opaque;
+ int release = ch & 0x80;
+
+ trace_escc_sunkbd_event_in(ch);
+ switch (ch) {
+ case 58: // Caps lock press
+ s->caps_lock_mode ^= 1;
+ if (s->caps_lock_mode == 2)
+ return; // Drop second press
+ break;
+ case 69: // Num lock press
+ s->num_lock_mode ^= 1;
+ if (s->num_lock_mode == 2)
+ return; // Drop second press
+ break;
+ case 186: // Caps lock release
+ s->caps_lock_mode ^= 2;
+ if (s->caps_lock_mode == 3)
+ return; // Drop first release
+ break;
+ case 197: // Num lock release
+ s->num_lock_mode ^= 2;
+ if (s->num_lock_mode == 3)
+ return; // Drop first release
+ break;
+ case 0xe0:
+ s->e0_mode = 1;
+ return;
+ default:
+ break;
+ }
+ if (s->e0_mode) {
+ s->e0_mode = 0;
+ ch = e0_keycodes[ch & 0x7f];
+ } else {
+ ch = keycodes[ch & 0x7f];
+ }
+ trace_escc_sunkbd_event_out(ch);
+ put_queue(s, ch | release);
+}
+
+static void handle_kbd_command(ChannelState *s, int val)
+{
+ trace_escc_kbd_command(val);
+ if (s->led_mode) { // Ignore led byte
+ s->led_mode = 0;
+ return;
+ }
+ switch (val) {
+ case 1: // Reset, return type code
+ clear_queue(s);
+ put_queue(s, 0xff);
+ put_queue(s, 4); // Type 4
+ put_queue(s, 0x7f);
+ break;
+ case 0xe: // Set leds
+ s->led_mode = 1;
+ break;
+ case 7: // Query layout
+ case 0xf:
+ clear_queue(s);
+ put_queue(s, 0xfe);
+ put_queue(s, 0); // XXX, layout?
+ break;
+ default:
+ break;
+ }
+}
+
+static void sunmouse_event(void *opaque,
+ int dx, int dy, int dz, int buttons_state)
+{
+ ChannelState *s = opaque;
+ int ch;
+
+ trace_escc_sunmouse_event(dx, dy, buttons_state);
+ ch = 0x80 | 0x7; /* protocol start byte, no buttons pressed */
+
+ if (buttons_state & MOUSE_EVENT_LBUTTON)
+ ch ^= 0x4;
+ if (buttons_state & MOUSE_EVENT_MBUTTON)
+ ch ^= 0x2;
+ if (buttons_state & MOUSE_EVENT_RBUTTON)
+ ch ^= 0x1;
+
+ put_queue(s, ch);
+
+ ch = dx;
+
+ if (ch > 127)
+ ch = 127;
+ else if (ch < -127)
+ ch = -127;
+
+ put_queue(s, ch & 0xff);
+
+ ch = -dy;
+
+ if (ch > 127)
+ ch = 127;
+ else if (ch < -127)
+ ch = -127;
+
+ put_queue(s, ch & 0xff);
+
+ // MSC protocol specify two extra motion bytes
+
+ put_queue(s, 0);
+ put_queue(s, 0);
+}
+
+void slavio_serial_ms_kbd_init(hwaddr base, qemu_irq irq,
+ int disabled, int clock, int it_shift)
+{
+ DeviceState *dev;
+ SysBusDevice *s;
+
+ dev = qdev_create(NULL, "escc");
+ qdev_prop_set_uint32(dev, "disabled", disabled);
+ qdev_prop_set_uint32(dev, "frequency", clock);
+ qdev_prop_set_uint32(dev, "it_shift", it_shift);
+ qdev_prop_set_chr(dev, "chrB", NULL);
+ qdev_prop_set_chr(dev, "chrA", NULL);
+ qdev_prop_set_uint32(dev, "chnBtype", mouse);
+ qdev_prop_set_uint32(dev, "chnAtype", kbd);
+ qdev_init_nofail(dev);
+ s = SYS_BUS_DEVICE(dev);
+ sysbus_connect_irq(s, 0, irq);
+ sysbus_connect_irq(s, 1, irq);
+ sysbus_mmio_map(s, 0, base);
+}
+
+static int escc_init1(SysBusDevice *dev)
+{
+ SerialState *s = FROM_SYSBUS(SerialState, dev);
+ unsigned int i;
+
+ s->chn[0].disabled = s->disabled;
+ s->chn[1].disabled = s->disabled;
+ for (i = 0; i < 2; i++) {
+ sysbus_init_irq(dev, &s->chn[i].irq);
+ s->chn[i].chn = 1 - i;
+ s->chn[i].clock = s->frequency / 2;
+ if (s->chn[i].chr) {
+ qemu_chr_add_handlers(s->chn[i].chr, serial_can_receive,
+ serial_receive1, serial_event, &s->chn[i]);
+ }
+ }
+ s->chn[0].otherchn = &s->chn[1];
+ s->chn[1].otherchn = &s->chn[0];
+
+ memory_region_init_io(&s->mmio, &escc_mem_ops, s, "escc",
+ ESCC_SIZE << s->it_shift);
+ sysbus_init_mmio(dev, &s->mmio);
+
+ if (s->chn[0].type == mouse) {
+ qemu_add_mouse_event_handler(sunmouse_event, &s->chn[0], 0,
+ "QEMU Sun Mouse");
+ }
+ if (s->chn[1].type == kbd) {
+ qemu_add_kbd_event_handler(sunkbd_event, &s->chn[1]);
+ }
+
+ return 0;
+}
+
+static Property escc_properties[] = {
+ DEFINE_PROP_UINT32("frequency", SerialState, frequency, 0),
+ DEFINE_PROP_UINT32("it_shift", SerialState, it_shift, 0),
+ DEFINE_PROP_UINT32("disabled", SerialState, disabled, 0),
+ DEFINE_PROP_UINT32("chnBtype", SerialState, chn[0].type, 0),
+ DEFINE_PROP_UINT32("chnAtype", SerialState, chn[1].type, 0),
+ DEFINE_PROP_CHR("chrB", SerialState, chn[0].chr),
+ DEFINE_PROP_CHR("chrA", SerialState, chn[1].chr),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void escc_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+ k->init = escc_init1;
+ dc->reset = escc_reset;
+ dc->vmsd = &vmstate_escc;
+ dc->props = escc_properties;
+}
+
+static const TypeInfo escc_info = {
+ .name = "escc",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(SerialState),
+ .class_init = escc_class_init,
+};
+
+static void escc_register_types(void)
+{
+ type_register_static(&escc_info);
+}
+
+type_init(escc_register_types)
diff --git a/hw/char/ipack.c b/hw/char/ipack.c
new file mode 100644
index 0000000000..b1f46c10a4
--- /dev/null
+++ b/hw/char/ipack.c
@@ -0,0 +1,115 @@
+/*
+ * QEMU IndustryPack emulation
+ *
+ * Copyright (C) 2012 Igalia, S.L.
+ * Author: Alberto Garcia <agarcia@igalia.com>
+ *
+ * This code is licensed under the GNU GPL v2 or (at your option) any
+ * later version.
+ */
+
+#include "hw/ipack.h"
+
+IPackDevice *ipack_device_find(IPackBus *bus, int32_t slot)
+{
+ BusChild *kid;
+
+ QTAILQ_FOREACH(kid, &BUS(bus)->children, sibling) {
+ DeviceState *qdev = kid->child;
+ IPackDevice *ip = IPACK_DEVICE(qdev);
+ if (ip->slot == slot) {
+ return ip;
+ }
+ }
+ return NULL;
+}
+
+void ipack_bus_new_inplace(IPackBus *bus, DeviceState *parent,
+ const char *name, uint8_t n_slots,
+ qemu_irq_handler handler)
+{
+ qbus_create_inplace(&bus->qbus, TYPE_IPACK_BUS, parent, name);
+ bus->n_slots = n_slots;
+ bus->set_irq = handler;
+}
+
+static int ipack_device_dev_init(DeviceState *qdev)
+{
+ IPackBus *bus = IPACK_BUS(qdev_get_parent_bus(qdev));
+ IPackDevice *dev = IPACK_DEVICE(qdev);
+ IPackDeviceClass *k = IPACK_DEVICE_GET_CLASS(dev);
+
+ if (dev->slot < 0) {
+ dev->slot = bus->free_slot;
+ }
+ if (dev->slot >= bus->n_slots) {
+ return -1;
+ }
+ bus->free_slot = dev->slot + 1;
+
+ dev->irq = qemu_allocate_irqs(bus->set_irq, dev, 2);
+
+ return k->init(dev);
+}
+
+static int ipack_device_dev_exit(DeviceState *qdev)
+{
+ IPackDevice *dev = IPACK_DEVICE(qdev);
+ IPackDeviceClass *k = IPACK_DEVICE_GET_CLASS(dev);
+
+ if (k->exit) {
+ k->exit(dev);
+ }
+
+ qemu_free_irqs(dev->irq);
+
+ return 0;
+}
+
+static Property ipack_device_props[] = {
+ DEFINE_PROP_INT32("slot", IPackDevice, slot, -1),
+ DEFINE_PROP_END_OF_LIST()
+};
+
+static void ipack_device_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *k = DEVICE_CLASS(klass);
+ k->bus_type = TYPE_IPACK_BUS;
+ k->init = ipack_device_dev_init;
+ k->exit = ipack_device_dev_exit;
+ k->props = ipack_device_props;
+}
+
+const VMStateDescription vmstate_ipack_device = {
+ .name = "ipack_device",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_INT32(slot, IPackDevice),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const TypeInfo ipack_device_info = {
+ .name = TYPE_IPACK_DEVICE,
+ .parent = TYPE_DEVICE,
+ .instance_size = sizeof(IPackDevice),
+ .class_size = sizeof(IPackDeviceClass),
+ .class_init = ipack_device_class_init,
+ .abstract = true,
+};
+
+static const TypeInfo ipack_bus_info = {
+ .name = TYPE_IPACK_BUS,
+ .parent = TYPE_BUS,
+ .instance_size = sizeof(IPackBus),
+};
+
+static void ipack_register_types(void)
+{
+ type_register_static(&ipack_device_info);
+ type_register_static(&ipack_bus_info);
+}
+
+type_init(ipack_register_types)
diff --git a/hw/char/ipoctal232.c b/hw/char/ipoctal232.c
new file mode 100644
index 0000000000..685fee2d2e
--- /dev/null
+++ b/hw/char/ipoctal232.c
@@ -0,0 +1,605 @@
+/*
+ * QEMU GE IP-Octal 232 IndustryPack emulation
+ *
+ * Copyright (C) 2012 Igalia, S.L.
+ * Author: Alberto Garcia <agarcia@igalia.com>
+ *
+ * This code is licensed under the GNU GPL v2 or (at your option) any
+ * later version.
+ */
+
+#include "hw/ipack.h"
+#include "qemu/bitops.h"
+#include "char/char.h"
+
+/* #define DEBUG_IPOCTAL */
+
+#ifdef DEBUG_IPOCTAL
+#define DPRINTF2(fmt, ...) \
+ do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF2(fmt, ...) do { } while (0)
+#endif
+
+#define DPRINTF(fmt, ...) DPRINTF2("IP-Octal: " fmt, ## __VA_ARGS__)
+
+#define RX_FIFO_SIZE 3
+
+/* The IP-Octal has 8 channels (a-h)
+ divided into 4 blocks (A-D) */
+#define N_CHANNELS 8
+#define N_BLOCKS 4
+
+#define REG_MRa 0x01
+#define REG_MRb 0x11
+#define REG_SRa 0x03
+#define REG_SRb 0x13
+#define REG_CSRa 0x03
+#define REG_CSRb 0x13
+#define REG_CRa 0x05
+#define REG_CRb 0x15
+#define REG_RHRa 0x07
+#define REG_RHRb 0x17
+#define REG_THRa 0x07
+#define REG_THRb 0x17
+#define REG_ACR 0x09
+#define REG_ISR 0x0B
+#define REG_IMR 0x0B
+#define REG_OPCR 0x1B
+
+#define CR_ENABLE_RX BIT(0)
+#define CR_DISABLE_RX BIT(1)
+#define CR_ENABLE_TX BIT(2)
+#define CR_DISABLE_TX BIT(3)
+#define CR_CMD(cr) ((cr) >> 4)
+#define CR_NO_OP 0
+#define CR_RESET_MR 1
+#define CR_RESET_RX 2
+#define CR_RESET_TX 3
+#define CR_RESET_ERR 4
+#define CR_RESET_BRKINT 5
+#define CR_START_BRK 6
+#define CR_STOP_BRK 7
+#define CR_ASSERT_RTSN 8
+#define CR_NEGATE_RTSN 9
+#define CR_TIMEOUT_ON 10
+#define CR_TIMEOUT_OFF 12
+
+#define SR_RXRDY BIT(0)
+#define SR_FFULL BIT(1)
+#define SR_TXRDY BIT(2)
+#define SR_TXEMT BIT(3)
+#define SR_OVERRUN BIT(4)
+#define SR_PARITY BIT(5)
+#define SR_FRAMING BIT(6)
+#define SR_BREAK BIT(7)
+
+#define ISR_TXRDYA BIT(0)
+#define ISR_RXRDYA BIT(1)
+#define ISR_BREAKA BIT(2)
+#define ISR_CNTRDY BIT(3)
+#define ISR_TXRDYB BIT(4)
+#define ISR_RXRDYB BIT(5)
+#define ISR_BREAKB BIT(6)
+#define ISR_MPICHG BIT(7)
+#define ISR_TXRDY(CH) (((CH) & 1) ? BIT(4) : BIT(0))
+#define ISR_RXRDY(CH) (((CH) & 1) ? BIT(5) : BIT(1))
+#define ISR_BREAK(CH) (((CH) & 1) ? BIT(6) : BIT(2))
+
+typedef struct IPOctalState IPOctalState;
+typedef struct SCC2698Channel SCC2698Channel;
+typedef struct SCC2698Block SCC2698Block;
+
+struct SCC2698Channel {
+ IPOctalState *ipoctal;
+ CharDriverState *dev;
+ bool rx_enabled;
+ uint8_t mr[2];
+ uint8_t mr_idx;
+ uint8_t sr;
+ uint8_t rhr[RX_FIFO_SIZE];
+ uint8_t rhr_idx;
+ uint8_t rx_pending;
+};
+
+struct SCC2698Block {
+ uint8_t imr;
+ uint8_t isr;
+};
+
+struct IPOctalState {
+ IPackDevice dev;
+ SCC2698Channel ch[N_CHANNELS];
+ SCC2698Block blk[N_BLOCKS];
+ uint8_t irq_vector;
+};
+
+#define TYPE_IPOCTAL "ipoctal232"
+
+#define IPOCTAL(obj) \
+ OBJECT_CHECK(IPOctalState, (obj), TYPE_IPOCTAL)
+
+static const VMStateDescription vmstate_scc2698_channel = {
+ .name = "scc2698_channel",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_BOOL(rx_enabled, SCC2698Channel),
+ VMSTATE_UINT8_ARRAY(mr, SCC2698Channel, 2),
+ VMSTATE_UINT8(mr_idx, SCC2698Channel),
+ VMSTATE_UINT8(sr, SCC2698Channel),
+ VMSTATE_UINT8_ARRAY(rhr, SCC2698Channel, RX_FIFO_SIZE),
+ VMSTATE_UINT8(rhr_idx, SCC2698Channel),
+ VMSTATE_UINT8(rx_pending, SCC2698Channel),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_scc2698_block = {
+ .name = "scc2698_block",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT8(imr, SCC2698Block),
+ VMSTATE_UINT8(isr, SCC2698Block),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_ipoctal = {
+ .name = "ipoctal232",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_IPACK_DEVICE(dev, IPOctalState),
+ VMSTATE_STRUCT_ARRAY(ch, IPOctalState, N_CHANNELS, 1,
+ vmstate_scc2698_channel, SCC2698Channel),
+ VMSTATE_STRUCT_ARRAY(blk, IPOctalState, N_BLOCKS, 1,
+ vmstate_scc2698_block, SCC2698Block),
+ VMSTATE_UINT8(irq_vector, IPOctalState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+/* data[10] is 0x0C, not 0x0B as the doc says */
+static const uint8_t id_prom_data[] = {
+ 0x49, 0x50, 0x41, 0x43, 0xF0, 0x22,
+ 0xA1, 0x00, 0x00, 0x00, 0x0C, 0xCC
+};
+
+static void update_irq(IPOctalState *dev, unsigned block)
+{
+ /* Blocks A and B interrupt on INT0#, C and D on INT1#.
+ Thus, to get the status we have to check two blocks. */
+ SCC2698Block *blk0 = &dev->blk[block];
+ SCC2698Block *blk1 = &dev->blk[block^1];
+ unsigned intno = block / 2;
+
+ if ((blk0->isr & blk0->imr) || (blk1->isr & blk1->imr)) {
+ qemu_irq_raise(dev->dev.irq[intno]);
+ } else {
+ qemu_irq_lower(dev->dev.irq[intno]);
+ }
+}
+
+static void write_cr(IPOctalState *dev, unsigned channel, uint8_t val)
+{
+ SCC2698Channel *ch = &dev->ch[channel];
+ SCC2698Block *blk = &dev->blk[channel / 2];
+
+ DPRINTF("Write CR%c %u: ", channel + 'a', val);
+
+ /* The lower 4 bits are used to enable and disable Tx and Rx */
+ if (val & CR_ENABLE_RX) {
+ DPRINTF2("Rx on, ");
+ ch->rx_enabled = true;
+ }
+ if (val & CR_DISABLE_RX) {
+ DPRINTF2("Rx off, ");
+ ch->rx_enabled = false;
+ }
+ if (val & CR_ENABLE_TX) {
+ DPRINTF2("Tx on, ");
+ ch->sr |= SR_TXRDY | SR_TXEMT;
+ blk->isr |= ISR_TXRDY(channel);
+ }
+ if (val & CR_DISABLE_TX) {
+ DPRINTF2("Tx off, ");
+ ch->sr &= ~(SR_TXRDY | SR_TXEMT);
+ blk->isr &= ~ISR_TXRDY(channel);
+ }
+
+ DPRINTF2("cmd: ");
+
+ /* The rest of the bits implement different commands */
+ switch (CR_CMD(val)) {
+ case CR_NO_OP:
+ DPRINTF2("none");
+ break;
+ case CR_RESET_MR:
+ DPRINTF2("reset MR");
+ ch->mr_idx = 0;
+ break;
+ case CR_RESET_RX:
+ DPRINTF2("reset Rx");
+ ch->rx_enabled = false;
+ ch->rx_pending = 0;
+ ch->sr &= ~SR_RXRDY;
+ blk->isr &= ~ISR_RXRDY(channel);
+ break;
+ case CR_RESET_TX:
+ DPRINTF2("reset Tx");
+ ch->sr &= ~(SR_TXRDY | SR_TXEMT);
+ blk->isr &= ~ISR_TXRDY(channel);
+ break;
+ case CR_RESET_ERR:
+ DPRINTF2("reset err");
+ ch->sr &= ~(SR_OVERRUN | SR_PARITY | SR_FRAMING | SR_BREAK);
+ break;
+ case CR_RESET_BRKINT:
+ DPRINTF2("reset brk ch int");
+ blk->isr &= ~(ISR_BREAKA | ISR_BREAKB);
+ break;
+ default:
+ DPRINTF2("unsupported 0x%x", CR_CMD(val));
+ }
+
+ DPRINTF2("\n");
+}
+
+static uint16_t io_read(IPackDevice *ip, uint8_t addr)
+{
+ IPOctalState *dev = IPOCTAL(ip);
+ uint16_t ret = 0;
+ /* addr[7:6]: block (A-D)
+ addr[7:5]: channel (a-h)
+ addr[5:0]: register */
+ unsigned block = addr >> 5;
+ unsigned channel = addr >> 4;
+ /* Big endian, accessed using 8-bit bytes at odd locations */
+ unsigned offset = (addr & 0x1F) ^ 1;
+ SCC2698Channel *ch = &dev->ch[channel];
+ SCC2698Block *blk = &dev->blk[block];
+ uint8_t old_isr = blk->isr;
+
+ switch (offset) {
+
+ case REG_MRa:
+ case REG_MRb:
+ ret = ch->mr[ch->mr_idx];
+ DPRINTF("Read MR%u%c: 0x%x\n", ch->mr_idx + 1, channel + 'a', ret);
+ ch->mr_idx = 1;
+ break;
+
+ case REG_SRa:
+ case REG_SRb:
+ ret = ch->sr;
+ DPRINTF("Read SR%c: 0x%x\n", channel + 'a', ret);
+ break;
+
+ case REG_RHRa:
+ case REG_RHRb:
+ ret = ch->rhr[ch->rhr_idx];
+ if (ch->rx_pending > 0) {
+ ch->rx_pending--;
+ if (ch->rx_pending == 0) {
+ ch->sr &= ~SR_RXRDY;
+ blk->isr &= ~ISR_RXRDY(channel);
+ if (ch->dev) {
+ qemu_chr_accept_input(ch->dev);
+ }
+ } else {
+ ch->rhr_idx = (ch->rhr_idx + 1) % RX_FIFO_SIZE;
+ }
+ if (ch->sr & SR_BREAK) {
+ ch->sr &= ~SR_BREAK;
+ blk->isr |= ISR_BREAK(channel);
+ }
+ }
+ DPRINTF("Read RHR%c (0x%x)\n", channel + 'a', ret);
+ break;
+
+ case REG_ISR:
+ ret = blk->isr;
+ DPRINTF("Read ISR%c: 0x%x\n", block + 'A', ret);
+ break;
+
+ default:
+ DPRINTF("Read unknown/unsupported register 0x%02x\n", offset);
+ }
+
+ if (old_isr != blk->isr) {
+ update_irq(dev, block);
+ }
+
+ return ret;
+}
+
+static void io_write(IPackDevice *ip, uint8_t addr, uint16_t val)
+{
+ IPOctalState *dev = IPOCTAL(ip);
+ unsigned reg = val & 0xFF;
+ /* addr[7:6]: block (A-D)
+ addr[7:5]: channel (a-h)
+ addr[5:0]: register */
+ unsigned block = addr >> 5;
+ unsigned channel = addr >> 4;
+ /* Big endian, accessed using 8-bit bytes at odd locations */
+ unsigned offset = (addr & 0x1F) ^ 1;
+ SCC2698Channel *ch = &dev->ch[channel];
+ SCC2698Block *blk = &dev->blk[block];
+ uint8_t old_isr = blk->isr;
+ uint8_t old_imr = blk->imr;
+
+ switch (offset) {
+
+ case REG_MRa:
+ case REG_MRb:
+ ch->mr[ch->mr_idx] = reg;
+ DPRINTF("Write MR%u%c 0x%x\n", ch->mr_idx + 1, channel + 'a', reg);
+ ch->mr_idx = 1;
+ break;
+
+ /* Not implemented */
+ case REG_CSRa:
+ case REG_CSRb:
+ DPRINTF("Write CSR%c: 0x%x\n", channel + 'a', reg);
+ break;
+
+ case REG_CRa:
+ case REG_CRb:
+ write_cr(dev, channel, reg);
+ break;
+
+ case REG_THRa:
+ case REG_THRb:
+ if (ch->sr & SR_TXRDY) {
+ DPRINTF("Write THR%c (0x%x)\n", channel + 'a', reg);
+ if (ch->dev) {
+ uint8_t thr = reg;
+ qemu_chr_fe_write(ch->dev, &thr, 1);
+ }
+ } else {
+ DPRINTF("Write THR%c (0x%x), Tx disabled\n", channel + 'a', reg);
+ }
+ break;
+
+ /* Not implemented */
+ case REG_ACR:
+ DPRINTF("Write ACR%c 0x%x\n", block + 'A', val);
+ break;
+
+ case REG_IMR:
+ DPRINTF("Write IMR%c 0x%x\n", block + 'A', val);
+ blk->imr = reg;
+ break;
+
+ /* Not implemented */
+ case REG_OPCR:
+ DPRINTF("Write OPCR%c 0x%x\n", block + 'A', val);
+ break;
+
+ default:
+ DPRINTF("Write unknown/unsupported register 0x%02x %u\n", offset, val);
+ }
+
+ if (old_isr != blk->isr || old_imr != blk->imr) {
+ update_irq(dev, block);
+ }
+}
+
+static uint16_t id_read(IPackDevice *ip, uint8_t addr)
+{
+ uint16_t ret = 0;
+ unsigned pos = addr / 2; /* The ID PROM data is stored every other byte */
+
+ if (pos < ARRAY_SIZE(id_prom_data)) {
+ ret = id_prom_data[pos];
+ } else {
+ DPRINTF("Attempt to read unavailable PROM data at 0x%x\n", addr);
+ }
+
+ return ret;
+}
+
+static void id_write(IPackDevice *ip, uint8_t addr, uint16_t val)
+{
+ IPOctalState *dev = IPOCTAL(ip);
+ if (addr == 1) {
+ DPRINTF("Write IRQ vector: %u\n", (unsigned) val);
+ dev->irq_vector = val; /* Undocumented, but the hw works like that */
+ } else {
+ DPRINTF("Attempt to write 0x%x to 0x%x\n", val, addr);
+ }
+}
+
+static uint16_t int_read(IPackDevice *ip, uint8_t addr)
+{
+ IPOctalState *dev = IPOCTAL(ip);
+ /* Read address 0 to ACK INT0# and address 2 to ACK INT1# */
+ if (addr != 0 && addr != 2) {
+ DPRINTF("Attempt to read from 0x%x\n", addr);
+ return 0;
+ } else {
+ /* Update interrupts if necessary */
+ update_irq(dev, addr);
+ return dev->irq_vector;
+ }
+}
+
+static void int_write(IPackDevice *ip, uint8_t addr, uint16_t val)
+{
+ DPRINTF("Attempt to write 0x%x to 0x%x\n", val, addr);
+}
+
+static uint16_t mem_read16(IPackDevice *ip, uint32_t addr)
+{
+ DPRINTF("Attempt to read from 0x%x\n", addr);
+ return 0;
+}
+
+static void mem_write16(IPackDevice *ip, uint32_t addr, uint16_t val)
+{
+ DPRINTF("Attempt to write 0x%x to 0x%x\n", val, addr);
+}
+
+static uint8_t mem_read8(IPackDevice *ip, uint32_t addr)
+{
+ DPRINTF("Attempt to read from 0x%x\n", addr);
+ return 0;
+}
+
+static void mem_write8(IPackDevice *ip, uint32_t addr, uint8_t val)
+{
+ IPOctalState *dev = IPOCTAL(ip);
+ if (addr == 1) {
+ DPRINTF("Write IRQ vector: %u\n", (unsigned) val);
+ dev->irq_vector = val;
+ } else {
+ DPRINTF("Attempt to write 0x%x to 0x%x\n", val, addr);
+ }
+}
+
+static int hostdev_can_receive(void *opaque)
+{
+ SCC2698Channel *ch = opaque;
+ int available_bytes = RX_FIFO_SIZE - ch->rx_pending;
+ return ch->rx_enabled ? available_bytes : 0;
+}
+
+static void hostdev_receive(void *opaque, const uint8_t *buf, int size)
+{
+ SCC2698Channel *ch = opaque;
+ IPOctalState *dev = ch->ipoctal;
+ unsigned pos = ch->rhr_idx + ch->rx_pending;
+ int i;
+
+ assert(size + ch->rx_pending <= RX_FIFO_SIZE);
+
+ /* Copy data to the RxFIFO */
+ for (i = 0; i < size; i++) {
+ pos %= RX_FIFO_SIZE;
+ ch->rhr[pos++] = buf[i];
+ }
+
+ ch->rx_pending += size;
+
+ /* If the RxFIFO was empty raise an interrupt */
+ if (!(ch->sr & SR_RXRDY)) {
+ unsigned block, channel = 0;
+ /* Find channel number to update the ISR register */
+ while (&dev->ch[channel] != ch) {
+ channel++;
+ }
+ block = channel / 2;
+ dev->blk[block].isr |= ISR_RXRDY(channel);
+ ch->sr |= SR_RXRDY;
+ update_irq(dev, block);
+ }
+}
+
+static void hostdev_event(void *opaque, int event)
+{
+ SCC2698Channel *ch = opaque;
+ switch (event) {
+ case CHR_EVENT_OPENED:
+ DPRINTF("Device %s opened\n", ch->dev->label);
+ break;
+ case CHR_EVENT_BREAK: {
+ uint8_t zero = 0;
+ DPRINTF("Device %s received break\n", ch->dev->label);
+
+ if (!(ch->sr & SR_BREAK)) {
+ IPOctalState *dev = ch->ipoctal;
+ unsigned block, channel = 0;
+
+ while (&dev->ch[channel] != ch) {
+ channel++;
+ }
+ block = channel / 2;
+
+ ch->sr |= SR_BREAK;
+ dev->blk[block].isr |= ISR_BREAK(channel);
+ }
+
+ /* Put a zero character in the buffer */
+ hostdev_receive(ch, &zero, 1);
+ }
+ break;
+ default:
+ DPRINTF("Device %s received event %d\n", ch->dev->label, event);
+ }
+}
+
+static int ipoctal_init(IPackDevice *ip)
+{
+ IPOctalState *s = IPOCTAL(ip);
+ unsigned i;
+
+ for (i = 0; i < N_CHANNELS; i++) {
+ SCC2698Channel *ch = &s->ch[i];
+ ch->ipoctal = s;
+
+ /* Redirect IP-Octal channels to host character devices */
+ if (ch->dev) {
+ qemu_chr_add_handlers(ch->dev, hostdev_can_receive,
+ hostdev_receive, hostdev_event, ch);
+ DPRINTF("Redirecting channel %u to %s\n", i, ch->dev->label);
+ } else {
+ DPRINTF("Could not redirect channel %u, no chardev set\n", i);
+ }
+ }
+
+ return 0;
+}
+
+static Property ipoctal_properties[] = {
+ DEFINE_PROP_CHR("chardev0", IPOctalState, ch[0].dev),
+ DEFINE_PROP_CHR("chardev1", IPOctalState, ch[1].dev),
+ DEFINE_PROP_CHR("chardev2", IPOctalState, ch[2].dev),
+ DEFINE_PROP_CHR("chardev3", IPOctalState, ch[3].dev),
+ DEFINE_PROP_CHR("chardev4", IPOctalState, ch[4].dev),
+ DEFINE_PROP_CHR("chardev5", IPOctalState, ch[5].dev),
+ DEFINE_PROP_CHR("chardev6", IPOctalState, ch[6].dev),
+ DEFINE_PROP_CHR("chardev7", IPOctalState, ch[7].dev),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void ipoctal_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ IPackDeviceClass *ic = IPACK_DEVICE_CLASS(klass);
+
+ ic->init = ipoctal_init;
+ ic->io_read = io_read;
+ ic->io_write = io_write;
+ ic->id_read = id_read;
+ ic->id_write = id_write;
+ ic->int_read = int_read;
+ ic->int_write = int_write;
+ ic->mem_read16 = mem_read16;
+ ic->mem_write16 = mem_write16;
+ ic->mem_read8 = mem_read8;
+ ic->mem_write8 = mem_write8;
+
+ dc->desc = "GE IP-Octal 232 8-channel RS-232 IndustryPack";
+ dc->props = ipoctal_properties;
+ dc->vmsd = &vmstate_ipoctal;
+}
+
+static const TypeInfo ipoctal_info = {
+ .name = TYPE_IPOCTAL,
+ .parent = TYPE_IPACK_DEVICE,
+ .instance_size = sizeof(IPOctalState),
+ .class_init = ipoctal_class_init,
+};
+
+static void ipoctal_register_types(void)
+{
+ type_register_static(&ipoctal_info);
+}
+
+type_init(ipoctal_register_types)
diff --git a/hw/char/parallel.c b/hw/char/parallel.c
new file mode 100644
index 0000000000..863a6fb4a9
--- /dev/null
+++ b/hw/char/parallel.c
@@ -0,0 +1,614 @@
+/*
+ * QEMU Parallel PORT emulation
+ *
+ * Copyright (c) 2003-2005 Fabrice Bellard
+ * Copyright (c) 2007 Marko Kohtala
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw/hw.h"
+#include "char/char.h"
+#include "hw/isa/isa.h"
+#include "hw/i386/pc.h"
+#include "sysemu/sysemu.h"
+
+//#define DEBUG_PARALLEL
+
+#ifdef DEBUG_PARALLEL
+#define pdebug(fmt, ...) printf("pp: " fmt, ## __VA_ARGS__)
+#else
+#define pdebug(fmt, ...) ((void)0)
+#endif
+
+#define PARA_REG_DATA 0
+#define PARA_REG_STS 1
+#define PARA_REG_CTR 2
+#define PARA_REG_EPP_ADDR 3
+#define PARA_REG_EPP_DATA 4
+
+/*
+ * These are the definitions for the Printer Status Register
+ */
+#define PARA_STS_BUSY 0x80 /* Busy complement */
+#define PARA_STS_ACK 0x40 /* Acknowledge */
+#define PARA_STS_PAPER 0x20 /* Out of paper */
+#define PARA_STS_ONLINE 0x10 /* Online */
+#define PARA_STS_ERROR 0x08 /* Error complement */
+#define PARA_STS_TMOUT 0x01 /* EPP timeout */
+
+/*
+ * These are the definitions for the Printer Control Register
+ */
+#define PARA_CTR_DIR 0x20 /* Direction (1=read, 0=write) */
+#define PARA_CTR_INTEN 0x10 /* IRQ Enable */
+#define PARA_CTR_SELECT 0x08 /* Select In complement */
+#define PARA_CTR_INIT 0x04 /* Initialize Printer complement */
+#define PARA_CTR_AUTOLF 0x02 /* Auto linefeed complement */
+#define PARA_CTR_STROBE 0x01 /* Strobe complement */
+
+#define PARA_CTR_SIGNAL (PARA_CTR_SELECT|PARA_CTR_INIT|PARA_CTR_AUTOLF|PARA_CTR_STROBE)
+
+typedef struct ParallelState {
+ MemoryRegion iomem;
+ uint8_t dataw;
+ uint8_t datar;
+ uint8_t status;
+ uint8_t control;
+ qemu_irq irq;
+ int irq_pending;
+ CharDriverState *chr;
+ int hw_driver;
+ int epp_timeout;
+ uint32_t last_read_offset; /* For debugging */
+ /* Memory-mapped interface */
+ int it_shift;
+} ParallelState;
+
+typedef struct ISAParallelState {
+ ISADevice dev;
+ uint32_t index;
+ uint32_t iobase;
+ uint32_t isairq;
+ ParallelState state;
+} ISAParallelState;
+
+static void parallel_update_irq(ParallelState *s)
+{
+ if (s->irq_pending)
+ qemu_irq_raise(s->irq);
+ else
+ qemu_irq_lower(s->irq);
+}
+
+static void
+parallel_ioport_write_sw(void *opaque, uint32_t addr, uint32_t val)
+{
+ ParallelState *s = opaque;
+
+ pdebug("write addr=0x%02x val=0x%02x\n", addr, val);
+
+ addr &= 7;
+ switch(addr) {
+ case PARA_REG_DATA:
+ s->dataw = val;
+ parallel_update_irq(s);
+ break;
+ case PARA_REG_CTR:
+ val |= 0xc0;
+ if ((val & PARA_CTR_INIT) == 0 ) {
+ s->status = PARA_STS_BUSY;
+ s->status |= PARA_STS_ACK;
+ s->status |= PARA_STS_ONLINE;
+ s->status |= PARA_STS_ERROR;
+ }
+ else if (val & PARA_CTR_SELECT) {
+ if (val & PARA_CTR_STROBE) {
+ s->status &= ~PARA_STS_BUSY;
+ if ((s->control & PARA_CTR_STROBE) == 0)
+ qemu_chr_fe_write(s->chr, &s->dataw, 1);
+ } else {
+ if (s->control & PARA_CTR_INTEN) {
+ s->irq_pending = 1;
+ }
+ }
+ }
+ parallel_update_irq(s);
+ s->control = val;
+ break;
+ }
+}
+
+static void parallel_ioport_write_hw(void *opaque, uint32_t addr, uint32_t val)
+{
+ ParallelState *s = opaque;
+ uint8_t parm = val;
+ int dir;
+
+ /* Sometimes programs do several writes for timing purposes on old
+ HW. Take care not to waste time on writes that do nothing. */
+
+ s->last_read_offset = ~0U;
+
+ addr &= 7;
+ switch(addr) {
+ case PARA_REG_DATA:
+ if (s->dataw == val)
+ return;
+ pdebug("wd%02x\n", val);
+ qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_PP_WRITE_DATA, &parm);
+ s->dataw = val;
+ break;
+ case PARA_REG_STS:
+ pdebug("ws%02x\n", val);
+ if (val & PARA_STS_TMOUT)
+ s->epp_timeout = 0;
+ break;
+ case PARA_REG_CTR:
+ val |= 0xc0;
+ if (s->control == val)
+ return;
+ pdebug("wc%02x\n", val);
+
+ if ((val & PARA_CTR_DIR) != (s->control & PARA_CTR_DIR)) {
+ if (val & PARA_CTR_DIR) {
+ dir = 1;
+ } else {
+ dir = 0;
+ }
+ qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_PP_DATA_DIR, &dir);
+ parm &= ~PARA_CTR_DIR;
+ }
+
+ qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_PP_WRITE_CONTROL, &parm);
+ s->control = val;
+ break;
+ case PARA_REG_EPP_ADDR:
+ if ((s->control & (PARA_CTR_DIR|PARA_CTR_SIGNAL)) != PARA_CTR_INIT)
+ /* Controls not correct for EPP address cycle, so do nothing */
+ pdebug("wa%02x s\n", val);
+ else {
+ struct ParallelIOArg ioarg = { .buffer = &parm, .count = 1 };
+ if (qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_PP_EPP_WRITE_ADDR, &ioarg)) {
+ s->epp_timeout = 1;
+ pdebug("wa%02x t\n", val);
+ }
+ else
+ pdebug("wa%02x\n", val);
+ }
+ break;
+ case PARA_REG_EPP_DATA:
+ if ((s->control & (PARA_CTR_DIR|PARA_CTR_SIGNAL)) != PARA_CTR_INIT)
+ /* Controls not correct for EPP data cycle, so do nothing */
+ pdebug("we%02x s\n", val);
+ else {
+ struct ParallelIOArg ioarg = { .buffer = &parm, .count = 1 };
+ if (qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_PP_EPP_WRITE, &ioarg)) {
+ s->epp_timeout = 1;
+ pdebug("we%02x t\n", val);
+ }
+ else
+ pdebug("we%02x\n", val);
+ }
+ break;
+ }
+}
+
+static void
+parallel_ioport_eppdata_write_hw2(void *opaque, uint32_t addr, uint32_t val)
+{
+ ParallelState *s = opaque;
+ uint16_t eppdata = cpu_to_le16(val);
+ int err;
+ struct ParallelIOArg ioarg = {
+ .buffer = &eppdata, .count = sizeof(eppdata)
+ };
+ if ((s->control & (PARA_CTR_DIR|PARA_CTR_SIGNAL)) != PARA_CTR_INIT) {
+ /* Controls not correct for EPP data cycle, so do nothing */
+ pdebug("we%04x s\n", val);
+ return;
+ }
+ err = qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_PP_EPP_WRITE, &ioarg);
+ if (err) {
+ s->epp_timeout = 1;
+ pdebug("we%04x t\n", val);
+ }
+ else
+ pdebug("we%04x\n", val);
+}
+
+static void
+parallel_ioport_eppdata_write_hw4(void *opaque, uint32_t addr, uint32_t val)
+{
+ ParallelState *s = opaque;
+ uint32_t eppdata = cpu_to_le32(val);
+ int err;
+ struct ParallelIOArg ioarg = {
+ .buffer = &eppdata, .count = sizeof(eppdata)
+ };
+ if ((s->control & (PARA_CTR_DIR|PARA_CTR_SIGNAL)) != PARA_CTR_INIT) {
+ /* Controls not correct for EPP data cycle, so do nothing */
+ pdebug("we%08x s\n", val);
+ return;
+ }
+ err = qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_PP_EPP_WRITE, &ioarg);
+ if (err) {
+ s->epp_timeout = 1;
+ pdebug("we%08x t\n", val);
+ }
+ else
+ pdebug("we%08x\n", val);
+}
+
+static uint32_t parallel_ioport_read_sw(void *opaque, uint32_t addr)
+{
+ ParallelState *s = opaque;
+ uint32_t ret = 0xff;
+
+ addr &= 7;
+ switch(addr) {
+ case PARA_REG_DATA:
+ if (s->control & PARA_CTR_DIR)
+ ret = s->datar;
+ else
+ ret = s->dataw;
+ break;
+ case PARA_REG_STS:
+ ret = s->status;
+ s->irq_pending = 0;
+ if ((s->status & PARA_STS_BUSY) == 0 && (s->control & PARA_CTR_STROBE) == 0) {
+ /* XXX Fixme: wait 5 microseconds */
+ if (s->status & PARA_STS_ACK)
+ s->status &= ~PARA_STS_ACK;
+ else {
+ /* XXX Fixme: wait 5 microseconds */
+ s->status |= PARA_STS_ACK;
+ s->status |= PARA_STS_BUSY;
+ }
+ }
+ parallel_update_irq(s);
+ break;
+ case PARA_REG_CTR:
+ ret = s->control;
+ break;
+ }
+ pdebug("read addr=0x%02x val=0x%02x\n", addr, ret);
+ return ret;
+}
+
+static uint32_t parallel_ioport_read_hw(void *opaque, uint32_t addr)
+{
+ ParallelState *s = opaque;
+ uint8_t ret = 0xff;
+ addr &= 7;
+ switch(addr) {
+ case PARA_REG_DATA:
+ qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_PP_READ_DATA, &ret);
+ if (s->last_read_offset != addr || s->datar != ret)
+ pdebug("rd%02x\n", ret);
+ s->datar = ret;
+ break;
+ case PARA_REG_STS:
+ qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_PP_READ_STATUS, &ret);
+ ret &= ~PARA_STS_TMOUT;
+ if (s->epp_timeout)
+ ret |= PARA_STS_TMOUT;
+ if (s->last_read_offset != addr || s->status != ret)
+ pdebug("rs%02x\n", ret);
+ s->status = ret;
+ break;
+ case PARA_REG_CTR:
+ /* s->control has some bits fixed to 1. It is zero only when
+ it has not been yet written to. */
+ if (s->control == 0) {
+ qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_PP_READ_CONTROL, &ret);
+ if (s->last_read_offset != addr)
+ pdebug("rc%02x\n", ret);
+ s->control = ret;
+ }
+ else {
+ ret = s->control;
+ if (s->last_read_offset != addr)
+ pdebug("rc%02x\n", ret);
+ }
+ break;
+ case PARA_REG_EPP_ADDR:
+ if ((s->control & (PARA_CTR_DIR|PARA_CTR_SIGNAL)) != (PARA_CTR_DIR|PARA_CTR_INIT))
+ /* Controls not correct for EPP addr cycle, so do nothing */
+ pdebug("ra%02x s\n", ret);
+ else {
+ struct ParallelIOArg ioarg = { .buffer = &ret, .count = 1 };
+ if (qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_PP_EPP_READ_ADDR, &ioarg)) {
+ s->epp_timeout = 1;
+ pdebug("ra%02x t\n", ret);
+ }
+ else
+ pdebug("ra%02x\n", ret);
+ }
+ break;
+ case PARA_REG_EPP_DATA:
+ if ((s->control & (PARA_CTR_DIR|PARA_CTR_SIGNAL)) != (PARA_CTR_DIR|PARA_CTR_INIT))
+ /* Controls not correct for EPP data cycle, so do nothing */
+ pdebug("re%02x s\n", ret);
+ else {
+ struct ParallelIOArg ioarg = { .buffer = &ret, .count = 1 };
+ if (qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_PP_EPP_READ, &ioarg)) {
+ s->epp_timeout = 1;
+ pdebug("re%02x t\n", ret);
+ }
+ else
+ pdebug("re%02x\n", ret);
+ }
+ break;
+ }
+ s->last_read_offset = addr;
+ return ret;
+}
+
+static uint32_t
+parallel_ioport_eppdata_read_hw2(void *opaque, uint32_t addr)
+{
+ ParallelState *s = opaque;
+ uint32_t ret;
+ uint16_t eppdata = ~0;
+ int err;
+ struct ParallelIOArg ioarg = {
+ .buffer = &eppdata, .count = sizeof(eppdata)
+ };
+ if ((s->control & (PARA_CTR_DIR|PARA_CTR_SIGNAL)) != (PARA_CTR_DIR|PARA_CTR_INIT)) {
+ /* Controls not correct for EPP data cycle, so do nothing */
+ pdebug("re%04x s\n", eppdata);
+ return eppdata;
+ }
+ err = qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_PP_EPP_READ, &ioarg);
+ ret = le16_to_cpu(eppdata);
+
+ if (err) {
+ s->epp_timeout = 1;
+ pdebug("re%04x t\n", ret);
+ }
+ else
+ pdebug("re%04x\n", ret);
+ return ret;
+}
+
+static uint32_t
+parallel_ioport_eppdata_read_hw4(void *opaque, uint32_t addr)
+{
+ ParallelState *s = opaque;
+ uint32_t ret;
+ uint32_t eppdata = ~0U;
+ int err;
+ struct ParallelIOArg ioarg = {
+ .buffer = &eppdata, .count = sizeof(eppdata)
+ };
+ if ((s->control & (PARA_CTR_DIR|PARA_CTR_SIGNAL)) != (PARA_CTR_DIR|PARA_CTR_INIT)) {
+ /* Controls not correct for EPP data cycle, so do nothing */
+ pdebug("re%08x s\n", eppdata);
+ return eppdata;
+ }
+ err = qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_PP_EPP_READ, &ioarg);
+ ret = le32_to_cpu(eppdata);
+
+ if (err) {
+ s->epp_timeout = 1;
+ pdebug("re%08x t\n", ret);
+ }
+ else
+ pdebug("re%08x\n", ret);
+ return ret;
+}
+
+static void parallel_ioport_ecp_write(void *opaque, uint32_t addr, uint32_t val)
+{
+ pdebug("wecp%d=%02x\n", addr & 7, val);
+}
+
+static uint32_t parallel_ioport_ecp_read(void *opaque, uint32_t addr)
+{
+ uint8_t ret = 0xff;
+
+ pdebug("recp%d:%02x\n", addr & 7, ret);
+ return ret;
+}
+
+static void parallel_reset(void *opaque)
+{
+ ParallelState *s = opaque;
+
+ s->datar = ~0;
+ s->dataw = ~0;
+ s->status = PARA_STS_BUSY;
+ s->status |= PARA_STS_ACK;
+ s->status |= PARA_STS_ONLINE;
+ s->status |= PARA_STS_ERROR;
+ s->status |= PARA_STS_TMOUT;
+ s->control = PARA_CTR_SELECT;
+ s->control |= PARA_CTR_INIT;
+ s->control |= 0xc0;
+ s->irq_pending = 0;
+ s->hw_driver = 0;
+ s->epp_timeout = 0;
+ s->last_read_offset = ~0U;
+}
+
+static const int isa_parallel_io[MAX_PARALLEL_PORTS] = { 0x378, 0x278, 0x3bc };
+
+static const MemoryRegionPortio isa_parallel_portio_hw_list[] = {
+ { 0, 8, 1,
+ .read = parallel_ioport_read_hw,
+ .write = parallel_ioport_write_hw },
+ { 4, 1, 2,
+ .read = parallel_ioport_eppdata_read_hw2,
+ .write = parallel_ioport_eppdata_write_hw2 },
+ { 4, 1, 4,
+ .read = parallel_ioport_eppdata_read_hw4,
+ .write = parallel_ioport_eppdata_write_hw4 },
+ { 0x400, 8, 1,
+ .read = parallel_ioport_ecp_read,
+ .write = parallel_ioport_ecp_write },
+ PORTIO_END_OF_LIST(),
+};
+
+static const MemoryRegionPortio isa_parallel_portio_sw_list[] = {
+ { 0, 8, 1,
+ .read = parallel_ioport_read_sw,
+ .write = parallel_ioport_write_sw },
+ PORTIO_END_OF_LIST(),
+};
+
+static int parallel_isa_initfn(ISADevice *dev)
+{
+ static int index;
+ ISAParallelState *isa = DO_UPCAST(ISAParallelState, dev, dev);
+ ParallelState *s = &isa->state;
+ int base;
+ uint8_t dummy;
+
+ if (!s->chr) {
+ fprintf(stderr, "Can't create parallel device, empty char device\n");
+ exit(1);
+ }
+
+ if (isa->index == -1)
+ isa->index = index;
+ if (isa->index >= MAX_PARALLEL_PORTS)
+ return -1;
+ if (isa->iobase == -1)
+ isa->iobase = isa_parallel_io[isa->index];
+ index++;
+
+ base = isa->iobase;
+ isa_init_irq(dev, &s->irq, isa->isairq);
+ qemu_register_reset(parallel_reset, s);
+
+ if (qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_PP_READ_STATUS, &dummy) == 0) {
+ s->hw_driver = 1;
+ s->status = dummy;
+ }
+
+ isa_register_portio_list(dev, base,
+ (s->hw_driver
+ ? &isa_parallel_portio_hw_list[0]
+ : &isa_parallel_portio_sw_list[0]),
+ s, "parallel");
+ return 0;
+}
+
+/* Memory mapped interface */
+static uint32_t parallel_mm_readb (void *opaque, hwaddr addr)
+{
+ ParallelState *s = opaque;
+
+ return parallel_ioport_read_sw(s, addr >> s->it_shift) & 0xFF;
+}
+
+static void parallel_mm_writeb (void *opaque,
+ hwaddr addr, uint32_t value)
+{
+ ParallelState *s = opaque;
+
+ parallel_ioport_write_sw(s, addr >> s->it_shift, value & 0xFF);
+}
+
+static uint32_t parallel_mm_readw (void *opaque, hwaddr addr)
+{
+ ParallelState *s = opaque;
+
+ return parallel_ioport_read_sw(s, addr >> s->it_shift) & 0xFFFF;
+}
+
+static void parallel_mm_writew (void *opaque,
+ hwaddr addr, uint32_t value)
+{
+ ParallelState *s = opaque;
+
+ parallel_ioport_write_sw(s, addr >> s->it_shift, value & 0xFFFF);
+}
+
+static uint32_t parallel_mm_readl (void *opaque, hwaddr addr)
+{
+ ParallelState *s = opaque;
+
+ return parallel_ioport_read_sw(s, addr >> s->it_shift);
+}
+
+static void parallel_mm_writel (void *opaque,
+ hwaddr addr, uint32_t value)
+{
+ ParallelState *s = opaque;
+
+ parallel_ioport_write_sw(s, addr >> s->it_shift, value);
+}
+
+static const MemoryRegionOps parallel_mm_ops = {
+ .old_mmio = {
+ .read = { parallel_mm_readb, parallel_mm_readw, parallel_mm_readl },
+ .write = { parallel_mm_writeb, parallel_mm_writew, parallel_mm_writel },
+ },
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+/* If fd is zero, it means that the parallel device uses the console */
+bool parallel_mm_init(MemoryRegion *address_space,
+ hwaddr base, int it_shift, qemu_irq irq,
+ CharDriverState *chr)
+{
+ ParallelState *s;
+
+ s = g_malloc0(sizeof(ParallelState));
+ s->irq = irq;
+ s->chr = chr;
+ s->it_shift = it_shift;
+ qemu_register_reset(parallel_reset, s);
+
+ memory_region_init_io(&s->iomem, &parallel_mm_ops, s,
+ "parallel", 8 << it_shift);
+ memory_region_add_subregion(address_space, base, &s->iomem);
+ return true;
+}
+
+static Property parallel_isa_properties[] = {
+ DEFINE_PROP_UINT32("index", ISAParallelState, index, -1),
+ DEFINE_PROP_HEX32("iobase", ISAParallelState, iobase, -1),
+ DEFINE_PROP_UINT32("irq", ISAParallelState, isairq, 7),
+ DEFINE_PROP_CHR("chardev", ISAParallelState, state.chr),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void parallel_isa_class_initfn(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ ISADeviceClass *ic = ISA_DEVICE_CLASS(klass);
+ ic->init = parallel_isa_initfn;
+ dc->props = parallel_isa_properties;
+}
+
+static const TypeInfo parallel_isa_info = {
+ .name = "isa-parallel",
+ .parent = TYPE_ISA_DEVICE,
+ .instance_size = sizeof(ISAParallelState),
+ .class_init = parallel_isa_class_initfn,
+};
+
+static void parallel_register_types(void)
+{
+ type_register_static(&parallel_isa_info);
+}
+
+type_init(parallel_register_types)
diff --git a/hw/char/pl011.c b/hw/char/pl011.c
new file mode 100644
index 0000000000..332d5b970c
--- /dev/null
+++ b/hw/char/pl011.c
@@ -0,0 +1,330 @@
+/*
+ * Arm PrimeCell PL011 UART
+ *
+ * Copyright (c) 2006 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licensed under the GPL.
+ */
+
+#include "hw/sysbus.h"
+#include "char/char.h"
+
+typedef struct {
+ SysBusDevice busdev;
+ MemoryRegion iomem;
+ uint32_t readbuff;
+ uint32_t flags;
+ uint32_t lcr;
+ uint32_t cr;
+ uint32_t dmacr;
+ uint32_t int_enabled;
+ uint32_t int_level;
+ uint32_t read_fifo[16];
+ uint32_t ilpr;
+ uint32_t ibrd;
+ uint32_t fbrd;
+ uint32_t ifl;
+ int read_pos;
+ int read_count;
+ int read_trigger;
+ CharDriverState *chr;
+ qemu_irq irq;
+ const unsigned char *id;
+} pl011_state;
+
+#define PL011_INT_TX 0x20
+#define PL011_INT_RX 0x10
+
+#define PL011_FLAG_TXFE 0x80
+#define PL011_FLAG_RXFF 0x40
+#define PL011_FLAG_TXFF 0x20
+#define PL011_FLAG_RXFE 0x10
+
+static const unsigned char pl011_id_arm[8] =
+ { 0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
+static const unsigned char pl011_id_luminary[8] =
+ { 0x11, 0x00, 0x18, 0x01, 0x0d, 0xf0, 0x05, 0xb1 };
+
+static void pl011_update(pl011_state *s)
+{
+ uint32_t flags;
+
+ flags = s->int_level & s->int_enabled;
+ qemu_set_irq(s->irq, flags != 0);
+}
+
+static uint64_t pl011_read(void *opaque, hwaddr offset,
+ unsigned size)
+{
+ pl011_state *s = (pl011_state *)opaque;
+ uint32_t c;
+
+ if (offset >= 0xfe0 && offset < 0x1000) {
+ return s->id[(offset - 0xfe0) >> 2];
+ }
+ switch (offset >> 2) {
+ case 0: /* UARTDR */
+ s->flags &= ~PL011_FLAG_RXFF;
+ c = s->read_fifo[s->read_pos];
+ if (s->read_count > 0) {
+ s->read_count--;
+ if (++s->read_pos == 16)
+ s->read_pos = 0;
+ }
+ if (s->read_count == 0) {
+ s->flags |= PL011_FLAG_RXFE;
+ }
+ if (s->read_count == s->read_trigger - 1)
+ s->int_level &= ~ PL011_INT_RX;
+ pl011_update(s);
+ if (s->chr) {
+ qemu_chr_accept_input(s->chr);
+ }
+ return c;
+ case 1: /* UARTCR */
+ return 0;
+ case 6: /* UARTFR */
+ return s->flags;
+ case 8: /* UARTILPR */
+ return s->ilpr;
+ case 9: /* UARTIBRD */
+ return s->ibrd;
+ case 10: /* UARTFBRD */
+ return s->fbrd;
+ case 11: /* UARTLCR_H */
+ return s->lcr;
+ case 12: /* UARTCR */
+ return s->cr;
+ case 13: /* UARTIFLS */
+ return s->ifl;
+ case 14: /* UARTIMSC */
+ return s->int_enabled;
+ case 15: /* UARTRIS */
+ return s->int_level;
+ case 16: /* UARTMIS */
+ return s->int_level & s->int_enabled;
+ case 18: /* UARTDMACR */
+ return s->dmacr;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "pl011_read: Bad offset %x\n", (int)offset);
+ return 0;
+ }
+}
+
+static void pl011_set_read_trigger(pl011_state *s)
+{
+#if 0
+ /* The docs say the RX interrupt is triggered when the FIFO exceeds
+ the threshold. However linux only reads the FIFO in response to an
+ interrupt. Triggering the interrupt when the FIFO is non-empty seems
+ to make things work. */
+ if (s->lcr & 0x10)
+ s->read_trigger = (s->ifl >> 1) & 0x1c;
+ else
+#endif
+ s->read_trigger = 1;
+}
+
+static void pl011_write(void *opaque, hwaddr offset,
+ uint64_t value, unsigned size)
+{
+ pl011_state *s = (pl011_state *)opaque;
+ unsigned char ch;
+
+ switch (offset >> 2) {
+ case 0: /* UARTDR */
+ /* ??? Check if transmitter is enabled. */
+ ch = value;
+ if (s->chr)
+ qemu_chr_fe_write(s->chr, &ch, 1);
+ s->int_level |= PL011_INT_TX;
+ pl011_update(s);
+ break;
+ case 1: /* UARTCR */
+ s->cr = value;
+ break;
+ case 6: /* UARTFR */
+ /* Writes to Flag register are ignored. */
+ break;
+ case 8: /* UARTUARTILPR */
+ s->ilpr = value;
+ break;
+ case 9: /* UARTIBRD */
+ s->ibrd = value;
+ break;
+ case 10: /* UARTFBRD */
+ s->fbrd = value;
+ break;
+ case 11: /* UARTLCR_H */
+ s->lcr = value;
+ pl011_set_read_trigger(s);
+ break;
+ case 12: /* UARTCR */
+ /* ??? Need to implement the enable and loopback bits. */
+ s->cr = value;
+ break;
+ case 13: /* UARTIFS */
+ s->ifl = value;
+ pl011_set_read_trigger(s);
+ break;
+ case 14: /* UARTIMSC */
+ s->int_enabled = value;
+ pl011_update(s);
+ break;
+ case 17: /* UARTICR */
+ s->int_level &= ~value;
+ pl011_update(s);
+ break;
+ case 18: /* UARTDMACR */
+ s->dmacr = value;
+ if (value & 3) {
+ qemu_log_mask(LOG_UNIMP, "pl011: DMA not implemented\n");
+ }
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "pl011_write: Bad offset %x\n", (int)offset);
+ }
+}
+
+static int pl011_can_receive(void *opaque)
+{
+ pl011_state *s = (pl011_state *)opaque;
+
+ if (s->lcr & 0x10)
+ return s->read_count < 16;
+ else
+ return s->read_count < 1;
+}
+
+static void pl011_put_fifo(void *opaque, uint32_t value)
+{
+ pl011_state *s = (pl011_state *)opaque;
+ int slot;
+
+ slot = s->read_pos + s->read_count;
+ if (slot >= 16)
+ slot -= 16;
+ s->read_fifo[slot] = value;
+ s->read_count++;
+ s->flags &= ~PL011_FLAG_RXFE;
+ if (s->cr & 0x10 || s->read_count == 16) {
+ s->flags |= PL011_FLAG_RXFF;
+ }
+ if (s->read_count == s->read_trigger) {
+ s->int_level |= PL011_INT_RX;
+ pl011_update(s);
+ }
+}
+
+static void pl011_receive(void *opaque, const uint8_t *buf, int size)
+{
+ pl011_put_fifo(opaque, *buf);
+}
+
+static void pl011_event(void *opaque, int event)
+{
+ if (event == CHR_EVENT_BREAK)
+ pl011_put_fifo(opaque, 0x400);
+}
+
+static const MemoryRegionOps pl011_ops = {
+ .read = pl011_read,
+ .write = pl011_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static const VMStateDescription vmstate_pl011 = {
+ .name = "pl011",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(readbuff, pl011_state),
+ VMSTATE_UINT32(flags, pl011_state),
+ VMSTATE_UINT32(lcr, pl011_state),
+ VMSTATE_UINT32(cr, pl011_state),
+ VMSTATE_UINT32(dmacr, pl011_state),
+ VMSTATE_UINT32(int_enabled, pl011_state),
+ VMSTATE_UINT32(int_level, pl011_state),
+ VMSTATE_UINT32_ARRAY(read_fifo, pl011_state, 16),
+ VMSTATE_UINT32(ilpr, pl011_state),
+ VMSTATE_UINT32(ibrd, pl011_state),
+ VMSTATE_UINT32(fbrd, pl011_state),
+ VMSTATE_UINT32(ifl, pl011_state),
+ VMSTATE_INT32(read_pos, pl011_state),
+ VMSTATE_INT32(read_count, pl011_state),
+ VMSTATE_INT32(read_trigger, pl011_state),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static int pl011_init(SysBusDevice *dev, const unsigned char *id)
+{
+ pl011_state *s = FROM_SYSBUS(pl011_state, dev);
+
+ memory_region_init_io(&s->iomem, &pl011_ops, s, "pl011", 0x1000);
+ sysbus_init_mmio(dev, &s->iomem);
+ sysbus_init_irq(dev, &s->irq);
+ s->id = id;
+ s->chr = qemu_char_get_next_serial();
+
+ s->read_trigger = 1;
+ s->ifl = 0x12;
+ s->cr = 0x300;
+ s->flags = 0x90;
+ if (s->chr) {
+ qemu_chr_add_handlers(s->chr, pl011_can_receive, pl011_receive,
+ pl011_event, s);
+ }
+ vmstate_register(&dev->qdev, -1, &vmstate_pl011, s);
+ return 0;
+}
+
+static int pl011_arm_init(SysBusDevice *dev)
+{
+ return pl011_init(dev, pl011_id_arm);
+}
+
+static int pl011_luminary_init(SysBusDevice *dev)
+{
+ return pl011_init(dev, pl011_id_luminary);
+}
+
+static void pl011_arm_class_init(ObjectClass *klass, void *data)
+{
+ SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
+
+ sdc->init = pl011_arm_init;
+}
+
+static const TypeInfo pl011_arm_info = {
+ .name = "pl011",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(pl011_state),
+ .class_init = pl011_arm_class_init,
+};
+
+static void pl011_luminary_class_init(ObjectClass *klass, void *data)
+{
+ SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
+
+ sdc->init = pl011_luminary_init;
+}
+
+static const TypeInfo pl011_luminary_info = {
+ .name = "pl011_luminary",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(pl011_state),
+ .class_init = pl011_luminary_class_init,
+};
+
+static void pl011_register_types(void)
+{
+ type_register_static(&pl011_arm_info);
+ type_register_static(&pl011_luminary_info);
+}
+
+type_init(pl011_register_types)
diff --git a/hw/char/serial-isa.c b/hw/char/serial-isa.c
new file mode 100644
index 0000000000..ed140d04a6
--- /dev/null
+++ b/hw/char/serial-isa.c
@@ -0,0 +1,130 @@
+/*
+ * QEMU 16550A UART emulation
+ *
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ * Copyright (c) 2008 Citrix Systems, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "hw/char/serial.h"
+#include "hw/isa/isa.h"
+
+typedef struct ISASerialState {
+ ISADevice dev;
+ uint32_t index;
+ uint32_t iobase;
+ uint32_t isairq;
+ SerialState state;
+} ISASerialState;
+
+static const int isa_serial_io[MAX_SERIAL_PORTS] = {
+ 0x3f8, 0x2f8, 0x3e8, 0x2e8
+};
+static const int isa_serial_irq[MAX_SERIAL_PORTS] = {
+ 4, 3, 4, 3
+};
+
+static int serial_isa_initfn(ISADevice *dev)
+{
+ static int index;
+ ISASerialState *isa = DO_UPCAST(ISASerialState, dev, dev);
+ SerialState *s = &isa->state;
+
+ if (isa->index == -1) {
+ isa->index = index;
+ }
+ if (isa->index >= MAX_SERIAL_PORTS) {
+ return -1;
+ }
+ if (isa->iobase == -1) {
+ isa->iobase = isa_serial_io[isa->index];
+ }
+ if (isa->isairq == -1) {
+ isa->isairq = isa_serial_irq[isa->index];
+ }
+ index++;
+
+ s->baudbase = 115200;
+ isa_init_irq(dev, &s->irq, isa->isairq);
+ serial_init_core(s);
+ qdev_set_legacy_instance_id(&dev->qdev, isa->iobase, 3);
+
+ memory_region_init_io(&s->io, &serial_io_ops, s, "serial", 8);
+ isa_register_ioport(dev, &s->io, isa->iobase);
+ return 0;
+}
+
+static const VMStateDescription vmstate_isa_serial = {
+ .name = "serial",
+ .version_id = 3,
+ .minimum_version_id = 2,
+ .fields = (VMStateField[]) {
+ VMSTATE_STRUCT(state, ISASerialState, 0, vmstate_serial, SerialState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static Property serial_isa_properties[] = {
+ DEFINE_PROP_UINT32("index", ISASerialState, index, -1),
+ DEFINE_PROP_HEX32("iobase", ISASerialState, iobase, -1),
+ DEFINE_PROP_UINT32("irq", ISASerialState, isairq, -1),
+ DEFINE_PROP_CHR("chardev", ISASerialState, state.chr),
+ DEFINE_PROP_UINT32("wakeup", ISASerialState, state.wakeup, 0),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void serial_isa_class_initfn(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ ISADeviceClass *ic = ISA_DEVICE_CLASS(klass);
+ ic->init = serial_isa_initfn;
+ dc->vmsd = &vmstate_isa_serial;
+ dc->props = serial_isa_properties;
+}
+
+static const TypeInfo serial_isa_info = {
+ .name = "isa-serial",
+ .parent = TYPE_ISA_DEVICE,
+ .instance_size = sizeof(ISASerialState),
+ .class_init = serial_isa_class_initfn,
+};
+
+static void serial_register_types(void)
+{
+ type_register_static(&serial_isa_info);
+}
+
+type_init(serial_register_types)
+
+bool serial_isa_init(ISABus *bus, int index, CharDriverState *chr)
+{
+ ISADevice *dev;
+
+ dev = isa_try_create(bus, "isa-serial");
+ if (!dev) {
+ return false;
+ }
+ qdev_prop_set_uint32(&dev->qdev, "index", index);
+ qdev_prop_set_chr(&dev->qdev, "chardev", chr);
+ if (qdev_init(&dev->qdev) < 0) {
+ return false;
+ }
+ return true;
+}
diff --git a/hw/char/serial-pci.c b/hw/char/serial-pci.c
new file mode 100644
index 0000000000..2138e35851
--- /dev/null
+++ b/hw/char/serial-pci.c
@@ -0,0 +1,252 @@
+/*
+ * QEMU 16550A UART emulation
+ *
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ * Copyright (c) 2008 Citrix Systems, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/* see docs/specs/pci-serial.txt */
+
+#include "hw/char/serial.h"
+#include "hw/pci/pci.h"
+
+#define PCI_SERIAL_MAX_PORTS 4
+
+typedef struct PCISerialState {
+ PCIDevice dev;
+ SerialState state;
+} PCISerialState;
+
+typedef struct PCIMultiSerialState {
+ PCIDevice dev;
+ MemoryRegion iobar;
+ uint32_t ports;
+ char *name[PCI_SERIAL_MAX_PORTS];
+ SerialState state[PCI_SERIAL_MAX_PORTS];
+ uint32_t level[PCI_SERIAL_MAX_PORTS];
+ qemu_irq *irqs;
+} PCIMultiSerialState;
+
+static int serial_pci_init(PCIDevice *dev)
+{
+ PCISerialState *pci = DO_UPCAST(PCISerialState, dev, dev);
+ SerialState *s = &pci->state;
+
+ s->baudbase = 115200;
+ serial_init_core(s);
+
+ pci->dev.config[PCI_INTERRUPT_PIN] = 0x01;
+ s->irq = pci->dev.irq[0];
+
+ memory_region_init_io(&s->io, &serial_io_ops, s, "serial", 8);
+ pci_register_bar(&pci->dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &s->io);
+ return 0;
+}
+
+static void multi_serial_irq_mux(void *opaque, int n, int level)
+{
+ PCIMultiSerialState *pci = opaque;
+ int i, pending = 0;
+
+ pci->level[n] = level;
+ for (i = 0; i < pci->ports; i++) {
+ if (pci->level[i]) {
+ pending = 1;
+ }
+ }
+ qemu_set_irq(pci->dev.irq[0], pending);
+}
+
+static int multi_serial_pci_init(PCIDevice *dev)
+{
+ PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(dev);
+ PCIMultiSerialState *pci = DO_UPCAST(PCIMultiSerialState, dev, dev);
+ SerialState *s;
+ int i;
+
+ switch (pc->device_id) {
+ case 0x0003:
+ pci->ports = 2;
+ break;
+ case 0x0004:
+ pci->ports = 4;
+ break;
+ }
+ assert(pci->ports > 0);
+ assert(pci->ports <= PCI_SERIAL_MAX_PORTS);
+
+ pci->dev.config[PCI_INTERRUPT_PIN] = 0x01;
+ memory_region_init(&pci->iobar, "multiserial", 8 * pci->ports);
+ pci_register_bar(&pci->dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &pci->iobar);
+ pci->irqs = qemu_allocate_irqs(multi_serial_irq_mux, pci,
+ pci->ports);
+
+ for (i = 0; i < pci->ports; i++) {
+ s = pci->state + i;
+ s->baudbase = 115200;
+ serial_init_core(s);
+ s->irq = pci->irqs[i];
+ pci->name[i] = g_strdup_printf("uart #%d", i+1);
+ memory_region_init_io(&s->io, &serial_io_ops, s, pci->name[i], 8);
+ memory_region_add_subregion(&pci->iobar, 8 * i, &s->io);
+ }
+ return 0;
+}
+
+static void serial_pci_exit(PCIDevice *dev)
+{
+ PCISerialState *pci = DO_UPCAST(PCISerialState, dev, dev);
+ SerialState *s = &pci->state;
+
+ serial_exit_core(s);
+ memory_region_destroy(&s->io);
+}
+
+static void multi_serial_pci_exit(PCIDevice *dev)
+{
+ PCIMultiSerialState *pci = DO_UPCAST(PCIMultiSerialState, dev, dev);
+ SerialState *s;
+ int i;
+
+ for (i = 0; i < pci->ports; i++) {
+ s = pci->state + i;
+ serial_exit_core(s);
+ memory_region_destroy(&s->io);
+ g_free(pci->name[i]);
+ }
+ memory_region_destroy(&pci->iobar);
+ qemu_free_irqs(pci->irqs);
+}
+
+static const VMStateDescription vmstate_pci_serial = {
+ .name = "pci-serial",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_PCI_DEVICE(dev, PCISerialState),
+ VMSTATE_STRUCT(state, PCISerialState, 0, vmstate_serial, SerialState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_pci_multi_serial = {
+ .name = "pci-serial-multi",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_PCI_DEVICE(dev, PCIMultiSerialState),
+ VMSTATE_STRUCT_ARRAY(state, PCIMultiSerialState, PCI_SERIAL_MAX_PORTS,
+ 0, vmstate_serial, SerialState),
+ VMSTATE_UINT32_ARRAY(level, PCIMultiSerialState, PCI_SERIAL_MAX_PORTS),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static Property serial_pci_properties[] = {
+ DEFINE_PROP_CHR("chardev", PCISerialState, state.chr),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static Property multi_2x_serial_pci_properties[] = {
+ DEFINE_PROP_CHR("chardev1", PCIMultiSerialState, state[0].chr),
+ DEFINE_PROP_CHR("chardev2", PCIMultiSerialState, state[1].chr),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static Property multi_4x_serial_pci_properties[] = {
+ DEFINE_PROP_CHR("chardev1", PCIMultiSerialState, state[0].chr),
+ DEFINE_PROP_CHR("chardev2", PCIMultiSerialState, state[1].chr),
+ DEFINE_PROP_CHR("chardev3", PCIMultiSerialState, state[2].chr),
+ DEFINE_PROP_CHR("chardev4", PCIMultiSerialState, state[3].chr),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void serial_pci_class_initfn(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ PCIDeviceClass *pc = PCI_DEVICE_CLASS(klass);
+ pc->init = serial_pci_init;
+ pc->exit = serial_pci_exit;
+ pc->vendor_id = PCI_VENDOR_ID_REDHAT;
+ pc->device_id = PCI_DEVICE_ID_REDHAT_SERIAL;
+ pc->revision = 1;
+ pc->class_id = PCI_CLASS_COMMUNICATION_SERIAL;
+ dc->vmsd = &vmstate_pci_serial;
+ dc->props = serial_pci_properties;
+}
+
+static void multi_2x_serial_pci_class_initfn(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ PCIDeviceClass *pc = PCI_DEVICE_CLASS(klass);
+ pc->init = multi_serial_pci_init;
+ pc->exit = multi_serial_pci_exit;
+ pc->vendor_id = PCI_VENDOR_ID_REDHAT;
+ pc->device_id = PCI_DEVICE_ID_REDHAT_SERIAL2;
+ pc->revision = 1;
+ pc->class_id = PCI_CLASS_COMMUNICATION_SERIAL;
+ dc->vmsd = &vmstate_pci_multi_serial;
+ dc->props = multi_2x_serial_pci_properties;
+}
+
+static void multi_4x_serial_pci_class_initfn(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ PCIDeviceClass *pc = PCI_DEVICE_CLASS(klass);
+ pc->init = multi_serial_pci_init;
+ pc->exit = multi_serial_pci_exit;
+ pc->vendor_id = PCI_VENDOR_ID_REDHAT;
+ pc->device_id = PCI_DEVICE_ID_REDHAT_SERIAL4;
+ pc->revision = 1;
+ pc->class_id = PCI_CLASS_COMMUNICATION_SERIAL;
+ dc->vmsd = &vmstate_pci_multi_serial;
+ dc->props = multi_4x_serial_pci_properties;
+}
+
+static const TypeInfo serial_pci_info = {
+ .name = "pci-serial",
+ .parent = TYPE_PCI_DEVICE,
+ .instance_size = sizeof(PCISerialState),
+ .class_init = serial_pci_class_initfn,
+};
+
+static const TypeInfo multi_2x_serial_pci_info = {
+ .name = "pci-serial-2x",
+ .parent = TYPE_PCI_DEVICE,
+ .instance_size = sizeof(PCIMultiSerialState),
+ .class_init = multi_2x_serial_pci_class_initfn,
+};
+
+static const TypeInfo multi_4x_serial_pci_info = {
+ .name = "pci-serial-4x",
+ .parent = TYPE_PCI_DEVICE,
+ .instance_size = sizeof(PCIMultiSerialState),
+ .class_init = multi_4x_serial_pci_class_initfn,
+};
+
+static void serial_pci_register_types(void)
+{
+ type_register_static(&serial_pci_info);
+ type_register_static(&multi_2x_serial_pci_info);
+ type_register_static(&multi_4x_serial_pci_info);
+}
+
+type_init(serial_pci_register_types)
diff --git a/hw/char/serial.c b/hw/char/serial.c
new file mode 100644
index 0000000000..1151bf1bab
--- /dev/null
+++ b/hw/char/serial.c
@@ -0,0 +1,789 @@
+/*
+ * QEMU 16550A UART emulation
+ *
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ * Copyright (c) 2008 Citrix Systems, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "hw/char/serial.h"
+#include "char/char.h"
+#include "qemu/timer.h"
+#include "exec/address-spaces.h"
+
+//#define DEBUG_SERIAL
+
+#define UART_LCR_DLAB 0x80 /* Divisor latch access bit */
+
+#define UART_IER_MSI 0x08 /* Enable Modem status interrupt */
+#define UART_IER_RLSI 0x04 /* Enable receiver line status interrupt */
+#define UART_IER_THRI 0x02 /* Enable Transmitter holding register int. */
+#define UART_IER_RDI 0x01 /* Enable receiver data interrupt */
+
+#define UART_IIR_NO_INT 0x01 /* No interrupts pending */
+#define UART_IIR_ID 0x06 /* Mask for the interrupt ID */
+
+#define UART_IIR_MSI 0x00 /* Modem status interrupt */
+#define UART_IIR_THRI 0x02 /* Transmitter holding register empty */
+#define UART_IIR_RDI 0x04 /* Receiver data interrupt */
+#define UART_IIR_RLSI 0x06 /* Receiver line status interrupt */
+#define UART_IIR_CTI 0x0C /* Character Timeout Indication */
+
+#define UART_IIR_FENF 0x80 /* Fifo enabled, but not functionning */
+#define UART_IIR_FE 0xC0 /* Fifo enabled */
+
+/*
+ * These are the definitions for the Modem Control Register
+ */
+#define UART_MCR_LOOP 0x10 /* Enable loopback test mode */
+#define UART_MCR_OUT2 0x08 /* Out2 complement */
+#define UART_MCR_OUT1 0x04 /* Out1 complement */
+#define UART_MCR_RTS 0x02 /* RTS complement */
+#define UART_MCR_DTR 0x01 /* DTR complement */
+
+/*
+ * These are the definitions for the Modem Status Register
+ */
+#define UART_MSR_DCD 0x80 /* Data Carrier Detect */
+#define UART_MSR_RI 0x40 /* Ring Indicator */
+#define UART_MSR_DSR 0x20 /* Data Set Ready */
+#define UART_MSR_CTS 0x10 /* Clear to Send */
+#define UART_MSR_DDCD 0x08 /* Delta DCD */
+#define UART_MSR_TERI 0x04 /* Trailing edge ring indicator */
+#define UART_MSR_DDSR 0x02 /* Delta DSR */
+#define UART_MSR_DCTS 0x01 /* Delta CTS */
+#define UART_MSR_ANY_DELTA 0x0F /* Any of the delta bits! */
+
+#define UART_LSR_TEMT 0x40 /* Transmitter empty */
+#define UART_LSR_THRE 0x20 /* Transmit-hold-register empty */
+#define UART_LSR_BI 0x10 /* Break interrupt indicator */
+#define UART_LSR_FE 0x08 /* Frame error indicator */
+#define UART_LSR_PE 0x04 /* Parity error indicator */
+#define UART_LSR_OE 0x02 /* Overrun error indicator */
+#define UART_LSR_DR 0x01 /* Receiver data ready */
+#define UART_LSR_INT_ANY 0x1E /* Any of the lsr-interrupt-triggering status bits */
+
+/* Interrupt trigger levels. The byte-counts are for 16550A - in newer UARTs the byte-count for each ITL is higher. */
+
+#define UART_FCR_ITL_1 0x00 /* 1 byte ITL */
+#define UART_FCR_ITL_2 0x40 /* 4 bytes ITL */
+#define UART_FCR_ITL_3 0x80 /* 8 bytes ITL */
+#define UART_FCR_ITL_4 0xC0 /* 14 bytes ITL */
+
+#define UART_FCR_DMS 0x08 /* DMA Mode Select */
+#define UART_FCR_XFR 0x04 /* XMIT Fifo Reset */
+#define UART_FCR_RFR 0x02 /* RCVR Fifo Reset */
+#define UART_FCR_FE 0x01 /* FIFO Enable */
+
+#define XMIT_FIFO 0
+#define RECV_FIFO 1
+#define MAX_XMIT_RETRY 4
+
+#ifdef DEBUG_SERIAL
+#define DPRINTF(fmt, ...) \
+do { fprintf(stderr, "serial: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) \
+do {} while (0)
+#endif
+
+static void serial_receive1(void *opaque, const uint8_t *buf, int size);
+
+static void fifo_clear(SerialState *s, int fifo)
+{
+ SerialFIFO *f = (fifo) ? &s->recv_fifo : &s->xmit_fifo;
+ memset(f->data, 0, UART_FIFO_LENGTH);
+ f->count = 0;
+ f->head = 0;
+ f->tail = 0;
+}
+
+static int fifo_put(SerialState *s, int fifo, uint8_t chr)
+{
+ SerialFIFO *f = (fifo) ? &s->recv_fifo : &s->xmit_fifo;
+
+ /* Receive overruns do not overwrite FIFO contents. */
+ if (fifo == XMIT_FIFO || f->count < UART_FIFO_LENGTH) {
+
+ f->data[f->head++] = chr;
+
+ if (f->head == UART_FIFO_LENGTH)
+ f->head = 0;
+ }
+
+ if (f->count < UART_FIFO_LENGTH)
+ f->count++;
+ else if (fifo == RECV_FIFO)
+ s->lsr |= UART_LSR_OE;
+
+ return 1;
+}
+
+static uint8_t fifo_get(SerialState *s, int fifo)
+{
+ SerialFIFO *f = (fifo) ? &s->recv_fifo : &s->xmit_fifo;
+ uint8_t c;
+
+ if(f->count == 0)
+ return 0;
+
+ c = f->data[f->tail++];
+ if (f->tail == UART_FIFO_LENGTH)
+ f->tail = 0;
+ f->count--;
+
+ return c;
+}
+
+static void serial_update_irq(SerialState *s)
+{
+ uint8_t tmp_iir = UART_IIR_NO_INT;
+
+ if ((s->ier & UART_IER_RLSI) && (s->lsr & UART_LSR_INT_ANY)) {
+ tmp_iir = UART_IIR_RLSI;
+ } else if ((s->ier & UART_IER_RDI) && s->timeout_ipending) {
+ /* Note that(s->ier & UART_IER_RDI) can mask this interrupt,
+ * this is not in the specification but is observed on existing
+ * hardware. */
+ tmp_iir = UART_IIR_CTI;
+ } else if ((s->ier & UART_IER_RDI) && (s->lsr & UART_LSR_DR) &&
+ (!(s->fcr & UART_FCR_FE) ||
+ s->recv_fifo.count >= s->recv_fifo.itl)) {
+ tmp_iir = UART_IIR_RDI;
+ } else if ((s->ier & UART_IER_THRI) && s->thr_ipending) {
+ tmp_iir = UART_IIR_THRI;
+ } else if ((s->ier & UART_IER_MSI) && (s->msr & UART_MSR_ANY_DELTA)) {
+ tmp_iir = UART_IIR_MSI;
+ }
+
+ s->iir = tmp_iir | (s->iir & 0xF0);
+
+ if (tmp_iir != UART_IIR_NO_INT) {
+ qemu_irq_raise(s->irq);
+ } else {
+ qemu_irq_lower(s->irq);
+ }
+}
+
+static void serial_update_parameters(SerialState *s)
+{
+ int speed, parity, data_bits, stop_bits, frame_size;
+ QEMUSerialSetParams ssp;
+
+ if (s->divider == 0)
+ return;
+
+ /* Start bit. */
+ frame_size = 1;
+ if (s->lcr & 0x08) {
+ /* Parity bit. */
+ frame_size++;
+ if (s->lcr & 0x10)
+ parity = 'E';
+ else
+ parity = 'O';
+ } else {
+ parity = 'N';
+ }
+ if (s->lcr & 0x04)
+ stop_bits = 2;
+ else
+ stop_bits = 1;
+
+ data_bits = (s->lcr & 0x03) + 5;
+ frame_size += data_bits + stop_bits;
+ speed = s->baudbase / s->divider;
+ ssp.speed = speed;
+ ssp.parity = parity;
+ ssp.data_bits = data_bits;
+ ssp.stop_bits = stop_bits;
+ s->char_transmit_time = (get_ticks_per_sec() / speed) * frame_size;
+ qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp);
+
+ DPRINTF("speed=%d parity=%c data=%d stop=%d\n",
+ speed, parity, data_bits, stop_bits);
+}
+
+static void serial_update_msl(SerialState *s)
+{
+ uint8_t omsr;
+ int flags;
+
+ qemu_del_timer(s->modem_status_poll);
+
+ if (qemu_chr_fe_ioctl(s->chr,CHR_IOCTL_SERIAL_GET_TIOCM, &flags) == -ENOTSUP) {
+ s->poll_msl = -1;
+ return;
+ }
+
+ omsr = s->msr;
+
+ s->msr = (flags & CHR_TIOCM_CTS) ? s->msr | UART_MSR_CTS : s->msr & ~UART_MSR_CTS;
+ s->msr = (flags & CHR_TIOCM_DSR) ? s->msr | UART_MSR_DSR : s->msr & ~UART_MSR_DSR;
+ s->msr = (flags & CHR_TIOCM_CAR) ? s->msr | UART_MSR_DCD : s->msr & ~UART_MSR_DCD;
+ s->msr = (flags & CHR_TIOCM_RI) ? s->msr | UART_MSR_RI : s->msr & ~UART_MSR_RI;
+
+ if (s->msr != omsr) {
+ /* Set delta bits */
+ s->msr = s->msr | ((s->msr >> 4) ^ (omsr >> 4));
+ /* UART_MSR_TERI only if change was from 1 -> 0 */
+ if ((s->msr & UART_MSR_TERI) && !(omsr & UART_MSR_RI))
+ s->msr &= ~UART_MSR_TERI;
+ serial_update_irq(s);
+ }
+
+ /* The real 16550A apparently has a 250ns response latency to line status changes.
+ We'll be lazy and poll only every 10ms, and only poll it at all if MSI interrupts are turned on */
+
+ if (s->poll_msl)
+ qemu_mod_timer(s->modem_status_poll, qemu_get_clock_ns(vm_clock) + get_ticks_per_sec() / 100);
+}
+
+static gboolean serial_xmit(GIOChannel *chan, GIOCondition cond, void *opaque)
+{
+ SerialState *s = opaque;
+
+ if (s->tsr_retry <= 0) {
+ if (s->fcr & UART_FCR_FE) {
+ s->tsr = fifo_get(s,XMIT_FIFO);
+ if (!s->xmit_fifo.count)
+ s->lsr |= UART_LSR_THRE;
+ } else if ((s->lsr & UART_LSR_THRE)) {
+ return FALSE;
+ } else {
+ s->tsr = s->thr;
+ s->lsr |= UART_LSR_THRE;
+ s->lsr &= ~UART_LSR_TEMT;
+ }
+ }
+
+ if (s->mcr & UART_MCR_LOOP) {
+ /* in loopback mode, say that we just received a char */
+ serial_receive1(s, &s->tsr, 1);
+ } else if (qemu_chr_fe_write(s->chr, &s->tsr, 1) != 1) {
+ if (s->tsr_retry >= 0 && s->tsr_retry < MAX_XMIT_RETRY &&
+ qemu_chr_fe_add_watch(s->chr, G_IO_OUT, serial_xmit, s) > 0) {
+ s->tsr_retry++;
+ return FALSE;
+ }
+ s->tsr_retry = 0;
+ } else {
+ s->tsr_retry = 0;
+ }
+
+ s->last_xmit_ts = qemu_get_clock_ns(vm_clock);
+
+ if (s->lsr & UART_LSR_THRE) {
+ s->lsr |= UART_LSR_TEMT;
+ s->thr_ipending = 1;
+ serial_update_irq(s);
+ }
+
+ return FALSE;
+}
+
+
+static void serial_ioport_write(void *opaque, hwaddr addr, uint64_t val,
+ unsigned size)
+{
+ SerialState *s = opaque;
+
+ addr &= 7;
+ DPRINTF("write addr=0x%" HWADDR_PRIx " val=0x%" PRIx64 "\n", addr, val);
+ switch(addr) {
+ default:
+ case 0:
+ if (s->lcr & UART_LCR_DLAB) {
+ s->divider = (s->divider & 0xff00) | val;
+ serial_update_parameters(s);
+ } else {
+ s->thr = (uint8_t) val;
+ if(s->fcr & UART_FCR_FE) {
+ fifo_put(s, XMIT_FIFO, s->thr);
+ s->thr_ipending = 0;
+ s->lsr &= ~UART_LSR_TEMT;
+ s->lsr &= ~UART_LSR_THRE;
+ serial_update_irq(s);
+ } else {
+ s->thr_ipending = 0;
+ s->lsr &= ~UART_LSR_THRE;
+ serial_update_irq(s);
+ }
+ serial_xmit(NULL, G_IO_OUT, s);
+ }
+ break;
+ case 1:
+ if (s->lcr & UART_LCR_DLAB) {
+ s->divider = (s->divider & 0x00ff) | (val << 8);
+ serial_update_parameters(s);
+ } else {
+ s->ier = val & 0x0f;
+ /* If the backend device is a real serial port, turn polling of the modem
+ status lines on physical port on or off depending on UART_IER_MSI state */
+ if (s->poll_msl >= 0) {
+ if (s->ier & UART_IER_MSI) {
+ s->poll_msl = 1;
+ serial_update_msl(s);
+ } else {
+ qemu_del_timer(s->modem_status_poll);
+ s->poll_msl = 0;
+ }
+ }
+ if (s->lsr & UART_LSR_THRE) {
+ s->thr_ipending = 1;
+ serial_update_irq(s);
+ }
+ }
+ break;
+ case 2:
+ val = val & 0xFF;
+
+ if (s->fcr == val)
+ break;
+
+ /* Did the enable/disable flag change? If so, make sure FIFOs get flushed */
+ if ((val ^ s->fcr) & UART_FCR_FE)
+ val |= UART_FCR_XFR | UART_FCR_RFR;
+
+ /* FIFO clear */
+
+ if (val & UART_FCR_RFR) {
+ qemu_del_timer(s->fifo_timeout_timer);
+ s->timeout_ipending=0;
+ fifo_clear(s,RECV_FIFO);
+ }
+
+ if (val & UART_FCR_XFR) {
+ fifo_clear(s,XMIT_FIFO);
+ }
+
+ if (val & UART_FCR_FE) {
+ s->iir |= UART_IIR_FE;
+ /* Set RECV_FIFO trigger Level */
+ switch (val & 0xC0) {
+ case UART_FCR_ITL_1:
+ s->recv_fifo.itl = 1;
+ break;
+ case UART_FCR_ITL_2:
+ s->recv_fifo.itl = 4;
+ break;
+ case UART_FCR_ITL_3:
+ s->recv_fifo.itl = 8;
+ break;
+ case UART_FCR_ITL_4:
+ s->recv_fifo.itl = 14;
+ break;
+ }
+ } else
+ s->iir &= ~UART_IIR_FE;
+
+ /* Set fcr - or at least the bits in it that are supposed to "stick" */
+ s->fcr = val & 0xC9;
+ serial_update_irq(s);
+ break;
+ case 3:
+ {
+ int break_enable;
+ s->lcr = val;
+ serial_update_parameters(s);
+ break_enable = (val >> 6) & 1;
+ if (break_enable != s->last_break_enable) {
+ s->last_break_enable = break_enable;
+ qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_SERIAL_SET_BREAK,
+ &break_enable);
+ }
+ }
+ break;
+ case 4:
+ {
+ int flags;
+ int old_mcr = s->mcr;
+ s->mcr = val & 0x1f;
+ if (val & UART_MCR_LOOP)
+ break;
+
+ if (s->poll_msl >= 0 && old_mcr != s->mcr) {
+
+ qemu_chr_fe_ioctl(s->chr,CHR_IOCTL_SERIAL_GET_TIOCM, &flags);
+
+ flags &= ~(CHR_TIOCM_RTS | CHR_TIOCM_DTR);
+
+ if (val & UART_MCR_RTS)
+ flags |= CHR_TIOCM_RTS;
+ if (val & UART_MCR_DTR)
+ flags |= CHR_TIOCM_DTR;
+
+ qemu_chr_fe_ioctl(s->chr,CHR_IOCTL_SERIAL_SET_TIOCM, &flags);
+ /* Update the modem status after a one-character-send wait-time, since there may be a response
+ from the device/computer at the other end of the serial line */
+ qemu_mod_timer(s->modem_status_poll, qemu_get_clock_ns(vm_clock) + s->char_transmit_time);
+ }
+ }
+ break;
+ case 5:
+ break;
+ case 6:
+ break;
+ case 7:
+ s->scr = val;
+ break;
+ }
+}
+
+static uint64_t serial_ioport_read(void *opaque, hwaddr addr, unsigned size)
+{
+ SerialState *s = opaque;
+ uint32_t ret;
+
+ addr &= 7;
+ switch(addr) {
+ default:
+ case 0:
+ if (s->lcr & UART_LCR_DLAB) {
+ ret = s->divider & 0xff;
+ } else {
+ if(s->fcr & UART_FCR_FE) {
+ ret = fifo_get(s,RECV_FIFO);
+ if (s->recv_fifo.count == 0)
+ s->lsr &= ~(UART_LSR_DR | UART_LSR_BI);
+ else
+ qemu_mod_timer(s->fifo_timeout_timer, qemu_get_clock_ns (vm_clock) + s->char_transmit_time * 4);
+ s->timeout_ipending = 0;
+ } else {
+ ret = s->rbr;
+ s->lsr &= ~(UART_LSR_DR | UART_LSR_BI);
+ }
+ serial_update_irq(s);
+ if (!(s->mcr & UART_MCR_LOOP)) {
+ /* in loopback mode, don't receive any data */
+ qemu_chr_accept_input(s->chr);
+ }
+ }
+ break;
+ case 1:
+ if (s->lcr & UART_LCR_DLAB) {
+ ret = (s->divider >> 8) & 0xff;
+ } else {
+ ret = s->ier;
+ }
+ break;
+ case 2:
+ ret = s->iir;
+ if ((ret & UART_IIR_ID) == UART_IIR_THRI) {
+ s->thr_ipending = 0;
+ serial_update_irq(s);
+ }
+ break;
+ case 3:
+ ret = s->lcr;
+ break;
+ case 4:
+ ret = s->mcr;
+ break;
+ case 5:
+ ret = s->lsr;
+ /* Clear break and overrun interrupts */
+ if (s->lsr & (UART_LSR_BI|UART_LSR_OE)) {
+ s->lsr &= ~(UART_LSR_BI|UART_LSR_OE);
+ serial_update_irq(s);
+ }
+ break;
+ case 6:
+ if (s->mcr & UART_MCR_LOOP) {
+ /* in loopback, the modem output pins are connected to the
+ inputs */
+ ret = (s->mcr & 0x0c) << 4;
+ ret |= (s->mcr & 0x02) << 3;
+ ret |= (s->mcr & 0x01) << 5;
+ } else {
+ if (s->poll_msl >= 0)
+ serial_update_msl(s);
+ ret = s->msr;
+ /* Clear delta bits & msr int after read, if they were set */
+ if (s->msr & UART_MSR_ANY_DELTA) {
+ s->msr &= 0xF0;
+ serial_update_irq(s);
+ }
+ }
+ break;
+ case 7:
+ ret = s->scr;
+ break;
+ }
+ DPRINTF("read addr=0x%" HWADDR_PRIx " val=0x%02x\n", addr, ret);
+ return ret;
+}
+
+static int serial_can_receive(SerialState *s)
+{
+ if(s->fcr & UART_FCR_FE) {
+ if(s->recv_fifo.count < UART_FIFO_LENGTH)
+ /* Advertise (fifo.itl - fifo.count) bytes when count < ITL, and 1 if above. If UART_FIFO_LENGTH - fifo.count is
+ advertised the effect will be to almost always fill the fifo completely before the guest has a chance to respond,
+ effectively overriding the ITL that the guest has set. */
+ return (s->recv_fifo.count <= s->recv_fifo.itl) ? s->recv_fifo.itl - s->recv_fifo.count : 1;
+ else
+ return 0;
+ } else {
+ return !(s->lsr & UART_LSR_DR);
+ }
+}
+
+static void serial_receive_break(SerialState *s)
+{
+ s->rbr = 0;
+ /* When the LSR_DR is set a null byte is pushed into the fifo */
+ fifo_put(s, RECV_FIFO, '\0');
+ s->lsr |= UART_LSR_BI | UART_LSR_DR;
+ serial_update_irq(s);
+}
+
+/* There's data in recv_fifo and s->rbr has not been read for 4 char transmit times */
+static void fifo_timeout_int (void *opaque) {
+ SerialState *s = opaque;
+ if (s->recv_fifo.count) {
+ s->timeout_ipending = 1;
+ serial_update_irq(s);
+ }
+}
+
+static int serial_can_receive1(void *opaque)
+{
+ SerialState *s = opaque;
+ return serial_can_receive(s);
+}
+
+static void serial_receive1(void *opaque, const uint8_t *buf, int size)
+{
+ SerialState *s = opaque;
+
+ if (s->wakeup) {
+ qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER);
+ }
+ if(s->fcr & UART_FCR_FE) {
+ int i;
+ for (i = 0; i < size; i++) {
+ fifo_put(s, RECV_FIFO, buf[i]);
+ }
+ s->lsr |= UART_LSR_DR;
+ /* call the timeout receive callback in 4 char transmit time */
+ qemu_mod_timer(s->fifo_timeout_timer, qemu_get_clock_ns (vm_clock) + s->char_transmit_time * 4);
+ } else {
+ if (s->lsr & UART_LSR_DR)
+ s->lsr |= UART_LSR_OE;
+ s->rbr = buf[0];
+ s->lsr |= UART_LSR_DR;
+ }
+ serial_update_irq(s);
+}
+
+static void serial_event(void *opaque, int event)
+{
+ SerialState *s = opaque;
+ DPRINTF("event %x\n", event);
+ if (event == CHR_EVENT_BREAK)
+ serial_receive_break(s);
+}
+
+static void serial_pre_save(void *opaque)
+{
+ SerialState *s = opaque;
+ s->fcr_vmstate = s->fcr;
+}
+
+static int serial_post_load(void *opaque, int version_id)
+{
+ SerialState *s = opaque;
+
+ if (version_id < 3) {
+ s->fcr_vmstate = 0;
+ }
+ /* Initialize fcr via setter to perform essential side-effects */
+ serial_ioport_write(s, 0x02, s->fcr_vmstate, 1);
+ serial_update_parameters(s);
+ return 0;
+}
+
+const VMStateDescription vmstate_serial = {
+ .name = "serial",
+ .version_id = 3,
+ .minimum_version_id = 2,
+ .pre_save = serial_pre_save,
+ .post_load = serial_post_load,
+ .fields = (VMStateField []) {
+ VMSTATE_UINT16_V(divider, SerialState, 2),
+ VMSTATE_UINT8(rbr, SerialState),
+ VMSTATE_UINT8(ier, SerialState),
+ VMSTATE_UINT8(iir, SerialState),
+ VMSTATE_UINT8(lcr, SerialState),
+ VMSTATE_UINT8(mcr, SerialState),
+ VMSTATE_UINT8(lsr, SerialState),
+ VMSTATE_UINT8(msr, SerialState),
+ VMSTATE_UINT8(scr, SerialState),
+ VMSTATE_UINT8_V(fcr_vmstate, SerialState, 3),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void serial_reset(void *opaque)
+{
+ SerialState *s = opaque;
+
+ s->rbr = 0;
+ s->ier = 0;
+ s->iir = UART_IIR_NO_INT;
+ s->lcr = 0;
+ s->lsr = UART_LSR_TEMT | UART_LSR_THRE;
+ s->msr = UART_MSR_DCD | UART_MSR_DSR | UART_MSR_CTS;
+ /* Default to 9600 baud, 1 start bit, 8 data bits, 1 stop bit, no parity. */
+ s->divider = 0x0C;
+ s->mcr = UART_MCR_OUT2;
+ s->scr = 0;
+ s->tsr_retry = 0;
+ s->char_transmit_time = (get_ticks_per_sec() / 9600) * 10;
+ s->poll_msl = 0;
+
+ fifo_clear(s,RECV_FIFO);
+ fifo_clear(s,XMIT_FIFO);
+
+ s->last_xmit_ts = qemu_get_clock_ns(vm_clock);
+
+ s->thr_ipending = 0;
+ s->last_break_enable = 0;
+ qemu_irq_lower(s->irq);
+}
+
+void serial_init_core(SerialState *s)
+{
+ if (!s->chr) {
+ fprintf(stderr, "Can't create serial device, empty char device\n");
+ exit(1);
+ }
+
+ s->modem_status_poll = qemu_new_timer_ns(vm_clock, (QEMUTimerCB *) serial_update_msl, s);
+
+ s->fifo_timeout_timer = qemu_new_timer_ns(vm_clock, (QEMUTimerCB *) fifo_timeout_int, s);
+ qemu_register_reset(serial_reset, s);
+
+ qemu_chr_add_handlers(s->chr, serial_can_receive1, serial_receive1,
+ serial_event, s);
+}
+
+void serial_exit_core(SerialState *s)
+{
+ qemu_chr_add_handlers(s->chr, NULL, NULL, NULL, NULL);
+ qemu_unregister_reset(serial_reset, s);
+}
+
+/* Change the main reference oscillator frequency. */
+void serial_set_frequency(SerialState *s, uint32_t frequency)
+{
+ s->baudbase = frequency;
+ serial_update_parameters(s);
+}
+
+const MemoryRegionOps serial_io_ops = {
+ .read = serial_ioport_read,
+ .write = serial_ioport_write,
+ .impl = {
+ .min_access_size = 1,
+ .max_access_size = 1,
+ },
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+SerialState *serial_init(int base, qemu_irq irq, int baudbase,
+ CharDriverState *chr, MemoryRegion *system_io)
+{
+ SerialState *s;
+
+ s = g_malloc0(sizeof(SerialState));
+
+ s->irq = irq;
+ s->baudbase = baudbase;
+ s->chr = chr;
+ serial_init_core(s);
+
+ vmstate_register(NULL, base, &vmstate_serial, s);
+
+ memory_region_init_io(&s->io, &serial_io_ops, s, "serial", 8);
+ memory_region_add_subregion(system_io, base, &s->io);
+
+ return s;
+}
+
+/* Memory mapped interface */
+static uint64_t serial_mm_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ SerialState *s = opaque;
+ return serial_ioport_read(s, addr >> s->it_shift, 1);
+}
+
+static void serial_mm_write(void *opaque, hwaddr addr,
+ uint64_t value, unsigned size)
+{
+ SerialState *s = opaque;
+ value &= ~0u >> (32 - (size * 8));
+ serial_ioport_write(s, addr >> s->it_shift, value, 1);
+}
+
+static const MemoryRegionOps serial_mm_ops[3] = {
+ [DEVICE_NATIVE_ENDIAN] = {
+ .read = serial_mm_read,
+ .write = serial_mm_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ },
+ [DEVICE_LITTLE_ENDIAN] = {
+ .read = serial_mm_read,
+ .write = serial_mm_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ },
+ [DEVICE_BIG_ENDIAN] = {
+ .read = serial_mm_read,
+ .write = serial_mm_write,
+ .endianness = DEVICE_BIG_ENDIAN,
+ },
+};
+
+SerialState *serial_mm_init(MemoryRegion *address_space,
+ hwaddr base, int it_shift,
+ qemu_irq irq, int baudbase,
+ CharDriverState *chr, enum device_endian end)
+{
+ SerialState *s;
+
+ s = g_malloc0(sizeof(SerialState));
+
+ s->it_shift = it_shift;
+ s->irq = irq;
+ s->baudbase = baudbase;
+ s->chr = chr;
+
+ serial_init_core(s);
+ vmstate_register(NULL, base, &vmstate_serial, s);
+
+ memory_region_init_io(&s->io, &serial_mm_ops[end], s,
+ "serial", 8 << it_shift);
+ memory_region_add_subregion(address_space, base, &s->io);
+
+ serial_update_msl(s);
+ return s;
+}
diff --git a/hw/char/tpci200.c b/hw/char/tpci200.c
new file mode 100644
index 0000000000..e3408ef4ba
--- /dev/null
+++ b/hw/char/tpci200.c
@@ -0,0 +1,671 @@
+/*
+ * QEMU TEWS TPCI200 IndustryPack carrier emulation
+ *
+ * Copyright (C) 2012 Igalia, S.L.
+ * Author: Alberto Garcia <agarcia@igalia.com>
+ *
+ * This code is licensed under the GNU GPL v2 or (at your option) any
+ * later version.
+ */
+
+#include "hw/ipack.h"
+#include "hw/pci/pci.h"
+#include "qemu/bitops.h"
+#include <stdio.h>
+
+/* #define DEBUG_TPCI */
+
+#ifdef DEBUG_TPCI
+#define DPRINTF(fmt, ...) \
+ do { fprintf(stderr, "TPCI200: " fmt, ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) do { } while (0)
+#endif
+
+#define N_MODULES 4
+
+#define IP_ID_SPACE 2
+#define IP_INT_SPACE 3
+#define IP_IO_SPACE_ADDR_MASK 0x7F
+#define IP_ID_SPACE_ADDR_MASK 0x3F
+#define IP_INT_SPACE_ADDR_MASK 0x3F
+
+#define STATUS_INT(IP, INTNO) BIT((IP) * 2 + (INTNO))
+#define STATUS_TIME(IP) BIT((IP) + 12)
+#define STATUS_ERR_ANY 0xF00
+
+#define CTRL_CLKRATE BIT(0)
+#define CTRL_RECOVER BIT(1)
+#define CTRL_TIME_INT BIT(2)
+#define CTRL_ERR_INT BIT(3)
+#define CTRL_INT_EDGE(INTNO) BIT(4 + (INTNO))
+#define CTRL_INT(INTNO) BIT(6 + (INTNO))
+
+#define REG_REV_ID 0x00
+#define REG_IP_A_CTRL 0x02
+#define REG_IP_B_CTRL 0x04
+#define REG_IP_C_CTRL 0x06
+#define REG_IP_D_CTRL 0x08
+#define REG_RESET 0x0A
+#define REG_STATUS 0x0C
+#define IP_N_FROM_REG(REG) ((REG) / 2 - 1)
+
+typedef struct {
+ PCIDevice dev;
+ IPackBus bus;
+ MemoryRegion mmio;
+ MemoryRegion io;
+ MemoryRegion las0;
+ MemoryRegion las1;
+ MemoryRegion las2;
+ MemoryRegion las3;
+ bool big_endian[3];
+ uint8_t ctrl[N_MODULES];
+ uint16_t status;
+ uint8_t int_set;
+} TPCI200State;
+
+#define TYPE_TPCI200 "tpci200"
+
+#define TPCI200(obj) \
+ OBJECT_CHECK(TPCI200State, (obj), TYPE_TPCI200)
+
+static const uint8_t local_config_regs[] = {
+ 0x00, 0xFF, 0xFF, 0x0F, 0x00, 0xFC, 0xFF, 0x0F, 0x00, 0x00, 0x00,
+ 0x0E, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
+ 0x00, 0x08, 0x01, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0xA0, 0x60, 0x41, 0xD4,
+ 0xA2, 0x20, 0x41, 0x14, 0xA2, 0x20, 0x41, 0x14, 0xA2, 0x20, 0x01,
+ 0x14, 0x00, 0x00, 0x00, 0x00, 0x81, 0x00, 0x00, 0x08, 0x01, 0x02,
+ 0x00, 0x04, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x80, 0x02, 0x41,
+ 0x00, 0x00, 0x00, 0x00, 0x40, 0x7A, 0x00, 0x52, 0x92, 0x24, 0x02
+};
+
+static void adjust_addr(bool big_endian, hwaddr *addr, unsigned size)
+{
+ /* During 8 bit access in big endian mode,
+ odd and even addresses are swapped */
+ if (big_endian && size == 1) {
+ *addr ^= 1;
+ }
+}
+
+static uint64_t adjust_value(bool big_endian, uint64_t *val, unsigned size)
+{
+ /* Local spaces only support 8/16 bit access,
+ * so there's no need to care for sizes > 2 */
+ if (big_endian && size == 2) {
+ *val = bswap16(*val);
+ }
+ return *val;
+}
+
+static void tpci200_set_irq(void *opaque, int intno, int level)
+{
+ IPackDevice *ip = opaque;
+ IPackBus *bus = IPACK_BUS(qdev_get_parent_bus(DEVICE(ip)));
+ PCIDevice *pcidev = PCI_DEVICE(BUS(bus)->parent);
+ TPCI200State *dev = TPCI200(pcidev);
+ unsigned ip_n = ip->slot;
+ uint16_t prev_status = dev->status;
+
+ assert(ip->slot >= 0 && ip->slot < N_MODULES);
+
+ /* The requested interrupt must be enabled in the IP CONTROL
+ * register */
+ if (!(dev->ctrl[ip_n] & CTRL_INT(intno))) {
+ return;
+ }
+
+ /* Update the interrupt status in the IP STATUS register */
+ if (level) {
+ dev->status |= STATUS_INT(ip_n, intno);
+ } else {
+ dev->status &= ~STATUS_INT(ip_n, intno);
+ }
+
+ /* Return if there are no changes */
+ if (dev->status == prev_status) {
+ return;
+ }
+
+ DPRINTF("IP %u INT%u#: %u\n", ip_n, intno, level);
+
+ /* Check if the interrupt is edge sensitive */
+ if (dev->ctrl[ip_n] & CTRL_INT_EDGE(intno)) {
+ if (level) {
+ qemu_set_irq(dev->dev.irq[0], !dev->int_set);
+ qemu_set_irq(dev->dev.irq[0], dev->int_set);
+ }
+ } else {
+ unsigned i, j;
+ uint16_t level_status = dev->status;
+
+ /* Check if there are any level sensitive interrupts set by
+ removing the ones that are edge sensitive from the status
+ register */
+ for (i = 0; i < N_MODULES; i++) {
+ for (j = 0; j < 2; j++) {
+ if (dev->ctrl[i] & CTRL_INT_EDGE(j)) {
+ level_status &= ~STATUS_INT(i, j);
+ }
+ }
+ }
+
+ if (level_status && !dev->int_set) {
+ qemu_irq_raise(dev->dev.irq[0]);
+ dev->int_set = 1;
+ } else if (!level_status && dev->int_set) {
+ qemu_irq_lower(dev->dev.irq[0]);
+ dev->int_set = 0;
+ }
+ }
+}
+
+static uint64_t tpci200_read_cfg(void *opaque, hwaddr addr, unsigned size)
+{
+ TPCI200State *s = opaque;
+ uint8_t ret = 0;
+ if (addr < ARRAY_SIZE(local_config_regs)) {
+ ret = local_config_regs[addr];
+ }
+ /* Endianness is stored in the first bit of these registers */
+ if ((addr == 0x2b && s->big_endian[0]) ||
+ (addr == 0x2f && s->big_endian[1]) ||
+ (addr == 0x33 && s->big_endian[2])) {
+ ret |= 1;
+ }
+ DPRINTF("Read from LCR 0x%x: 0x%x\n", (unsigned) addr, (unsigned) ret);
+ return ret;
+}
+
+static void tpci200_write_cfg(void *opaque, hwaddr addr, uint64_t val,
+ unsigned size)
+{
+ TPCI200State *s = opaque;
+ /* Endianness is stored in the first bit of these registers */
+ if (addr == 0x2b || addr == 0x2f || addr == 0x33) {
+ unsigned las = (addr - 0x2b) / 4;
+ s->big_endian[las] = val & 1;
+ DPRINTF("LAS%u big endian mode: %u\n", las, (unsigned) val & 1);
+ } else {
+ DPRINTF("Write to LCR 0x%x: 0x%x\n", (unsigned) addr, (unsigned) val);
+ }
+}
+
+static uint64_t tpci200_read_las0(void *opaque, hwaddr addr, unsigned size)
+{
+ TPCI200State *s = opaque;
+ uint64_t ret = 0;
+
+ switch (addr) {
+
+ case REG_REV_ID:
+ DPRINTF("Read REVISION ID\n"); /* Current value is 0x00 */
+ break;
+
+ case REG_IP_A_CTRL:
+ case REG_IP_B_CTRL:
+ case REG_IP_C_CTRL:
+ case REG_IP_D_CTRL:
+ {
+ unsigned ip_n = IP_N_FROM_REG(addr);
+ ret = s->ctrl[ip_n];
+ DPRINTF("Read IP %c CONTROL: 0x%x\n", 'A' + ip_n, (unsigned) ret);
+ }
+ break;
+
+ case REG_RESET:
+ DPRINTF("Read RESET\n"); /* Not implemented */
+ break;
+
+ case REG_STATUS:
+ ret = s->status;
+ DPRINTF("Read STATUS: 0x%x\n", (unsigned) ret);
+ break;
+
+ /* Reserved */
+ default:
+ DPRINTF("Unsupported read from LAS0 0x%x\n", (unsigned) addr);
+ break;
+ }
+
+ return adjust_value(s->big_endian[0], &ret, size);
+}
+
+static void tpci200_write_las0(void *opaque, hwaddr addr, uint64_t val,
+ unsigned size)
+{
+ TPCI200State *s = opaque;
+
+ adjust_value(s->big_endian[0], &val, size);
+
+ switch (addr) {
+
+ case REG_REV_ID:
+ DPRINTF("Write Revision ID: 0x%x\n", (unsigned) val); /* No effect */
+ break;
+
+ case REG_IP_A_CTRL:
+ case REG_IP_B_CTRL:
+ case REG_IP_C_CTRL:
+ case REG_IP_D_CTRL:
+ {
+ unsigned ip_n = IP_N_FROM_REG(addr);
+ s->ctrl[ip_n] = val;
+ DPRINTF("Write IP %c CONTROL: 0x%x\n", 'A' + ip_n, (unsigned) val);
+ }
+ break;
+
+ case REG_RESET:
+ DPRINTF("Write RESET: 0x%x\n", (unsigned) val); /* Not implemented */
+ break;
+
+ case REG_STATUS:
+ {
+ unsigned i;
+
+ for (i = 0; i < N_MODULES; i++) {
+ IPackDevice *ip = ipack_device_find(&s->bus, i);
+
+ if (ip != NULL) {
+ if (val & STATUS_INT(i, 0)) {
+ DPRINTF("Clear IP %c INT0# status\n", 'A' + i);
+ qemu_irq_lower(ip->irq[0]);
+ }
+ if (val & STATUS_INT(i, 1)) {
+ DPRINTF("Clear IP %c INT1# status\n", 'A' + i);
+ qemu_irq_lower(ip->irq[1]);
+ }
+ }
+
+ if (val & STATUS_TIME(i)) {
+ DPRINTF("Clear IP %c timeout\n", 'A' + i);
+ s->status &= ~STATUS_TIME(i);
+ }
+ }
+
+ if (val & STATUS_ERR_ANY) {
+ DPRINTF("Unexpected write to STATUS register: 0x%x\n",
+ (unsigned) val);
+ }
+ }
+ break;
+
+ /* Reserved */
+ default:
+ DPRINTF("Unsupported write to LAS0 0x%x: 0x%x\n",
+ (unsigned) addr, (unsigned) val);
+ break;
+ }
+}
+
+static uint64_t tpci200_read_las1(void *opaque, hwaddr addr, unsigned size)
+{
+ TPCI200State *s = opaque;
+ IPackDevice *ip;
+ uint64_t ret = 0;
+ unsigned ip_n, space;
+ uint8_t offset;
+
+ adjust_addr(s->big_endian[1], &addr, size);
+
+ /*
+ * The address is divided into the IP module number (0-4), the IP
+ * address space (I/O, ID, INT) and the offset within that space.
+ */
+ ip_n = addr >> 8;
+ space = (addr >> 6) & 3;
+ ip = ipack_device_find(&s->bus, ip_n);
+
+ if (ip == NULL) {
+ DPRINTF("Read LAS1: IP module %u not installed\n", ip_n);
+ } else {
+ IPackDeviceClass *k = IPACK_DEVICE_GET_CLASS(ip);
+ switch (space) {
+
+ case IP_ID_SPACE:
+ offset = addr & IP_ID_SPACE_ADDR_MASK;
+ if (k->id_read) {
+ ret = k->id_read(ip, offset);
+ }
+ break;
+
+ case IP_INT_SPACE:
+ offset = addr & IP_INT_SPACE_ADDR_MASK;
+
+ /* Read address 0 to ACK IP INT0# and address 2 to ACK IP INT1# */
+ if (offset == 0 || offset == 2) {
+ unsigned intno = offset / 2;
+ bool int_set = s->status & STATUS_INT(ip_n, intno);
+ bool int_edge_sensitive = s->ctrl[ip_n] & CTRL_INT_EDGE(intno);
+ if (int_set && !int_edge_sensitive) {
+ qemu_irq_lower(ip->irq[intno]);
+ }
+ }
+
+ if (k->int_read) {
+ ret = k->int_read(ip, offset);
+ }
+ break;
+
+ default:
+ offset = addr & IP_IO_SPACE_ADDR_MASK;
+ if (k->io_read) {
+ ret = k->io_read(ip, offset);
+ }
+ break;
+ }
+ }
+
+ return adjust_value(s->big_endian[1], &ret, size);
+}
+
+static void tpci200_write_las1(void *opaque, hwaddr addr, uint64_t val,
+ unsigned size)
+{
+ TPCI200State *s = opaque;
+ IPackDevice *ip;
+ unsigned ip_n, space;
+ uint8_t offset;
+
+ adjust_addr(s->big_endian[1], &addr, size);
+ adjust_value(s->big_endian[1], &val, size);
+
+ /*
+ * The address is divided into the IP module number, the IP
+ * address space (I/O, ID, INT) and the offset within that space.
+ */
+ ip_n = addr >> 8;
+ space = (addr >> 6) & 3;
+ ip = ipack_device_find(&s->bus, ip_n);
+
+ if (ip == NULL) {
+ DPRINTF("Write LAS1: IP module %u not installed\n", ip_n);
+ } else {
+ IPackDeviceClass *k = IPACK_DEVICE_GET_CLASS(ip);
+ switch (space) {
+
+ case IP_ID_SPACE:
+ offset = addr & IP_ID_SPACE_ADDR_MASK;
+ if (k->id_write) {
+ k->id_write(ip, offset, val);
+ }
+ break;
+
+ case IP_INT_SPACE:
+ offset = addr & IP_INT_SPACE_ADDR_MASK;
+ if (k->int_write) {
+ k->int_write(ip, offset, val);
+ }
+ break;
+
+ default:
+ offset = addr & IP_IO_SPACE_ADDR_MASK;
+ if (k->io_write) {
+ k->io_write(ip, offset, val);
+ }
+ break;
+ }
+ }
+}
+
+static uint64_t tpci200_read_las2(void *opaque, hwaddr addr, unsigned size)
+{
+ TPCI200State *s = opaque;
+ IPackDevice *ip;
+ uint64_t ret = 0;
+ unsigned ip_n;
+ uint32_t offset;
+
+ adjust_addr(s->big_endian[2], &addr, size);
+
+ /*
+ * The address is divided into the IP module number and the offset
+ * within the IP module MEM space.
+ */
+ ip_n = addr >> 23;
+ offset = addr & 0x7fffff;
+ ip = ipack_device_find(&s->bus, ip_n);
+
+ if (ip == NULL) {
+ DPRINTF("Read LAS2: IP module %u not installed\n", ip_n);
+ } else {
+ IPackDeviceClass *k = IPACK_DEVICE_GET_CLASS(ip);
+ if (k->mem_read16) {
+ ret = k->mem_read16(ip, offset);
+ }
+ }
+
+ return adjust_value(s->big_endian[2], &ret, size);
+}
+
+static void tpci200_write_las2(void *opaque, hwaddr addr, uint64_t val,
+ unsigned size)
+{
+ TPCI200State *s = opaque;
+ IPackDevice *ip;
+ unsigned ip_n;
+ uint32_t offset;
+
+ adjust_addr(s->big_endian[2], &addr, size);
+ adjust_value(s->big_endian[2], &val, size);
+
+ /*
+ * The address is divided into the IP module number and the offset
+ * within the IP module MEM space.
+ */
+ ip_n = addr >> 23;
+ offset = addr & 0x7fffff;
+ ip = ipack_device_find(&s->bus, ip_n);
+
+ if (ip == NULL) {
+ DPRINTF("Write LAS2: IP module %u not installed\n", ip_n);
+ } else {
+ IPackDeviceClass *k = IPACK_DEVICE_GET_CLASS(ip);
+ if (k->mem_write16) {
+ k->mem_write16(ip, offset, val);
+ }
+ }
+}
+
+static uint64_t tpci200_read_las3(void *opaque, hwaddr addr, unsigned size)
+{
+ TPCI200State *s = opaque;
+ IPackDevice *ip;
+ uint64_t ret = 0;
+ /*
+ * The address is divided into the IP module number and the offset
+ * within the IP module MEM space.
+ */
+ unsigned ip_n = addr >> 22;
+ uint32_t offset = addr & 0x3fffff;
+
+ ip = ipack_device_find(&s->bus, ip_n);
+
+ if (ip == NULL) {
+ DPRINTF("Read LAS3: IP module %u not installed\n", ip_n);
+ } else {
+ IPackDeviceClass *k = IPACK_DEVICE_GET_CLASS(ip);
+ if (k->mem_read8) {
+ ret = k->mem_read8(ip, offset);
+ }
+ }
+
+ return ret;
+}
+
+static void tpci200_write_las3(void *opaque, hwaddr addr, uint64_t val,
+ unsigned size)
+{
+ TPCI200State *s = opaque;
+ IPackDevice *ip;
+ /*
+ * The address is divided into the IP module number and the offset
+ * within the IP module MEM space.
+ */
+ unsigned ip_n = addr >> 22;
+ uint32_t offset = addr & 0x3fffff;
+
+ ip = ipack_device_find(&s->bus, ip_n);
+
+ if (ip == NULL) {
+ DPRINTF("Write LAS3: IP module %u not installed\n", ip_n);
+ } else {
+ IPackDeviceClass *k = IPACK_DEVICE_GET_CLASS(ip);
+ if (k->mem_write8) {
+ k->mem_write8(ip, offset, val);
+ }
+ }
+}
+
+static const MemoryRegionOps tpci200_cfg_ops = {
+ .read = tpci200_read_cfg,
+ .write = tpci200_write_cfg,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid = {
+ .min_access_size = 1,
+ .max_access_size = 4
+ },
+ .impl = {
+ .min_access_size = 1,
+ .max_access_size = 1
+ }
+};
+
+static const MemoryRegionOps tpci200_las0_ops = {
+ .read = tpci200_read_las0,
+ .write = tpci200_write_las0,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid = {
+ .min_access_size = 2,
+ .max_access_size = 2
+ }
+};
+
+static const MemoryRegionOps tpci200_las1_ops = {
+ .read = tpci200_read_las1,
+ .write = tpci200_write_las1,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid = {
+ .min_access_size = 1,
+ .max_access_size = 2
+ }
+};
+
+static const MemoryRegionOps tpci200_las2_ops = {
+ .read = tpci200_read_las2,
+ .write = tpci200_write_las2,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid = {
+ .min_access_size = 1,
+ .max_access_size = 2
+ }
+};
+
+static const MemoryRegionOps tpci200_las3_ops = {
+ .read = tpci200_read_las3,
+ .write = tpci200_write_las3,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid = {
+ .min_access_size = 1,
+ .max_access_size = 1
+ }
+};
+
+static int tpci200_initfn(PCIDevice *pci_dev)
+{
+ TPCI200State *s = TPCI200(pci_dev);
+ uint8_t *c = s->dev.config;
+
+ pci_set_word(c + PCI_COMMAND, 0x0003);
+ pci_set_word(c + PCI_STATUS, 0x0280);
+
+ pci_set_byte(c + PCI_INTERRUPT_PIN, 0x01); /* Interrupt pin A */
+
+ pci_set_byte(c + PCI_CAPABILITY_LIST, 0x40);
+ pci_set_long(c + 0x40, 0x48014801);
+ pci_set_long(c + 0x48, 0x00024C06);
+ pci_set_long(c + 0x4C, 0x00000003);
+
+ memory_region_init_io(&s->mmio, &tpci200_cfg_ops,
+ s, "tpci200_mmio", 128);
+ memory_region_init_io(&s->io, &tpci200_cfg_ops,
+ s, "tpci200_io", 128);
+ memory_region_init_io(&s->las0, &tpci200_las0_ops,
+ s, "tpci200_las0", 256);
+ memory_region_init_io(&s->las1, &tpci200_las1_ops,
+ s, "tpci200_las1", 1024);
+ memory_region_init_io(&s->las2, &tpci200_las2_ops,
+ s, "tpci200_las2", 1024*1024*32);
+ memory_region_init_io(&s->las3, &tpci200_las3_ops,
+ s, "tpci200_las3", 1024*1024*16);
+ pci_register_bar(&s->dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mmio);
+ pci_register_bar(&s->dev, 1, PCI_BASE_ADDRESS_SPACE_IO, &s->io);
+ pci_register_bar(&s->dev, 2, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->las0);
+ pci_register_bar(&s->dev, 3, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->las1);
+ pci_register_bar(&s->dev, 4, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->las2);
+ pci_register_bar(&s->dev, 5, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->las3);
+
+ ipack_bus_new_inplace(&s->bus, DEVICE(&s->dev), NULL,
+ N_MODULES, tpci200_set_irq);
+
+ return 0;
+}
+
+static void tpci200_exitfn(PCIDevice *pci_dev)
+{
+ TPCI200State *s = TPCI200(pci_dev);
+
+ memory_region_destroy(&s->mmio);
+ memory_region_destroy(&s->io);
+ memory_region_destroy(&s->las0);
+ memory_region_destroy(&s->las1);
+ memory_region_destroy(&s->las2);
+ memory_region_destroy(&s->las3);
+}
+
+static const VMStateDescription vmstate_tpci200 = {
+ .name = "tpci200",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_PCI_DEVICE(dev, TPCI200State),
+ VMSTATE_BOOL_ARRAY(big_endian, TPCI200State, 3),
+ VMSTATE_UINT8_ARRAY(ctrl, TPCI200State, N_MODULES),
+ VMSTATE_UINT16(status, TPCI200State),
+ VMSTATE_UINT8(int_set, TPCI200State),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void tpci200_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+ k->init = tpci200_initfn;
+ k->exit = tpci200_exitfn;
+ k->vendor_id = PCI_VENDOR_ID_TEWS;
+ k->device_id = PCI_DEVICE_ID_TEWS_TPCI200;
+ k->class_id = PCI_CLASS_BRIDGE_OTHER;
+ k->subsystem_vendor_id = PCI_VENDOR_ID_TEWS;
+ k->subsystem_id = 0x300A;
+ dc->desc = "TEWS TPCI200 IndustryPack carrier";
+ dc->vmsd = &vmstate_tpci200;
+}
+
+static const TypeInfo tpci200_info = {
+ .name = TYPE_TPCI200,
+ .parent = TYPE_PCI_DEVICE,
+ .instance_size = sizeof(TPCI200State),
+ .class_init = tpci200_class_init,
+};
+
+static void tpci200_register_types(void)
+{
+ type_register_static(&tpci200_info);
+}
+
+type_init(tpci200_register_types)
diff --git a/hw/char/virtio-console.c b/hw/char/virtio-console.c
new file mode 100644
index 0000000000..31f672c9a3
--- /dev/null
+++ b/hw/char/virtio-console.c
@@ -0,0 +1,184 @@
+/*
+ * Virtio Console and Generic Serial Port Devices
+ *
+ * Copyright Red Hat, Inc. 2009, 2010
+ *
+ * Authors:
+ * Amit Shah <amit.shah@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ */
+
+#include "char/char.h"
+#include "qemu/error-report.h"
+#include "trace.h"
+#include "hw/virtio/virtio-serial.h"
+
+typedef struct VirtConsole {
+ VirtIOSerialPort port;
+ CharDriverState *chr;
+} VirtConsole;
+
+/*
+ * Callback function that's called from chardevs when backend becomes
+ * writable.
+ */
+static gboolean chr_write_unblocked(GIOChannel *chan, GIOCondition cond,
+ void *opaque)
+{
+ VirtConsole *vcon = opaque;
+
+ virtio_serial_throttle_port(&vcon->port, false);
+ return FALSE;
+}
+
+/* Callback function that's called when the guest sends us data */
+static ssize_t flush_buf(VirtIOSerialPort *port, const uint8_t *buf, size_t len)
+{
+ VirtConsole *vcon = DO_UPCAST(VirtConsole, port, port);
+ ssize_t ret;
+
+ if (!vcon->chr) {
+ /* If there's no backend, we can just say we consumed all data. */
+ return len;
+ }
+
+ ret = qemu_chr_fe_write(vcon->chr, buf, len);
+ trace_virtio_console_flush_buf(port->id, len, ret);
+
+ if (ret <= 0) {
+ VirtIOSerialPortClass *k = VIRTIO_SERIAL_PORT_GET_CLASS(port);
+
+ /*
+ * Ideally we'd get a better error code than just -1, but
+ * that's what the chardev interface gives us right now. If
+ * we had a finer-grained message, like -EPIPE, we could close
+ * this connection.
+ */
+ ret = 0;
+ if (!k->is_console) {
+ virtio_serial_throttle_port(port, true);
+ qemu_chr_fe_add_watch(vcon->chr, G_IO_OUT, chr_write_unblocked,
+ vcon);
+ }
+ }
+ return ret;
+}
+
+/* Callback function that's called when the guest opens/closes the port */
+static void set_guest_connected(VirtIOSerialPort *port, int guest_connected)
+{
+ VirtConsole *vcon = DO_UPCAST(VirtConsole, port, port);
+
+ if (!vcon->chr) {
+ return;
+ }
+ qemu_chr_fe_set_open(vcon->chr, guest_connected);
+}
+
+/* Readiness of the guest to accept data on a port */
+static int chr_can_read(void *opaque)
+{
+ VirtConsole *vcon = opaque;
+
+ return virtio_serial_guest_ready(&vcon->port);
+}
+
+/* Send data from a char device over to the guest */
+static void chr_read(void *opaque, const uint8_t *buf, int size)
+{
+ VirtConsole *vcon = opaque;
+
+ trace_virtio_console_chr_read(vcon->port.id, size);
+ virtio_serial_write(&vcon->port, buf, size);
+}
+
+static void chr_event(void *opaque, int event)
+{
+ VirtConsole *vcon = opaque;
+
+ trace_virtio_console_chr_event(vcon->port.id, event);
+ switch (event) {
+ case CHR_EVENT_OPENED:
+ virtio_serial_open(&vcon->port);
+ break;
+ case CHR_EVENT_CLOSED:
+ virtio_serial_close(&vcon->port);
+ break;
+ }
+}
+
+static int virtconsole_initfn(VirtIOSerialPort *port)
+{
+ VirtConsole *vcon = DO_UPCAST(VirtConsole, port, port);
+ VirtIOSerialPortClass *k = VIRTIO_SERIAL_PORT_GET_CLASS(port);
+
+ if (port->id == 0 && !k->is_console) {
+ error_report("Port number 0 on virtio-serial devices reserved for virtconsole devices for backward compatibility.");
+ return -1;
+ }
+
+ if (vcon->chr) {
+ vcon->chr->explicit_fe_open = 1;
+ qemu_chr_add_handlers(vcon->chr, chr_can_read, chr_read, chr_event,
+ vcon);
+ }
+
+ return 0;
+}
+
+static Property virtconsole_properties[] = {
+ DEFINE_PROP_CHR("chardev", VirtConsole, chr),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void virtconsole_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ VirtIOSerialPortClass *k = VIRTIO_SERIAL_PORT_CLASS(klass);
+
+ k->is_console = true;
+ k->init = virtconsole_initfn;
+ k->have_data = flush_buf;
+ k->set_guest_connected = set_guest_connected;
+ dc->props = virtconsole_properties;
+}
+
+static const TypeInfo virtconsole_info = {
+ .name = "virtconsole",
+ .parent = TYPE_VIRTIO_SERIAL_PORT,
+ .instance_size = sizeof(VirtConsole),
+ .class_init = virtconsole_class_init,
+};
+
+static Property virtserialport_properties[] = {
+ DEFINE_PROP_CHR("chardev", VirtConsole, chr),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void virtserialport_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ VirtIOSerialPortClass *k = VIRTIO_SERIAL_PORT_CLASS(klass);
+
+ k->init = virtconsole_initfn;
+ k->have_data = flush_buf;
+ k->set_guest_connected = set_guest_connected;
+ dc->props = virtserialport_properties;
+}
+
+static const TypeInfo virtserialport_info = {
+ .name = "virtserialport",
+ .parent = TYPE_VIRTIO_SERIAL_PORT,
+ .instance_size = sizeof(VirtConsole),
+ .class_init = virtserialport_class_init,
+};
+
+static void virtconsole_register_types(void)
+{
+ type_register_static(&virtconsole_info);
+ type_register_static(&virtserialport_info);
+}
+
+type_init(virtconsole_register_types)
diff --git a/hw/char/xen_console.c b/hw/char/xen_console.c
new file mode 100644
index 0000000000..efc32320fa
--- /dev/null
+++ b/hw/char/xen_console.c
@@ -0,0 +1,305 @@
+/*
+ * Copyright (C) International Business Machines Corp., 2005
+ * Author(s): Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * Copyright (C) Red Hat 2007
+ *
+ * Xen Console
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; under version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/select.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <termios.h>
+#include <stdarg.h>
+#include <sys/mman.h>
+
+#include "hw/hw.h"
+#include "char/char.h"
+#include "hw/xen/xen_backend.h"
+
+#include <xen/io/console.h>
+
+struct buffer {
+ uint8_t *data;
+ size_t consumed;
+ size_t size;
+ size_t capacity;
+ size_t max_capacity;
+};
+
+struct XenConsole {
+ struct XenDevice xendev; /* must be first */
+ struct buffer buffer;
+ char console[XEN_BUFSIZE];
+ int ring_ref;
+ void *sring;
+ CharDriverState *chr;
+ int backlog;
+};
+
+static void buffer_append(struct XenConsole *con)
+{
+ struct buffer *buffer = &con->buffer;
+ XENCONS_RING_IDX cons, prod, size;
+ struct xencons_interface *intf = con->sring;
+
+ cons = intf->out_cons;
+ prod = intf->out_prod;
+ xen_mb();
+
+ size = prod - cons;
+ if ((size == 0) || (size > sizeof(intf->out)))
+ return;
+
+ if ((buffer->capacity - buffer->size) < size) {
+ buffer->capacity += (size + 1024);
+ buffer->data = g_realloc(buffer->data, buffer->capacity);
+ }
+
+ while (cons != prod)
+ buffer->data[buffer->size++] = intf->out[
+ MASK_XENCONS_IDX(cons++, intf->out)];
+
+ xen_mb();
+ intf->out_cons = cons;
+ xen_be_send_notify(&con->xendev);
+
+ if (buffer->max_capacity &&
+ buffer->size > buffer->max_capacity) {
+ /* Discard the middle of the data. */
+
+ size_t over = buffer->size - buffer->max_capacity;
+ uint8_t *maxpos = buffer->data + buffer->max_capacity;
+
+ memmove(maxpos - over, maxpos, over);
+ buffer->data = g_realloc(buffer->data, buffer->max_capacity);
+ buffer->size = buffer->capacity = buffer->max_capacity;
+
+ if (buffer->consumed > buffer->max_capacity - over)
+ buffer->consumed = buffer->max_capacity - over;
+ }
+}
+
+static void buffer_advance(struct buffer *buffer, size_t len)
+{
+ buffer->consumed += len;
+ if (buffer->consumed == buffer->size) {
+ buffer->consumed = 0;
+ buffer->size = 0;
+ }
+}
+
+static int ring_free_bytes(struct XenConsole *con)
+{
+ struct xencons_interface *intf = con->sring;
+ XENCONS_RING_IDX cons, prod, space;
+
+ cons = intf->in_cons;
+ prod = intf->in_prod;
+ xen_mb();
+
+ space = prod - cons;
+ if (space > sizeof(intf->in))
+ return 0; /* ring is screwed: ignore it */
+
+ return (sizeof(intf->in) - space);
+}
+
+static int xencons_can_receive(void *opaque)
+{
+ struct XenConsole *con = opaque;
+ return ring_free_bytes(con);
+}
+
+static void xencons_receive(void *opaque, const uint8_t *buf, int len)
+{
+ struct XenConsole *con = opaque;
+ struct xencons_interface *intf = con->sring;
+ XENCONS_RING_IDX prod;
+ int i, max;
+
+ max = ring_free_bytes(con);
+ /* The can_receive() func limits this, but check again anyway */
+ if (max < len)
+ len = max;
+
+ prod = intf->in_prod;
+ for (i = 0; i < len; i++) {
+ intf->in[MASK_XENCONS_IDX(prod++, intf->in)] =
+ buf[i];
+ }
+ xen_wmb();
+ intf->in_prod = prod;
+ xen_be_send_notify(&con->xendev);
+}
+
+static void xencons_send(struct XenConsole *con)
+{
+ ssize_t len, size;
+
+ size = con->buffer.size - con->buffer.consumed;
+ if (con->chr)
+ len = qemu_chr_fe_write(con->chr, con->buffer.data + con->buffer.consumed,
+ size);
+ else
+ len = size;
+ if (len < 1) {
+ if (!con->backlog) {
+ con->backlog = 1;
+ xen_be_printf(&con->xendev, 1, "backlog piling up, nobody listening?\n");
+ }
+ } else {
+ buffer_advance(&con->buffer, len);
+ if (con->backlog && len == size) {
+ con->backlog = 0;
+ xen_be_printf(&con->xendev, 1, "backlog is gone\n");
+ }
+ }
+}
+
+/* -------------------------------------------------------------------- */
+
+static int con_init(struct XenDevice *xendev)
+{
+ struct XenConsole *con = container_of(xendev, struct XenConsole, xendev);
+ char *type, *dom, label[32];
+ int ret = 0;
+ const char *output;
+
+ /* setup */
+ dom = xs_get_domain_path(xenstore, con->xendev.dom);
+ if (!xendev->dev) {
+ snprintf(con->console, sizeof(con->console), "%s/console", dom);
+ } else {
+ snprintf(con->console, sizeof(con->console), "%s/device/console/%d", dom, xendev->dev);
+ }
+ free(dom);
+
+ type = xenstore_read_str(con->console, "type");
+ if (!type || strcmp(type, "ioemu") != 0) {
+ xen_be_printf(xendev, 1, "not for me (type=%s)\n", type);
+ ret = -1;
+ goto out;
+ }
+
+ output = xenstore_read_str(con->console, "output");
+
+ /* no Xen override, use qemu output device */
+ if (output == NULL) {
+ con->chr = serial_hds[con->xendev.dev];
+ } else {
+ snprintf(label, sizeof(label), "xencons%d", con->xendev.dev);
+ con->chr = qemu_chr_new(label, output, NULL);
+ }
+
+ xenstore_store_pv_console_info(con->xendev.dev, con->chr);
+
+out:
+ g_free(type);
+ return ret;
+}
+
+static int con_initialise(struct XenDevice *xendev)
+{
+ struct XenConsole *con = container_of(xendev, struct XenConsole, xendev);
+ int limit;
+
+ if (xenstore_read_int(con->console, "ring-ref", &con->ring_ref) == -1)
+ return -1;
+ if (xenstore_read_int(con->console, "port", &con->xendev.remote_port) == -1)
+ return -1;
+ if (xenstore_read_int(con->console, "limit", &limit) == 0)
+ con->buffer.max_capacity = limit;
+
+ if (!xendev->dev) {
+ con->sring = xc_map_foreign_range(xen_xc, con->xendev.dom,
+ XC_PAGE_SIZE,
+ PROT_READ|PROT_WRITE,
+ con->ring_ref);
+ } else {
+ con->sring = xc_gnttab_map_grant_ref(xendev->gnttabdev, con->xendev.dom,
+ con->ring_ref,
+ PROT_READ|PROT_WRITE);
+ }
+ if (!con->sring)
+ return -1;
+
+ xen_be_bind_evtchn(&con->xendev);
+ if (con->chr) {
+ if (qemu_chr_fe_claim(con->chr) == 0) {
+ qemu_chr_add_handlers(con->chr, xencons_can_receive,
+ xencons_receive, NULL, con);
+ } else {
+ xen_be_printf(xendev, 0,
+ "xen_console_init error chardev %s already used\n",
+ con->chr->label);
+ con->chr = NULL;
+ }
+ }
+
+ xen_be_printf(xendev, 1, "ring mfn %d, remote port %d, local port %d, limit %zd\n",
+ con->ring_ref,
+ con->xendev.remote_port,
+ con->xendev.local_port,
+ con->buffer.max_capacity);
+ return 0;
+}
+
+static void con_disconnect(struct XenDevice *xendev)
+{
+ struct XenConsole *con = container_of(xendev, struct XenConsole, xendev);
+
+ if (!xendev->dev) {
+ return;
+ }
+ if (con->chr) {
+ qemu_chr_add_handlers(con->chr, NULL, NULL, NULL, NULL);
+ qemu_chr_fe_release(con->chr);
+ }
+ xen_be_unbind_evtchn(&con->xendev);
+
+ if (con->sring) {
+ if (!xendev->gnttabdev) {
+ munmap(con->sring, XC_PAGE_SIZE);
+ } else {
+ xc_gnttab_munmap(xendev->gnttabdev, con->sring, 1);
+ }
+ con->sring = NULL;
+ }
+}
+
+static void con_event(struct XenDevice *xendev)
+{
+ struct XenConsole *con = container_of(xendev, struct XenConsole, xendev);
+
+ buffer_append(con);
+ if (con->buffer.size - con->buffer.consumed)
+ xencons_send(con);
+}
+
+/* -------------------------------------------------------------------- */
+
+struct XenDevOps xen_console_ops = {
+ .size = sizeof(struct XenConsole),
+ .flags = DEVOPS_FLAG_IGNORE_STATE|DEVOPS_FLAG_NEED_GNTDEV,
+ .init = con_init,
+ .initialise = con_initialise,
+ .event = con_event,
+ .disconnect = con_disconnect,
+};
diff --git a/hw/char/xilinx_uartlite.c b/hw/char/xilinx_uartlite.c
new file mode 100644
index 0000000000..079f4d4e1a
--- /dev/null
+++ b/hw/char/xilinx_uartlite.c
@@ -0,0 +1,231 @@
+/*
+ * QEMU model of Xilinx uartlite.
+ *
+ * Copyright (c) 2009 Edgar E. Iglesias.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "hw/sysbus.h"
+#include "char/char.h"
+
+#define DUART(x)
+
+#define R_RX 0
+#define R_TX 1
+#define R_STATUS 2
+#define R_CTRL 3
+#define R_MAX 4
+
+#define STATUS_RXVALID 0x01
+#define STATUS_RXFULL 0x02
+#define STATUS_TXEMPTY 0x04
+#define STATUS_TXFULL 0x08
+#define STATUS_IE 0x10
+#define STATUS_OVERRUN 0x20
+#define STATUS_FRAME 0x40
+#define STATUS_PARITY 0x80
+
+#define CONTROL_RST_TX 0x01
+#define CONTROL_RST_RX 0x02
+#define CONTROL_IE 0x10
+
+struct xlx_uartlite
+{
+ SysBusDevice busdev;
+ MemoryRegion mmio;
+ CharDriverState *chr;
+ qemu_irq irq;
+
+ uint8_t rx_fifo[8];
+ unsigned int rx_fifo_pos;
+ unsigned int rx_fifo_len;
+
+ uint32_t regs[R_MAX];
+};
+
+static void uart_update_irq(struct xlx_uartlite *s)
+{
+ unsigned int irq;
+
+ if (s->rx_fifo_len)
+ s->regs[R_STATUS] |= STATUS_IE;
+
+ irq = (s->regs[R_STATUS] & STATUS_IE) && (s->regs[R_CTRL] & CONTROL_IE);
+ qemu_set_irq(s->irq, irq);
+}
+
+static void uart_update_status(struct xlx_uartlite *s)
+{
+ uint32_t r;
+
+ r = s->regs[R_STATUS];
+ r &= ~7;
+ r |= 1 << 2; /* Tx fifo is always empty. We are fast :) */
+ r |= (s->rx_fifo_len == sizeof (s->rx_fifo)) << 1;
+ r |= (!!s->rx_fifo_len);
+ s->regs[R_STATUS] = r;
+}
+
+static uint64_t
+uart_read(void *opaque, hwaddr addr, unsigned int size)
+{
+ struct xlx_uartlite *s = opaque;
+ uint32_t r = 0;
+ addr >>= 2;
+ switch (addr)
+ {
+ case R_RX:
+ r = s->rx_fifo[(s->rx_fifo_pos - s->rx_fifo_len) & 7];
+ if (s->rx_fifo_len)
+ s->rx_fifo_len--;
+ uart_update_status(s);
+ uart_update_irq(s);
+ qemu_chr_accept_input(s->chr);
+ break;
+
+ default:
+ if (addr < ARRAY_SIZE(s->regs))
+ r = s->regs[addr];
+ DUART(qemu_log("%s addr=%x v=%x\n", __func__, addr, r));
+ break;
+ }
+ return r;
+}
+
+static void
+uart_write(void *opaque, hwaddr addr,
+ uint64_t val64, unsigned int size)
+{
+ struct xlx_uartlite *s = opaque;
+ uint32_t value = val64;
+ unsigned char ch = value;
+
+ addr >>= 2;
+ switch (addr)
+ {
+ case R_STATUS:
+ hw_error("write to UART STATUS?\n");
+ break;
+
+ case R_CTRL:
+ if (value & CONTROL_RST_RX) {
+ s->rx_fifo_pos = 0;
+ s->rx_fifo_len = 0;
+ }
+ s->regs[addr] = value;
+ break;
+
+ case R_TX:
+ if (s->chr)
+ qemu_chr_fe_write(s->chr, &ch, 1);
+
+ s->regs[addr] = value;
+
+ /* hax. */
+ s->regs[R_STATUS] |= STATUS_IE;
+ break;
+
+ default:
+ DUART(printf("%s addr=%x v=%x\n", __func__, addr, value));
+ if (addr < ARRAY_SIZE(s->regs))
+ s->regs[addr] = value;
+ break;
+ }
+ uart_update_status(s);
+ uart_update_irq(s);
+}
+
+static const MemoryRegionOps uart_ops = {
+ .read = uart_read,
+ .write = uart_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid = {
+ .min_access_size = 1,
+ .max_access_size = 4
+ }
+};
+
+static void uart_rx(void *opaque, const uint8_t *buf, int size)
+{
+ struct xlx_uartlite *s = opaque;
+
+ /* Got a byte. */
+ if (s->rx_fifo_len >= 8) {
+ printf("WARNING: UART dropped char.\n");
+ return;
+ }
+ s->rx_fifo[s->rx_fifo_pos] = *buf;
+ s->rx_fifo_pos++;
+ s->rx_fifo_pos &= 0x7;
+ s->rx_fifo_len++;
+
+ uart_update_status(s);
+ uart_update_irq(s);
+}
+
+static int uart_can_rx(void *opaque)
+{
+ struct xlx_uartlite *s = opaque;
+
+ return s->rx_fifo_len < sizeof(s->rx_fifo);
+}
+
+static void uart_event(void *opaque, int event)
+{
+
+}
+
+static int xilinx_uartlite_init(SysBusDevice *dev)
+{
+ struct xlx_uartlite *s = FROM_SYSBUS(typeof (*s), dev);
+
+ sysbus_init_irq(dev, &s->irq);
+
+ uart_update_status(s);
+ memory_region_init_io(&s->mmio, &uart_ops, s, "xlnx.xps-uartlite",
+ R_MAX * 4);
+ sysbus_init_mmio(dev, &s->mmio);
+
+ s->chr = qemu_char_get_next_serial();
+ if (s->chr)
+ qemu_chr_add_handlers(s->chr, uart_can_rx, uart_rx, uart_event, s);
+ return 0;
+}
+
+static void xilinx_uartlite_class_init(ObjectClass *klass, void *data)
+{
+ SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
+
+ sdc->init = xilinx_uartlite_init;
+}
+
+static const TypeInfo xilinx_uartlite_info = {
+ .name = "xlnx.xps-uartlite",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof (struct xlx_uartlite),
+ .class_init = xilinx_uartlite_class_init,
+};
+
+static void xilinx_uart_register_types(void)
+{
+ type_register_static(&xilinx_uartlite_info);
+}
+
+type_init(xilinx_uart_register_types)