From patchwork Wed Sep 26 12:14:34 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Gerd Hoffmann X-Patchwork-Id: 187061 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id D4E952C008F for ; Wed, 26 Sep 2012 22:14:56 +1000 (EST) Received: from localhost ([::1]:42676 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1TGqVy-00015c-Fb for incoming@patchwork.ozlabs.org; Wed, 26 Sep 2012 08:14:54 -0400 Received: from eggs.gnu.org ([208.118.235.92]:37160) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1TGqVo-00015T-Hf for qemu-devel@nongnu.org; Wed, 26 Sep 2012 08:14:46 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1TGqVj-0000ZF-Gb for qemu-devel@nongnu.org; Wed, 26 Sep 2012 08:14:44 -0400 Received: from mx1.redhat.com ([209.132.183.28]:60740) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1TGqVj-0000Yu-7k for qemu-devel@nongnu.org; Wed, 26 Sep 2012 08:14:39 -0400 Received: from int-mx12.intmail.prod.int.phx2.redhat.com (int-mx12.intmail.prod.int.phx2.redhat.com [10.5.11.25]) by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id q8QCEbRT029084 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK) for ; Wed, 26 Sep 2012 08:14:37 -0400 Received: from rincewind.home.kraxel.org (ovpn-116-33.ams2.redhat.com [10.36.116.33]) by int-mx12.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id q8QCEahr001636; Wed, 26 Sep 2012 08:14:36 -0400 Received: by rincewind.home.kraxel.org (Postfix, from userid 500) id 8EC9742740; Wed, 26 Sep 2012 14:14:35 +0200 (CEST) From: Gerd Hoffmann To: qemu-devel@nongnu.org Date: Wed, 26 Sep 2012 14:14:34 +0200 Message-Id: <1348661674-16722-5-git-send-email-kraxel@redhat.com> In-Reply-To: <1348661674-16722-1-git-send-email-kraxel@redhat.com> References: <1348661674-16722-1-git-send-email-kraxel@redhat.com> X-Scanned-By: MIMEDefang 2.68 on 10.5.11.25 X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 209.132.183.28 Cc: Gerd Hoffmann Subject: [Qemu-devel] [PATCH v2 4/4] serial: add 2x + 4x pci variant X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Add multiport serial card implementation, with two variants, one featuring two and one featuring four ports. Signed-off-by: Gerd Hoffmann --- 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. + [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)