qemu/hw/char/serial-pci.c
<<
>>
Prefs
   1/*
   2 * QEMU 16550A UART emulation
   3 *
   4 * Copyright (c) 2003-2004 Fabrice Bellard
   5 * Copyright (c) 2008 Citrix Systems, Inc.
   6 *
   7 * Permission is hereby granted, free of charge, to any person obtaining a copy
   8 * of this software and associated documentation files (the "Software"), to deal
   9 * in the Software without restriction, including without limitation the rights
  10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  11 * copies of the Software, and to permit persons to whom the Software is
  12 * furnished to do so, subject to the following conditions:
  13 *
  14 * The above copyright notice and this permission notice shall be included in
  15 * all copies or substantial portions of the Software.
  16 *
  17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  23 * THE SOFTWARE.
  24 */
  25
  26/* see docs/specs/pci-serial.txt */
  27
  28#include "qemu/osdep.h"
  29#include "qapi/error.h"
  30#include "hw/char/serial.h"
  31#include "hw/pci/pci.h"
  32
  33#define PCI_SERIAL_MAX_PORTS 4
  34
  35typedef struct PCISerialState {
  36    PCIDevice dev;
  37    SerialState state;
  38    uint8_t prog_if;
  39} PCISerialState;
  40
  41typedef struct PCIMultiSerialState {
  42    PCIDevice    dev;
  43    MemoryRegion iobar;
  44    uint32_t     ports;
  45    char         *name[PCI_SERIAL_MAX_PORTS];
  46    SerialState  state[PCI_SERIAL_MAX_PORTS];
  47    uint32_t     level[PCI_SERIAL_MAX_PORTS];
  48    qemu_irq     *irqs;
  49    uint8_t      prog_if;
  50} PCIMultiSerialState;
  51
  52static void multi_serial_pci_exit(PCIDevice *dev);
  53
  54static void serial_pci_realize(PCIDevice *dev, Error **errp)
  55{
  56    PCISerialState *pci = DO_UPCAST(PCISerialState, dev, dev);
  57    SerialState *s = &pci->state;
  58    Error *err = NULL;
  59
  60    s->baudbase = 115200;
  61    serial_realize_core(s, &err);
  62    if (err != NULL) {
  63        error_propagate(errp, err);
  64        return;
  65    }
  66
  67    pci->dev.config[PCI_CLASS_PROG] = pci->prog_if;
  68    pci->dev.config[PCI_INTERRUPT_PIN] = 0x01;
  69    s->irq = pci_allocate_irq(&pci->dev);
  70
  71    memory_region_init_io(&s->io, OBJECT(pci), &serial_io_ops, s, "serial", 8);
  72    pci_register_bar(&pci->dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &s->io);
  73}
  74
  75static void multi_serial_irq_mux(void *opaque, int n, int level)
  76{
  77    PCIMultiSerialState *pci = opaque;
  78    int i, pending = 0;
  79
  80    pci->level[n] = level;
  81    for (i = 0; i < pci->ports; i++) {
  82        if (pci->level[i]) {
  83            pending = 1;
  84        }
  85    }
  86    pci_set_irq(&pci->dev, pending);
  87}
  88
  89static void multi_serial_pci_realize(PCIDevice *dev, Error **errp)
  90{
  91    PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(dev);
  92    PCIMultiSerialState *pci = DO_UPCAST(PCIMultiSerialState, dev, dev);
  93    SerialState *s;
  94    Error *err = NULL;
  95    int i, nr_ports = 0;
  96
  97    switch (pc->device_id) {
  98    case 0x0003:
  99        nr_ports = 2;
 100        break;
 101    case 0x0004:
 102        nr_ports = 4;
 103        break;
 104    }
 105    assert(nr_ports > 0);
 106    assert(nr_ports <= PCI_SERIAL_MAX_PORTS);
 107
 108    pci->dev.config[PCI_CLASS_PROG] = pci->prog_if;
 109    pci->dev.config[PCI_INTERRUPT_PIN] = 0x01;
 110    memory_region_init(&pci->iobar, OBJECT(pci), "multiserial", 8 * nr_ports);
 111    pci_register_bar(&pci->dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &pci->iobar);
 112    pci->irqs = qemu_allocate_irqs(multi_serial_irq_mux, pci,
 113                                   nr_ports);
 114
 115    for (i = 0; i < nr_ports; i++) {
 116        s = pci->state + i;
 117        s->baudbase = 115200;
 118        serial_realize_core(s, &err);
 119        if (err != NULL) {
 120            error_propagate(errp, err);
 121            multi_serial_pci_exit(dev);
 122            return;
 123        }
 124        s->irq = pci->irqs[i];
 125        pci->name[i] = g_strdup_printf("uart #%d", i+1);
 126        memory_region_init_io(&s->io, OBJECT(pci), &serial_io_ops, s,
 127                              pci->name[i], 8);
 128        memory_region_add_subregion(&pci->iobar, 8 * i, &s->io);
 129        pci->ports++;
 130    }
 131}
 132
 133static void serial_pci_exit(PCIDevice *dev)
 134{
 135    PCISerialState *pci = DO_UPCAST(PCISerialState, dev, dev);
 136    SerialState *s = &pci->state;
 137
 138    serial_exit_core(s);
 139    qemu_free_irq(s->irq);
 140}
 141
 142static void multi_serial_pci_exit(PCIDevice *dev)
 143{
 144    PCIMultiSerialState *pci = DO_UPCAST(PCIMultiSerialState, dev, dev);
 145    SerialState *s;
 146    int i;
 147
 148    for (i = 0; i < pci->ports; i++) {
 149        s = pci->state + i;
 150        serial_exit_core(s);
 151        memory_region_del_subregion(&pci->iobar, &s->io);
 152        g_free(pci->name[i]);
 153    }
 154    qemu_free_irqs(pci->irqs, pci->ports);
 155}
 156
 157static const VMStateDescription vmstate_pci_serial = {
 158    .name = "pci-serial",
 159    .version_id = 1,
 160    .minimum_version_id = 1,
 161    .fields = (VMStateField[]) {
 162        VMSTATE_PCI_DEVICE(dev, PCISerialState),
 163        VMSTATE_STRUCT(state, PCISerialState, 0, vmstate_serial, SerialState),
 164        VMSTATE_END_OF_LIST()
 165    }
 166};
 167
 168static const VMStateDescription vmstate_pci_multi_serial = {
 169    .name = "pci-serial-multi",
 170    .version_id = 1,
 171    .minimum_version_id = 1,
 172    .fields = (VMStateField[]) {
 173        VMSTATE_PCI_DEVICE(dev, PCIMultiSerialState),
 174        VMSTATE_STRUCT_ARRAY(state, PCIMultiSerialState, PCI_SERIAL_MAX_PORTS,
 175                             0, vmstate_serial, SerialState),
 176        VMSTATE_UINT32_ARRAY(level, PCIMultiSerialState, PCI_SERIAL_MAX_PORTS),
 177        VMSTATE_END_OF_LIST()
 178    }
 179};
 180
 181static Property serial_pci_properties[] = {
 182    DEFINE_PROP_CHR("chardev",  PCISerialState, state.chr),
 183    DEFINE_PROP_UINT8("prog_if",  PCISerialState, prog_if, 0x02),
 184    DEFINE_PROP_END_OF_LIST(),
 185};
 186
 187static Property multi_2x_serial_pci_properties[] = {
 188    DEFINE_PROP_CHR("chardev1",  PCIMultiSerialState, state[0].chr),
 189    DEFINE_PROP_CHR("chardev2",  PCIMultiSerialState, state[1].chr),
 190    DEFINE_PROP_UINT8("prog_if",  PCIMultiSerialState, prog_if, 0x02),
 191    DEFINE_PROP_END_OF_LIST(),
 192};
 193
 194static Property multi_4x_serial_pci_properties[] = {
 195    DEFINE_PROP_CHR("chardev1",  PCIMultiSerialState, state[0].chr),
 196    DEFINE_PROP_CHR("chardev2",  PCIMultiSerialState, state[1].chr),
 197    DEFINE_PROP_CHR("chardev3",  PCIMultiSerialState, state[2].chr),
 198    DEFINE_PROP_CHR("chardev4",  PCIMultiSerialState, state[3].chr),
 199    DEFINE_PROP_UINT8("prog_if",  PCIMultiSerialState, prog_if, 0x02),
 200    DEFINE_PROP_END_OF_LIST(),
 201};
 202
 203static void serial_pci_class_initfn(ObjectClass *klass, void *data)
 204{
 205    DeviceClass *dc = DEVICE_CLASS(klass);
 206    PCIDeviceClass *pc = PCI_DEVICE_CLASS(klass);
 207    pc->realize = serial_pci_realize;
 208    pc->exit = serial_pci_exit;
 209    pc->vendor_id = PCI_VENDOR_ID_REDHAT;
 210    pc->device_id = PCI_DEVICE_ID_REDHAT_SERIAL;
 211    pc->revision = 1;
 212    pc->class_id = PCI_CLASS_COMMUNICATION_SERIAL;
 213    dc->vmsd = &vmstate_pci_serial;
 214    dc->props = serial_pci_properties;
 215    set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
 216}
 217
 218static void multi_2x_serial_pci_class_initfn(ObjectClass *klass, void *data)
 219{
 220    DeviceClass *dc = DEVICE_CLASS(klass);
 221    PCIDeviceClass *pc = PCI_DEVICE_CLASS(klass);
 222    pc->realize = multi_serial_pci_realize;
 223    pc->exit = multi_serial_pci_exit;
 224    pc->vendor_id = PCI_VENDOR_ID_REDHAT;
 225    pc->device_id = PCI_DEVICE_ID_REDHAT_SERIAL2;
 226    pc->revision = 1;
 227    pc->class_id = PCI_CLASS_COMMUNICATION_SERIAL;
 228    dc->vmsd = &vmstate_pci_multi_serial;
 229    dc->props = multi_2x_serial_pci_properties;
 230    set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
 231}
 232
 233static void multi_4x_serial_pci_class_initfn(ObjectClass *klass, void *data)
 234{
 235    DeviceClass *dc = DEVICE_CLASS(klass);
 236    PCIDeviceClass *pc = PCI_DEVICE_CLASS(klass);
 237    pc->realize = multi_serial_pci_realize;
 238    pc->exit = multi_serial_pci_exit;
 239    pc->vendor_id = PCI_VENDOR_ID_REDHAT;
 240    pc->device_id = PCI_DEVICE_ID_REDHAT_SERIAL4;
 241    pc->revision = 1;
 242    pc->class_id = PCI_CLASS_COMMUNICATION_SERIAL;
 243    dc->vmsd = &vmstate_pci_multi_serial;
 244    dc->props = multi_4x_serial_pci_properties;
 245    set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
 246}
 247
 248static const TypeInfo serial_pci_info = {
 249    .name          = "pci-serial",
 250    .parent        = TYPE_PCI_DEVICE,
 251    .instance_size = sizeof(PCISerialState),
 252    .class_init    = serial_pci_class_initfn,
 253    .interfaces = (InterfaceInfo[]) {
 254        { INTERFACE_CONVENTIONAL_PCI_DEVICE },
 255        { },
 256    },
 257};
 258
 259static const TypeInfo multi_2x_serial_pci_info = {
 260    .name          = "pci-serial-2x",
 261    .parent        = TYPE_PCI_DEVICE,
 262    .instance_size = sizeof(PCIMultiSerialState),
 263    .class_init    = multi_2x_serial_pci_class_initfn,
 264    .interfaces = (InterfaceInfo[]) {
 265        { INTERFACE_CONVENTIONAL_PCI_DEVICE },
 266        { },
 267    },
 268};
 269
 270static const TypeInfo multi_4x_serial_pci_info = {
 271    .name          = "pci-serial-4x",
 272    .parent        = TYPE_PCI_DEVICE,
 273    .instance_size = sizeof(PCIMultiSerialState),
 274    .class_init    = multi_4x_serial_pci_class_initfn,
 275    .interfaces = (InterfaceInfo[]) {
 276        { INTERFACE_CONVENTIONAL_PCI_DEVICE },
 277        { },
 278    },
 279};
 280
 281static void serial_pci_register_types(void)
 282{
 283    type_register_static(&serial_pci_info);
 284    type_register_static(&multi_2x_serial_pci_info);
 285    type_register_static(&multi_4x_serial_pci_info);
 286}
 287
 288type_init(serial_pci_register_types)
 289