#endif
#include <stdio.h>
#include <stdlib.h>
+#include <stdbool.h>
#include <unistd.h>
#include <string.h>
#include <strings.h>
#include <stddef.h>
#include <fcntl.h>
#include <errno.h>
+#include <limits.h>
#include <signal.h>
#include <time.h>
#include <sys/types.h>
#ifdef HAVE_SYS_MKDEV_H
# include <sys/mkdev.h> /* defines major(), minor(), and makedev() on Solaris */
#endif
+#include <math.h>
/* Not all systems have MAP_FAILED defined */
#ifndef MAP_FAILED
#include "xf86drm.h"
#include "libdrm_macros.h"
+#include "util_math.h"
+
+#ifdef __OpenBSD__
+#define DRM_PRIMARY_MINOR_NAME "drm"
+#define DRM_CONTROL_MINOR_NAME "drmC"
+#define DRM_RENDER_MINOR_NAME "drmR"
+#else
+#define DRM_PRIMARY_MINOR_NAME "card"
+#define DRM_CONTROL_MINOR_NAME "controlD"
+#define DRM_RENDER_MINOR_NAME "renderD"
+#endif
+
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
#define DRM_MAJOR 145
#endif
#define DRM_MAJOR 34
#endif
-# ifdef __OpenBSD__
-# define DRM_MAJOR 81
-# endif
+#ifdef __OpenBSD__
+#ifdef __i386__
+#define DRM_MAJOR 88
+#else
+#define DRM_MAJOR 87
+#endif
+#endif /* __OpenBSD__ */
#ifndef DRM_MAJOR
#define DRM_MAJOR 226 /* Linux */
#endif
-/*
- * This definition needs to be changed on some systems if dev_t is a structure.
- * If there is a header file we can get it from, there would be best.
- */
-#ifndef makedev
-#define makedev(x,y) ((dev_t)(((x) << 8) | (y)))
-#endif
-
#define DRM_MSG_VERBOSITY 3
#define memclear(s) memset(&s, 0, sizeof(s))
{
va_list ap;
const char *env;
- if (((env = getenv("LIBGL_DEBUG")) && strstr(env, "verbose")) || drm_server_info)
+ if (((env = getenv("LIBGL_DEBUG")) && strstr(env, "verbose")) ||
+ (drm_server_info && drm_server_info->debug_print))
{
va_start(ap, format);
if (drm_server_info) {
void *drmMalloc(int size)
{
- void *pt;
- if ((pt = malloc(size)))
- memset(pt, 0, size);
- return pt;
+ return calloc(1, size);
}
void drmFree(void *pt)
{
- if (pt)
- free(pt);
+ free(pt);
}
/**
sprintf(buf, dev_name, DRM_DIR_NAME, minor);
drmMsg("drmOpenDevice: node name is %s\n", buf);
- if (drm_server_info) {
+ if (drm_server_info && drm_server_info->get_perms) {
drm_server_info->get_perms(&serv_group, &serv_mode);
devmode = serv_mode ? serv_mode : DRM_DEV_MODE;
devmode &= ~(S_IXUSR|S_IXGRP|S_IXOTH);
mknod(buf, S_IFCHR | devmode, dev);
}
- if (drm_server_info) {
- group = (serv_group >= 0) ? serv_group : DRM_DEV_GID;
+ if (drm_server_info && drm_server_info->get_perms) {
+ group = ((int)serv_group >= 0) ? serv_group : DRM_DEV_GID;
chown_check_return(buf, user, group);
chmod(buf, devmode);
}
return DRM_ERR_NOT_ROOT;
remove(buf);
mknod(buf, S_IFCHR | devmode, dev);
- if (drm_server_info) {
+ if (drm_server_info && drm_server_info->get_perms) {
chown_check_return(buf, user, group);
chmod(buf, devmode);
}
{
switch (type) {
case DRM_NODE_PRIMARY:
- return "card";
+ return DRM_PRIMARY_MINOR_NAME;
case DRM_NODE_CONTROL:
- return "controlD";
+ return DRM_CONTROL_MINOR_NAME;
case DRM_NODE_RENDER:
- return "renderD";
+ return DRM_RENDER_MINOR_NAME;
default:
return NULL;
}
*/
int drmOpenWithType(const char *name, const char *busid, int type)
{
- if (!drmAvailable() && name != NULL && drm_server_info) {
+ if (!drmAvailable() && name != NULL && drm_server_info &&
+ drm_server_info->load_module) {
/* try to load the kernel module */
if (!drm_server_info->load_module(name)) {
drmMsg("[drm] failed to load kernel module \"%s\"\n", name);
}
fd = drmOpenWithType(NULL, BusID, type);
- if (fd <= 0 || nr_fds == DRM_MAX_FDS)
+ if (fd < 0 || nr_fds == DRM_MAX_FDS)
return fd;
connection[nr_fds].BusID = strdup(BusID);
struct drm_prime_handle args;
int ret;
+ memclear(args);
+ args.fd = -1;
args.handle = handle;
args.flags = flags;
ret = drmIoctl(fd, DRM_IOCTL_PRIME_HANDLE_TO_FD, &args);
struct drm_prime_handle args;
int ret;
+ memclear(args);
args.fd = prime_fd;
- args.flags = 0;
ret = drmIoctl(fd, DRM_IOCTL_PRIME_FD_TO_HANDLE, &args);
if (ret)
return ret;
while (readdir_r(sysdir, pent, &ent) == 0 && ent != NULL) {
if (strncmp(ent->d_name, name, len) == 0) {
+ snprintf(dev_name, sizeof(dev_name), DRM_DIR_NAME "/%s",
+ ent->d_name);
+
free(pent);
closedir(sysdir);
- snprintf(dev_name, sizeof(dev_name), DRM_DIR_NAME "/%s",
- ent->d_name);
return strdup(dev_name);
}
}
out_close_dir:
closedir(sysdir);
+#else
+#warning "Missing implementation of drmGetMinorNameForFD"
#endif
return NULL;
}
{
return drmGetMinorNameForFD(fd, DRM_NODE_RENDER);
}
+
+static int drmParseSubsystemType(int maj, int min)
+{
+#ifdef __linux__
+ char path[PATH_MAX + 1];
+ char link[PATH_MAX + 1] = "";
+ char *name;
+
+ snprintf(path, PATH_MAX, "/sys/dev/char/%d:%d/device/subsystem",
+ maj, min);
+
+ if (readlink(path, link, PATH_MAX) < 0)
+ return -errno;
+
+ name = strrchr(link, '/');
+ if (!name)
+ return -EINVAL;
+
+ if (strncmp(name, "/pci", 4) == 0)
+ return DRM_BUS_PCI;
+
+ return -EINVAL;
+#else
+#warning "Missing implementation of drmParseSubsystemType"
+ return -EINVAL;
+#endif
+}
+
+static int drmParsePciBusInfo(int maj, int min, drmPciBusInfoPtr info)
+{
+#ifdef __linux__
+ char path[PATH_MAX + 1];
+ char data[128 + 1];
+ char *str;
+ int domain, bus, dev, func;
+ int fd, ret;
+
+ snprintf(path, PATH_MAX, "/sys/dev/char/%d:%d/device/uevent", maj, min);
+ fd = open(path, O_RDONLY);
+ if (fd < 0)
+ return -errno;
+
+ ret = read(fd, data, sizeof(data));
+ data[128] = '\0';
+ close(fd);
+ if (ret < 0)
+ return -errno;
+
+#define TAG "PCI_SLOT_NAME="
+ str = strstr(data, TAG);
+ if (str == NULL)
+ return -EINVAL;
+
+ if (sscanf(str, TAG "%04x:%02x:%02x.%1u",
+ &domain, &bus, &dev, &func) != 4)
+ return -EINVAL;
+#undef TAG
+
+ info->domain = domain;
+ info->bus = bus;
+ info->dev = dev;
+ info->func = func;
+
+ return 0;
+#else
+#warning "Missing implementation of drmParsePciBusInfo"
+ return -EINVAL;
+#endif
+}
+
+static int drmCompareBusInfo(drmDevicePtr a, drmDevicePtr b)
+{
+ if (a == NULL || b == NULL)
+ return -1;
+
+ if (a->bustype != b->bustype)
+ return -1;
+
+ switch (a->bustype) {
+ case DRM_BUS_PCI:
+ return memcmp(a->businfo.pci, b->businfo.pci, sizeof(drmPciBusInfo));
+ default:
+ break;
+ }
+
+ return -1;
+}
+
+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) +
+ MAX3(sizeof(DRM_PRIMARY_MINOR_NAME),
+ sizeof(DRM_CONTROL_MINOR_NAME),
+ sizeof(DRM_RENDER_MINOR_NAME)) +
+ 3 /* lenght of the node number */;
+}
+
+static int drmParsePciDeviceInfo(const char *d_name,
+ drmPciDeviceInfoPtr device)
+{
+#ifdef __linux__
+ char path[PATH_MAX + 1];
+ unsigned char config[64];
+ int fd, ret;
+
+ snprintf(path, PATH_MAX, "/sys/class/drm/%s/device/config", d_name);
+ fd = open(path, O_RDONLY);
+ if (fd < 0)
+ return -errno;
+
+ ret = read(fd, config, sizeof(config));
+ close(fd);
+ if (ret < 0)
+ return -errno;
+
+ device->vendor_id = config[0] | (config[1] << 8);
+ device->device_id = config[2] | (config[3] << 8);
+ device->revision_id = config[8];
+ device->subvendor_id = config[44] | (config[45] << 8);
+ device->subdevice_id = config[46] | (config[47] << 8);
+
+ return 0;
+#else
+#warning "Missing implementation of drmParsePciDeviceInfo"
+ return -EINVAL;
+#endif
+}
+
+void drmFreeDevice(drmDevicePtr *device)
+{
+ if (device == NULL)
+ return;
+
+ free(*device);
+ *device = NULL;
+}
+
+void drmFreeDevices(drmDevicePtr devices[], int count)
+{
+ int i;
+
+ if (devices == NULL)
+ return;
+
+ for (i = 0; i < count && devices[i] != NULL; i++)
+ drmFreeDevice(&devices[i]);
+}
+
+static int drmProcessPciDevice(drmDevicePtr *device, const char *d_name,
+ const char *node, int node_type,
+ int maj, int min, bool fetch_deviceinfo)
+{
+ const int max_node_str = drmGetMaxNodeName();
+ int ret, i;
+ char *addr;
+
+ *device = calloc(1, sizeof(drmDevice) +
+ (DRM_NODE_MAX * (sizeof(void *) + max_node_str)) +
+ sizeof(drmPciBusInfo) +
+ sizeof(drmPciDeviceInfo));
+ if (!*device)
+ return -ENOMEM;
+
+ addr = (char*)*device;
+
+ (*device)->bustype = DRM_BUS_PCI;
+ (*device)->available_nodes = 1 << node_type;
+
+ addr += sizeof(drmDevice);
+ (*device)->nodes = (char**)addr;
+
+ addr += DRM_NODE_MAX * sizeof(void *);
+ for (i = 0; i < DRM_NODE_MAX; i++) {
+ (*device)->nodes[i] = addr;
+ addr += max_node_str;
+ }
+ memcpy((*device)->nodes[node_type], node, max_node_str);
+
+ (*device)->businfo.pci = (drmPciBusInfoPtr)addr;
+
+ ret = drmParsePciBusInfo(maj, min, (*device)->businfo.pci);
+ if (ret)
+ goto free_device;
+
+ // Fetch the device info if the user has requested it
+ if (fetch_deviceinfo) {
+ addr += sizeof(drmPciBusInfo);
+ (*device)->deviceinfo.pci = (drmPciDeviceInfoPtr)addr;
+
+ ret = drmParsePciDeviceInfo(d_name, (*device)->deviceinfo.pci);
+ if (ret)
+ goto free_device;
+ }
+ return 0;
+
+free_device:
+ free(*device);
+ *device = NULL;
+ return ret;
+}
+
+static void drmFoldDuplicatedDevices(drmDevicePtr local_devices[], int count)
+{
+ int node_type, i, j;
+
+ for (i = 0; i < count; i++) {
+ for (j = i + 1; j < count; j++) {
+ if (drmCompareBusInfo(local_devices[i], local_devices[j]) == 0) {
+ local_devices[i]->available_nodes |= local_devices[j]->available_nodes;
+ node_type = log2(local_devices[j]->available_nodes);
+ memcpy(local_devices[i]->nodes[node_type],
+ local_devices[j]->nodes[node_type], drmGetMaxNodeName());
+ drmFreeDevice(&local_devices[j]);
+ }
+ }
+ }
+}
+
+/**
+ * Get information about the opened drm device
+ *
+ * \param fd file descriptor of the drm device
+ * \param device the address of a drmDevicePtr where the information
+ * will be allocated in stored
+ *
+ * \return zero on success, negative error code otherwise.
+ */
+int drmGetDevice(int fd, drmDevicePtr *device)
+{
+ drmDevicePtr *local_devices;
+ drmDevicePtr d;
+ DIR *sysdir;
+ struct dirent *dent;
+ struct stat sbuf;
+ char node[PATH_MAX + 1];
+ int node_type, subsystem_type;
+ int maj, min;
+ int ret, i, node_count;
+ int max_count = 16;
+
+ if (fd == -1 || device == NULL)
+ return -EINVAL;
+
+ if (fstat(fd, &sbuf))
+ return -errno;
+
+ maj = major(sbuf.st_rdev);
+ min = minor(sbuf.st_rdev);
+
+ if (maj != DRM_MAJOR || !S_ISCHR(sbuf.st_mode))
+ return -EINVAL;
+
+ subsystem_type = drmParseSubsystemType(maj, min);
+
+ local_devices = calloc(max_count, sizeof(drmDevicePtr));
+ if (local_devices == NULL)
+ return -ENOMEM;
+
+ sysdir = opendir(DRM_DIR_NAME);
+ if (!sysdir) {
+ ret = -errno;
+ goto free_locals;
+ }
+
+ i = 0;
+ while ((dent = readdir(sysdir))) {
+ node_type = drmGetNodeType(dent->d_name);
+ if (node_type < 0)
+ continue;
+
+ snprintf(node, PATH_MAX, "%s/%s", DRM_DIR_NAME, dent->d_name);
+ if (stat(node, &sbuf))
+ continue;
+
+ maj = major(sbuf.st_rdev);
+ min = minor(sbuf.st_rdev);
+
+ if (maj != DRM_MAJOR || !S_ISCHR(sbuf.st_mode))
+ continue;
+
+ if (drmParseSubsystemType(maj, min) != subsystem_type)
+ continue;
+
+ switch (subsystem_type) {
+ case DRM_BUS_PCI:
+ ret = drmProcessPciDevice(&d, dent->d_name, node, node_type,
+ maj, min, true);
+ if (ret)
+ goto free_devices;
+
+ break;
+ default:
+ fprintf(stderr, "The subsystem type is not supported yet\n");
+ continue;
+ }
+
+ if (i >= max_count) {
+ drmDevicePtr *temp;
+
+ max_count += 16;
+ temp = realloc(local_devices, max_count * sizeof(drmDevicePtr));
+ if (!temp)
+ goto free_devices;
+ local_devices = temp;
+ }
+
+ local_devices[i] = d;
+ i++;
+ }
+ node_count = i;
+
+ /* Fold nodes into a single device if they share the same bus info */
+ drmFoldDuplicatedDevices(local_devices, node_count);
+
+ *device = local_devices[0];
+ for (i = 1; i < node_count && local_devices[i]; i++)
+ drmFreeDevice(&local_devices[i]);
+
+ closedir(sysdir);
+ free(local_devices);
+ return 0;
+
+free_devices:
+ drmFreeDevices(local_devices, i);
+ closedir(sysdir);
+
+free_locals:
+ free(local_devices);
+ return ret;
+}
+
+/**
+ * Get drm devices on the system
+ *
+ * \param devices the array of devices with drmDevicePtr elements
+ * can be NULL to get the device number first
+ * \param max_devices the maximum number of devices for the array
+ *
+ * \return on error - negative error code,
+ * if devices is NULL - total number of devices available on the system,
+ * alternatively the number of devices stored in devices[], which is
+ * capped by the max_devices.
+ */
+int drmGetDevices(drmDevicePtr devices[], int max_devices)
+{
+ drmDevicePtr *local_devices;
+ drmDevicePtr device;
+ DIR *sysdir;
+ struct dirent *dent;
+ struct stat sbuf;
+ char node[PATH_MAX + 1];
+ int node_type, subsystem_type;
+ int maj, min;
+ int ret, i, node_count, device_count;
+ int max_count = 16;
+
+ local_devices = calloc(max_count, sizeof(drmDevicePtr));
+ if (local_devices == NULL)
+ return -ENOMEM;
+
+ sysdir = opendir(DRM_DIR_NAME);
+ if (!sysdir) {
+ ret = -errno;
+ goto free_locals;
+ }
+
+ i = 0;
+ while ((dent = readdir(sysdir))) {
+ node_type = drmGetNodeType(dent->d_name);
+ if (node_type < 0)
+ continue;
+
+ snprintf(node, PATH_MAX, "%s/%s", DRM_DIR_NAME, dent->d_name);
+ if (stat(node, &sbuf))
+ continue;
+
+ maj = major(sbuf.st_rdev);
+ min = minor(sbuf.st_rdev);
+
+ if (maj != DRM_MAJOR || !S_ISCHR(sbuf.st_mode))
+ continue;
+
+ subsystem_type = drmParseSubsystemType(maj, min);
+
+ if (subsystem_type < 0)
+ continue;
+
+ switch (subsystem_type) {
+ case DRM_BUS_PCI:
+ ret = drmProcessPciDevice(&device, dent->d_name, node, node_type,
+ maj, min, devices != NULL);
+ if (ret)
+ goto free_devices;
+
+ break;
+ default:
+ fprintf(stderr, "The subsystem type is not supported yet\n");
+ continue;
+ }
+
+ if (i >= max_count) {
+ drmDevicePtr *temp;
+
+ max_count += 16;
+ temp = realloc(local_devices, max_count * sizeof(drmDevicePtr));
+ if (!temp)
+ goto free_devices;
+ local_devices = temp;
+ }
+
+ local_devices[i] = device;
+ i++;
+ }
+ node_count = i;
+
+ /* Fold nodes into a single device if they share the same bus info */
+ drmFoldDuplicatedDevices(local_devices, node_count);
+
+ device_count = 0;
+ for (i = 0; i < node_count && local_devices[i]; i++) {
+ if ((devices != NULL) && (device_count < max_devices))
+ devices[device_count] = local_devices[i];
+ else
+ drmFreeDevice(&local_devices[i]);
+
+ device_count++;
+ }
+
+ closedir(sysdir);
+ free(local_devices);
+ return device_count;
+
+free_devices:
+ drmFreeDevices(local_devices, i);
+ closedir(sysdir);
+
+free_locals:
+ free(local_devices);
+ return ret;
+}