+}
+
+static int drmProcessHost1xDevice(drmDevicePtr *device,
+ const char *node, int node_type,
+ int maj, int min, bool fetch_deviceinfo,
+ uint32_t flags)
+{
+ drmDevicePtr dev;
+ char *ptr;
+ int ret;
+
+ dev = drmDeviceAlloc(node_type, node, sizeof(drmHost1xBusInfo),
+ sizeof(drmHost1xDeviceInfo), &ptr);
+ if (!dev)
+ return -ENOMEM;
+
+ dev->bustype = DRM_BUS_HOST1X;
+
+ dev->businfo.host1x = (drmHost1xBusInfoPtr)ptr;
+
+ ret = drmParseHost1xBusInfo(maj, min, dev->businfo.host1x);
+ if (ret < 0)
+ goto free_device;
+
+ if (fetch_deviceinfo) {
+ ptr += sizeof(drmHost1xBusInfo);
+ dev->deviceinfo.host1x = (drmHost1xDeviceInfoPtr)ptr;
+
+ ret = drmParseHost1xDeviceInfo(maj, min, dev->deviceinfo.host1x);
+ if (ret < 0)
+ goto free_device;
+ }
+
+ *device = dev;
+
+ return 0;
+
+free_device:
+ free(dev);
+ return ret;
+}
+
+/* Consider devices located on the same bus as duplicate and fold the respective
+ * entries into a single one.
+ *
+ * Note: this leaves "gaps" in the array, while preserving the length.
+ */
+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 (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);
+ memcpy(local_devices[i]->nodes[node_type],
+ local_devices[j]->nodes[node_type], drmGetMaxNodeName());
+ drmFreeDevice(&local_devices[j]);
+ }
+ }
+ }
+}
+
+/* Check that the given flags are valid returning 0 on success */
+static int
+drm_device_validate_flags(uint32_t flags)
+{
+ return (flags & ~DRM_DEVICE_GET_PCI_REVISION);
+}
+
+/**
+ * Get information about the opened drm device
+ *
+ * \param fd file descriptor of the drm device
+ * \param flags feature/behaviour bitmask
+ * \param device the address of a drmDevicePtr where the information
+ * will be allocated in stored
+ *
+ * \return zero on success, negative error code otherwise.
+ *
+ * \note Unlike drmGetDevice it does not retrieve the pci device revision field
+ * unless the DRM_DEVICE_GET_PCI_REVISION \p flag is set.
+ */
+int drmGetDevice2(int fd, uint32_t flags, drmDevicePtr *device)
+{
+#ifdef __OpenBSD__
+ /*
+ * DRI device nodes on OpenBSD are not in their own directory, they reside
+ * in /dev along with a large number of statically generated /dev nodes.
+ * Avoid stat'ing all of /dev needlessly by implementing this custom path.
+ */
+ drmDevicePtr d;
+ struct stat sbuf;
+ char node[PATH_MAX + 1];
+ const char *dev_name;
+ int node_type, subsystem_type;
+ int maj, min, n, ret, base;
+
+ 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;
+
+ node_type = drmGetMinorType(min);
+ if (node_type == -1)
+ return -ENODEV;
+
+ switch (node_type) {
+ case DRM_NODE_PRIMARY:
+ dev_name = DRM_DEV_NAME;
+ break;
+ case DRM_NODE_CONTROL:
+ dev_name = DRM_CONTROL_DEV_NAME;
+ break;
+ case DRM_NODE_RENDER:
+ dev_name = DRM_RENDER_DEV_NAME;
+ break;
+ default:
+ return -EINVAL;
+ };
+
+ base = drmGetMinorBase(node_type);
+ if (base < 0)
+ return -EINVAL;
+
+ n = snprintf(node, PATH_MAX, dev_name, DRM_DIR_NAME, min - base);
+ if (n == -1 || n >= PATH_MAX)
+ return -errno;
+ if (stat(node, &sbuf))
+ return -EINVAL;
+
+ subsystem_type = drmParseSubsystemType(maj, min);
+ if (subsystem_type != DRM_BUS_PCI)
+ return -ENODEV;
+
+ ret = drmProcessPciDevice(&d, node, node_type, maj, min, true, flags);
+ if (ret)
+ return ret;
+
+ *device = d;
+
+ return 0;
+#else
+ 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;
+ dev_t find_rdev;
+
+ if (drm_device_validate_flags(flags))
+ return -EINVAL;
+
+ if (fd == -1 || device == NULL)
+ return -EINVAL;
+
+ if (fstat(fd, &sbuf))
+ return -errno;
+
+ find_rdev = sbuf.st_rdev;
+ 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, node, node_type, maj, min, true, flags);
+ if (ret)
+ continue;
+
+ break;
+
+ case DRM_BUS_USB:
+ ret = drmProcessUsbDevice(&d, node, node_type, maj, min, true, flags);
+ if (ret)
+ continue;
+
+ break;
+
+ case DRM_BUS_PLATFORM:
+ ret = drmProcessPlatformDevice(&d, node, node_type, maj, min, true, flags);
+ if (ret)
+ continue;
+
+ break;
+
+ case DRM_BUS_HOST1X:
+ ret = drmProcessHost1xDevice(&d, node, node_type, maj, min, true, flags);
+ if (ret)
+ continue;
+
+ break;
+
+ default:
+ 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;
+ }
+
+ /* store target at local_devices[0] for ease to use below */
+ if (find_rdev == sbuf.st_rdev && i) {
+ local_devices[i] = local_devices[0];
+ local_devices[0] = d;
+ }
+ else
+ local_devices[i] = d;
+ i++;
+ }
+ node_count = i;
+
+ drmFoldDuplicatedDevices(local_devices, node_count);
+
+ *device = local_devices[0];
+ drmFreeDevices(&local_devices[1], node_count - 1);
+
+ closedir(sysdir);
+ free(local_devices);
+ if (*device == NULL)
+ return -ENODEV;
+ return 0;
+
+free_devices:
+ drmFreeDevices(local_devices, i);
+ closedir(sysdir);
+
+free_locals:
+ free(local_devices);
+ return ret;
+#endif
+}
+
+/**
+ * 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)
+{
+ return drmGetDevice2(fd, DRM_DEVICE_GET_PCI_REVISION, device);
+}
+
+/**
+ * Get drm devices on the system
+ *
+ * \param flags feature/behaviour bitmask
+ * \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.
+ *
+ * \note Unlike drmGetDevices it does not retrieve the pci device revision field
+ * unless the DRM_DEVICE_GET_PCI_REVISION \p flag is set.
+ */
+int drmGetDevices2(uint32_t flags, 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;
+
+ if (drm_device_validate_flags(flags))
+ return -EINVAL;
+
+ 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, node, node_type,
+ maj, min, devices != NULL, flags);
+ if (ret)
+ continue;
+
+ break;
+
+ case DRM_BUS_USB:
+ ret = drmProcessUsbDevice(&device, node, node_type, maj, min,
+ devices != NULL, flags);
+ if (ret)
+ continue;
+
+ break;
+
+ case DRM_BUS_PLATFORM:
+ ret = drmProcessPlatformDevice(&device, node, node_type, maj, min,
+ devices != NULL, flags);
+ if (ret)
+ continue;
+
+ break;
+
+ case DRM_BUS_HOST1X:
+ ret = drmProcessHost1xDevice(&device, node, node_type, maj, min,
+ devices != NULL, flags);
+ if (ret)
+ continue;
+
+ break;
+
+ default:
+ 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;
+
+ drmFoldDuplicatedDevices(local_devices, node_count);
+
+ device_count = 0;
+ for (i = 0; i < node_count; i++) {
+ if (!local_devices[i])
+ continue;
+
+ 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;
+}
+
+/**
+ * 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)
+{
+ return drmGetDevices2(DRM_DEVICE_GET_PCI_REVISION, devices, max_devices);
+}
+
+char *drmGetDeviceNameFromFd2(int fd)
+{
+#ifdef __linux__
+ struct stat sbuf;
+ char path[PATH_MAX + 1], *value;
+ unsigned int maj, min;
+
+ if (fstat(fd, &sbuf))
+ return NULL;
+
+ maj = major(sbuf.st_rdev);
+ min = minor(sbuf.st_rdev);
+
+ if (maj != DRM_MAJOR || !S_ISCHR(sbuf.st_mode))
+ return NULL;
+
+ snprintf(path, sizeof(path), "/sys/dev/char/%d:%d", maj, min);
+
+ value = sysfs_uevent_get(path, "DEVNAME");
+ if (!value)
+ return NULL;
+
+ snprintf(path, sizeof(path), "/dev/%s", value);
+ free(value);
+
+ return strdup(path);
+#else
+ struct stat sbuf;
+ char node[PATH_MAX + 1];
+ const char *dev_name;
+ int node_type;
+ int maj, min, n, base;
+
+ if (fstat(fd, &sbuf))
+ return NULL;
+
+ maj = major(sbuf.st_rdev);
+ min = minor(sbuf.st_rdev);
+
+ if (maj != DRM_MAJOR || !S_ISCHR(sbuf.st_mode))
+ return NULL;
+
+ node_type = drmGetMinorType(min);
+ if (node_type == -1)
+ return NULL;
+
+ switch (node_type) {
+ case DRM_NODE_PRIMARY:
+ dev_name = DRM_DEV_NAME;
+ break;
+ case DRM_NODE_CONTROL:
+ dev_name = DRM_CONTROL_DEV_NAME;
+ break;
+ case DRM_NODE_RENDER:
+ dev_name = DRM_RENDER_DEV_NAME;
+ break;
+ default:
+ return NULL;
+ };
+
+ base = drmGetMinorBase(node_type);
+ if (base < 0)
+ return NULL;
+
+ n = snprintf(node, PATH_MAX, dev_name, DRM_DIR_NAME, min - base);
+ if (n == -1 || n >= PATH_MAX)
+ return NULL;
+
+ return strdup(node);
+#endif
+}
+
+int drmSyncobjCreate(int fd, uint32_t flags, uint32_t *handle)
+{
+ struct drm_syncobj_create args;
+ int ret;
+
+ memclear(args);
+ args.flags = flags;
+ args.handle = 0;
+ ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_CREATE, &args);
+ if (ret)
+ return ret;
+ *handle = args.handle;
+ return 0;
+}
+
+int drmSyncobjDestroy(int fd, uint32_t handle)
+{
+ struct drm_syncobj_destroy args;
+
+ memclear(args);
+ args.handle = handle;
+ return drmIoctl(fd, DRM_IOCTL_SYNCOBJ_DESTROY, &args);
+}
+
+int drmSyncobjHandleToFD(int fd, uint32_t handle, int *obj_fd)
+{
+ struct drm_syncobj_handle args;
+ int ret;
+
+ memclear(args);
+ args.fd = -1;
+ args.handle = handle;
+ ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_HANDLE_TO_FD, &args);
+ if (ret)
+ return ret;
+ *obj_fd = args.fd;
+ return 0;
+}
+
+int drmSyncobjFDToHandle(int fd, int obj_fd, uint32_t *handle)
+{
+ struct drm_syncobj_handle args;
+ int ret;
+
+ memclear(args);
+ args.fd = obj_fd;
+ args.handle = 0;
+ ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_FD_TO_HANDLE, &args);
+ if (ret)
+ return ret;
+ *handle = args.handle;
+ return 0;
+}
+
+int drmSyncobjImportSyncFile(int fd, uint32_t handle, int sync_file_fd)
+{
+ struct drm_syncobj_handle args;
+
+ memclear(args);
+ args.fd = sync_file_fd;
+ args.handle = handle;
+ args.flags = DRM_SYNCOBJ_FD_TO_HANDLE_FLAGS_IMPORT_SYNC_FILE;
+ return drmIoctl(fd, DRM_IOCTL_SYNCOBJ_FD_TO_HANDLE, &args);
+}
+
+int drmSyncobjExportSyncFile(int fd, uint32_t handle, int *sync_file_fd)
+{
+ struct drm_syncobj_handle args;
+ int ret;
+
+ memclear(args);
+ args.fd = -1;
+ args.handle = handle;
+ args.flags = DRM_SYNCOBJ_HANDLE_TO_FD_FLAGS_EXPORT_SYNC_FILE;
+ ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_HANDLE_TO_FD, &args);
+ if (ret)
+ return ret;
+ *sync_file_fd = args.fd;
+ return 0;
+}