]> git.proxmox.com Git - qemu.git/blobdiff - hw/serial.c
vnc: rename vnc-encoding-* vnc-enc-*
[qemu.git] / hw / serial.c
index f5988a5fc6babdf0bea136609400e124dbd5afac..b66d13ad4195adc99c8a01f6b5dea5c26f05d2c4 100644 (file)
@@ -27,6 +27,7 @@
 #include "isa.h"
 #include "pc.h"
 #include "qemu-timer.h"
+#include "sysemu.h"
 
 //#define DEBUG_SERIAL
 
 #define RECV_FIFO           1
 #define MAX_XMIT_RETRY      4
 
-struct SerialFIFO {
+typedef struct SerialFIFO {
     uint8_t data[UART_FIFO_LENGTH];
     uint8_t count;
     uint8_t itl;                        /* Interrupt Trigger Level */
     uint8_t tail;
     uint8_t head;
-} typedef SerialFIFO;
+} SerialFIFO;
 
 struct SerialState {
     uint16_t divider;
@@ -119,6 +120,8 @@ struct SerialState {
     uint8_t msr; /* read only */
     uint8_t scr;
     uint8_t fcr;
+    uint8_t fcr_vmstate; /* we can't write directly this value
+                            it has side effects */
     /* NOTE: this hidden state is necessary for tx irq generation as
        it can be reset while reading iir */
     int thr_ipending;
@@ -144,6 +147,14 @@ struct SerialState {
     struct QEMUTimer *modem_status_poll;
 };
 
+typedef struct ISASerialState {
+    ISADevice dev;
+    uint32_t index;
+    uint32_t iobase;
+    uint32_t isairq;
+    SerialState state;
+} ISASerialState;
+
 static void serial_receive1(void *opaque, const uint8_t *buf, int size);
 
 static void fifo_clear(SerialState *s, int fifo)
@@ -159,11 +170,19 @@ static int fifo_put(SerialState *s, int fifo, uint8_t chr)
 {
     SerialFIFO *f = (fifo) ? &s->recv_fifo : &s->xmit_fifo;
 
-    f->data[f->head++] = chr;
+    /* 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->head == UART_FIFO_LENGTH)
-        f->head = 0;
-    f->count++;
+    if (f->count < UART_FIFO_LENGTH)
+        f->count++;
+    else if (fifo == RECV_FIFO)
+        s->lsr |= UART_LSR_OE;
 
     return 1;
 }
@@ -195,12 +214,10 @@ static void serial_update_irq(SerialState *s)
          * 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)) {
-        if (!(s->fcr & UART_FCR_FE)) {
-           tmp_iir = UART_IIR_RDI;
-        } else if (s->recv_fifo.count >= s->recv_fifo.itl) {
-           tmp_iir = UART_IIR_RDI;
-        }
+    } 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)) {
@@ -224,15 +241,17 @@ static void serial_update_parameters(SerialState *s)
     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';
-            frame_size = 0;
     }
     if (s->lcr & 0x04)
         stop_bits = 2;
@@ -246,7 +265,7 @@ static void serial_update_parameters(SerialState *s)
     ssp.parity = parity;
     ssp.data_bits = data_bits;
     ssp.stop_bits = stop_bits;
-    s->char_transmit_time =  (ticks_per_sec / speed) * frame_size;
+    s->char_transmit_time =  (get_ticks_per_sec() / speed) * frame_size;
     qemu_chr_ioctl(s->chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp);
 #if 0
     printf("speed=%d parity=%c data=%d stop=%d\n",
@@ -286,7 +305,7 @@ static void serial_update_msl(SerialState *s)
        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(vm_clock) + ticks_per_sec / 100);
+        qemu_mod_timer(s->modem_status_poll, qemu_get_clock(vm_clock) + get_ticks_per_sec() / 100);
 }
 
 static void serial_xmit(void *opaque)
@@ -353,15 +372,15 @@ static void serial_ioport_write(void *opaque, uint32_t addr, uint32_t val)
         } 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);
+                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);
+                s->thr_ipending = 0;
+                s->lsr &= ~UART_LSR_THRE;
+                serial_update_irq(s);
             }
             serial_xmit(s);
         }
@@ -523,8 +542,10 @@ static uint32_t serial_ioport_read(void *opaque, uint32_t addr)
         break;
     case 2:
         ret = s->iir;
+        if ((ret & UART_IIR_ID) == UART_IIR_THRI) {
             s->thr_ipending = 0;
-        serial_update_irq(s);
+            serial_update_irq(s);
+        }
         break;
     case 3:
         ret = s->lcr;
@@ -534,9 +555,9 @@ static uint32_t serial_ioport_read(void *opaque, uint32_t addr)
         break;
     case 5:
         ret = s->lsr;
-        /* Clear break interrupt */
-        if (s->lsr & UART_LSR_BI) {
-            s->lsr &= ~UART_LSR_BI;
+        /* 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;
@@ -619,6 +640,8 @@ static void serial_receive1(void *opaque, const uint8_t *buf, int size)
         /* call the timeout receive callback in 4 char transmit time */
         qemu_mod_timer(s->fifo_timeout_timer, qemu_get_clock (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;
     }
@@ -635,51 +658,45 @@ static void serial_event(void *opaque, int event)
         serial_receive_break(s);
 }
 
-static void serial_save(QEMUFile *f, void *opaque)
+static void serial_pre_save(void *opaque)
 {
     SerialState *s = opaque;
-
-    qemu_put_be16s(f,&s->divider);
-    qemu_put_8s(f,&s->rbr);
-    qemu_put_8s(f,&s->ier);
-    qemu_put_8s(f,&s->iir);
-    qemu_put_8s(f,&s->lcr);
-    qemu_put_8s(f,&s->mcr);
-    qemu_put_8s(f,&s->lsr);
-    qemu_put_8s(f,&s->msr);
-    qemu_put_8s(f,&s->scr);
-    qemu_put_8s(f,&s->fcr);
+    s->fcr_vmstate = s->fcr;
 }
 
-static int serial_load(QEMUFile *f, void *opaque, int version_id)
+static int serial_post_load(void *opaque, int version_id)
 {
     SerialState *s = opaque;
-    uint8_t fcr = 0;
-
-    if(version_id > 3)
-        return -EINVAL;
-
-    if (version_id >= 2)
-        qemu_get_be16s(f, &s->divider);
-    else
-        s->divider = qemu_get_byte(f);
-    qemu_get_8s(f,&s->rbr);
-    qemu_get_8s(f,&s->ier);
-    qemu_get_8s(f,&s->iir);
-    qemu_get_8s(f,&s->lcr);
-    qemu_get_8s(f,&s->mcr);
-    qemu_get_8s(f,&s->lsr);
-    qemu_get_8s(f,&s->msr);
-    qemu_get_8s(f,&s->scr);
-
-    if (version_id >= 3)
-        qemu_get_8s(f,&fcr);
 
+    if (version_id < 3) {
+        s->fcr_vmstate = 0;
+    }
     /* Initialize fcr via setter to perform essential side-effects */
-    serial_ioport_write(s, 0x02, fcr);
+    serial_ioport_write(s, 0x02, s->fcr_vmstate);
     return 0;
 }
 
+static 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;
@@ -690,12 +707,12 @@ static void serial_reset(void *opaque)
     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, no parity, one stop bit */
+    /* 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 = (ticks_per_sec / 9600) * 9;
+    s->char_transmit_time = (get_ticks_per_sec() / 9600) * 10;
     s->poll_msl = 0;
 
     fifo_clear(s,RECV_FIFO);
@@ -708,12 +725,12 @@ static void serial_reset(void *opaque)
     qemu_irq_lower(s->irq);
 }
 
-static void serial_init_core(SerialState *s, qemu_irq irq, int baudbase,
-                            CharDriverState *chr)
+static void serial_init_core(SerialState *s)
 {
-    s->irq = irq;
-    s->baudbase = baudbase;
-    s->chr = chr ?: qemu_chr_open("null", "null", NULL);
+    if (!s->chr) {
+        fprintf(stderr, "Can't create serial device, empty char device\n");
+       exit(1);
+    }
 
     s->modem_status_poll = qemu_new_timer(vm_clock, (QEMUTimerCB *) serial_update_msl, s);
 
@@ -721,13 +738,69 @@ static void serial_init_core(SerialState *s, qemu_irq irq, int baudbase,
     s->transmit_timer = qemu_new_timer(vm_clock, (QEMUTimerCB *) serial_xmit, s);
 
     qemu_register_reset(serial_reset, s);
-    serial_reset(s);
 
     qemu_chr_add_handlers(s->chr, serial_can_receive1, serial_receive1,
                           serial_event, s);
 }
 
-/* If fd is zero, it means that the serial device uses the console */
+/* Change the main reference oscillator frequency. */
+void serial_set_frequency(SerialState *s, uint32_t frequency)
+{
+    s->baudbase = frequency;
+    serial_update_parameters(s);
+}
+
+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);
+
+    register_ioport_write(isa->iobase, 8, 1, serial_ioport_write, s);
+    register_ioport_read(isa->iobase, 8, 1, serial_ioport_read, s);
+    return 0;
+}
+
+SerialState *serial_isa_init(int index, CharDriverState *chr)
+{
+    ISADevice *dev;
+
+    dev = isa_create("isa-serial");
+    qdev_prop_set_uint32(&dev->qdev, "index", index);
+    qdev_prop_set_chr(&dev->qdev, "chardev", chr);
+    if (qdev_init(&dev->qdev) < 0)
+        return NULL;
+    return &DO_UPCAST(ISASerialState, dev, dev)->state;
+}
+
+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()
+    }
+};
+
 SerialState *serial_init(int base, qemu_irq irq, int baudbase,
                          CharDriverState *chr)
 {
@@ -735,9 +808,12 @@ SerialState *serial_init(int base, qemu_irq irq, int baudbase,
 
     s = qemu_mallocz(sizeof(SerialState));
 
-    serial_init_core(s, irq, baudbase, chr);
+    s->irq = irq;
+    s->baudbase = baudbase;
+    s->chr = chr;
+    serial_init_core(s);
 
-    register_savevm("serial", base, 3, serial_save, serial_load, s);
+    vmstate_register(NULL, base, &vmstate_serial, s);
 
     register_ioport_write(base, 8, 1, serial_ioport_write, s);
     register_ioport_read(base, 8, 1, serial_ioport_read, s);
@@ -760,65 +836,106 @@ static void serial_mm_writeb(void *opaque, target_phys_addr_t addr,
     serial_ioport_write(s, addr >> s->it_shift, value & 0xFF);
 }
 
-static uint32_t serial_mm_readw(void *opaque, target_phys_addr_t addr)
+static uint32_t serial_mm_readw_be(void *opaque, target_phys_addr_t addr)
 {
     SerialState *s = opaque;
     uint32_t val;
 
     val = serial_ioport_read(s, addr >> s->it_shift) & 0xFFFF;
-#ifdef TARGET_WORDS_BIGENDIAN
     val = bswap16(val);
-#endif
     return val;
 }
 
-static void serial_mm_writew(void *opaque, target_phys_addr_t addr,
-                             uint32_t value)
+static uint32_t serial_mm_readw_le(void *opaque, target_phys_addr_t addr)
 {
     SerialState *s = opaque;
-#ifdef TARGET_WORDS_BIGENDIAN
+    uint32_t val;
+
+    val = serial_ioport_read(s, addr >> s->it_shift) & 0xFFFF;
+    return val;
+}
+
+static void serial_mm_writew_be(void *opaque, target_phys_addr_t addr,
+                                uint32_t value)
+{
+    SerialState *s = opaque;
+
     value = bswap16(value);
-#endif
     serial_ioport_write(s, addr >> s->it_shift, value & 0xFFFF);
 }
 
-static uint32_t serial_mm_readl(void *opaque, target_phys_addr_t addr)
+static void serial_mm_writew_le(void *opaque, target_phys_addr_t addr,
+                                uint32_t value)
+{
+    SerialState *s = opaque;
+
+    serial_ioport_write(s, addr >> s->it_shift, value & 0xFFFF);
+}
+
+static uint32_t serial_mm_readl_be(void *opaque, target_phys_addr_t addr)
 {
     SerialState *s = opaque;
     uint32_t val;
 
     val = serial_ioport_read(s, addr >> s->it_shift);
-#ifdef TARGET_WORDS_BIGENDIAN
     val = bswap32(val);
-#endif
     return val;
 }
 
-static void serial_mm_writel(void *opaque, target_phys_addr_t addr,
-                             uint32_t value)
+static uint32_t serial_mm_readl_le(void *opaque, target_phys_addr_t addr)
 {
     SerialState *s = opaque;
-#ifdef TARGET_WORDS_BIGENDIAN
+    uint32_t val;
+
+    val = serial_ioport_read(s, addr >> s->it_shift);
+    return val;
+}
+
+static void serial_mm_writel_be(void *opaque, target_phys_addr_t addr,
+                                uint32_t value)
+{
+    SerialState *s = opaque;
+
     value = bswap32(value);
-#endif
     serial_ioport_write(s, addr >> s->it_shift, value);
 }
 
-static CPUReadMemoryFunc * const serial_mm_read[] = {
+static void serial_mm_writel_le(void *opaque, target_phys_addr_t addr,
+                                uint32_t value)
+{
+    SerialState *s = opaque;
+
+    serial_ioport_write(s, addr >> s->it_shift, value);
+}
+
+static CPUReadMemoryFunc * const serial_mm_read_be[] = {
+    &serial_mm_readb,
+    &serial_mm_readw_be,
+    &serial_mm_readl_be,
+};
+
+static CPUWriteMemoryFunc * const serial_mm_write_be[] = {
+    &serial_mm_writeb,
+    &serial_mm_writew_be,
+    &serial_mm_writel_be,
+};
+
+static CPUReadMemoryFunc * const serial_mm_read_le[] = {
     &serial_mm_readb,
-    &serial_mm_readw,
-    &serial_mm_readl,
+    &serial_mm_readw_le,
+    &serial_mm_readl_le,
 };
 
-static CPUWriteMemoryFunc * const serial_mm_write[] = {
+static CPUWriteMemoryFunc * const serial_mm_write_le[] = {
     &serial_mm_writeb,
-    &serial_mm_writew,
-    &serial_mm_writel,
+    &serial_mm_writew_le,
+    &serial_mm_writel_le,
 };
 
 SerialState *serial_mm_init (target_phys_addr_t base, int it_shift,
                              qemu_irq irq, int baudbase,
-                             CharDriverState *chr, int ioregister)
+                             CharDriverState *chr, int ioregister,
+                             int be)
 {
     SerialState *s;
     int s_io_memory;
@@ -826,15 +943,44 @@ SerialState *serial_mm_init (target_phys_addr_t base, int it_shift,
     s = qemu_mallocz(sizeof(SerialState));
 
     s->it_shift = it_shift;
+    s->irq = irq;
+    s->baudbase = baudbase;
+    s->chr = chr;
 
-    serial_init_core(s, irq, baudbase, chr);
-    register_savevm("serial", base, 3, serial_save, serial_load, s);
+    serial_init_core(s);
+    vmstate_register(NULL, base, &vmstate_serial, s);
 
     if (ioregister) {
-        s_io_memory = cpu_register_io_memory(serial_mm_read,
-                                             serial_mm_write, s);
+        if (be) {
+            s_io_memory = cpu_register_io_memory(serial_mm_read_be,
+                                                 serial_mm_write_be, s);
+        } else {
+            s_io_memory = cpu_register_io_memory(serial_mm_read_le,
+                                                 serial_mm_write_le, s);
+        }
         cpu_register_physical_memory(base, 8 << it_shift, s_io_memory);
     }
     serial_update_msl(s);
     return s;
 }
+
+static ISADeviceInfo serial_isa_info = {
+    .qdev.name  = "isa-serial",
+    .qdev.size  = sizeof(ISASerialState),
+    .qdev.vmsd  = &vmstate_isa_serial,
+    .init       = serial_isa_initfn,
+    .qdev.props = (Property[]) {
+        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_END_OF_LIST(),
+    },
+};
+
+static void serial_register_devices(void)
+{
+    isa_qdev_register(&serial_isa_info);
+}
+
+device_init(serial_register_devices)