OSDN Git Service

libdrm: Handle usb_interface devices for usb parsing
authorScott Anderson <scott@anderso.nz>
Fri, 31 Jan 2020 03:18:19 +0000 (16:18 +1300)
committerEmil Velikov <emil.l.velikov@gmail.com>
Fri, 24 Apr 2020 08:53:53 +0000 (08:53 +0000)
Currently the code expects that the device found at
/sys/char/$maj:$min/device for USB devices is a "usb_device". However,
at least for some devices, such as for the udl driver, they are instead
a "usb_interface".

A usb_interface is a child of the usb_device we're interested in, so we
walk up one in the /sys path to get there.

For example, with a USB device I have, trimmed to show the relevant
information:
```
$ udevadm info /dev/dri/card1
P: /devices/pci0000:00/0000:00:01.3/0000:02:00.0/usb1/1-4/1-4:1.0/drm/card1
E: DEVTYPE=drm_minor
$ udevadm info /sys/devices/pci0000:00/0000:00:01.3/0000:02:00.0/usb1/1-4/1-4:1.0
E: DEVTYPE=usb_interface
E: DRIVER=udl
$ udevadm info /sys/devices/pci0000:00/0000:00:01.3/0000:02:00.0/usb1/1-4
E: DEVTYPE=usb_device
E: DRIVER=usb
E: BUSNUM=001
E: DEVNUM=009
```

Signed-off-by: Scott Anderson <scott@anderso.nz>
xf86drm.c

index a341c4a..08e5caf 100644 (file)
--- a/xf86drm.c
+++ b/xf86drm.c
@@ -3611,6 +3611,46 @@ free_device:
     return ret;
 }
 
+#ifdef __linux__
+static int drm_usb_dev_path(int maj, int min, char *path, size_t len)
+{
+    char *value, *tmp_path, *slash;
+
+    snprintf(path, len, "/sys/dev/char/%d:%d/device", maj, min);
+
+    value = sysfs_uevent_get(path, "DEVTYPE");
+    if (!value)
+        return -ENOENT;
+
+    if (strcmp(value, "usb_device") == 0)
+        return 0;
+    if (strcmp(value, "usb_interface") != 0)
+        return -ENOTSUP;
+
+    /* The parent of a usb_interface is a usb_device */
+
+    tmp_path = realpath(path, NULL);
+    if (!tmp_path)
+        return -errno;
+
+    slash = strrchr(tmp_path, '/');
+    if (!slash) {
+        free(tmp_path);
+        return -EINVAL;
+    }
+
+    *slash = '\0';
+
+    if (snprintf(path, len, "%s", tmp_path) >= (int)len) {
+        free(tmp_path);
+        return -EINVAL;
+    }
+
+    free(tmp_path);
+    return 0;
+}
+#endif
+
 static int drmParseUsbBusInfo(int maj, int min, drmUsbBusInfoPtr info)
 {
 #ifdef __linux__
@@ -3618,7 +3658,9 @@ static int drmParseUsbBusInfo(int maj, int min, drmUsbBusInfoPtr info)
     unsigned int bus, dev;
     int ret;
 
-    snprintf(path, sizeof(path), "/sys/dev/char/%d:%d/device", maj, min);
+    ret = drm_usb_dev_path(maj, min, path, sizeof(path));
+    if (ret < 0)
+        return ret;
 
     value = sysfs_uevent_get(path, "BUSNUM");
     if (!value)
@@ -3657,7 +3699,9 @@ static int drmParseUsbDeviceInfo(int maj, int min, drmUsbDeviceInfoPtr info)
     unsigned int vendor, product;
     int ret;
 
-    snprintf(path, sizeof(path), "/sys/dev/char/%d:%d/device", maj, min);
+    ret = drm_usb_dev_path(maj, min, path, sizeof(path));
+    if (ret < 0)
+        return ret;
 
     value = sysfs_uevent_get(path, "PRODUCT");
     if (!value)