summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDave Airlie <airlied@redhat.com>2009-08-21 13:27:11 +1000
committerDave Airlie <airlied@redhat.com>2009-08-21 13:27:11 +1000
commitf5148525d546015c12b703a768b5e6c3726446a3 (patch)
tree4da151ba09fdedfeef11d8386b7cc95dc84ae9c6
parent76363486864b325b8f156ece35736ddb64c4e697 (diff)
parentef8aca5da46155e9ea0d1227215921ad9ce59150 (diff)
Merge remote branch 'origin/vga-arbiter'
-rw-r--r--include/pciaccess.h56
-rw-r--r--src/Makefile.am5
-rw-r--r--src/common_interface.c15
-rw-r--r--src/common_vgaarb.c324
-rw-r--r--src/linux_sysfs.c21
-rw-r--r--src/pciaccess_private.h5
6 files changed, 426 insertions, 0 deletions
diff --git a/include/pciaccess.h b/include/pciaccess.h
index 6413ae0..067954f 100644
--- a/include/pciaccess.h
+++ b/include/pciaccess.h
@@ -21,6 +21,31 @@
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
+/*
+ * Copyright (c) 2007 Paulo R. Zanoni, Tiago Vignatti
+ *
+ * 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 the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice 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 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS 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.
+ *
+ */
/**
* \file pciaccess.h
@@ -50,6 +75,8 @@ struct pci_slot_match;
extern "C" {
#endif
+int pci_device_has_kernel_driver(struct pci_device *dev);
+
int pci_device_is_boot_vga(struct pci_device *dev);
int pci_device_read_rom(struct pci_device *dev, void *buffer);
@@ -350,6 +377,11 @@ struct pci_device {
* the \c pci_device structure.
*/
intptr_t user_data;
+
+ /**
+ * Used by the VGA arbiter. Type of resource decoded by the device and
+ * the file descriptor (/dev/vga_arbiter). */
+ int vgaarb_rsrc;
};
@@ -449,4 +481,28 @@ struct pci_pcmcia_bridge_info {
};
+
+/**
+ * VGA Arbiter definitions, functions and related.
+ */
+
+/* Legacy VGA regions */
+#define VGA_ARB_RSRC_NONE 0x00
+#define VGA_ARB_RSRC_LEGACY_IO 0x01
+#define VGA_ARB_RSRC_LEGACY_MEM 0x02
+/* Non-legacy access */
+#define VGA_ARB_RSRC_NORMAL_IO 0x04
+#define VGA_ARB_RSRC_NORMAL_MEM 0x08
+
+int pci_device_vgaarb_init (void);
+void pci_device_vgaarb_fini (void);
+int pci_device_vgaarb_set_target (struct pci_device *dev);
+/* use the targetted device */
+int pci_device_vgaarb_decodes (int new_vga_rsrc);
+int pci_device_vgaarb_lock (void);
+int pci_device_vgaarb_trylock (void);
+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);
+
#endif /* PCIACCESS_H */
diff --git a/src/Makefile.am b/src/Makefile.am
index 14f2e48..c7be653 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -45,6 +45,10 @@ if SOLARIS
OS_SUPPORT = solx_devfs.c pci_tools.h
endif
+if LINUX
+VGA_ARBITER = common_vgaarb.c
+endif
+
libpciaccess_la_SOURCES = common_bridge.c \
common_iterator.c \
common_init.c \
@@ -53,6 +57,7 @@ libpciaccess_la_SOURCES = common_bridge.c \
common_device_name.c \
common_map.c \
pciaccess_private.h \
+ $(VGA_ARBITER) \
$(OS_SUPPORT)
INCLUDES = -I$(top_srcdir)/include
diff --git a/src/common_interface.c b/src/common_interface.c
index 5dcc707..d46feab 100644
--- a/src/common_interface.c
+++ b/src/common_interface.c
@@ -124,6 +124,21 @@ pci_device_is_boot_vga( struct pci_device * dev )
}
/**
+ * Probe a PCI device to determine if a kernel driver is attached.
+ *
+ * \param dev Device to query
+ * \return
+ * Zero if no driver attached, 1 if attached kernel drviver
+ */
+int
+pci_device_has_kernel_driver( struct pci_device * dev )
+{
+ if (!pci_sys->methods->has_kernel_driver)
+ return 0;
+ return pci_sys->methods->has_kernel_driver( dev );
+}
+
+/**
* Probe a PCI device to learn information about the device.
*
* Probes a PCI device to learn various information about the device. Before
diff --git a/src/common_vgaarb.c b/src/common_vgaarb.c
new file mode 100644
index 0000000..b7ef021
--- /dev/null
+++ b/src/common_vgaarb.c
@@ -0,0 +1,324 @@
+/*
+ * Copyright (c) 2007 Paulo R. Zanoni, Tiago Vignatti
+ * 2009 Tiago Vignatti
+ *
+ * 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 the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice 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 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS 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.
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <limits.h>
+
+#include "pciaccess.h"
+#include "pciaccess_private.h"
+
+#define BUFSIZE 64
+
+static int
+parse_string_to_decodes_rsrc(char *input, int *vga_count, struct pci_slot_match *match)
+{
+ char *tok;
+ char *input_sp, *count_sp, *pci_sp;
+ char tmp[32];
+
+ tok = strtok_r(input,",",&input_sp);
+ if (!tok)
+ goto fail;
+
+ strncpy(tmp, input, 15);
+ tmp[15] = 0;
+
+ tok = strtok_r(tmp,":",&count_sp);
+ if (!tok)
+ goto fail;
+ tok = strtok_r(NULL, ":",&count_sp);
+ if (!tok)
+ goto fail;
+
+ *vga_count = strtoul(tok, NULL, 10);
+ if (*vga_count == LONG_MAX)
+ goto fail;
+
+#ifdef DEBUG
+ fprintf(stderr,"vga count is %d\n", *vga_count);
+#endif
+
+ tok = strtok_r(NULL, ",",&input_sp);
+ if (!tok)
+ goto fail;
+
+ if (match) {
+ strncpy(tmp, tok, 32);
+ tmp[31] = 0;
+ tok = strtok_r(tmp, ":", &pci_sp);
+ if (!tok)
+ goto fail;
+ tok = strtok_r(NULL, ":", &pci_sp);
+ if (!tok)
+ goto fail;
+ match->domain = strtoul(tok, NULL, 16);
+
+ tok = strtok_r(NULL, ":", &pci_sp);
+ if (!tok)
+ goto fail;
+ match->bus = strtoul(tok, NULL, 16);
+
+ tok = strtok_r(NULL, ".", &pci_sp);
+ if (!tok)
+ goto fail;
+ match->dev = strtoul(tok, NULL, 16);
+
+ tok = strtok_r(NULL, ".", &pci_sp);
+ if (!tok)
+ goto fail;
+ match->func = strtoul(tok, NULL, 16);
+ }
+
+ tok = strtok_r(NULL, ",",&input_sp);
+ if (!tok)
+ goto fail;
+ tok = strtok_r(tok, "=", &input_sp);
+ if (!tok)
+ goto fail;
+ tok = strtok_r(NULL, "=", &input_sp);
+ if (!tok)
+ goto fail;
+
+ if (!strncmp(tok, "io+mem", 6))
+ return VGA_ARB_RSRC_LEGACY_IO | VGA_ARB_RSRC_LEGACY_MEM;
+ if (!strncmp(tok, "io", 2))
+ return VGA_ARB_RSRC_LEGACY_IO;
+ if (!strncmp(tok, "mem", 3))
+ return VGA_ARB_RSRC_LEGACY_MEM;
+fail:
+ return VGA_ARB_RSRC_NONE;
+}
+
+int
+pci_device_vgaarb_init(void)
+{
+ struct pci_slot_match match;
+ char buf[BUFSIZE];
+ int ret, rsrc;
+ if ((pci_sys->vgaarb_fd = open ("/dev/vga_arbiter", O_RDWR)) < 0) {
+ return errno;
+ }
+
+ ret = read(pci_sys->vgaarb_fd, buf, BUFSIZE);
+ if (ret <= 0)
+ return -1;
+
+ memset(&match, 0xff, sizeof(match));
+ /* need to find the device to go back to and what it was decoding */
+ rsrc = parse_string_to_decodes_rsrc(buf, &pci_sys->vga_count, &match);
+
+ pci_sys->vga_default_dev = pci_device_find_by_slot(match.domain, match.bus, match.dev, match.func);
+
+ if (pci_sys->vga_default_dev)
+ pci_sys->vga_default_dev->vgaarb_rsrc = rsrc;
+ return 0;
+}
+
+void
+pci_device_vgaarb_fini(void)
+{
+ close(pci_sys->vgaarb_fd);
+}
+
+/**
+ * Writes message on vga device. The messages are defined by the kernel
+ * implementation.
+ *
+ * \param fd vga arbiter device.
+ * \param buf message itself.
+ * \param len message length.
+ *
+ * \return
+ * Zero on success, 1 if something gets wrong and 2 if fd is busy (only for
+ * 'trylock')
+ */
+static int
+vgaarb_write(int fd, char *buf, int len)
+{
+ int ret;
+
+
+ buf[len] = '\0';
+
+ ret = write(fd, buf, len);
+ if (ret == -1) {
+ /* the user may have called "trylock" and didn't get the lock */
+ if (errno == EBUSY)
+ return 2;
+
+#ifdef DEBUG
+ fprintf(stderr, "write error");
+#endif
+ return 1;
+ }
+ else if (ret != len) {
+ /* it's need to receive the exactly amount required. */
+#ifdef DEBUG
+ fprintf(stderr, "write error: wrote different than expected\n");
+#endif
+ return 1;
+ }
+
+#ifdef DEBUG
+ fprintf(stderr, "%s: successfully wrote: '%s'\n", __FUNCTION__, buf);
+#endif
+
+ return 0;
+}
+
+
+static const char *
+rsrc_to_str(int iostate)
+{
+ switch (iostate) {
+ case VGA_ARB_RSRC_LEGACY_IO | VGA_ARB_RSRC_LEGACY_MEM:
+ return "io+mem";
+ case VGA_ARB_RSRC_LEGACY_IO:
+ return "io";
+ case VGA_ARB_RSRC_LEGACY_MEM:
+ return "mem";
+ }
+
+ return "none";
+}
+
+int
+pci_device_vgaarb_set_target(struct pci_device *dev)
+{
+ int len;
+ char buf[BUFSIZE];
+ int ret;
+
+ if (!dev)
+ dev = pci_sys->vga_default_dev;
+ if (!dev)
+ return -1;
+
+ len = snprintf(buf, BUFSIZE, "target PCI:%04x:%02x:%02x.%x",
+ dev->domain, dev->bus, dev->dev, dev->func);
+
+ ret = vgaarb_write(pci_sys->vgaarb_fd, buf, len);
+ if (ret)
+ return ret;
+
+ ret = read(pci_sys->vgaarb_fd, buf, BUFSIZE);
+ if (ret <= 0)
+ return -1;
+
+ dev->vgaarb_rsrc = parse_string_to_decodes_rsrc(buf, &pci_sys->vga_count, NULL);
+ pci_sys->vga_target = dev;
+ return 0;
+}
+
+int
+pci_device_vgaarb_decodes(int new_vgaarb_rsrc)
+{
+ int len;
+ char buf[BUFSIZE];
+ int ret;
+ struct pci_device *dev = pci_sys->vga_target;
+
+ if (!dev)
+ return -1;
+ if (dev->vgaarb_rsrc == new_vgaarb_rsrc)
+ return 0;
+
+ len = snprintf(buf, BUFSIZE, "decodes %s", rsrc_to_str(dev->vgaarb_rsrc));
+ ret = vgaarb_write(pci_sys->vgaarb_fd, buf, len);
+ if (ret == 0)
+ dev->vgaarb_rsrc = new_vgaarb_rsrc;
+ return ret;
+}
+
+int
+pci_device_vgaarb_lock(void)
+{
+ int len;
+ char buf[BUFSIZE];
+ struct pci_device *dev = pci_sys->vga_target;
+
+ if (!dev)
+ return -1;
+
+ if (dev->vgaarb_rsrc == 0 || pci_sys->vga_count == 1)
+ return 0;
+
+ len = snprintf(buf, BUFSIZE, "lock %s", rsrc_to_str(dev->vgaarb_rsrc));
+
+ return vgaarb_write(pci_sys->vgaarb_fd, buf, len);
+}
+
+int
+pci_device_vgaarb_trylock(void)
+{
+ int len;
+ char buf[BUFSIZE];
+ struct pci_device *dev = pci_sys->vga_target;
+
+ if (!dev)
+ return -1;
+
+ if (dev->vgaarb_rsrc == 0 || pci_sys->vga_count == 1)
+ return 0;
+
+ len = snprintf(buf, BUFSIZE, "trylock %s", rsrc_to_str(dev->vgaarb_rsrc));
+
+ return vgaarb_write(pci_sys->vgaarb_fd, buf, len);
+}
+
+int
+pci_device_vgaarb_unlock(void)
+{
+ int len;
+ char buf[BUFSIZE];
+ struct pci_device *dev = pci_sys->vga_target;
+
+ if (!dev)
+ return -1;
+
+ if (dev->vgaarb_rsrc == 0 || pci_sys->vga_count == 1)
+ return 0;
+
+ len = snprintf(buf, BUFSIZE, "unlock %s", rsrc_to_str(dev->vgaarb_rsrc));
+
+ return vgaarb_write(pci_sys->vgaarb_fd, buf, len);
+}
+
+int pci_device_vgaarb_get_info(struct pci_device *dev, int *vga_count, int *rsrc_decodes)
+{
+ *vga_count = pci_sys->vga_count;
+ if (!dev)
+ return -1;
+
+ *rsrc_decodes = dev->vgaarb_rsrc;
+ return 0;
+}
diff --git a/src/linux_sysfs.c b/src/linux_sysfs.c
index 7fcd327..02fbd13 100644
--- a/src/linux_sysfs.c
+++ b/src/linux_sysfs.c
@@ -76,6 +76,7 @@ static int pci_device_linux_sysfs_write( struct pci_device * dev,
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,
@@ -91,6 +92,7 @@ static const struct pci_system_methods linux_sysfs_methods = {
.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,
};
#define SYS_BUS_PCI "/sys/bus/pci/devices"
@@ -734,3 +736,22 @@ out:
close(fd);
return ret;
}
+
+static int pci_device_linux_sysfs_has_kernel_driver(struct pci_device *dev)
+{
+ char name[256];
+ struct stat dummy;
+ int ret;
+
+ snprintf( name, 255, "%s/%04x:%02x:%02x.%1u/driver",
+ SYS_BUS_PCI,
+ dev->domain,
+ dev->bus,
+ dev->dev,
+ dev->func );
+
+ ret = stat(name, &dummy);
+ if (ret < 0)
+ return 0;
+ return 1;
+}
diff --git a/src/pciaccess_private.h b/src/pciaccess_private.h
index 769e181..a9d8df0 100644
--- a/src/pciaccess_private.h
+++ b/src/pciaccess_private.h
@@ -61,6 +61,7 @@ struct pci_system_methods {
int (*fill_capabilities)( struct pci_device * dev );
void (*enable)( struct pci_device *dev );
int (*boot_vga)( struct pci_device *dev );
+ int (*has_kernel_driver)( struct pci_device *dev );
};
struct pci_device_mapping {
@@ -131,6 +132,10 @@ struct pci_system {
#ifdef HAVE_MTRR
int mtrr_fd;
#endif
+ int vgaarb_fd;
+ int vga_count;
+ struct pci_device *vga_target;
+ struct pci_device *vga_default_dev;
};
extern struct pci_system * pci_sys;