From aae3f318d5aca81b87a14360116bb766a51e82ef Mon Sep 17 00:00:00 2001 From: Emil Velikov Date: Tue, 1 Nov 2016 18:04:06 +0000 Subject: [PATCH] xf86drm: parse the separate sysfs files for vendor... info MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Up-to recently (patch should land in 4.10) the kernel did not expose the PCI device revision field as a separate sysfs file. Thus one needed too parse the config file to retrieve it. This in itself wakes up the device, which in some cases can be quite slow. To avoid that, just check for the separate files and fall-back to the original if kernel is not new enough. v3: rework alongside drmGetDevice[s]2 Cc: Michel Dänzer Cc: Nicolai Hähnle Cc: Mauro Santos Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=98502 Signed-off-by: Emil Velikov Reviewed-by: Michel Dänzer --- xf86drm.c | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 54 insertions(+), 4 deletions(-) diff --git a/xf86drm.c b/xf86drm.c index ddb8f9f7..701cf291 100644 --- a/xf86drm.c +++ b/xf86drm.c @@ -2946,11 +2946,49 @@ static int drmGetMaxNodeName(void) 3 /* length of the node number */; } -static int drmParsePciDeviceInfo(int maj, int min, - drmPciDeviceInfoPtr device, - uint32_t flags) -{ #ifdef __linux__ +static int parse_separate_sysfs_files(int maj, int min, + drmPciDeviceInfoPtr device) +{ +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) + static const char *attrs[] = { + "revision", /* Older kernels are missing the file, so check for it first */ + "vendor", + "device", + "subsystem_vendor", + "subsystem_device", + }; + char path[PATH_MAX + 1]; + unsigned int data[ARRAY_SIZE(attrs)]; + FILE *fp; + int ret; + + for (unsigned i = 0; i < ARRAY_SIZE(attrs); i++) { + snprintf(path, PATH_MAX, "/sys/dev/char/%d:%d/device/%s", maj, min, + attrs[i]); + fp = fopen(path, "r"); + if (!fp) + return -errno; + + ret = fscanf(fp, "%x", &data[i]); + fclose(fp); + if (ret != 1) + return -errno; + + } + + device->revision_id = data[0] & 0xff; + device->vendor_id = data[1] & 0xffff; + device->device_id = data[2] & 0xffff; + device->subvendor_id = data[3] & 0xffff; + device->subdevice_id = data[4] & 0xffff; + + return 0; +} + +static int parse_config_sysfs_file(int maj, int min, + drmPciDeviceInfoPtr device) +{ char path[PATH_MAX + 1]; unsigned char config[64]; int fd, ret; @@ -2971,6 +3009,18 @@ static int drmParsePciDeviceInfo(int maj, int min, device->subvendor_id = config[44] | (config[45] << 8); device->subdevice_id = config[46] | (config[47] << 8); + return 0; +} +#endif + +static int drmParsePciDeviceInfo(int maj, int min, + drmPciDeviceInfoPtr device, + uint32_t flags) +{ +#ifdef __linux__ + if (parse_separate_sysfs_files(maj, min, device)) + return parse_config_sysfs_file(maj, min, device); + return 0; #else #warning "Missing implementation of drmParsePciDeviceInfo"