diff options
author | Gary Wong <[email protected]> | 2014-05-21 21:07:42 -0600 |
---|---|---|
committer | Gary Wong <[email protected]> | 2014-05-29 20:25:37 -0600 |
commit | 85b6f36ca5238dd3fec7c5fcacb8b7074ce53c8e (patch) | |
tree | 5b33791f4c28a62878bb6dc96519bed6a66fe938 /src | |
parent | 090c772b8ac50c99e293862b496344a7ab8d297e (diff) |
loader: add optional /sys filesystem method for PCI identification.
Introduce a simple PCI identification method of looking up the answer
the /sys filesystem (available on Linux). Attempted after libudev, but
before DRM.
Disabled by default (available only when the --enable-sysfs configure
option is specified).
Signed-off-by: Gary Wong <[email protected]>
Acked-by: Emil Velikov <[email protected]>
Diffstat (limited to 'src')
-rw-r--r-- | src/loader/loader.c | 112 |
1 files changed, 112 insertions, 0 deletions
diff --git a/src/loader/loader.c b/src/loader/loader.c index 52be7061c19..0f262653b29 100644 --- a/src/loader/loader.c +++ b/src/loader/loader.c @@ -71,6 +71,10 @@ #include <assert.h> #include <dlfcn.h> #endif +#ifdef HAVE_SYSFS +#include <sys/stat.h> +#include <sys/types.h> +#endif #include "loader.h" #ifndef __NOT_HAVE_DRM_H @@ -212,6 +216,66 @@ out: } #endif +#if defined(HAVE_SYSFS) +static int +dev_node_from_fd(int fd, unsigned int *maj, unsigned int *min) +{ + struct stat buf; + + if (fstat(fd, &buf) < 0) { + log_(_LOADER_WARNING, "MESA-LOADER: failed to stat fd %d\n", fd); + return -1; + } + + if (!S_ISCHR(buf.st_mode)) { + log_(_LOADER_WARNING, "MESA-LOADER: fd %d not a character device\n", fd); + return -1; + } + + *maj = major(buf.st_rdev); + *min = minor(buf.st_rdev); + + return 0; +} + +static int +sysfs_get_pci_id_for_fd(int fd, int *vendor_id, int *chip_id) +{ + unsigned int maj, min; + FILE *f; + char buf[0x40]; + + if (dev_node_from_fd(fd, &maj, &min) < 0) { + *chip_id = -1; + return 0; + } + + snprintf(buf, sizeof(buf), "/sys/dev/char/%d:%d/device/vendor", maj, min); + if (!(f = fopen(buf, "r"))) { + *chip_id = -1; + return 0; + } + if (fscanf(f, "%x", vendor_id) != 1) { + *chip_id = -1; + fclose(f); + return 0; + } + fclose(f); + snprintf(buf, sizeof(buf), "/sys/dev/char/%d:%d/device/device", maj, min); + if (!(f = fopen(buf, "r"))) { + *chip_id = -1; + return 0; + } + if (fscanf(f, "%x", chip_id) != 1) { + *chip_id = -1; + fclose(f); + return 0; + } + fclose(f); + return 1; +} +#endif + #if !defined(__NOT_HAVE_DRM_H) /* for i915 */ #include <i915_drm.h> @@ -291,6 +355,10 @@ loader_get_pci_id_for_fd(int fd, int *vendor_id, int *chip_id) if (libudev_get_pci_id_for_fd(fd, vendor_id, chip_id)) return 1; #endif +#if HAVE_SYSFS + if (sysfs_get_pci_id_for_fd(fd, vendor_id, chip_id)) + return 1; +#endif #if !defined(__NOT_HAVE_DRM_H) if (drm_get_pci_id_for_fd(fd, vendor_id, chip_id)) return 1; @@ -332,6 +400,46 @@ out: #endif +#if HAVE_SYSFS +static char * +sysfs_get_device_name_for_fd(int fd) +{ + char *device_name = NULL; + unsigned int maj, min; + FILE *f; + char buf[0x40]; + static const char match[9] = "\0DEVNAME="; + int expected = 1; + + if (dev_node_from_fd(fd, &maj, &min) < 0) + return NULL; + + snprintf(buf, sizeof(buf), "/sys/dev/char/%d:%d/uevent", maj, min); + if (!(f = fopen(buf, "r"))) + return NULL; + + while (expected < sizeof(match)) { + int c = getc(f); + + if (c == EOF) { + fclose(f); + return NULL; + } else if (c == match[expected] ) + expected++; + else + expected = 0; + } + + strcpy(buf, "/dev/"); + if (fgets(buf + 5, sizeof(buf) - 5, f)) + device_name = strdup(buf); + + fclose(f); + return device_name; +} +#endif + + char * loader_get_device_name_for_fd(int fd) { @@ -341,6 +449,10 @@ loader_get_device_name_for_fd(int fd) if ((result = libudev_get_device_name_for_fd(fd))) return result; #endif +#if HAVE_SYSFS + if ((result = sysfs_get_device_name_for_fd(fd))) + return result; +#endif return result; } |