[PATCH] I/O port access routines

Adam Jackson ajax at redhat.com
Thu Dec 3 12:27:19 PST 2009


Signed-off-by: Adam Jackson <ajax at redhat.com>
---
 include/pciaccess.h     |   19 ++++
 src/Makefile.am         |    1 +
 src/common_init.c       |    1 +
 src/common_io.c         |  250 +++++++++++++++++++++++++++++++++++++++++++++++
 src/linux_sysfs.c       |  183 +++++++++++++++++++++++++++--------
 src/pciaccess_private.h |   22 ++++
 6 files changed, 436 insertions(+), 40 deletions(-)
 create mode 100644 src/common_io.c

diff --git a/include/pciaccess.h b/include/pciaccess.h
index 8128656..88515e2 100644
--- a/include/pciaccess.h
+++ b/include/pciaccess.h
@@ -1,5 +1,6 @@
 /*
  * (C) Copyright IBM Corporation 2006
+ * Copyright 2009 Red Hat, Inc.
  * All Rights Reserved.
  *
  * Permission is hereby granted, free of charge, to any person obtaining a
@@ -507,4 +508,22 @@ int  pci_device_vgaarb_unlock       (void);
 /* return the current device count + resource decodes for the device */
 int pci_device_vgaarb_get_info	    (struct pci_device *dev, int *vga_count, int *rsrc_decodes);
 
+/*
+ * I/O space access.
+ */
+
+struct pci_io_handle;
+
+struct pci_io_handle *pci_device_open_io(struct pci_device *dev, pciaddr_t base,
+					 pciaddr_t size);
+struct pci_io_handle *pci_legacy_open_io(struct pci_device *dev, pciaddr_t base,
+					 pciaddr_t size);
+void pci_device_close_io(struct pci_device *dev, struct pci_io_handle *handle);
+uint32_t pci_io_read32(struct pci_io_handle *handle, uint32_t reg);
+uint16_t pci_io_read16(struct pci_io_handle *handle, uint32_t reg);
+uint8_t pci_io_read8(struct pci_io_handle *handle, uint32_t reg);
+void pci_io_write32(struct pci_io_handle *handle, uint32_t reg, uint32_t data);
+void pci_io_write16(struct pci_io_handle *handle, uint32_t reg, uint16_t data);
+void pci_io_write8(struct pci_io_handle *handle, uint32_t reg, uint8_t data);
+
 #endif /* PCIACCESS_H */
diff --git a/src/Makefile.am b/src/Makefile.am
index 4dd7a5f..4c06c25 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -55,6 +55,7 @@ libpciaccess_la_SOURCES = common_bridge.c \
 	common_iterator.c \
 	common_init.c \
 	common_interface.c \
+	common_io.c \
 	common_capability.c \
 	common_device_name.c \
 	common_map.c \
diff --git a/src/common_init.c b/src/common_init.c
index 6b83d97..6b9b936 100644
--- a/src/common_init.c
+++ b/src/common_init.c
@@ -91,6 +91,7 @@ pci_system_cleanup( void )
 	return;
     }
 
+    pci_io_cleanup();
 
     if ( pci_sys->devices ) {
 	for ( i = 0 ; i < pci_sys->num_devices ; i++ ) {
diff --git a/src/common_io.c b/src/common_io.c
new file mode 100644
index 0000000..bc5ba0a
--- /dev/null
+++ b/src/common_io.c
@@ -0,0 +1,250 @@
+/*
+ * Copyright 2009 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software")
+ * to deal in the software without restriction, including without limitation
+ * on the rights to use, copy, modify, merge, publish, distribute, sub
+ * license, and/or sell copies of the Software, and to permit persons to whom
+ * them Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTIBILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER
+ * IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Author:
+ *	Adam Jackson <ajax at redhat.com>
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "pciaccess.h"
+#include "pciaccess_private.h"
+
+static struct pci_io_handle **ios;
+static unsigned int num_ios;
+
+static struct pci_io_handle *
+new_io_handle(void)
+{
+    struct pci_io_handle **new;
+
+    new = realloc(ios, sizeof(struct pci_io_handle) * (num_ios + 1));
+    if (!new)
+	return NULL;
+
+    ios = new;
+    num_ios++;
+
+    return ios[num_ios - 1];
+}
+
+static void
+delete_io_handle(struct pci_io_handle *handle)
+{
+    struct pci_io_handle **new;
+    int i = 0;
+
+    if (!handle || !num_ios || (void *)handle < (void *)ios ||
+        (void *)handle > (void *)(ios + num_ios - 1))
+        return;
+
+    for (i = 0; i < num_ios; i++) {
+        if (ios[i] == handle) {
+            memmove(&ios[i], &ios[i+1], sizeof(struct pci_io_handle) *
+                                        (num_ios - i - 1));
+            break;
+        }
+    }
+
+    new = realloc(ios, sizeof(struct pci_io_handle) * (num_ios - 1));
+    if (new)
+        ios = new;
+    num_ios--;
+}
+
+_pci_hidden void
+pci_io_cleanup(void)
+{
+    free(ios);
+    ios = NULL;
+    num_ios = 0;
+}
+
+/**
+ * Open a handle to a PCI device I/O range.  The \c base and \c size
+ * requested must fit entirely within a single I/O BAR on the device.
+ * \c size is in bytes.
+ *
+ * \returns
+ * An opaque handle to the I/O BAR, or \c NULL on error.
+ */
+struct pci_io_handle *
+pci_device_open_io(struct pci_device *dev, pciaddr_t base, pciaddr_t size)
+{
+    struct pci_io_handle *ret;
+    int bar;
+
+    if (!pci_sys->methods->open_device_io)
+	return NULL;
+
+    for (bar = 0; bar < 6; bar++) {
+	struct pci_mem_region *region = &(dev->regions[bar]);
+	if (!region->is_IO)
+	    continue;
+
+	if (base < region->base_addr || base > (region->base_addr+region->size))
+	    continue;
+
+	if ((base + size) > (region->base_addr + region->size))
+	    continue;
+
+	ret = new_io_handle();
+	if (!ret)
+	    return NULL;
+	
+	if (!pci_sys->methods->open_device_io(ret, dev, bar, base, size)) {
+	    delete_io_handle(ret);
+	    return NULL;
+	}
+    }
+
+    return NULL;
+}
+
+/**
+ * Open a handle to the legacy I/O space for the PCI domain containing
+ * \c dev. \c size is in bytes.
+ *
+ * \returns
+ * An opaque handle to the requested range, or \c NULL on error.
+ */
+struct pci_io_handle *
+pci_legacy_open_io(struct pci_device *dev, pciaddr_t base, pciaddr_t size)
+{
+    struct pci_io_handle *ret;
+
+    if (!pci_sys->methods->open_legacy_io)
+	return NULL;
+
+    ret = new_io_handle();
+    if (!ret)
+	return NULL;
+
+    if (!pci_sys->methods->open_legacy_io(ret, dev, base, size)) {
+	delete_io_handle(ret);
+	return NULL;
+    }
+
+    return ret;
+}
+
+/**
+ * Close an I/O handle.
+ */
+void
+pci_device_close_io(struct pci_device *dev, struct pci_io_handle *handle)
+{
+    if (dev && handle && pci_sys->methods->close_io)
+	pci_sys->methods->close_io(dev, handle);
+
+    delete_io_handle(handle);
+}
+
+/**
+ * Read a 32-bit value from the I/O space.  \c reg is relative to the
+ * \c base specified when the handle was opened.  Some platforms may
+ * require that \c reg be 32-bit-aligned.
+ *
+ * \returns
+ * The value read from the I/O port, or undefined on any error.
+ */
+uint32_t
+pci_io_read32(struct pci_io_handle *handle, uint32_t reg)
+{
+    if (reg + 4 > handle->size)
+	return UINT32_MAX;
+
+    return pci_sys->methods->read32(handle, reg);
+}
+
+/**
+ * Read a 16-bit value from the I/O space.  \c reg is relative to the
+ * \c base specified when the handle was opened.  Some platforms may
+ * require that \c reg be 16-bit-aligned.
+ *
+ * \returns
+ * The value read from the I/O port, or undefined on any error.
+ */
+uint16_t
+pci_io_read16(struct pci_io_handle *handle, uint32_t reg)
+{
+    if (reg + 2 > handle->size)
+	return UINT16_MAX;
+
+    return pci_sys->methods->read16(handle, reg);
+}
+
+/**
+ * Read a 8-bit value from the I/O space.  \c reg is relative to the
+ * \c base specified when the handle was opened.
+ *
+ * \returns
+ * The value read from the I/O port, or undefined on any error.
+ */
+uint8_t
+pci_io_read8(struct pci_io_handle *handle, uint32_t reg)
+{
+    if (reg + 1 > handle->size)
+	return UINT8_MAX;
+
+    return pci_sys->methods->read8(handle, reg);
+}
+
+/**
+ * Write a 32-bit value to the I/O space.  \c reg is relative to the
+ * \c base specified when the handle was opened.  Some platforms may
+ * require that \c reg be 32-bit-aligned.
+ */
+void
+pci_io_write32(struct pci_io_handle *handle, uint32_t reg, uint32_t data)
+{
+    if (reg + 4 > handle->size)
+	return;
+
+    pci_sys->methods->write32(handle, reg, data);
+}
+
+/**
+ * Write a 16-bit value to the I/O space.  \c reg is relative to the
+ * \c base specified when the handle was opened.  Some platforms may
+ * require that \c reg be 16-bit-aligned.
+ */
+void
+pci_io_write16(struct pci_io_handle *handle, uint32_t reg, uint16_t data)
+{
+    if (reg + 2 > handle->size)
+	return;
+
+    pci_sys->methods->write16(handle, reg, data);
+}
+
+/**
+ * Write a 8-bit value to the I/O space.  \c reg is relative to the
+ * \c base specified when the handle was opened.
+ */
+void
+pci_io_write8(struct pci_io_handle *handle, uint32_t reg, uint8_t data)
+{
+    if (reg + 1 > handle->size)
+	return;
+
+    pci_sys->methods->write8(handle, reg, data);
+}
diff --git a/src/linux_sysfs.c b/src/linux_sysfs.c
index 85095b3..1832ee7 100644
--- a/src/linux_sysfs.c
+++ b/src/linux_sysfs.c
@@ -55,52 +55,17 @@
 #include "pciaccess_private.h"
 #include "linux_devmem.h"
 
-static void pci_device_linux_sysfs_enable(struct pci_device *dev);
-
-static int pci_device_linux_sysfs_read_rom( struct pci_device * dev,
-    void * buffer );
-
-static int pci_device_linux_sysfs_probe( struct pci_device * dev );
-
-static int pci_device_linux_sysfs_map_range(struct pci_device *dev,
-    struct pci_device_mapping *map);
-
-static int pci_device_linux_sysfs_unmap_range(struct pci_device *dev,
-    struct pci_device_mapping *map);
-
-static int pci_device_linux_sysfs_read( struct pci_device * dev, void * data,
-    pciaddr_t offset, pciaddr_t size, pciaddr_t * bytes_read );
-
-static int pci_device_linux_sysfs_write( struct pci_device * dev,
-    const void * data, pciaddr_t offset, pciaddr_t size,
-    pciaddr_t * bytes_written );
-
-static int pci_device_linux_sysfs_boot_vga( struct pci_device * dev );
-static int pci_device_linux_sysfs_has_kernel_driver(struct pci_device *dev);
-
-static const struct pci_system_methods linux_sysfs_methods = {
-    .destroy = NULL,
-    .destroy_device = NULL,
-    .read_rom = pci_device_linux_sysfs_read_rom,
-    .probe = pci_device_linux_sysfs_probe,
-    .map_range = pci_device_linux_sysfs_map_range,
-    .unmap_range = pci_device_linux_sysfs_unmap_range,
-
-    .read = pci_device_linux_sysfs_read,
-    .write = pci_device_linux_sysfs_write,
-
-    .fill_capabilities = pci_fill_capabilities_generic,
-    .enable = pci_device_linux_sysfs_enable,
-    .boot_vga = pci_device_linux_sysfs_boot_vga,
-    .has_kernel_driver = pci_device_linux_sysfs_has_kernel_driver,
-};
+static const struct pci_system_methods linux_sysfs_methods;
 
 #define SYS_BUS_PCI "/sys/bus/pci/devices"
 
+static int
+pci_device_linux_sysfs_read( struct pci_device * dev, void * data,
+			     pciaddr_t offset, pciaddr_t size,
+			     pciaddr_t * bytes_read );
 
 static int populate_entries(struct pci_system * pci_sys);
 
-
 /**
  * Attempt to access PCI subsystem using Linux's sysfs interface.
  */
@@ -759,3 +724,141 @@ static int pci_device_linux_sysfs_has_kernel_driver(struct pci_device *dev)
 	return 0;
     return 1;
 }
+
+static struct pci_io_handle *
+pci_device_linux_sysfs_open_device_io(struct pci_io_handle *ret,
+				      struct pci_device *dev, int bar,
+				      pciaddr_t base, pciaddr_t size)
+{
+    char name[PATH_MAX];
+
+    snprintf(name, PATH_MAX, "%s/%04x:%02x:%02x.%1u/resource%d",
+	     SYS_BUS_PCI, dev->domain, dev->bus, dev->dev, dev->func, bar);
+
+    ret->fd = open(name, O_RDWR);
+
+    if (ret->fd < 0)
+	return NULL;
+
+    ret->base = base;
+    ret->size = size;
+
+    return ret;
+}
+
+static struct pci_io_handle *
+pci_device_linux_sysfs_open_legacy_io(struct pci_io_handle *ret,
+				      struct pci_device *dev, pciaddr_t base,
+				      pciaddr_t size)
+{
+    char name[PATH_MAX];
+
+    /* First check if there's a legacy io method for the device */
+    while (dev) {
+	snprintf(name, PATH_MAX, "/sys/class/pci_bus/%04x:%02x/legacy_io",
+		 dev->domain, dev->bus);
+
+	ret->fd = open(name, O_RDWR);
+	if (ret->fd >= 0)
+	    break;
+
+	dev = pci_device_get_parent_bridge(dev);
+    }
+
+    /* If not, /dev/port is the best we can do */
+    if (!dev)
+	ret->fd = open("/dev/port", O_RDWR);
+
+    if (ret->fd < 0)
+	return NULL;
+
+    ret->base = base;
+    ret->size = size;
+
+    return ret;
+}
+
+static void
+pci_device_linux_sysfs_close_io(struct pci_device *dev,
+				struct pci_io_handle *handle)
+{
+    close(handle->fd);
+}
+
+static uint32_t
+pci_device_linux_sysfs_read32(struct pci_io_handle *handle, uint32_t port)
+{
+    uint32_t ret;
+
+    pread(handle->fd, &ret, 4, port + handle->base);
+
+    return ret;
+}
+
+static uint16_t
+pci_device_linux_sysfs_read16(struct pci_io_handle *handle, uint32_t port)
+{
+    uint16_t ret;
+
+    pread(handle->fd, &ret, 2, port + handle->base);
+
+    return ret;
+}
+
+static uint8_t
+pci_device_linux_sysfs_read8(struct pci_io_handle *handle, uint32_t port)
+{
+    uint8_t ret;
+
+    pread(handle->fd, &ret, 1, port + handle->base);
+
+    return ret;
+}
+
+static void
+pci_device_linux_sysfs_write32(struct pci_io_handle *handle, uint32_t port,
+			       uint32_t data)
+{
+    pwrite(handle->fd, &data, 4, port + handle->base);
+}
+
+static void
+pci_device_linux_sysfs_write16(struct pci_io_handle *handle, uint32_t port,
+			       uint16_t data)
+{
+    pwrite(handle->fd, &data, 2, port + handle->base);
+}
+
+static void
+pci_device_linux_sysfs_write8(struct pci_io_handle *handle, uint32_t port,
+			      uint8_t data)
+{
+    pwrite(handle->fd, &data, 1, port + handle->base);
+}
+
+static const struct pci_system_methods linux_sysfs_methods = {
+    .destroy = NULL,
+    .destroy_device = NULL,
+    .read_rom = pci_device_linux_sysfs_read_rom,
+    .probe = pci_device_linux_sysfs_probe,
+    .map_range = pci_device_linux_sysfs_map_range,
+    .unmap_range = pci_device_linux_sysfs_unmap_range,
+
+    .read = pci_device_linux_sysfs_read,
+    .write = pci_device_linux_sysfs_write,
+
+    .fill_capabilities = pci_fill_capabilities_generic,
+    .enable = pci_device_linux_sysfs_enable,
+    .boot_vga = pci_device_linux_sysfs_boot_vga,
+    .has_kernel_driver = pci_device_linux_sysfs_has_kernel_driver,
+
+    .open_device_io = pci_device_linux_sysfs_open_device_io,
+    .open_legacy_io = pci_device_linux_sysfs_open_legacy_io,
+    .close_io = pci_device_linux_sysfs_close_io,
+    .read32 = pci_device_linux_sysfs_read32,
+    .read16 = pci_device_linux_sysfs_read16,
+    .read8 = pci_device_linux_sysfs_read8,
+    .write32 = pci_device_linux_sysfs_write32,
+    .write16 = pci_device_linux_sysfs_write16,
+    .write8 = pci_device_linux_sysfs_write8,
+};
diff --git a/src/pciaccess_private.h b/src/pciaccess_private.h
index a9d8df0..77eb57b 100644
--- a/src/pciaccess_private.h
+++ b/src/pciaccess_private.h
@@ -62,6 +62,21 @@ struct pci_system_methods {
     void (*enable)( struct pci_device *dev );
     int (*boot_vga)( struct pci_device *dev );
     int (*has_kernel_driver)( struct pci_device *dev );
+    struct pci_io_handle *(*open_device_io)( struct pci_io_handle *handle,
+					     struct pci_device *dev, int bar,
+					     pciaddr_t base, pciaddr_t size );
+    struct pci_io_handle *(*open_legacy_io)( struct pci_io_handle *handle,
+					     struct pci_device *dev,
+					     pciaddr_t base, pciaddr_t size );
+    void (*close_io)( struct pci_device *dev, struct pci_io_handle *handle );
+    uint32_t (*read32)( struct pci_io_handle *handle, uint32_t reg );
+    uint16_t (*read16)( struct pci_io_handle *handle, uint32_t reg );
+    uint8_t  (*read8)( struct pci_io_handle *handle, uint32_t reg );
+    void (*write32)( struct pci_io_handle *handle, uint32_t reg,
+		     uint32_t data );
+    void (*write16)( struct pci_io_handle *handle, uint32_t reg,
+		     uint16_t data );
+    void (*write8)( struct pci_io_handle *handle, uint32_t reg, uint8_t data );
 };
 
 struct pci_device_mapping {
@@ -72,6 +87,12 @@ struct pci_device_mapping {
     void *memory;
 };
 
+struct pci_io_handle {
+    pciaddr_t base;
+    pciaddr_t size;
+    int fd;
+};
+
 struct pci_device_private {
     struct pci_device  base;
     const char * device_string;
@@ -146,3 +167,4 @@ extern int pci_system_netbsd_create( void );
 extern int pci_system_openbsd_create( void );
 extern void pci_system_openbsd_init_dev_mem( int );
 extern int pci_system_solx_devfs_create( void );
+extern void pci_io_cleanup( void );
-- 
1.6.5.2



More information about the xorg-devel mailing list