Patchwork [v2,4/4] serial: add 2x + 4x pci variant

login
register
mail settings
Submitter Gerd Hoffmann
Date Sept. 26, 2012, 12:14 p.m.
Message ID <1348661674-16722-5-git-send-email-kraxel@redhat.com>
Download mbox | patch
Permalink /patch/187061/
State New
Headers show

Comments

Gerd Hoffmann - Sept. 26, 2012, 12:14 p.m.
Add multiport serial card implementation, with two variants,
one featuring two and one featuring four ports.

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
 docs/qemupciserial.inf |    2 +
 hw/serial-pci.c        |  157 ++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 159 insertions(+), 0 deletions(-)
Paolo Bonzini - Sept. 28, 2012, 2:08 p.m.
Il 26/09/2012 14:14, Gerd Hoffmann ha scritto:
> Add multiport serial card implementation, with two variants,
> one featuring two and one featuring four ports.
> 
> Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
> ---
>  docs/qemupciserial.inf |    2 +
>  hw/serial-pci.c        |  157 ++++++++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 159 insertions(+), 0 deletions(-)
> 
> diff --git a/docs/qemupciserial.inf b/docs/qemupciserial.inf
> index 905a929..911eaa6 100644
> --- a/docs/qemupciserial.inf
> +++ b/docs/qemupciserial.inf
> @@ -11,6 +11,8 @@
>  ; (Com+Lpt)" from the list.  Click "Have a disk".  Select this file.
>  ; Procedure may vary a bit depending on the windows version.
>  
> +; FIXME: This file covers the single port version only.
> +

Looks like this is what you want for Windows:

http://msdn.microsoft.com/en-us/library/windows/hardware/ff542737%28v=vs.85%29.aspx

It's a special "splitter" driver that makes a fake bus with multiple
devices on it, out of a single device

Paolo

>  [Version]
>  Signature="$CHICAGO$"
>  Class=Ports
> diff --git a/hw/serial-pci.c b/hw/serial-pci.c
> index 88b71f5..54bd4eb 100644
> --- a/hw/serial-pci.c
> +++ b/hw/serial-pci.c
> @@ -28,6 +28,14 @@
>   *    pci region 0 is a io bar, 8 bytes long, with the 16550 uart mapped to it.
>   *    interrupt is wired to pin A.
>   *
> + * pci-serial-4x spec:
> + *    pci region 0 is a io bar, with four 16550 uarts mapped after each other,
> + *    the first at offset 0, second at 8, third at 16 and fourth at 24.
> + *    interrupt is wired to pin A.
> + *
> + * pci-serial-2x spec:
> + *    same as pci-serial-4x but with two uarts only.
> + *
>   * [root@fedora ~]# lspci -vnse
>   * 00:0e.0 0700: 1b36:0002 (rev 01) (prog-if 00 [8250])
>   *         Subsystem: 1af4:1100
> @@ -40,11 +48,23 @@
>  #include "serial.h"
>  #include "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);
> @@ -61,6 +81,56 @@ static int serial_pci_init(PCIDevice *dev)
>      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);
> @@ -70,6 +140,22 @@ static void serial_pci_exit(PCIDevice *dev)
>      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;
> +        qemu_chr_add_handlers(s->chr, NULL, NULL, NULL, NULL);
> +        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,
> @@ -81,11 +167,38 @@ static const VMStateDescription vmstate_pci_serial = {
>      }
>  };
>  
> +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);
> @@ -100,6 +213,34 @@ static void serial_pci_class_initfn(ObjectClass *klass, void *data)
>      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 = 0x1b36; /* Red Hat */
> +    pc->device_id = 0x0003;
> +    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 = 0x1b36; /* Red Hat */
> +    pc->device_id = 0x0004;
> +    pc->revision = 1;
> +    pc->class_id = PCI_CLASS_COMMUNICATION_SERIAL;
> +    dc->vmsd = &vmstate_pci_multi_serial;
> +    dc->props = multi_4x_serial_pci_properties;
> +}
> +
>  static TypeInfo serial_pci_info = {
>      .name          = "pci-serial",
>      .parent        = TYPE_PCI_DEVICE,
> @@ -107,9 +248,25 @@ static TypeInfo serial_pci_info = {
>      .class_init    = serial_pci_class_initfn,
>  };
>  
> +static 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 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)
>

Patch

diff --git a/docs/qemupciserial.inf b/docs/qemupciserial.inf
index 905a929..911eaa6 100644
--- a/docs/qemupciserial.inf
+++ b/docs/qemupciserial.inf
@@ -11,6 +11,8 @@ 
 ; (Com+Lpt)" from the list.  Click "Have a disk".  Select this file.
 ; Procedure may vary a bit depending on the windows version.
 
+; FIXME: This file covers the single port version only.
+
 [Version]
 Signature="$CHICAGO$"
 Class=Ports
diff --git a/hw/serial-pci.c b/hw/serial-pci.c
index 88b71f5..54bd4eb 100644
--- a/hw/serial-pci.c
+++ b/hw/serial-pci.c
@@ -28,6 +28,14 @@ 
  *    pci region 0 is a io bar, 8 bytes long, with the 16550 uart mapped to it.
  *    interrupt is wired to pin A.
  *
+ * pci-serial-4x spec:
+ *    pci region 0 is a io bar, with four 16550 uarts mapped after each other,
+ *    the first at offset 0, second at 8, third at 16 and fourth at 24.
+ *    interrupt is wired to pin A.
+ *
+ * pci-serial-2x spec:
+ *    same as pci-serial-4x but with two uarts only.
+ *
  * [root@fedora ~]# lspci -vnse
  * 00:0e.0 0700: 1b36:0002 (rev 01) (prog-if 00 [8250])
  *         Subsystem: 1af4:1100
@@ -40,11 +48,23 @@ 
 #include "serial.h"
 #include "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);
@@ -61,6 +81,56 @@  static int serial_pci_init(PCIDevice *dev)
     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);
@@ -70,6 +140,22 @@  static void serial_pci_exit(PCIDevice *dev)
     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;
+        qemu_chr_add_handlers(s->chr, NULL, NULL, NULL, NULL);
+        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,
@@ -81,11 +167,38 @@  static const VMStateDescription vmstate_pci_serial = {
     }
 };
 
+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);
@@ -100,6 +213,34 @@  static void serial_pci_class_initfn(ObjectClass *klass, void *data)
     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 = 0x1b36; /* Red Hat */
+    pc->device_id = 0x0003;
+    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 = 0x1b36; /* Red Hat */
+    pc->device_id = 0x0004;
+    pc->revision = 1;
+    pc->class_id = PCI_CLASS_COMMUNICATION_SERIAL;
+    dc->vmsd = &vmstate_pci_multi_serial;
+    dc->props = multi_4x_serial_pci_properties;
+}
+
 static TypeInfo serial_pci_info = {
     .name          = "pci-serial",
     .parent        = TYPE_PCI_DEVICE,
@@ -107,9 +248,25 @@  static TypeInfo serial_pci_info = {
     .class_init    = serial_pci_class_initfn,
 };
 
+static 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 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)