#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 "libudev.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
#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 */
}
if (drm_server_info) {
- group = (serv_group >= 0) ? serv_group : DRM_DEV_GID;
+ group = ((int)serv_group >= 0) ? serv_group : DRM_DEV_GID;
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;
}
}
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);
}
}
return drmGetMinorNameForFD(fd, DRM_NODE_RENDER);
}
+#ifdef __linux__
+static int drmParseSubsystemType(const char *str)
+{
+ char link[PATH_MAX + 1] = "";
+ char *name;
+
+ if (readlink(str, link, PATH_MAX) < 0)
+ return -EINVAL;
+
+ name = strrchr(link, '/');
+ if (!name)
+ return -EINVAL;
+
+ name++;
+
+ if (strncmp(name, "pci", 3) == 0)
+ return DRM_BUS_PCI;
+
+ return -EINVAL;
+}
+
+static int drmParsePciBusInfo(const char *str, drmPciBusInfoPtr info)
+{
+ int domain, bus, dev, func;
+ char *value;
+
+ if (str == NULL)
+ return -EINVAL;
+
+ value = strstr(str, "PCI_SLOT_NAME=");
+ if (value == NULL)
+ return -EINVAL;
+
+ value += strlen("PCI_SLOT_NAME=");
+
+ if (sscanf(value, "%04x:%02x:%02x.%1u",
+ &domain, &bus, &dev, &func) != 4)
+ return -EINVAL;
+
+ info->domain = domain;
+ info->bus = bus;
+ info->dev = dev;
+ info->func = func;
+
+ return 0;
+}
+
+static int drmSameDevice(drmDevicePtr a, drmDevicePtr b)
+{
+ if (a->bustype != b->bustype)
+ return 0;
+
+ switch (a->bustype) {
+ case DRM_BUS_PCI:
+ if (memcmp(a->businfo.pci, b->businfo.pci, sizeof(drmPciBusInfo)) == 0)
+ return 1;
+ default:
+ break;
+ }
+
+ 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 drmParsePciDeviceInfo(const unsigned char *config,
+ drmPciDeviceInfoPtr device)
+{
+ if (config == NULL)
+ return -EINVAL;
+
+ 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;
+}
+
+static void drmFreeDevice(drmDevicePtr device)
+{
+ int i;
+
+ if (device == NULL)
+ return;
+
+ if (device->nodes != NULL)
+ for (i = 0; i < DRM_NODE_MAX; i++)
+ free(device->nodes[i]);
+
+ free(device->nodes);
+ free(device->businfo.pci);
+ free(device->deviceinfo.pci);
+}
+
+void drmFreeDevices(drmDevicePtr devices[], int count)
+{
+ int i;
+
+ if (devices == NULL)
+ return;
+
+ for (i = 0; i < count; i++) {
+ drmFreeDevice(devices[i]);
+ free(devices[i]);
+ devices[i] = NULL;
+ }
+}
+
/**
-* Enumerate the GPU devices on the system
-*
-* \param devs device array set to return the device information
-* (if NULL, the number of device is returned)
-* \param vendor the vendor ID for GPU devices to list
-* (optional, if not specified, all GPU devices are returned)
-*
-* \return the number of GPU devices
-*/
-int drmGetPciDevices(drmPciDevicePtr devSet, uint16_t vendorId)
-{
- struct udev *udev = NULL;
- struct udev_enumerate *udev_enumerate;
- struct udev_list_entry *list_entry;
- struct udev_device *device;
- int drmDevCount = 0;
- int vendor = 0;
- int devid = 0;
- int subvendor = 0;
- int subdevid = 0;
- int revid = 0xff;
- int domain = 0;
- int bus = 0;
- int dev = 0;
- int func = 0;
- char config[64] = {0};
- char node[128] = {'\0'};
- char slot[] = "0000:00:00.0";
- const char *info = NULL;
- int fd = 0;
- int ret = 0;
-
- udev = udev_new();
- if (udev == NULL) {
- fprintf(stderr, "no context\n");
- return -EINVAL;
- }
- udev_enumerate = udev_enumerate_new(udev);
- if (udev_enumerate == NULL)
- return -EINVAL;
- udev_enumerate_add_match_subsystem(udev_enumerate, "drm");
- udev_enumerate_add_match_property(udev_enumerate, "DEVTYPE", "drm_minor");
-
- udev_enumerate_scan_devices(udev_enumerate);
-
- udev_list_entry_foreach(list_entry, udev_enumerate_get_list_entry(udev_enumerate)) {
- device = udev_device_new_from_syspath(udev_enumerate_get_udev(udev_enumerate),
- udev_list_entry_get_name(list_entry));
- if (device != NULL) {
- info = udev_device_get_property_value(device, "MINOR");
- if (!strcmp(info, "0")) {
- strcpy(node, udev_device_get_syspath(device));
- info = strstr(node, "/drm");
- strncpy(slot, info - strlen(slot), strlen(slot));
- if (sscanf(slot, "%4x:%2x:%2x.%1x", &domain, &bus, &dev, &func) != 4) {
- domain = 0;
- bus = 0;
- dev = 0;
- func = 0;
- }
- strcpy(node + strlen(node), "/device/config");
-
- fd = open(node, O_RDONLY);
- if (fd >= 0) {
- ret = read(fd, config, 64);
- if (ret == 64) {
- vendor = config[0] + (config[1] << 8);
- devid = config[2] + (config[3] << 8);
- revid = config[8];
- subdevid = config[44] + (config[45] << 8);
- }
- close(fd);
- }
+ * 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 devs = NULL;
+ drmPciBusInfoPtr pcibus = NULL;
+ drmPciDeviceInfoPtr pcidevice = NULL;
+ DIR *sysdir = NULL;
+ struct dirent *dent = NULL;
+ struct stat sbuf = {0};
+ char node[PATH_MAX + 1] = "";
+ char path[PATH_MAX + 1] = "";
+ char data[128] = "";
+ unsigned char config[64] = "";
+ int node_type, subsystem_type;
+ int maj, min;
+ int fd;
+ int ret, i = 0, j, node_count, device_count = 0;
+ int max_count = 16;
+ int *duplicated = NULL;
+
+ devs = calloc(max_count, sizeof(*devs));
+ if (devs == NULL)
+ return -ENOMEM;
+
+ sysdir = opendir(DRM_DIR_NAME);
+ if (!sysdir) {
+ ret = -errno;
+ goto free_locals;
+ }
- if((vendorId == 0) || (vendorId == vendor)) {
- if(devSet != NULL) {
- devSet[drmDevCount].domain = domain;
- devSet[drmDevCount].bus = bus;
- devSet[drmDevCount].dev = dev;
- devSet[drmDevCount].func = func;
- devSet[drmDevCount].vendor_id = vendor;
- devSet[drmDevCount].device_id = devid;
- devSet[drmDevCount].subdevice_id = subdevid;
- devSet[drmDevCount].subvendor_id = subvendor;
- devSet[drmDevCount].revision_id = revid;
- }
- drmDevCount++;
- }
- }
- }
- udev_device_unref(device);
- }
- udev_enumerate_unref(udev_enumerate);
- udev_unref(udev);
+ 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;
+
+ snprintf(path, PATH_MAX, "/sys/dev/char/%d:%d/device/subsystem",
+ maj, min);
+ subsystem_type = drmParseSubsystemType(path);
+
+ if (subsystem_type < 0)
+ continue;
+
+ switch (subsystem_type) {
+ case DRM_BUS_PCI:
+ pcibus = calloc(1, sizeof(*pcibus));
+ if (pcibus == NULL) {
+ ret = -ENOMEM;
+ goto free_locals;
+ }
+
+ snprintf(path, PATH_MAX, "/sys/dev/char/%d:%d/device/uevent",
+ maj, min);
+ fd = open(path, O_RDONLY);
+ if (fd < 0) {
+ ret = -errno;
+ goto free_locals;
+ }
+ ret = read(fd, data, sizeof(data));
+ if (ret < 0) {
+ ret = -errno;
+ close(fd);
+ goto free_locals;
+ }
+
+ ret = drmParsePciBusInfo(data, pcibus);
+ close(fd);
+ if (ret)
+ goto free_locals;
+
+ if (i >= max_count) {
+ max_count += 16;
+ devs = realloc(devs, max_count * sizeof(*devs));
+ }
+
+ devs[i].businfo.pci = pcibus;
+ devs[i].bustype = subsystem_type;
+ devs[i].nodes = calloc(DRM_NODE_MAX, sizeof(char *));
+ if (devs[i].nodes == NULL) {
+ ret = -ENOMEM;
+ goto free_locals;
+ }
+ devs[i].nodes[node_type] = strdup(node);
+ if (devs[i].nodes[node_type] == NULL) {
+ ret = -ENOMEM;
+ goto free_locals;
+ }
+ devs[i].available_nodes = 1 << node_type;
+
+ if (devices != NULL) {
+ snprintf(path, PATH_MAX, "/sys/class/drm/%s/device/config",
+ dent->d_name);
+ fd = open(path, O_RDONLY);
+ if (fd < 0) {
+ ret = -errno;
+ goto free_locals;
+ }
+ ret = read(fd, config, 64);
+ if (ret < 0) {
+ ret = -errno;
+ close(fd);
+ goto free_locals;
+ }
+
+ pcidevice = calloc(1, sizeof(*pcidevice));
+ if (pcidevice == NULL) {
+ ret = -ENOMEM;
+ goto free_locals;
+ }
+
+ ret = drmParsePciDeviceInfo(config, pcidevice);
+ if (ret)
+ goto free_locals;
+
+ devs[i].deviceinfo.pci = pcidevice;
+ close(fd);
+ }
+ break;
+ default:
+ fprintf(stderr, "The subsystem type is not supported yet\n");
+ break;
+ }
+ i++;
+ }
+
+ node_count = i;
+
+ /* merge duplicated devices with same domain/bus/device/func IDs */
+ duplicated = calloc(node_count, sizeof(*duplicated));
+ if (duplicated == NULL) {
+ ret = -ENOMEM;
+ goto free_locals;
+ }
+
+ for (i = 0; i < node_count; i++) {
+ for (j = i+1; j < node_count; j++) {
+ if (duplicated[i] || duplicated[j])
+ continue;
+ if (drmSameDevice(&devs[i], &devs[j])) {
+ duplicated[j] = 1;
+ devs[i].available_nodes |= devs[j].available_nodes;
+ node_type = log2(devs[j].available_nodes);
+ devs[i].nodes[node_type] = devs[j].nodes[node_type];
+ free(devs[j].nodes);
+ free(devs[j].businfo.pci);
+ free(devs[j].deviceinfo.pci);
+ }
+ }
+ }
+
+ for (i = 0; i < node_count; i++) {
+ if(duplicated[i] == 0) {
+ if ((devices != NULL) && (device_count < max_devices)) {
+ devices[device_count] = calloc(1, sizeof(drmDevice));
+ if (devices[device_count] == NULL) {
+ ret = -ENOMEM;
+ break;
+ }
+ memcpy(devices[device_count], &devs[i], sizeof(drmDevice));
+ } else
+ drmFreeDevice(&devs[i]);
+ device_count++;
+ }
+ }
- return drmDevCount;
+ if (i < node_count) {
+ drmFreeDevices(devices, device_count);
+ for ( ; i < node_count; i++)
+ if(duplicated[i] == 0)
+ drmFreeDevice(&devs[i]);
+ } else
+ ret = device_count;
+
+ free(duplicated);
+ free(devs);
+ closedir(sysdir);
+ return ret;
+
+free_locals:
+ for (j = 0; j < i; j++)
+ drmFreeDevice(&devs[j]);
+ free(pcidevice);
+ free(pcibus);
+ free(devs);
+ closedir(sysdir);
+ return ret;
+}
+#else
+void drmFreeDevices(drmDevicePtr devices[], int count)
+{
+ (void)devices;
+ (void)count;
}
+
+int drmGetDevices(drmDevicePtr devices[], int max_devices)
+{
+ (void)devices;
+ (void)max_devices;
+ return -EINVAL;
+}
+
+#warning "Missing implementation of drmGetDevices/drmFreeDevices"
+
+#endif