Patchwork [04/16] isa: Add isa_register_portio_list().

login
register
mail settings
Submitter Richard Henderson
Date Aug. 24, 2011, 12:13 a.m.
Message ID <1314144835-29098-5-git-send-email-rth@twiddle.net>
Download mbox | patch
Permalink /patch/111222/
State New
Headers show

Comments

Richard Henderson - Aug. 24, 2011, 12:13 a.m.
Signed-off-by: Richard Henderson <rth@twiddle.net>
---
 hw/isa-bus.c |   79 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/isa.h     |   31 ++++++++++++++++++++++-
 memory.c     |    8 +++---
 3 files changed, 113 insertions(+), 5 deletions(-)

Patch

diff --git a/hw/isa-bus.c b/hw/isa-bus.c
index e9c1712..648b421 100644
--- a/hw/isa-bus.c
+++ b/hw/isa-bus.c
@@ -103,6 +103,85 @@  void isa_register_ioport(ISADevice *dev, MemoryRegion *io, uint16_t start)
     }
 }
 
+static void isa_register_portio_1(ISADevice *dev,
+                                  const MemoryRegionPortio *pio_init,
+                                  unsigned count, unsigned start,
+                                  unsigned off_low, unsigned off_high,
+                                  void *opaque, const char *name)
+{
+    MemoryRegionPortio *pio;
+    MemoryRegionOps *ops;
+    MemoryRegion *region;
+    unsigned i;
+
+    if (off_low == 0 && pio_init[count].size == 0) {
+        /* Special case simple adjustments.  */
+        pio = (MemoryRegionPortio *) pio_init;
+    } else {
+        /* Copy the sub-list and null-terminate it.  */
+        pio = g_new(MemoryRegionPortio, count + 1);
+        memcpy(pio, pio_init, sizeof(MemoryRegionPortio) * count);
+        memset(pio + count, 0, sizeof(MemoryRegionPortio));
+
+        /* Adjust the offsets to all be zero-based for the region.  */
+        for (i = 0; i < count; ++i) {
+            pio[i].offset -= off_low;
+        }
+    }
+
+    ops = g_new0(MemoryRegionOps, 1);
+    ops->old_portio = pio;
+
+    region = g_new(MemoryRegion, 1);
+    memory_region_init_io(region, ops, opaque, name, off_high - off_low);
+    memory_region_set_offset(region, start + off_low);
+    memory_region_add_subregion(isabus->address_space_io,
+                                start + off_low, region);
+}
+
+void isa_register_portio_list(ISADevice *dev, uint16_t start,
+                              const MemoryRegionPortio *pio_start,
+                              void *opaque, const char *name)
+{
+    const MemoryRegionPortio *pio;
+    unsigned int off_low, off_high, off_last, count;
+
+    /* START is how we should treat DEV, regardless of the actual
+       contents of the portio array.  This is how the old code
+       actually handled e.g. the FDC device.  */
+    if (dev) {
+        isa_init_ioport(dev, start);
+    }
+
+    /* Handle the first entry specially.  */
+    off_last = off_low = pio_start->offset;
+    off_high = off_low + pio_start->len;
+    count = 1;
+
+    for (pio = pio_start + 1; pio->size != 0; pio++, count++) {
+        /* All entries must be sorted by offset.  */
+        assert(pio->offset >= off_last);
+        off_last = pio->offset;
+
+        /* If we see a hole, break the region.  */
+        if (off_last > off_high) {
+            isa_register_portio_1(dev, pio_start, count, start, off_low,
+                                  off_high, opaque, name);
+            /* ... and start collecting anew.  */
+            pio_start = pio;
+            off_low = off_last;
+            off_high = off_low + pio->len;
+            count = 0;
+        } else if (off_last + pio->len > off_high) {
+            off_high = off_last + pio->len;
+        }
+    }
+
+    /* There will always be an open sub-list.  */
+    isa_register_portio_1(dev, pio_start, count, start, off_low,
+                          off_high, opaque, name);
+}
+
 static int isa_qdev_init(DeviceState *qdev, DeviceInfo *base)
 {
     ISADevice *dev = DO_UPCAST(ISADevice, qdev, qdev);
diff --git a/hw/isa.h b/hw/isa.h
index c5c2618..177ef95 100644
--- a/hw/isa.h
+++ b/hw/isa.h
@@ -28,7 +28,6 @@  ISABus *isa_bus_new(DeviceState *dev, MemoryRegion *address_space_io);
 void isa_bus_irqs(qemu_irq *irqs);
 qemu_irq isa_get_irq(int isairq);
 void isa_init_irq(ISADevice *dev, qemu_irq *p, int isairq);
-void isa_register_ioport(ISADevice *dev, MemoryRegion *io, uint16_t start);
 void isa_init_ioport(ISADevice *dev, uint16_t ioport);
 void isa_init_ioport_range(ISADevice *dev, uint16_t start, uint16_t length);
 void isa_qdev_register(ISADeviceInfo *info);
@@ -37,6 +36,36 @@  ISADevice *isa_create(const char *name);
 ISADevice *isa_try_create(const char *name);
 ISADevice *isa_create_simple(const char *name);
 
+/**
+ * isa_register_ioport: Install an I/O port region on the ISA bus.
+ *
+ * Register an I/O port region via memory_region_add_subregion
+ * inside the ISA I/O address space.
+ *
+ * @dev: the ISADevice against which these are registered; may be NULL.
+ * @io: the #MemoryRegion being registered.
+ * @start: the base I/O port.
+ */
+void isa_register_ioport(ISADevice *dev, MemoryRegion *io, uint16_t start);
+
+/**
+ * isa_register_portio_list: Initialize a set of ISA io ports
+ *
+ * Several ISA devices have many dis-joint I/O ports.  Worse, these I/O
+ * ports can be interleaved with I/O ports from other devices.  This
+ * function makes it easy to create multiple MemoryRegions for a single
+ * device and use the legacy portio routines.
+ *
+ * @dev: the ISADevice against which these are registered; may be NULL.
+ * @start: the base I/O port against which the portio->offset is applied.
+ * @portio: the ports, sorted by offset.
+ * @opaque: passed into the old_portio callbacks.
+ * @name: passed into memory_region_init_io.
+ */
+void isa_register_portio_list(ISADevice *dev, uint16_t start,
+                              const MemoryRegionPortio *portio,
+                              void *opaque, const char *name);
+
 extern target_phys_addr_t isa_mem_base;
 
 void isa_mmio_setup(MemoryRegion *mr, target_phys_addr_t size);
diff --git a/memory.c b/memory.c
index 8e9ac46..6fc53b8 100644
--- a/memory.c
+++ b/memory.c
@@ -396,12 +396,12 @@  static void memory_region_iorange_read(IORange *iorange,
 
         *data = ((uint64_t)1 << (width * 8)) - 1;
         if (mrp) {
-            *data = mrp->read(mr->opaque, offset);
+            *data = mrp->read(mr->opaque, offset + mr->offset);
         }
         return;
     }
     *data = 0;
-    access_with_adjusted_size(offset, data, width,
+    access_with_adjusted_size(offset + mr->offset, data, width,
                               mr->ops->impl.min_access_size,
                               mr->ops->impl.max_access_size,
                               memory_region_read_accessor, mr);
@@ -418,11 +418,11 @@  static void memory_region_iorange_write(IORange *iorange,
         const MemoryRegionPortio *mrp = find_portio(mr, offset, width, true);
 
         if (mrp) {
-            mrp->write(mr->opaque, offset, data);
+            mrp->write(mr->opaque, offset + mr->offset, data);
         }
         return;
     }
-    access_with_adjusted_size(offset, &data, width,
+    access_with_adjusted_size(offset + mr->offset, &data, width,
                               mr->ops->impl.min_access_size,
                               mr->ops->impl.max_access_size,
                               memory_region_write_accessor, mr);