[PATCH 2/2] I/O port access routines

Luc Verhaegen libv at skynet.be
Wed Nov 18 15:37:10 PST 2009


On Wed, Nov 18, 2009 at 02:28:57PM -0500, Adam Jackson wrote:
> Signed-off-by: Adam Jackson <ajax at redhat.com>
> ---
>  include/pciaccess.h     |   14 ++++
>  src/Makefile.am         |    1 +
>  src/common_io.c         |   95 ++++++++++++++++++++++++
>  src/linux_sysfs.c       |  184 ++++++++++++++++++++++++++++++++++++----------
>  src/pciaccess_private.h |    9 +++
>  5 files changed, 263 insertions(+), 40 deletions(-)
>  create mode 100644 src/common_io.c
> 
> diff --git a/include/pciaccess.h b/include/pciaccess.h
> index 8128656..b4a431a 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,17 @@ 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.
> + */
> +void *pci_device_open_io(struct pci_device *dev, int bar);
> +void *pci_legacy_open_io(struct pci_device *dev);
> +void pci_device_close_io(struct pci_device *dev, void *handle);
> +uint32_t pci_io_inl(void *handle, uint16_t reg);
> +uint16_t pci_io_inw(void *handle, uint16_t reg);
> +uint8_t pci_io_inb(void *handle, uint16_t reg);
> +void pci_io_outl(void *handle, uint16_t reg, uint32_t data);
> +void pci_io_outw(void *handle, uint16_t reg, uint16_t data);
> +void pci_io_outb(void *handle, uint16_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_io.c b/src/common_io.c
> new file mode 100644
> index 0000000..9828af4
> --- /dev/null
> +++ b/src/common_io.c
> @@ -0,0 +1,95 @@
> +/*
> + * 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 "pciaccess.h"
> +#include "pciaccess_private.h"
> +
> +void *
> +pci_device_open_io(struct pci_device *dev, int bar)
> +{
> +    if (!pci_sys->methods->open_device_io)
> +	return NULL;
> +
> +    if (bar < 0 || bar > 6)
> +	return NULL;
> +
> +    if (!dev->regions[bar].is_IO)
> +	return NULL;
> +
> +    return pci_sys->methods->open_device_io(dev, bar);
> +}
> +
> +void *
> +pci_legacy_open_io(struct pci_device *dev)
> +{
> +    if (!pci_sys->methods->open_legacy_io)
> +	return NULL;
> +
> +    return pci_sys->methods->open_legacy_io(dev);
> +}
> +
> +void
> +pci_device_close_io(struct pci_device *dev, void *handle)
> +{
> +    if (dev && handle && pci_sys->methods->close_io)
> +	pci_sys->methods->close_io(dev, handle);
> +}
> +
> +uint32_t
> +pci_io_inl(void *handle, uint16_t reg)
> +{
> +    return pci_sys->methods->inl(handle, reg);
> +}
> +
> +uint16_t
> +pci_io_inw(void *handle, uint16_t reg)
> +{
> +    return pci_sys->methods->inw(handle, reg);
> +}
> +
> +uint8_t
> +pci_io_inb(void *handle, uint16_t reg)
> +{
> +    return pci_sys->methods->inb(handle, reg);
> +}
> +
> +void
> +pci_io_outl(void *handle, uint16_t reg, uint32_t data)
> +{
> +    pci_sys->methods->outl(handle, reg, data);
> +}
> +
> +void
> +pci_io_outw(void *handle, uint16_t reg, uint16_t data)
> +{
> +    pci_sys->methods->outw(handle, reg, data);
> +}
> +
> +void
> +pci_io_outb(void *handle, uint16_t reg, uint8_t data)
> +{
> +    pci_sys->methods->outb(handle, reg, data);
> +}
> diff --git a/src/linux_sysfs.c b/src/linux_sysfs.c
> index 85095b3..eb8ea5f 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,142 @@ static int pci_device_linux_sysfs_has_kernel_driver(struct pci_device *dev)
>  	return 0;
>      return 1;
>  }
> +
> +static void *
> +pci_device_linux_sysfs_open_device_io(struct pci_device *dev, int bar)
> +{
> +    char name[PATH_MAX];
> +    int ret;
> +
> +    snprintf(name, PATH_MAX, "%s/%04x:%02x:%02x.%1u/resource%d",
> +	     SYS_BUS_PCI, dev->domain, dev->bus, dev->dev, dev->func, bar);
> +
> +    ret = open(name, O_RDWR);
> +
> +    /* Avoid returning fd 0 since it's not distinguishable from failure */
> +    if (ret == 0) {
> +	int new = dup(ret);
> +	close(ret);
> +	ret = new;
> +    }
> +
> +    if (ret < 0)
> +	return NULL;
> +
> +    return (void *)ret;
> +}
> +
> +static void *
> +pci_device_linux_sysfs_open_legacy_io(struct pci_device *dev)
> +{
> +    char name[PATH_MAX];
> +    int ret;
> +
> +    /* 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 = open(name, O_RDWR);
> +	if (ret >= 0)
> +	    goto out;
> +
> +	dev = pci_device_get_parent_bridge(dev);
> +    }
> +
> +    /* If not, /dev/port is the best we can do */
> +    ret = open("/dev/port", O_RDWR);
> +
> +out:
> +    if (ret == 0) {
> +	int new = dup(ret);
> +	close(ret);
> +	ret = new;
> +    }
> +
> +    if (ret < 0)
> +	return NULL;
> +
> +    return (void *)ret;
> +}
> +
> +static void
> +pci_device_linux_sysfs_close_io(struct pci_device *dev, void *handle)
> +{
> +    close((int)handle);
> +}
> +
> +static uint32_t
> +pci_device_linux_sysfs_inl(void *handle, uint16_t port)
> +{
> +    uint32_t ret;
> +
> +    pread((int)handle, &ret, 4, port);
> +
> +    return ret;
> +}
> +
> +static uint16_t
> +pci_device_linux_sysfs_inw(void *handle, uint16_t port)
> +{
> +    uint16_t ret;
> +
> +    pread((int)handle, &ret, 2, port);
> +
> +    return ret;
> +}
> +
> +static uint8_t
> +pci_device_linux_sysfs_inb(void *handle, uint16_t port)
> +{
> +    uint8_t ret;
> +
> +    pread((int)handle, &ret, 1, port);
> +
> +    return ret;
> +}
> +
> +static void
> +pci_device_linux_sysfs_outl(void *handle, uint16_t port, uint32_t data)
> +{
> +    pwrite((int)handle, &data, 4, port);
> +}
> +
> +static void
> +pci_device_linux_sysfs_outw(void *handle, uint16_t port, uint16_t data)
> +{
> +    pwrite((int)handle, &data, 2, port);
> +}
> +
> +static void
> +pci_device_linux_sysfs_outb(void *handle, uint16_t port, uint8_t data)
> +{
> +    pwrite((int)handle, &data, 1, port);
> +}
> +
> +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,
> +    .inl = pci_device_linux_sysfs_inl,
> +    .inw = pci_device_linux_sysfs_inw,
> +    .inb = pci_device_linux_sysfs_inb,
> +    .outl = pci_device_linux_sysfs_outl,
> +    .outw = pci_device_linux_sysfs_outw,
> +    .outb = pci_device_linux_sysfs_outb,
> +};
> diff --git a/src/pciaccess_private.h b/src/pciaccess_private.h
> index a9d8df0..b39d9a7 100644
> --- a/src/pciaccess_private.h
> +++ b/src/pciaccess_private.h
> @@ -62,6 +62,15 @@ 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 );
> +    void *(*open_device_io)( struct pci_device *dev, int bar );
> +    void *(*open_legacy_io)( struct pci_device *dev );
> +    void (*close_io)( struct pci_device *dev, void *handle );
> +    uint32_t (*inl)( void *handle, uint16_t reg );
> +    uint16_t (*inw)( void *handle, uint16_t reg );
> +    uint8_t  (*inb)( void *handle, uint16_t reg );
> +    void (*outl)( void *handle, uint16_t reg, uint32_t data );
> +    void (*outw)( void *handle, uint16_t reg, uint16_t data );
> +    void (*outb)( void *handle, uint16_t reg, uint8_t data );
>  };
>  
>  struct pci_device_mapping {
> -- 
> 1.6.5.2

So how would we go about using any of this then? Do we get to have 
another huge API/ABI breakage like when libpciaccess was introduced just 
because the normal in/out routines should no longer be used?

And i just keep on wondering... Just like with the VGA arbitration... 
Didn't the RAC used wrap all this crap for us _outside_ calls to driver 
code so that we could just access IO and memory directly, without 
having to care at all? How hard would it have been to have written up a 
backend for the RAC for the linux pci code that popped up during the 
active life of RAC? My guess is, much easier than the stuff that has to 
be pulled now, all it took was that someone was willing to go into 
existing code and understand how it work(s/ed).

Oh, and that's before i start wondering about gratuitous void pointers 
and goto.

Luc Verhaegen.


More information about the xorg-devel mailing list