#ifdef MAJOR_IN_SYSMACROS
#include <sys/sysmacros.h>
#endif
+#if HAVE_SYS_SYSCTL_H
+#include <sys/sysctl.h>
+#endif
#include <math.h>
+#if defined(__FreeBSD__)
+#include <sys/param.h>
+#include <sys/pciio.h>
+#endif
+
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
/* Not all systems have MAP_FAILED defined */
#include "util_math.h"
-#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
+#ifdef __DragonFly__
#define DRM_MAJOR 145
#endif
static drmServerInfoPtr drm_server_info;
+static bool drmNodeIsDRM(int maj, int min);
+static char *drmGetMinorNameForFD(int fd, int type);
+
+static unsigned log2_int(unsigned x)
+{
+ unsigned l;
+
+ if (x < 2) {
+ return 0;
+ }
+ for (l = 2; ; l++) {
+ if ((unsigned)(1 << l) > x) {
+ return l - 1;
+ }
+ }
+ return 0;
+}
+
+
drm_public void drmSetServerInfo(drmServerInfoPtr info)
{
drm_server_info = info;
};
}
-static int drmGetMinorType(int minor)
+static int drmGetMinorType(int major, int minor)
{
+#ifdef __FreeBSD__
+ char name[SPECNAMELEN];
+ int id;
+
+ if (!devname_r(makedev(major, minor), S_IFCHR, name, sizeof(name)))
+ return -1;
+
+ if (sscanf(name, "drm/%d", &id) != 1) {
+ // If not in /dev/drm/ we have the type in the name
+ if (sscanf(name, "dri/card%d\n", &id) >= 1)
+ return DRM_NODE_PRIMARY;
+ else if (sscanf(name, "dri/control%d\n", &id) >= 1)
+ return DRM_NODE_CONTROL;
+ else if (sscanf(name, "dri/renderD%d\n", &id) >= 1)
+ return DRM_NODE_RENDER;
+ return -1;
+ }
+
+ minor = id;
+#endif
int type = minor >> 6;
if (minor < 0)
int retcode;
sprintf(proc_name, "/proc/dri/%d/name", i);
- if ((fd = open(proc_name, 0, 0)) >= 0) {
+ if ((fd = open(proc_name, O_RDONLY, 0)) >= 0) {
retcode = read(fd, buf, sizeof(buf)-1);
close(fd);
if (retcode) {
drm_public char *drmGetDeviceNameFromFd(int fd)
{
+#ifdef __FreeBSD__
+ struct stat sbuf;
+ int maj, min;
+ int nodetype;
+
+ if (fstat(fd, &sbuf))
+ return NULL;
+
+ maj = major(sbuf.st_rdev);
+ min = minor(sbuf.st_rdev);
+ nodetype = drmGetMinorType(maj, min);
+ return drmGetMinorNameForFD(fd, nodetype);
+#else
char name[128];
struct stat sbuf;
dev_t d;
return NULL;
return strdup(name);
+#endif
}
static bool drmNodeIsDRM(int maj, int min)
snprintf(path, sizeof(path), "/sys/dev/char/%d:%d/device/drm",
maj, min);
return stat(path, &sbuf) == 0;
+#elif defined(__FreeBSD__)
+ char name[SPECNAMELEN];
+
+ if (!devname_r(makedev(maj, min), S_IFCHR, name, sizeof(name)))
+ return 0;
+ /* Handle drm/ and dri/ as both are present in different FreeBSD version
+ * FreeBSD on amd64/i386/powerpc external kernel modules create node in
+ * in /dev/drm/ and links in /dev/dri while a WIP in kernel driver creates
+ * only device nodes in /dev/dri/ */
+ return (!strncmp(name, "drm/", 4) || !strncmp(name, "dri/", 4));
#else
return maj == DRM_MAJOR;
#endif
return -1;
}
- type = drmGetMinorType(min);
+ type = drmGetMinorType(maj, min);
if (type == -1)
errno = ENODEV;
return type;
closedir(sysdir);
return NULL;
+#elif defined(__FreeBSD__)
+ struct stat sbuf;
+ char dname[SPECNAMELEN];
+ const char *mname;
+ char name[SPECNAMELEN];
+ int id, maj, min, nodetype, i;
+
+ if (fstat(fd, &sbuf))
+ return NULL;
+
+ maj = major(sbuf.st_rdev);
+ min = minor(sbuf.st_rdev);
+
+ if (!drmNodeIsDRM(maj, min) || !S_ISCHR(sbuf.st_mode))
+ return NULL;
+
+ if (!devname_r(sbuf.st_rdev, S_IFCHR, dname, sizeof(dname)))
+ return NULL;
+
+ /* Handle both /dev/drm and /dev/dri
+ * FreeBSD on amd64/i386/powerpc external kernel modules create node in
+ * in /dev/drm/ and links in /dev/dri while a WIP in kernel driver creates
+ * only device nodes in /dev/dri/ */
+
+ /* Get the node type represented by fd so we can deduce the target name */
+ nodetype = drmGetMinorType(maj, min);
+ if (nodetype == -1)
+ return (NULL);
+ mname = drmGetMinorName(type);
+
+ for (i = 0; i < SPECNAMELEN; i++) {
+ if (isalpha(dname[i]) == 0 && dname[i] != '/')
+ break;
+ }
+ if (dname[i] == '\0')
+ return (NULL);
+
+ id = (int)strtol(&dname[i], NULL, 10);
+ id -= drmGetMinorBase(nodetype);
+ snprintf(name, sizeof(name), DRM_DIR_NAME "/%s%d", mname,
+ id + drmGetMinorBase(type));
+
+ return strdup(name);
#else
struct stat sbuf;
char buf[PATH_MAX + 1];
int subsystem_type;
snprintf(path, sizeof(path), "/sys/dev/char/%d:%d/device", maj, min);
- if (!realpath(path, real_path))
- return -errno;
- snprintf(path, sizeof(path), "%s", real_path);
subsystem_type = get_subsystem_type(path);
+ /* Try to get the parent (underlying) device type */
if (subsystem_type == DRM_BUS_VIRTIO) {
+ /* Assume virtio-pci on error */
+ if (!realpath(path, real_path))
+ return DRM_BUS_VIRTIO;
strncat(path, "/..", PATH_MAX);
subsystem_type = get_subsystem_type(path);
- }
+ if (subsystem_type < 0)
+ return DRM_BUS_VIRTIO;
+ }
return subsystem_type;
-#elif defined(__OpenBSD__) || defined(__DragonFly__)
+#elif defined(__OpenBSD__) || defined(__DragonFly__) || defined(__FreeBSD__)
return DRM_BUS_PCI;
#else
#warning "Missing implementation of drmParseSubsystemType"
#endif
}
+#ifdef __linux__
static void
get_pci_path(int maj, int min, char *pci_path)
{
if (term && strncmp(term, "/virtio", 7) == 0)
*term = 0;
}
+#endif
+
+#ifdef __FreeBSD__
+static int get_sysctl_pci_bus_info(int maj, int min, drmPciBusInfoPtr info)
+{
+ char dname[SPECNAMELEN];
+ char sysctl_name[16];
+ char sysctl_val[256];
+ size_t sysctl_len;
+ int id, type, nelem;
+ unsigned int rdev, majmin, domain, bus, dev, func;
+
+ rdev = makedev(maj, min);
+ if (!devname_r(rdev, S_IFCHR, dname, sizeof(dname)))
+ return -EINVAL;
+
+ if (sscanf(dname, "drm/%d\n", &id) != 1)
+ return -EINVAL;
+ type = drmGetMinorType(maj, min);
+ if (type == -1)
+ return -EINVAL;
+
+ /* BUG: This above section is iffy, since it mandates that a driver will
+ * create both card and render node.
+ * If it does not, the next DRM device will create card#X and
+ * renderD#(128+X)-1.
+ * This is a possibility in FreeBSD but for now there is no good way for
+ * obtaining the info.
+ */
+ switch (type) {
+ case DRM_NODE_PRIMARY:
+ break;
+ case DRM_NODE_CONTROL:
+ id -= 64;
+ break;
+ case DRM_NODE_RENDER:
+ id -= 128;
+ break;
+ }
+ if (id < 0)
+ return -EINVAL;
+
+ if (snprintf(sysctl_name, sizeof(sysctl_name), "hw.dri.%d.busid", id) <= 0)
+ return -EINVAL;
+ sysctl_len = sizeof(sysctl_val);
+ if (sysctlbyname(sysctl_name, sysctl_val, &sysctl_len, NULL, 0))
+ return -EINVAL;
+
+ #define bus_fmt "pci:%04x:%02x:%02x.%u"
+
+ nelem = sscanf(sysctl_val, bus_fmt, &domain, &bus, &dev, &func);
+ if (nelem != 4)
+ return -EINVAL;
+ info->domain = domain;
+ info->bus = bus;
+ info->dev = dev;
+ info->func = func;
+
+ return 0;
+}
+#endif
static int drmParsePciBusInfo(int maj, int min, drmPciBusInfoPtr info)
{
struct drm_pciinfo pinfo;
int fd, type;
- type = drmGetMinorType(min);
+ type = drmGetMinorType(maj, min);
if (type == -1)
return -ENODEV;
info->func = pinfo.func;
return 0;
+#elif defined(__FreeBSD__)
+ return get_sysctl_pci_bus_info(maj, min, info);
#else
#warning "Missing implementation of drmParsePciBusInfo"
return -EINVAL;
struct drm_pciinfo pinfo;
int fd, type;
- type = drmGetMinorType(min);
+ type = drmGetMinorType(maj, min);
if (type == -1)
return -ENODEV;
device->subdevice_id = pinfo.subdevice_id;
return 0;
+#elif defined(__FreeBSD__)
+ drmPciBusInfo info;
+ struct pci_conf_io pc;
+ struct pci_match_conf patterns[1];
+ struct pci_conf results[1];
+ int fd, error;
+
+ if (get_sysctl_pci_bus_info(maj, min, &info) != 0)
+ return -EINVAL;
+
+ fd = open("/dev/pci", O_RDONLY, 0);
+ if (fd < 0)
+ return -errno;
+
+ bzero(&patterns, sizeof(patterns));
+ patterns[0].pc_sel.pc_domain = info.domain;
+ patterns[0].pc_sel.pc_bus = info.bus;
+ patterns[0].pc_sel.pc_dev = info.dev;
+ patterns[0].pc_sel.pc_func = info.func;
+ patterns[0].flags = PCI_GETCONF_MATCH_DOMAIN | PCI_GETCONF_MATCH_BUS
+ | PCI_GETCONF_MATCH_DEV | PCI_GETCONF_MATCH_FUNC;
+ bzero(&pc, sizeof(struct pci_conf_io));
+ pc.num_patterns = 1;
+ pc.pat_buf_len = sizeof(patterns);
+ pc.patterns = patterns;
+ pc.match_buf_len = sizeof(results);
+ pc.matches = results;
+
+ if (ioctl(fd, PCIOCGETCONF, &pc) || pc.status == PCI_GETCONF_ERROR) {
+ error = errno;
+ close(fd);
+ return -error;
+ }
+ close(fd);
+
+ device->vendor_id = results[0].pc_vendor;
+ device->device_id = results[0].pc_device;
+ device->subvendor_id = results[0].pc_subvendor;
+ device->subdevice_id = results[0].pc_subdevice;
+ device->revision_id = results[0].pc_revid;
+
+ return 0;
#else
#warning "Missing implementation of drmParsePciDeviceInfo"
return -EINVAL;
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__
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)
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)
switch (subsystem_type) {
case DRM_BUS_PCI:
+ case DRM_BUS_VIRTIO:
return drmProcessPciDevice(device, node, node_type, maj, min,
fetch_deviceinfo, flags);
case DRM_BUS_USB:
for (j = i + 1; j < count; j++) {
if (drmDevicesEqual(local_devices[i], local_devices[j])) {
local_devices[i]->available_nodes |= local_devices[j]->available_nodes;
- node_type = log2(local_devices[j]->available_nodes);
+ node_type = log2_int(local_devices[j]->available_nodes);
memcpy(local_devices[i]->nodes[node_type],
local_devices[j]->nodes[node_type], drmGetMaxNodeName());
drmFreeDevice(&local_devices[j]);
if (!drmNodeIsDRM(maj, min) || !S_ISCHR(sbuf.st_mode))
return -EINVAL;
- node_type = drmGetMinorType(min);
+ node_type = drmGetMinorType(maj, min);
if (node_type == -1)
return -ENODEV;
free(value);
return strdup(path);
+#elif defined(__FreeBSD__)
+ return drmGetDeviceNameFromFd(fd);
#else
struct stat sbuf;
char node[PATH_MAX + 1];
if (!drmNodeIsDRM(maj, min) || !S_ISCHR(sbuf.st_mode))
return NULL;
- node_type = drmGetMinorType(min);
+ node_type = drmGetMinorType(maj, min);
if (node_type == -1)
return NULL;