$NetBSD: patch-ab,v 1.8 2015/08/14 17:12:35 wiz Exp $

Code for OpenBSD adapted for NetBSD.  Assumes NetBSD kernel has been
patched to use drm ioctl number 0x0f.

Patches from FreeBSD ports libdrm 2.4.75.

Patches from https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=214580
and code from libdevq 0.0.4.

Tries to account for DragonFly major number not well defined.

Tries to account for FreeBSD device control64D to be ignored.

Many debugging messages added for use with LIBGL_DEBUG=verbose.

Added code to deduce node type from a device name such as dri/card0
for FreeBSD, DragonFly, and NetBSD, OSes that support devname().
Moved static drmGetNodeType() for drmGetNodeTypeFromFd().

--- xf86drm.c.orig	2017-05-24 15:03:41.000000000 +0000
+++ xf86drm.c
@@ -31,6 +31,36 @@
  * DEALINGS IN THE SOFTWARE.
  */
 
+/* Port for FreeBSD and DragonFly uses code from libdevq 0.0.4
+ *     https://github.com/freebsd/libdevq
+ * with copyright notice:
+ *
+ * Copyright (c) 2014 Jean-Sebastien Pedron <dumbbell@FreeBSD.org>
+ * Copyright (c) 2016 Koop Mast <kwm@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer
+ *    in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
 #ifdef HAVE_CONFIG_H
 # include <config.h>
 #endif
@@ -72,6 +102,10 @@
 
 #include "util_math.h"
 
+#if defined(__FreeBSD__) || defined(__DragonFly__)
+#include <sys/sysctl.h>
+#endif
+
 #ifdef __OpenBSD__
 #define DRM_PRIMARY_MINOR_NAME  "drm"
 #define DRM_CONTROL_MINOR_NAME  "drmC"
@@ -82,12 +116,22 @@
 #define DRM_RENDER_MINOR_NAME   "renderD"
 #endif
 
-#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
-#define DRM_MAJOR 145
+/*
+ * The major number of the device corresponding to the file descriptor
+ * is not well defined on either FreeBSD or DragonFly, so for these
+ * OSes checking using DRM_MAJOR will be ignored.
+ */ 
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+#define DRM_MAJOR 0 
+#endif
+
+#if defined(__DragonFly__)
+#define DRM_MAJOR 64
 #endif
 
 #ifdef __NetBSD__
-#define DRM_MAJOR 34
+#undef DRM_MAJOR
+#define DRM_MAJOR 180
 #endif
 
 #ifdef __OpenBSD__
@@ -102,7 +146,7 @@
 #define DRM_MAJOR 226 /* Linux */
 #endif
 
-#ifdef __OpenBSD__
+#if defined(__OpenBSD__) || defined(__NetBSD__)
 struct drm_pciinfo {
 	uint16_t	domain;
 	uint8_t		bus;
@@ -114,8 +158,12 @@ struct drm_pciinfo {
 	uint16_t	subdevice_id;
 	uint8_t		revision_id;
 };
+#endif
 
+#if defined(__OpenBSD__)
 #define DRM_IOCTL_GET_PCIINFO	DRM_IOR(0x15, struct drm_pciinfo)
+#elif defined(__NetBSD__)
+#define DRM_IOCTL_GET_PCIINFO   DRM_IOR(0x0f, struct drm_pciinfo)
 #endif
 
 #define DRM_MSG_VERBOSITY 3
@@ -189,7 +237,15 @@ drmIoctl(int fd, unsigned long request,
 
     do {
         ret = ioctl(fd, request, arg);
+#if defined(__FreeBSD__)
+/*
+ * FreeBSD Bug 204174: error code 512 (ERESTARTSYS) leaked from kernel space
+ * for some older kernels.
+ */
+    } while (ret == -1 && (errno == EINTR || errno == EAGAIN || errno == 512));
+#else
     } while (ret == -1 && (errno == EINTR || errno == EAGAIN));
+#endif
     return ret;
 }
 
@@ -499,17 +555,25 @@ int drmAvailable(void)
     int           retval = 0;
     int           fd;
 
+    drmMsg("drmAvailable: Testing if DRM kernel driver has been loaded\n");
+
     if ((fd = drmOpenMinor(0, 1, DRM_NODE_PRIMARY)) < 0) {
 #ifdef __linux__
         /* Try proc for backward Linux compatibility */
         if (!access("/proc/dri/0", R_OK))
             return 1;
 #endif
+
+        drmMsg("drmAvailable: FAILED to open drm minor 0 (%d)\n", fd);
+
         return 0;
     }
 
+    drmMsg("drmAvailable: Opened drm minor 0 with fd (%d)\n", fd);
+
     if ((version = drmGetVersion(fd))) {
         retval = 1;
+        drmMsg("drmAvailable: Success version non-null for fd (%d)\n", fd);
         drmFreeVersion(version);
     }
     close(fd);
@@ -531,6 +595,7 @@ static int drmGetMinorBase(int type)
     };
 }
 
+#if !defined(__FreeBSD__) && !defined(__DragonFly__)
 static int drmGetMinorType(int minor)
 {
     int type = minor >> 6;
@@ -547,7 +612,9 @@ static int drmGetMinorType(int minor)
         return -1;
     }
 }
+#endif
 
+#if defined(__linux__)
 static const char *drmGetMinorName(int type)
 {
     switch (type) {
@@ -561,6 +628,7 @@ static const char *drmGetMinorName(int t
         return NULL;
     }
 }
+#endif
 
 /**
  * Open the device by bus ID.
@@ -2729,14 +2797,46 @@ char *drmGetDeviceNameFromFd(int fd)
     return strdup(name);
 }
 
+static int drmGetNodeType(const char *name)
+{
+    if (strncmp(name, DRM_PRIMARY_MINOR_NAME,
+        sizeof(DRM_PRIMARY_MINOR_NAME) - 1) == 0)
+        return DRM_NODE_PRIMARY;
+
+    if (strncmp(name, DRM_CONTROL_MINOR_NAME,
+        sizeof(DRM_CONTROL_MINOR_NAME ) - 1) == 0)
+        return DRM_NODE_CONTROL;
+
+    if (strncmp(name, DRM_RENDER_MINOR_NAME,
+        sizeof(DRM_RENDER_MINOR_NAME) - 1) == 0)
+        return DRM_NODE_RENDER;
+
+    return -EINVAL;
+}
+
 int drmGetNodeTypeFromFd(int fd)
 {
     struct stat sbuf;
+
+#if defined(__FreeBSD__) || defined(__DragonFly__)
+    int type;
+    char dev_name[64];
+    char base_name[64];
+#else
     int maj, min, type;
+#endif
+
+    drmMsg("drmGetNodeTypeFromFd: File descriptor fd (%d)\n", fd);
 
     if (fstat(fd, &sbuf))
         return -1;
 
+#if defined(__FreeBSD__) || defined(__DragonFly__)
+    if (!S_ISCHR(sbuf.st_mode)) {
+        errno = EINVAL;
+        return -1;
+    }
+#else
     maj = major(sbuf.st_rdev);
     min = minor(sbuf.st_rdev);
 
@@ -2744,8 +2844,31 @@ int drmGetNodeTypeFromFd(int fd)
         errno = EINVAL;
         return -1;
     }
+#endif
 
+#if defined(__FreeBSD__) || defined(__DragonFly__)
+    snprintf(dev_name, sizeof(dev_name), "%s",
+             devname(sbuf.st_rdev, S_IFCHR));
+
+    drmMsg("drmGetNodeTypeFromFd: Device name (%s)\n", dev_name);
+
+    memset(base_name, 0, sizeof(base_name));
+    if (sscanf(dev_name, "dri/%s", base_name) != 1) {
+        type = -1;
+        goto out;
+    }
+
+    drmMsg("drmGetNodeTypeFromFd: Device has basename (%s)\n", base_name);
+
+    type = drmGetNodeType(base_name);
+    if (type < 0)
+        type = -1;
+#else
     type = drmGetMinorType(min);
+#endif
+
+out:
+    drmMsg("drmGetNodeTypeFromFd: Device has type (%d)\n", type);
     if (type == -1)
         errno = ENODEV;
     return type;
@@ -2839,6 +2962,15 @@ static char *drmGetMinorNameForFD(int fd
 
 out_close_dir:
     closedir(sysdir);
+#elif defined(__FreeBSD__) || defined(__DragonFly__) || defined(__NetBSD__)
+    struct stat buf;
+    char name[64];
+
+    fstat(fd, &buf);
+    snprintf(name, sizeof(name), "/dev/%s",
+             devname(buf.st_rdev, S_IFCHR));
+
+    return strdup(name);
 #else
     struct stat sbuf;
     char buf[PATH_MAX + 1];
@@ -2966,7 +3098,9 @@ static int drmParseSubsystemType(int maj
         return DRM_BUS_HOST1X;
 
     return -EINVAL;
-#elif defined(__OpenBSD__)
+#elif defined(__OpenBSD__) || defined(__NetBSD__)
+    return DRM_BUS_PCI;
+#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
     return DRM_BUS_PCI;
 #else
 #warning "Missing implementation of drmParseSubsystemType"
@@ -2999,18 +3133,26 @@ static int drmParsePciBusInfo(int maj, i
     info->func = func;
 
     return 0;
-#elif defined(__OpenBSD__)
+#elif defined(__OpenBSD__) || defined(__NetBSD__)
     struct drm_pciinfo pinfo;
     int fd, type;
 
+    drmMsg("drmParsePciBusInfo: min (%d)\n", min);
+
     type = drmGetMinorType(min);
     if (type == -1)
         return -ENODEV;
 
+    drmMsg("drmParsePciBusInfo: type (%d), DRM_NODE_PRIMARY (%d)\n",
+        type, DRM_NODE_PRIMARY);
+
     fd = drmOpenMinor(min, 0, type);
     if (fd < 0)
         return -errno;
 
+    drmMsg("drmParsePciBusInfo: ioctl (0x%02x) using fd (%d)\n",
+        DRM_IOCTL_GET_PCIINFO, fd);
+
     if (drmIoctl(fd, DRM_IOCTL_GET_PCIINFO, &pinfo)) {
         close(fd);
         return -errno;
@@ -3023,6 +3165,59 @@ static int drmParsePciBusInfo(int maj, i
     info->func = pinfo.func;
 
     return 0;
+#elif defined(__FreeBSD__) || defined(__DragonFly__)
+/*
+ * Read the hw.dri.$min.busid sysctl
+ * Adapted from function devq_device_get_pcibusaddr(),
+ * project devq version 0.0.4, file src/freebsd/device.c
+ */
+    char sysctl_name[32], sysctl_value[128];
+    const char *busid_format;
+    size_t sysctl_value_len;
+    int domain, bus, dev, func;
+    int ret;
+
+    snprintf(sysctl_name, sizeof(sysctl_name), "hw.dri.%d.busid", min);
+
+    drmMsg("drmParsePciBusInfo: Reading sysctl (%s)\n", sysctl_name);
+
+    busid_format = "pci:%d:%d:%d.%d";
+    sysctl_value_len = sizeof(sysctl_value);
+    memset(sysctl_value, 0, sysctl_value_len);
+    ret = sysctlbyname(sysctl_name, sysctl_value, &sysctl_value_len,
+        NULL, 0);
+    if (ret != 0) {
+        /*
+         * If hw.dri.$n.busid isn't available, fallback on
+         * hw.dri.$n.name.
+         */
+         busid_format = "%*s %*s pci:%d:%d:%d.%d";
+         sysctl_value_len = sizeof(sysctl_value);
+         memset(sysctl_value, 0, sysctl_value_len);
+         snprintf(sysctl_name, sizeof(sysctl_name), "hw.dri.%d.name", min);
+
+         drmMsg("drmParsePciBusInfo: Reading sysctl (%s)\n", sysctl_name);
+
+         ret = sysctlbyname(sysctl_name, sysctl_value, &sysctl_value_len,
+             NULL, 0);
+    }
+
+    if (ret != 0)
+        return (-EINVAL);
+
+    drmMsg("drmParsePciBusInfo: Sysctl value (%s) scanned by format (%s)\n",
+        sysctl_value, busid_format);
+
+    if (sscanf(sysctl_value, busid_format,
+               &domain, &bus, &dev, &func) != 4)
+        return -EINVAL;
+    
+    info->domain = domain;
+    info->bus = bus;
+    info->dev = dev;
+    info->func = func;
+
+    return 0;
 #else
 #warning "Missing implementation of drmParsePciBusInfo"
     return -EINVAL;
@@ -3057,23 +3252,6 @@ int drmDevicesEqual(drmDevicePtr a, drmD
     return 0;
 }
 
-static int drmGetNodeType(const char *name)
-{
-    if (strncmp(name, DRM_PRIMARY_MINOR_NAME,
-        sizeof(DRM_PRIMARY_MINOR_NAME) - 1) == 0)
-        return DRM_NODE_PRIMARY;
-
-    if (strncmp(name, DRM_CONTROL_MINOR_NAME,
-        sizeof(DRM_CONTROL_MINOR_NAME ) - 1) == 0)
-        return DRM_NODE_CONTROL;
-
-    if (strncmp(name, DRM_RENDER_MINOR_NAME,
-        sizeof(DRM_RENDER_MINOR_NAME) - 1) == 0)
-        return DRM_NODE_RENDER;
-
-    return -EINVAL;
-}
-
 static int drmGetMaxNodeName(void)
 {
     return sizeof(DRM_DIR_NAME) +
@@ -3151,9 +3329,80 @@ static int parse_config_sysfs_file(int m
 }
 #endif
 
+#if defined(__FreeBSD__) || defined(__DragonFly__)
+/* Uses function of name prefixed by devq_
+ * from libdevq 0.0.4, file src/freebsd/device.c
+ */
+static int
+compare_vgapci_busaddr(int i, int *domain, int *bus, int *slot,
+    int *function)
+{
+    int ret;
+    char sysctl_name[32], sysctl_value[128];
+    size_t sysctl_value_len;
+
+    snprintf(sysctl_name, sizeof(sysctl_name), "dev.vgapci.%d.%%location", i);
+
+    sysctl_value_len = sizeof(sysctl_value);
+    memset(sysctl_value, 0, sysctl_value_len);
+    ret = sysctlbyname(sysctl_name, sysctl_value,
+        &sysctl_value_len, NULL, 0);
+    if (ret != 0)
+        return (-1);
+
+    drmMsg("compare_vgapci_busaddr: Read sysctl (%s)\n", sysctl_name);
+    drmMsg("compare_vgapci_busaddr: Sysctl value (%s)\n", sysctl_value);
+
+    /*
+     * dev.vgapci.$m.%location can have two formats:
+     *     o  "pci0:2:0:0 handle=\_SB_.PCI0.PEG3.MXM3" (FreeBSD 11+)
+     *     o  "slot=1 function=0" (DragonFly or up-to FreeBSD 10)
+     */
+
+    ret = sscanf(sysctl_value, "pci%d:%d:%d:%d %*s",
+        domain, bus, slot, function);
+
+    if (ret == 4)
+        return (0);
+
+    ret = sscanf(sysctl_value, "slot=%d function=%d %*s",
+        slot, function);
+    if (ret != 2)
+        return (-1);
+
+    snprintf(sysctl_name, sizeof(sysctl_name), "dev.vgapci.%d.%%parent", i);
+
+    sysctl_value_len = sizeof(sysctl_value);
+    memset(sysctl_value, 0, sysctl_value_len);
+    ret = sysctlbyname(sysctl_name, sysctl_value,
+        &sysctl_value_len, NULL, 0);
+    if (ret != 0)
+        return (-1);
+    
+    drmMsg("compare_vgapci_busaddr: Read sysctl (%s)\n", sysctl_name);
+    drmMsg("compare_vgapci_busaddr: Sysctl value (%s)\n", sysctl_value);
+
+    ret = sscanf(sysctl_value, "pci%d", bus);
+    if (ret != 1)
+        return (-1);
+
+    /* FIXME: What domain to assume? */
+    *domain = 0;
+
+    return (0);
+}
+#endif
+
+#if defined(__FreeBSD__) || defined(__DragonFly__)
+static int drmParsePciDeviceInfoBSD(int maj, int min,
+                                 drmPciDeviceInfoPtr device,
+                                 drmPciBusInfoPtr info,
+                                 uint32_t flags)
+#else
 static int drmParsePciDeviceInfo(int maj, int min,
                                  drmPciDeviceInfoPtr device,
                                  uint32_t flags)
+#endif
 {
 #ifdef __linux__
     if (!(flags & DRM_DEVICE_GET_PCI_REVISION))
@@ -3163,18 +3412,26 @@ static int drmParsePciDeviceInfo(int maj
         return parse_config_sysfs_file(maj, min, device);
 
     return 0;
-#elif defined(__OpenBSD__)
+#elif defined(__OpenBSD__) || defined(__NetBSD__)
     struct drm_pciinfo pinfo;
     int fd, type;
 
+    drmMsg("drmParsePciDeviceInfo: min (%d)\n", min);
+
     type = drmGetMinorType(min);
     if (type == -1)
         return -ENODEV;
 
+    drmMsg("drmParsePciDeviceInfo: type (%d), DRM_NODE_PRIMARY (%d)\n",
+        type, DRM_NODE_PRIMARY);
+
     fd = drmOpenMinor(min, 0, type);
     if (fd < 0)
         return -errno;
 
+    drmMsg("drmParsePciDeviceInfo: ioctl (0x%02x) using fd (%d)\n",
+        DRM_IOCTL_GET_PCIINFO, fd);
+
     if (drmIoctl(fd, DRM_IOCTL_GET_PCIINFO, &pinfo)) {
         close(fd);
         return -errno;
@@ -3188,6 +3445,88 @@ static int drmParsePciDeviceInfo(int maj
     device->subdevice_id = pinfo.subdevice_id;
 
     return 0;
+#elif defined(__FreeBSD__) || defined(__DragonFly__)
+/*
+ * Adapted from function devq_device_get_pciid_full_from_fd(),
+ * from libdevq 0.0.4, file src/freebsd/device.c,
+ */
+    unsigned int vendor_id = 0, device_id = 0, subvendor_id = 0, 
+        subdevice_id = 0, revision_id = 0;
+    int i, ret;
+    char sysctl_name[32], sysctl_value[128];
+    size_t sysctl_value_len;
+
+/*
+ * Now, look at all dev.vgapci.$m trees until we find the
+ * correct device. We specifically look at:
+ *     o  dev.vgapci.$m.%location
+ *     o  dev.vgapci.$m.%parent
+ */
+    for (i = 0; i < DRM_MAX_FDS; ++i) {
+        int tmp_domain, tmp_bus, tmp_slot, tmp_function;
+
+        ret = compare_vgapci_busaddr(i, &tmp_domain, &tmp_bus,
+            &tmp_slot, &tmp_function);
+
+        if (ret == 0 &&
+            tmp_domain == info->domain &&
+            tmp_bus == info->bus &&
+            tmp_slot == info->dev &&
+            tmp_function == info->func)
+            break;
+    }
+
+    if (i == DRM_MAX_FDS) {
+        drmMsg("drmParsePciDeviceInfoBSD: FAILED DRM_MAX_FDS (%d) vgapci!\n",
+            DRM_MAX_FDS);
+        errno = ENOENT;
+        return (-1);
+    }
+
+/*
+ * Ok, we have the right tree. Let's read dev.vgapci.$m.%pnpinfo
+ * to gather the PCI ID.
+ */
+    snprintf(sysctl_name, sizeof(sysctl_name), "dev.vgapci.%d.%%pnpinfo", i);
+
+    sysctl_value_len = sizeof(sysctl_value);
+    memset(sysctl_value, 0, sysctl_value_len);
+    ret = sysctlbyname(sysctl_name, sysctl_value,
+        &sysctl_value_len, NULL, 0);
+    if (ret != 0)
+        return (-1);
+    
+    drmMsg("drmParsePciDeviceInfoBSD: Sysctl (%s) has value (%s)\n",
+        sysctl_name, sysctl_value);
+
+#if defined(__DragonFly__)
+/* DragonFly has a device class field following the subdevice field */
+    ret = sscanf(sysctl_value,
+        "vendor=0x%04x device=0x%04x subvendor=0x%04x subdevice=0x%04x %*s",
+        &vendor_id, &device_id, &subvendor_id, &subdevice_id);
+#else /* FreeBSD */
+    ret = sscanf(sysctl_value,
+        "vendor=0x%04x device=0x%04x subvendor=0x%04x subdevice=0x%04x",
+        &vendor_id, &device_id, &subvendor_id, &subdevice_id);
+    if (ret != 4) {
+        ret = sscanf(sysctl_value,
+            "vendor=0x%04x device=0x%04x subvendor=0x%04x subdevice=0x%04x %*s",
+            &vendor_id, &device_id, &subvendor_id, &subdevice_id);
+    }
+#endif
+    if (ret != 4) {
+        errno = EINVAL;
+        return (-1);
+    }
+
+    device->vendor_id = (uint16_t) vendor_id;
+    device->device_id = (uint16_t) device_id;
+    device->subvendor_id = (uint16_t) subvendor_id;
+    device->subdevice_id = (uint16_t) subdevice_id;
+    /* XXX: add code to find out revision id */
+    device->revision_id = (uint8_t) revision_id;
+
+    return 0;
 #else
 #warning "Missing implementation of drmParsePciDeviceInfo"
     return -EINVAL;
@@ -3305,6 +3644,11 @@ static int drmProcessPciDevice(drmDevice
     char *addr;
     int ret;
 
+    drmMsg("drmProcessPciDevice: node (%s), node_type (%d)\n",
+        node, node_type);
+    drmMsg("drmProcessPciDevice: maj (%d), min (%d), flags (%d)\n",
+        maj, min, flags);
+
     dev = drmDeviceAlloc(node_type, node, sizeof(drmPciBusInfo),
                          sizeof(drmPciDeviceInfo), &addr);
     if (!dev)
@@ -3315,17 +3659,39 @@ static int drmProcessPciDevice(drmDevice
     dev->businfo.pci = (drmPciBusInfoPtr)addr;
 
     ret = drmParsePciBusInfo(maj, min, dev->businfo.pci);
+
     if (ret)
         goto free_device;
 
+    drmMsg("drmProcessPciDevice: domain (%04x)\n", dev->businfo.pci->domain);
+    drmMsg("drmProcessPciDevice: bus    (%02x)\n", dev->businfo.pci->bus);
+    drmMsg("drmProcessPciDevice: dev    (%02x)\n", dev->businfo.pci->dev);
+    drmMsg("drmProcessPciDevice: func   (%1u)\n",  dev->businfo.pci->func);
+
     // Fetch the device info if the user has requested it
     if (fetch_deviceinfo) {
         addr += sizeof(drmPciBusInfo);
         dev->deviceinfo.pci = (drmPciDeviceInfoPtr)addr;
 
+#if defined(__FreeBSD__) || defined(__DragonFly__)
+        ret = drmParsePciDeviceInfoBSD(maj, min, dev->deviceinfo.pci,
+            dev->businfo.pci, flags);
+#else
         ret = drmParsePciDeviceInfo(maj, min, dev->deviceinfo.pci, flags);
+#endif
         if (ret)
             goto free_device;
+
+        drmMsg("drmProcessPciDevice: vendor    (%04x)\n",
+            dev->deviceinfo.pci->vendor_id);
+        drmMsg("drmProcessPciDevice: device    (%04x_\n",
+            dev->deviceinfo.pci->device_id);
+        drmMsg("drmProcessPciDevice: subvendor (%04x)\n",
+            dev->deviceinfo.pci->subvendor_id);
+        drmMsg("drmProcessPciDevice: subdevice (%04x)\n",
+            dev->deviceinfo.pci->subdevice_id);
+        drmMsg("drmProcessPciDevice: revision  (%02x)\n",
+            dev->deviceinfo.pci->revision_id);
     }
 
     *device = dev;
@@ -3687,6 +4053,35 @@ drm_device_validate_flags(uint32_t flags
         return (flags & ~DRM_DEVICE_GET_PCI_REVISION);
 }
 
+#if defined(__FreeBSD__) || defined(__DragonFly__)
+/*
+ * XXX temporary workaround, because FreeBSD doesn't provide 
+ * pcibus device sysctl trees for renderD and controlD nodes (yet)
+ * Finds the corresponding /dev/dri/cardn device and also
+ * returns the number n.
+ */
+static int 
+drmBSDDeviceNameHack(const char *path, char *hacked_path, int length,
+                     int node_type)
+{
+    int start, number, base;
+    const char *errstr;
+
+    base = drmGetMinorBase(node_type);
+    if (node_type == DRM_NODE_RENDER) {
+        start = sizeof(DRM_RENDER_MINOR_NAME) - 1;
+    } else if (node_type == DRM_NODE_CONTROL) {
+        start = sizeof(DRM_CONTROL_MINOR_NAME) - 1;
+    } else {
+        start = sizeof(DRM_PRIMARY_MINOR_NAME) - 1;
+    }
+    number = strtonum(&(path[start]), 0, 256, &errstr) - base;
+    snprintf(hacked_path, length, "card%i", number);
+
+    return number;
+}
+#endif
+
 /**
  * Get information about the opened drm device
  *
@@ -3779,6 +4174,13 @@ int drmGetDevice2(int fd, uint32_t flags
     int max_count = 16;
     dev_t find_rdev;
 
+#if defined(__FreeBSD__) || defined(__DragonFly__)
+    char hacked_path[128];
+#endif
+
+    drmMsg("drmGetDevice2: fd (%d), flags (%d) vs expected (%d)\n",
+        fd, flags, DRM_DEVICE_GET_PCI_REVISION);
+
     if (drm_device_validate_flags(flags))
         return -EINVAL;
 
@@ -3792,7 +4194,11 @@ int drmGetDevice2(int fd, uint32_t flags
     maj = major(sbuf.st_rdev);
     min = minor(sbuf.st_rdev);
 
+#if defined(__FreeBSD__) || defined(__DragonFly__)
+    if (!S_ISCHR(sbuf.st_mode))
+#else
     if (maj != DRM_MAJOR || !S_ISCHR(sbuf.st_mode))
+#endif
         return -EINVAL;
 
     subsystem_type = drmParseSubsystemType(maj, min);
@@ -3809,18 +4215,34 @@ int drmGetDevice2(int fd, uint32_t flags
 
     i = 0;
     while ((dent = readdir(sysdir))) {
+
+#if defined(__FreeBSD__) || defined(__DragonFly__)
+        int number;
+#endif
+
         node_type = drmGetNodeType(dent->d_name);
         if (node_type < 0)
             continue;
 
+#if defined(__FreeBSD__) || defined(__DragonFly__)
+        number = drmBSDDeviceNameHack(dent->d_name, hacked_path,
+            sizeof(hacked_path), node_type);
+
+        snprintf(node, PATH_MAX, "%s/%s", DRM_DIR_NAME, hacked_path);
+#else
         snprintf(node, PATH_MAX, "%s/%s", DRM_DIR_NAME, dent->d_name);
+#endif
         if (stat(node, &sbuf))
             continue;
 
         maj = major(sbuf.st_rdev);
         min = minor(sbuf.st_rdev);
 
+#if defined(__FreeBSD__) || defined(__DragonFly__)
+        if (!S_ISCHR(sbuf.st_mode))
+#else
         if (maj != DRM_MAJOR || !S_ISCHR(sbuf.st_mode))
+#endif
             continue;
 
         if (drmParseSubsystemType(maj, min) != subsystem_type)
@@ -3828,7 +4250,13 @@ int drmGetDevice2(int fd, uint32_t flags
 
         switch (subsystem_type) {
         case DRM_BUS_PCI:
+
+#if defined (__FreeBSD__) || defined(__DragonFly__)
+            ret = drmProcessPciDevice(&d, node, node_type, maj, number, true, flags);
+#else
             ret = drmProcessPciDevice(&d, node, node_type, maj, min, true, flags);
+#endif
+
             if (ret)
                 continue;
 
@@ -3944,6 +4372,13 @@ int drmGetDevices2(uint32_t flags, drmDe
     int ret, i, node_count, device_count;
     int max_count = 16;
 
+#if defined(__FreeBSD__) || defined(__DragonFly__)
+    char hacked_path[128];
+#endif
+
+    drmMsg("drmGetDevices2: flags (%d) vs expected (%d), max_devices (%d)\n",
+        flags, DRM_DEVICE_GET_PCI_REVISION, max_devices);
+
     if (drm_device_validate_flags(flags))
         return -EINVAL;
 
@@ -3959,18 +4394,34 @@ int drmGetDevices2(uint32_t flags, drmDe
 
     i = 0;
     while ((dent = readdir(sysdir))) {
+
+#if defined(__FreeBSD__) || defined(__DragonFly__)
+        int number;
+#endif
+
         node_type = drmGetNodeType(dent->d_name);
         if (node_type < 0)
             continue;
 
+#if defined(__FreeBSD__) || defined(__DragonFly__)
+        number = drmBSDDeviceNameHack(dent->d_name, hacked_path,
+            sizeof(hacked_path), node_type);
+
+        snprintf(node, PATH_MAX, "%s/%s", DRM_DIR_NAME, hacked_path);
+#else
         snprintf(node, PATH_MAX, "%s/%s", DRM_DIR_NAME, dent->d_name);
+#endif
         if (stat(node, &sbuf))
             continue;
 
         maj = major(sbuf.st_rdev);
         min = minor(sbuf.st_rdev);
 
+#if defined(__FreeBSD__) || defined(__DragonFly__)
+        if (!S_ISCHR(sbuf.st_mode))
+#else
         if (maj != DRM_MAJOR || !S_ISCHR(sbuf.st_mode))
+#endif
             continue;
 
         subsystem_type = drmParseSubsystemType(maj, min);
@@ -3980,8 +4431,13 @@ int drmGetDevices2(uint32_t flags, drmDe
 
         switch (subsystem_type) {
         case DRM_BUS_PCI:
+#if defined (__FreeBSD__) || defined(__DragonFly__)
+            ret = drmProcessPciDevice(&device, node, node_type,
+                                      maj, number, devices != NULL, flags);
+#else
             ret = drmProcessPciDevice(&device, node, node_type,
                                       maj, min, devices != NULL, flags);
+#endif
             if (ret)
                 continue;
 
@@ -4101,6 +4557,19 @@ char *drmGetDeviceNameFromFd2(int fd)
     free(value);
 
     return strdup(path);
+#elif defined(__FreeBSD__) || defined(__DragonFly__) || defined(__NetBSD__)
+    struct stat sbuf;
+    char path[64];
+
+    if (fstat(fd, &sbuf))
+        return NULL;
+
+    snprintf(path, sizeof(path), "/dev/%s",
+             devname(sbuf.st_rdev, S_IFCHR));
+
+    drmMsg("drmGetDeviceNameFromFd2: fd (%d) has device name (%s)\n", fd, path);
+
+    return strdup(path);
 #else
     struct stat      sbuf;
     char             node[PATH_MAX + 1];
