#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>
#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))
#endif
}
-static void drmFreeDevice(drmDevicePtr *device)
+void drmFreeDevice(drmDevicePtr *device)
{
if (device == NULL)
return;
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
*
struct dirent *dent;
struct stat sbuf;
char node[PATH_MAX + 1];
- const int max_node_str = drmGetMaxNodeName();
int node_type, subsystem_type;
int maj, min;
- int ret, i, j, node_count, device_count;
+ int ret, i, node_count, device_count;
int max_count = 16;
- void *addr;
local_devices = calloc(max_count, sizeof(drmDevicePtr));
if (local_devices == NULL)
sysdir = opendir(DRM_DIR_NAME);
if (!sysdir) {
ret = -errno;
- goto close_sysdir;
+ goto free_locals;
}
i = 0;
switch (subsystem_type) {
case DRM_BUS_PCI:
- addr = device = calloc(1, sizeof(drmDevice) +
- (DRM_NODE_MAX *
- (sizeof(void *) + max_node_str)) +
- sizeof(drmPciBusInfo) +
- sizeof(drmPciDeviceInfo));
- if (!device)
- goto free_devices;
-
- device->bustype = subsystem_type;
- device->available_nodes = 1 << node_type;
-
- addr += sizeof(drmDevice);
- device->nodes = addr;
-
- addr += DRM_NODE_MAX * sizeof(void *);
- for (j = 0; j < DRM_NODE_MAX; j++) {
- device->nodes[j] = addr;
- addr += max_node_str;
- }
- memcpy(device->nodes[node_type], node, max_node_str);
-
- device->businfo.pci = addr;
-
- ret = drmParsePciBusInfo(maj, min, device->businfo.pci);
+ ret = drmProcessPciDevice(&device, dent->d_name, node, node_type,
+ maj, min, devices != NULL);
if (ret)
goto free_devices;
- // Fetch the device info if the user has requested it
- if (devices != NULL) {
- addr += sizeof(drmPciBusInfo);
- device->deviceinfo.pci = addr;
-
- ret = drmParsePciDeviceInfo(dent->d_name,
- device->deviceinfo.pci);
- if (ret)
- goto free_devices;
- }
-
break;
default:
fprintf(stderr, "The subsystem type is not supported yet\n");
- break;
+ continue;
}
if (i >= max_count) {
node_count = i;
/* Fold nodes into a single device if they share the same bus info */
- for (i = 0; i < node_count; i++) {
- for (j = i + 1; j < node_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], max_node_str);
- drmFreeDevice(&local_devices[j]);
- }
- }
- }
+ drmFoldDuplicatedDevices(local_devices, node_count);
device_count = 0;
for (i = 0; i < node_count && local_devices[i]; i++) {
device_count++;
}
- free(local_devices);
closedir(sysdir);
+ free(local_devices);
return device_count;
free_devices:
drmFreeDevices(local_devices, i);
- free(local_devices);
-
-close_sysdir:
closedir(sysdir);
+
+free_locals:
+ free(local_devices);
return ret;
}