diff options
author | Dave Airlie <airlied@redhat.com> | 2009-08-21 13:27:11 +1000 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2009-08-21 13:27:11 +1000 |
commit | f5148525d546015c12b703a768b5e6c3726446a3 (patch) | |
tree | 4da151ba09fdedfeef11d8386b7cc95dc84ae9c6 | |
parent | 76363486864b325b8f156ece35736ddb64c4e697 (diff) | |
parent | ef8aca5da46155e9ea0d1227215921ad9ce59150 (diff) |
Merge remote branch 'origin/vga-arbiter'
-rw-r--r-- | include/pciaccess.h | 56 | ||||
-rw-r--r-- | src/Makefile.am | 5 | ||||
-rw-r--r-- | src/common_interface.c | 15 | ||||
-rw-r--r-- | src/common_vgaarb.c | 324 | ||||
-rw-r--r-- | src/linux_sysfs.c | 21 | ||||
-rw-r--r-- | src/pciaccess_private.h | 5 |
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; |