[PATCH libdrm 3/5] xf86drm: parse the separate sysfs files for vendor... info

2016-12-02 Thread Emil Velikov
From: Emil Velikov 

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 ddb8f9f..701cf29 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", [i]);
+fclose(fp);
+if (ret != 1)
+return -errno;
+
+}
+
+device->revision_id = data[0] & 0xff;
+device->vendor_id = data[1] & 0x;
+device->device_id = data[2] & 0x;
+device->subvendor_id = data[3] & 0x;
+device->subdevice_id = data[4] & 0x;
+
+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;
@@ -2972,6 +3010,18 @@ static int drmParsePciDeviceInfo(int maj, int min,
 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"
 return -EINVAL;
-- 
2.10.2



[PATCH libdrm 3/5] xf86drm: parse the separate sysfs files for vendor... info

2016-11-30 Thread Emil Velikov
From: Emil Velikov 

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 
---
 xf86drm.c | 58 ++
 1 file changed, 54 insertions(+), 4 deletions(-)

diff --git a/xf86drm.c b/xf86drm.c
index ddb8f9f..701cf29 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", [i]);
+fclose(fp);
+if (ret != 1)
+return -errno;
+
+}
+
+device->revision_id = data[0] & 0xff;
+device->vendor_id = data[1] & 0x;
+device->device_id = data[2] & 0x;
+device->subvendor_id = data[3] & 0x;
+device->subdevice_id = data[4] & 0x;
+
+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;
@@ -2972,6 +3010,18 @@ static int drmParsePciDeviceInfo(int maj, int min,
 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"
 return -EINVAL;
-- 
2.10.2