diff options
author | Thierry Reding | 2016-12-21 17:39:36 -0600 |
---|---|---|
committer | Thierry Reding | 2017-01-20 09:25:53 -0600 |
commit | f8484ccbd12ba33ea5b3895efb7a39d986271be0 (patch) | |
tree | 842d2450df0e3f9751d0c9253f4eab1342e36e89 | |
parent | 2e57bba870399926e1a0d0be3f4918a0a8432474 (diff) | |
download | external-libdrm-f8484ccbd12ba33ea5b3895efb7a39d986271be0.tar.gz external-libdrm-f8484ccbd12ba33ea5b3895efb7a39d986271be0.tar.xz external-libdrm-f8484ccbd12ba33ea5b3895efb7a39d986271be0.zip |
xf86drm: Add USB support
Allow DRM/KMS devices hosted on USB to be detected by the drmDevice
infrastructure.
v4:
- continue on error to process USB devices
v3:
- guard Linux-specific sysfs parsing code with #ifdef __linux__
v2:
- make sysfs_uevent_get() more flexible using a format string
Reviewed-by: Emil Velikov <emil.l.velikov@gmail.com>
Signed-off-by: Thierry Reding <thierry.reding@gmail.com>
-rw-r--r-- | xf86drm.c | 175 | ||||
-rw-r--r-- | xf86drm.h | 13 |
2 files changed, 188 insertions, 0 deletions
@@ -2886,6 +2886,50 @@ char *drmGetRenderDeviceNameFromFd(int fd) | |||
2886 | return drmGetMinorNameForFD(fd, DRM_NODE_RENDER); | 2886 | return drmGetMinorNameForFD(fd, DRM_NODE_RENDER); |
2887 | } | 2887 | } |
2888 | 2888 | ||
2889 | #ifdef __linux__ | ||
2890 | static char * DRM_PRINTFLIKE(2, 3) | ||
2891 | sysfs_uevent_get(const char *path, const char *fmt, ...) | ||
2892 | { | ||
2893 | char filename[PATH_MAX + 1], *key, *line = NULL, *value = NULL; | ||
2894 | size_t size = 0, len; | ||
2895 | ssize_t num; | ||
2896 | va_list ap; | ||
2897 | FILE *fp; | ||
2898 | |||
2899 | va_start(ap, fmt); | ||
2900 | num = vasprintf(&key, fmt, ap); | ||
2901 | va_end(ap); | ||
2902 | len = num; | ||
2903 | |||
2904 | snprintf(filename, sizeof(filename), "%s/uevent", path); | ||
2905 | |||
2906 | fp = fopen(filename, "r"); | ||
2907 | if (!fp) { | ||
2908 | free(key); | ||
2909 | return NULL; | ||
2910 | } | ||
2911 | |||
2912 | while ((num = getline(&line, &size, fp)) >= 0) { | ||
2913 | if ((strncmp(line, key, len) == 0) && (line[len] == '=')) { | ||
2914 | char *start = line + len + 1, *end = line + num - 1; | ||
2915 | |||
2916 | if (*end != '\n') | ||
2917 | end++; | ||
2918 | |||
2919 | value = strndup(start, end - start); | ||
2920 | break; | ||
2921 | } | ||
2922 | } | ||
2923 | |||
2924 | free(line); | ||
2925 | fclose(fp); | ||
2926 | |||
2927 | free(key); | ||
2928 | |||
2929 | return value; | ||
2930 | } | ||
2931 | #endif | ||
2932 | |||
2889 | static int drmParseSubsystemType(int maj, int min) | 2933 | static int drmParseSubsystemType(int maj, int min) |
2890 | { | 2934 | { |
2891 | #ifdef __linux__ | 2935 | #ifdef __linux__ |
@@ -2906,6 +2950,9 @@ static int drmParseSubsystemType(int maj, int min) | |||
2906 | if (strncmp(name, "/pci", 4) == 0) | 2950 | if (strncmp(name, "/pci", 4) == 0) |
2907 | return DRM_BUS_PCI; | 2951 | return DRM_BUS_PCI; |
2908 | 2952 | ||
2953 | if (strncmp(name, "/usb", 4) == 0) | ||
2954 | return DRM_BUS_USB; | ||
2955 | |||
2909 | return -EINVAL; | 2956 | return -EINVAL; |
2910 | #elif defined(__OpenBSD__) | 2957 | #elif defined(__OpenBSD__) |
2911 | return DRM_BUS_PCI; | 2958 | return DRM_BUS_PCI; |
@@ -2992,6 +3039,10 @@ static int drmCompareBusInfo(drmDevicePtr a, drmDevicePtr b) | |||
2992 | switch (a->bustype) { | 3039 | switch (a->bustype) { |
2993 | case DRM_BUS_PCI: | 3040 | case DRM_BUS_PCI: |
2994 | return memcmp(a->businfo.pci, b->businfo.pci, sizeof(drmPciBusInfo)); | 3041 | return memcmp(a->businfo.pci, b->businfo.pci, sizeof(drmPciBusInfo)); |
3042 | |||
3043 | case DRM_BUS_USB: | ||
3044 | return memcmp(a->businfo.usb, b->businfo.usb, sizeof(drmUsbBusInfo)); | ||
3045 | |||
2995 | default: | 3046 | default: |
2996 | break; | 3047 | break; |
2997 | } | 3048 | } |
@@ -3235,6 +3286,113 @@ free_device: | |||
3235 | return ret; | 3286 | return ret; |
3236 | } | 3287 | } |
3237 | 3288 | ||
3289 | static int drmParseUsbBusInfo(int maj, int min, drmUsbBusInfoPtr info) | ||
3290 | { | ||
3291 | #ifdef __linux__ | ||
3292 | char path[PATH_MAX + 1], *value; | ||
3293 | unsigned int bus, dev; | ||
3294 | int ret; | ||
3295 | |||
3296 | snprintf(path, sizeof(path), "/sys/dev/char/%d:%d/device", maj, min); | ||
3297 | |||
3298 | value = sysfs_uevent_get(path, "BUSNUM"); | ||
3299 | if (!value) | ||
3300 | return -ENOENT; | ||
3301 | |||
3302 | ret = sscanf(value, "%03u", &bus); | ||
3303 | free(value); | ||
3304 | |||
3305 | if (ret <= 0) | ||
3306 | return -errno; | ||
3307 | |||
3308 | value = sysfs_uevent_get(path, "DEVNUM"); | ||
3309 | if (!value) | ||
3310 | return -ENOENT; | ||
3311 | |||
3312 | ret = sscanf(value, "%03u", &dev); | ||
3313 | free(value); | ||
3314 | |||
3315 | if (ret <= 0) | ||
3316 | return -errno; | ||
3317 | |||
3318 | info->bus = bus; | ||
3319 | info->dev = dev; | ||
3320 | |||
3321 | return 0; | ||
3322 | #else | ||
3323 | #warning "Missing implementation of drmParseUsbBusInfo" | ||
3324 | return -EINVAL; | ||
3325 | #endif | ||
3326 | } | ||
3327 | |||
3328 | static int drmParseUsbDeviceInfo(int maj, int min, drmUsbDeviceInfoPtr info) | ||
3329 | { | ||
3330 | #ifdef __linux__ | ||
3331 | char path[PATH_MAX + 1], *value; | ||
3332 | unsigned int vendor, product; | ||
3333 | int ret; | ||
3334 | |||
3335 | snprintf(path, sizeof(path), "/sys/dev/char/%d:%d/device", maj, min); | ||
3336 | |||
3337 | value = sysfs_uevent_get(path, "PRODUCT"); | ||
3338 | if (!value) | ||
3339 | return -ENOENT; | ||
3340 | |||
3341 | ret = sscanf(value, "%x/%x", &vendor, &product); | ||
3342 | free(value); | ||
3343 | |||
3344 | if (ret <= 0) | ||
3345 | return -errno; | ||
3346 | |||
3347 | info->vendor = vendor; | ||
3348 | info->product = product; | ||
3349 | |||
3350 | return 0; | ||
3351 | #else | ||
3352 | #warning "Missing implementation of drmParseUsbDeviceInfo" | ||
3353 | return -EINVAL; | ||
3354 | #endif | ||
3355 | } | ||
3356 | |||
3357 | static int drmProcessUsbDevice(drmDevicePtr *device, const char *node, | ||
3358 | int node_type, int maj, int min, | ||
3359 | bool fetch_deviceinfo, uint32_t flags) | ||
3360 | { | ||
3361 | drmDevicePtr dev; | ||
3362 | char *ptr; | ||
3363 | int ret; | ||
3364 | |||
3365 | dev = drmDeviceAlloc(node_type, node, sizeof(drmUsbBusInfo), | ||
3366 | sizeof(drmUsbDeviceInfo), &ptr); | ||
3367 | if (!dev) | ||
3368 | return -ENOMEM; | ||
3369 | |||
3370 | dev->bustype = DRM_BUS_USB; | ||
3371 | |||
3372 | dev->businfo.usb = (drmUsbBusInfoPtr)ptr; | ||
3373 | |||
3374 | ret = drmParseUsbBusInfo(maj, min, dev->businfo.usb); | ||
3375 | if (ret < 0) | ||
3376 | goto free_device; | ||
3377 | |||
3378 | if (fetch_deviceinfo) { | ||
3379 | ptr += sizeof(drmUsbBusInfo); | ||
3380 | dev->deviceinfo.usb = (drmUsbDeviceInfoPtr)ptr; | ||
3381 | |||
3382 | ret = drmParseUsbDeviceInfo(maj, min, dev->deviceinfo.usb); | ||
3383 | if (ret < 0) | ||
3384 | goto free_device; | ||
3385 | } | ||
3386 | |||
3387 | *device = dev; | ||
3388 | |||
3389 | return 0; | ||
3390 | |||
3391 | free_device: | ||
3392 | free(dev); | ||
3393 | return ret; | ||
3394 | } | ||
3395 | |||
3238 | /* Consider devices located on the same bus as duplicate and fold the respective | 3396 | /* Consider devices located on the same bus as duplicate and fold the respective |
3239 | * entries into a single one. | 3397 | * entries into a single one. |
3240 | * | 3398 | * |
@@ -3410,6 +3568,14 @@ int drmGetDevice2(int fd, uint32_t flags, drmDevicePtr *device) | |||
3410 | continue; | 3568 | continue; |
3411 | 3569 | ||
3412 | break; | 3570 | break; |
3571 | |||
3572 | case DRM_BUS_USB: | ||
3573 | ret = drmProcessUsbDevice(&d, node, node_type, maj, min, true, flags); | ||
3574 | if (ret) | ||
3575 | continue; | ||
3576 | |||
3577 | break; | ||
3578 | |||
3413 | default: | 3579 | default: |
3414 | continue; | 3580 | continue; |
3415 | } | 3581 | } |
@@ -3541,6 +3707,15 @@ int drmGetDevices2(uint32_t flags, drmDevicePtr devices[], int max_devices) | |||
3541 | continue; | 3707 | continue; |
3542 | 3708 | ||
3543 | break; | 3709 | break; |
3710 | |||
3711 | case DRM_BUS_USB: | ||
3712 | ret = drmProcessUsbDevice(&device, node, node_type, maj, min, | ||
3713 | devices != NULL, flags); | ||
3714 | if (ret) | ||
3715 | goto free_devices; | ||
3716 | |||
3717 | break; | ||
3718 | |||
3544 | default: | 3719 | default: |
3545 | continue; | 3720 | continue; |
3546 | } | 3721 | } |
@@ -767,6 +767,7 @@ extern char *drmGetPrimaryDeviceNameFromFd(int fd); | |||
767 | extern char *drmGetRenderDeviceNameFromFd(int fd); | 767 | extern char *drmGetRenderDeviceNameFromFd(int fd); |
768 | 768 | ||
769 | #define DRM_BUS_PCI 0 | 769 | #define DRM_BUS_PCI 0 |
770 | #define DRM_BUS_USB 1 | ||
770 | 771 | ||
771 | typedef struct _drmPciBusInfo { | 772 | typedef struct _drmPciBusInfo { |
772 | uint16_t domain; | 773 | uint16_t domain; |
@@ -783,15 +784,27 @@ typedef struct _drmPciDeviceInfo { | |||
783 | uint8_t revision_id; | 784 | uint8_t revision_id; |
784 | } drmPciDeviceInfo, *drmPciDeviceInfoPtr; | 785 | } drmPciDeviceInfo, *drmPciDeviceInfoPtr; |
785 | 786 | ||
787 | typedef struct _drmUsbBusInfo { | ||
788 | uint8_t bus; | ||
789 | uint8_t dev; | ||
790 | } drmUsbBusInfo, *drmUsbBusInfoPtr; | ||
791 | |||
792 | typedef struct _drmUsbDeviceInfo { | ||
793 | uint16_t vendor; | ||
794 | uint16_t product; | ||
795 | } drmUsbDeviceInfo, *drmUsbDeviceInfoPtr; | ||
796 | |||
786 | typedef struct _drmDevice { | 797 | typedef struct _drmDevice { |
787 | char **nodes; /* DRM_NODE_MAX sized array */ | 798 | char **nodes; /* DRM_NODE_MAX sized array */ |
788 | int available_nodes; /* DRM_NODE_* bitmask */ | 799 | int available_nodes; /* DRM_NODE_* bitmask */ |
789 | int bustype; | 800 | int bustype; |
790 | union { | 801 | union { |
791 | drmPciBusInfoPtr pci; | 802 | drmPciBusInfoPtr pci; |
803 | drmUsbBusInfoPtr usb; | ||
792 | } businfo; | 804 | } businfo; |
793 | union { | 805 | union { |
794 | drmPciDeviceInfoPtr pci; | 806 | drmPciDeviceInfoPtr pci; |
807 | drmUsbDeviceInfoPtr usb; | ||
795 | } deviceinfo; | 808 | } deviceinfo; |
796 | } drmDevice, *drmDevicePtr; | 809 | } drmDevice, *drmDevicePtr; |
797 | 810 | ||