summaryrefslogtreecommitdiff
path: root/lib/libdrm/xf86drm.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/libdrm/xf86drm.c')
-rw-r--r--lib/libdrm/xf86drm.c891
1 files changed, 799 insertions, 92 deletions
diff --git a/lib/libdrm/xf86drm.c b/lib/libdrm/xf86drm.c
index ed1ad7dd1..7ab64022e 100644
--- a/lib/libdrm/xf86drm.c
+++ b/lib/libdrm/xf86drm.c
@@ -103,7 +103,6 @@
#endif
#ifdef __OpenBSD__
-
#define X_PRIVSEP
struct drm_pciinfo {
@@ -119,7 +118,6 @@ struct drm_pciinfo {
};
#define DRM_IOCTL_GET_PCIINFO DRM_IOR(0x15, struct drm_pciinfo)
-
#endif
#define DRM_MSG_VERBOSITY 3
@@ -2862,9 +2860,10 @@ out_close_dir:
closedir(sysdir);
#else
struct stat sbuf;
- unsigned int maj, min;
char buf[PATH_MAX + 1];
- int n;
+ const char *dev_name;
+ unsigned int maj, min;
+ int n, base;
if (fstat(fd, &sbuf))
return NULL;
@@ -2875,7 +2874,25 @@ out_close_dir:
if (maj != DRM_MAJOR || !S_ISCHR(sbuf.st_mode))
return NULL;
- n = snprintf(buf, sizeof(buf), DRM_DEV_NAME, DRM_DIR_NAME, min);
+ switch (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(type);
+ if (base < 0)
+ return NULL;
+
+ n = snprintf(buf, sizeof(buf), dev_name, DRM_DIR_NAME, min - base);
if (n == -1 || n >= sizeof(buf))
return NULL;
@@ -2894,6 +2911,50 @@ char *drmGetRenderDeviceNameFromFd(int fd)
return drmGetMinorNameForFD(fd, DRM_NODE_RENDER);
}
+#ifdef __linux__
+static char * DRM_PRINTFLIKE(2, 3)
+sysfs_uevent_get(const char *path, const char *fmt, ...)
+{
+ char filename[PATH_MAX + 1], *key, *line = NULL, *value = NULL;
+ size_t size = 0, len;
+ ssize_t num;
+ va_list ap;
+ FILE *fp;
+
+ va_start(ap, fmt);
+ num = vasprintf(&key, fmt, ap);
+ va_end(ap);
+ len = num;
+
+ snprintf(filename, sizeof(filename), "%s/uevent", path);
+
+ fp = fopen(filename, "r");
+ if (!fp) {
+ free(key);
+ return NULL;
+ }
+
+ while ((num = getline(&line, &size, fp)) >= 0) {
+ if ((strncmp(line, key, len) == 0) && (line[len] == '=')) {
+ char *start = line + len + 1, *end = line + num - 1;
+
+ if (*end != '\n')
+ end++;
+
+ value = strndup(start, end - start);
+ break;
+ }
+ }
+
+ free(line);
+ fclose(fp);
+
+ free(key);
+
+ return value;
+}
+#endif
+
static int drmParseSubsystemType(int maj, int min)
{
#ifdef __linux__
@@ -2914,9 +2975,18 @@ static int drmParseSubsystemType(int maj, int min)
if (strncmp(name, "/pci", 4) == 0)
return DRM_BUS_PCI;
+ if (strncmp(name, "/usb", 4) == 0)
+ return DRM_BUS_USB;
+
+ if (strncmp(name, "/platform", 9) == 0)
+ return DRM_BUS_PLATFORM;
+
+ if (strncmp(name, "/host1x", 7) == 0)
+ return DRM_BUS_HOST1X;
+
return -EINVAL;
#elif defined(__OpenBSD__)
- return DRM_BUS_PCI;
+ return DRM_BUS_PCI;
#else
#warning "Missing implementation of drmParseSubsystemType"
return -EINVAL;
@@ -2926,32 +2996,21 @@ static int drmParseSubsystemType(int maj, int min)
static int drmParsePciBusInfo(int maj, int min, drmPciBusInfoPtr info)
{
#ifdef __linux__
- char path[PATH_MAX + 1];
- char data[512 + 1];
- char *str;
- int domain, bus, dev, func;
- int fd, ret;
+ unsigned int domain, bus, dev, func;
+ char path[PATH_MAX + 1], *value;
+ int num;
- snprintf(path, PATH_MAX, "/sys/dev/char/%d:%d/device/uevent", maj, min);
- fd = open(path, O_RDONLY);
- if (fd < 0)
- return -errno;
+ snprintf(path, sizeof(path), "/sys/dev/char/%d:%d/device", maj, min);
- ret = read(fd, data, sizeof(data));
- data[sizeof(data)-1] = '\0';
- close(fd);
- if (ret < 0)
- return -errno;
+ value = sysfs_uevent_get(path, "PCI_SLOT_NAME");
+ if (!value)
+ return -ENOENT;
-#define TAG "PCI_SLOT_NAME="
- str = strstr(data, TAG);
- if (str == NULL)
- return -EINVAL;
+ num = sscanf(value, "%04x:%02x:%02x.%1u", &domain, &bus, &dev, &func);
+ free(value);
- if (sscanf(str, TAG "%04x:%02x:%02x.%1u",
- &domain, &bus, &dev, &func) != 4)
+ if (num != 4)
return -EINVAL;
-#undef TAG
info->domain = domain;
info->bus = bus;
@@ -2961,9 +3020,13 @@ static int drmParsePciBusInfo(int maj, int min, drmPciBusInfoPtr info)
return 0;
#elif defined(__OpenBSD__)
struct drm_pciinfo pinfo;
- int fd;
+ int fd, type;
+
+ type = drmGetMinorType(min);
+ if (type == -1)
+ return -ENODEV;
- fd = drmOpenMinor(min, 0, DRM_NODE_PRIMARY);
+ fd = drmOpenMinor(min, 0, type);
if (fd < 0)
return -errno;
@@ -2996,6 +3059,16 @@ static int drmCompareBusInfo(drmDevicePtr a, drmDevicePtr b)
switch (a->bustype) {
case DRM_BUS_PCI:
return memcmp(a->businfo.pci, b->businfo.pci, sizeof(drmPciBusInfo));
+
+ case DRM_BUS_USB:
+ return memcmp(a->businfo.usb, b->businfo.usb, sizeof(drmUsbBusInfo));
+
+ case DRM_BUS_PLATFORM:
+ return memcmp(a->businfo.platform, b->businfo.platform, sizeof(drmPlatformBusInfo));
+
+ case DRM_BUS_HOST1X:
+ return memcmp(a->businfo.host1x, b->businfo.host1x, sizeof(drmHost1xBusInfo));
+
default:
break;
}
@@ -3029,15 +3102,55 @@ static int drmGetMaxNodeName(void)
3 /* length of the node number */;
}
-static int drmParsePciDeviceInfo(const char *d_name,
- drmPciDeviceInfoPtr device)
-{
#ifdef __linux__
+static int parse_separate_sysfs_files(int maj, int min,
+ drmPciDeviceInfoPtr device,
+ bool ignore_revision)
+{
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
+ static const char *attrs[] = {
+ "revision", /* Older kernels are missing the file, so check for it first */
+ "vendor",
+ "device",
+ "subsystem_vendor",
+ "subsystem_device",
+ };
+ char path[PATH_MAX + 1];
+ unsigned int data[ARRAY_SIZE(attrs)];
+ FILE *fp;
+ int ret;
+
+ for (unsigned i = ignore_revision ? 1 : 0; i < ARRAY_SIZE(attrs); i++) {
+ snprintf(path, PATH_MAX, "/sys/dev/char/%d:%d/device/%s", maj, min,
+ attrs[i]);
+ fp = fopen(path, "r");
+ if (!fp)
+ return -errno;
+
+ ret = fscanf(fp, "%x", &data[i]);
+ fclose(fp);
+ if (ret != 1)
+ return -errno;
+
+ }
+
+ device->revision_id = ignore_revision ? 0xff : data[0] & 0xff;
+ device->vendor_id = data[1] & 0xffff;
+ device->device_id = data[2] & 0xffff;
+ device->subvendor_id = data[3] & 0xffff;
+ device->subdevice_id = data[4] & 0xffff;
+
+ return 0;
+}
+
+static int parse_config_sysfs_file(int maj, int min,
+ drmPciDeviceInfoPtr device)
+{
char path[PATH_MAX + 1];
unsigned char config[64];
int fd, ret;
- snprintf(path, PATH_MAX, "/sys/class/drm/%s/device/config", d_name);
+ snprintf(path, PATH_MAX, "/sys/dev/char/%d:%d/device/config", maj, min);
fd = open(path, O_RDONLY);
if (fd < 0)
return -errno;
@@ -3054,21 +3167,30 @@ static int drmParsePciDeviceInfo(const char *d_name,
device->subdevice_id = config[46] | (config[47] << 8);
return 0;
+}
+#endif
+
+static int drmParsePciDeviceInfo(int maj, int min,
+ drmPciDeviceInfoPtr device,
+ uint32_t flags)
+{
+#ifdef __linux__
+ if (!(flags & DRM_DEVICE_GET_PCI_REVISION))
+ return parse_separate_sysfs_files(maj, min, device, true);
+
+ if (parse_separate_sysfs_files(maj, min, device, false))
+ return parse_config_sysfs_file(maj, min, device);
+
+ return 0;
#elif defined(__OpenBSD__)
struct drm_pciinfo pinfo;
- char buf[PATH_MAX + 1];
- int fd, n;
+ int fd, type;
- n = snprintf(buf, sizeof(buf), "%s/%s", DRM_DIR_NAME, d_name);
- if (n == -1 || n >= sizeof(buf))
- return -errno;
-
-#ifndef X_PRIVSEP
- fd = open(buf, O_RDWR, 0);
-#else
- fd = priv_open_device(buf);
-#endif
+ type = drmGetMinorType(min);
+ if (type == -1)
+ return -ENODEV;
+ fd = drmOpenMinor(min, 0, type);
if (fd < 0)
return -errno;
@@ -3091,11 +3213,55 @@ static int drmParsePciDeviceInfo(const char *d_name,
#endif
}
+static void drmFreePlatformDevice(drmDevicePtr device)
+{
+ if (device->deviceinfo.platform) {
+ if (device->deviceinfo.platform->compatible) {
+ char **compatible = device->deviceinfo.platform->compatible;
+
+ while (*compatible) {
+ free(*compatible);
+ compatible++;
+ }
+
+ free(device->deviceinfo.platform->compatible);
+ }
+ }
+}
+
+static void drmFreeHost1xDevice(drmDevicePtr device)
+{
+ if (device->deviceinfo.host1x) {
+ if (device->deviceinfo.host1x->compatible) {
+ char **compatible = device->deviceinfo.host1x->compatible;
+
+ while (*compatible) {
+ free(*compatible);
+ compatible++;
+ }
+
+ free(device->deviceinfo.host1x->compatible);
+ }
+ }
+}
+
void drmFreeDevice(drmDevicePtr *device)
{
if (device == NULL)
return;
+ if (*device) {
+ switch ((*device)->bustype) {
+ case DRM_BUS_PLATFORM:
+ drmFreePlatformDevice(*device);
+ break;
+
+ case DRM_BUS_HOST1X:
+ drmFreeHost1xDevice(*device);
+ break;
+ }
+ }
+
free(*device);
*device = NULL;
}
@@ -3112,56 +3278,402 @@ void drmFreeDevices(drmDevicePtr devices[], int count)
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)
+static drmDevicePtr drmDeviceAlloc(unsigned int type, const char *node,
+ size_t bus_size, size_t device_size,
+ char **ptrp)
{
- const int max_node_str = ALIGN(drmGetMaxNodeName(), sizeof(void *));
- int ret, i;
- char *addr;
+ size_t max_node_length, extra, size;
+ drmDevicePtr device;
+ unsigned int i;
+ char *ptr;
- *device = calloc(1, sizeof(drmDevice) +
- (DRM_NODE_MAX * (sizeof(void *) + max_node_str)) +
- sizeof(drmPciBusInfo) +
- sizeof(drmPciDeviceInfo));
- if (!*device)
- return -ENOMEM;
+ max_node_length = ALIGN(drmGetMaxNodeName(), sizeof(void *));
+ extra = DRM_NODE_MAX * (sizeof(void *) + max_node_length);
- addr = (char*)*device;
+ size = sizeof(*device) + extra + bus_size + device_size;
- (*device)->bustype = DRM_BUS_PCI;
- (*device)->available_nodes = 1 << node_type;
+ device = calloc(1, size);
+ if (!device)
+ return NULL;
- addr += sizeof(drmDevice);
- (*device)->nodes = (char**)addr;
+ device->available_nodes = 1 << type;
+
+ ptr = (char *)device + sizeof(*device);
+ device->nodes = (char **)ptr;
+
+ ptr += DRM_NODE_MAX * sizeof(void *);
- addr += DRM_NODE_MAX * sizeof(void *);
for (i = 0; i < DRM_NODE_MAX; i++) {
- (*device)->nodes[i] = addr;
- addr += max_node_str;
+ device->nodes[i] = ptr;
+ ptr += max_node_length;
}
- memcpy((*device)->nodes[node_type], node, max_node_str);
- (*device)->businfo.pci = (drmPciBusInfoPtr)addr;
+ memcpy(device->nodes[type], node, max_node_length);
- ret = drmParsePciBusInfo(maj, min, (*device)->businfo.pci);
+ *ptrp = ptr;
+
+ return device;
+}
+
+static int drmProcessPciDevice(drmDevicePtr *device,
+ const char *node, int node_type,
+ int maj, int min, bool fetch_deviceinfo,
+ uint32_t flags)
+{
+ drmDevicePtr dev;
+ char *addr;
+ int ret;
+
+ dev = drmDeviceAlloc(node_type, node, sizeof(drmPciBusInfo),
+ sizeof(drmPciDeviceInfo), &addr);
+ if (!dev)
+ return -ENOMEM;
+
+ dev->bustype = DRM_BUS_PCI;
+
+ dev->businfo.pci = (drmPciBusInfoPtr)addr;
+
+ ret = drmParsePciBusInfo(maj, min, dev->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;
+ dev->deviceinfo.pci = (drmPciDeviceInfoPtr)addr;
- ret = drmParsePciDeviceInfo(d_name, (*device)->deviceinfo.pci);
+ ret = drmParsePciDeviceInfo(maj, min, dev->deviceinfo.pci, flags);
if (ret)
goto free_device;
}
+
+ *device = dev;
+
return 0;
free_device:
- free(*device);
- *device = NULL;
+ free(dev);
+ return ret;
+}
+
+static int drmParseUsbBusInfo(int maj, int min, drmUsbBusInfoPtr info)
+{
+#ifdef __linux__
+ char path[PATH_MAX + 1], *value;
+ unsigned int bus, dev;
+ int ret;
+
+ snprintf(path, sizeof(path), "/sys/dev/char/%d:%d/device", maj, min);
+
+ value = sysfs_uevent_get(path, "BUSNUM");
+ if (!value)
+ return -ENOENT;
+
+ ret = sscanf(value, "%03u", &bus);
+ free(value);
+
+ if (ret <= 0)
+ return -errno;
+
+ value = sysfs_uevent_get(path, "DEVNUM");
+ if (!value)
+ return -ENOENT;
+
+ ret = sscanf(value, "%03u", &dev);
+ free(value);
+
+ if (ret <= 0)
+ return -errno;
+
+ info->bus = bus;
+ info->dev = dev;
+
+ return 0;
+#else
+#warning "Missing implementation of drmParseUsbBusInfo"
+ return -EINVAL;
+#endif
+}
+
+static int drmParseUsbDeviceInfo(int maj, int min, drmUsbDeviceInfoPtr info)
+{
+#ifdef __linux__
+ char path[PATH_MAX + 1], *value;
+ unsigned int vendor, product;
+ int ret;
+
+ snprintf(path, sizeof(path), "/sys/dev/char/%d:%d/device", maj, min);
+
+ value = sysfs_uevent_get(path, "PRODUCT");
+ if (!value)
+ return -ENOENT;
+
+ ret = sscanf(value, "%x/%x", &vendor, &product);
+ free(value);
+
+ if (ret <= 0)
+ return -errno;
+
+ info->vendor = vendor;
+ info->product = product;
+
+ return 0;
+#else
+#warning "Missing implementation of drmParseUsbDeviceInfo"
+ return -EINVAL;
+#endif
+}
+
+static int drmProcessUsbDevice(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(drmUsbBusInfo),
+ sizeof(drmUsbDeviceInfo), &ptr);
+ if (!dev)
+ return -ENOMEM;
+
+ dev->bustype = DRM_BUS_USB;
+
+ dev->businfo.usb = (drmUsbBusInfoPtr)ptr;
+
+ ret = drmParseUsbBusInfo(maj, min, dev->businfo.usb);
+ if (ret < 0)
+ goto free_device;
+
+ if (fetch_deviceinfo) {
+ ptr += sizeof(drmUsbBusInfo);
+ dev->deviceinfo.usb = (drmUsbDeviceInfoPtr)ptr;
+
+ ret = drmParseUsbDeviceInfo(maj, min, dev->deviceinfo.usb);
+ if (ret < 0)
+ goto free_device;
+ }
+
+ *device = dev;
+
+ return 0;
+
+free_device:
+ free(dev);
+ return ret;
+}
+
+static int drmParsePlatformBusInfo(int maj, int min, drmPlatformBusInfoPtr info)
+{
+#ifdef __linux__
+ char path[PATH_MAX + 1], *name;
+
+ snprintf(path, sizeof(path), "/sys/dev/char/%d:%d/device", maj, min);
+
+ name = sysfs_uevent_get(path, "OF_FULLNAME");
+ if (!name)
+ return -ENOENT;
+
+ strncpy(info->fullname, name, DRM_PLATFORM_DEVICE_NAME_LEN);
+ info->fullname[DRM_PLATFORM_DEVICE_NAME_LEN - 1] = '\0';
+ free(name);
+
+ return 0;
+#else
+#warning "Missing implementation of drmParsePlatformBusInfo"
+ return -EINVAL;
+#endif
+}
+
+static int drmParsePlatformDeviceInfo(int maj, int min,
+ drmPlatformDeviceInfoPtr info)
+{
+#ifdef __linux__
+ char path[PATH_MAX + 1], *value;
+ unsigned int count, i;
+ int err;
+
+ snprintf(path, sizeof(path), "/sys/dev/char/%d:%d/device", maj, min);
+
+ value = sysfs_uevent_get(path, "OF_COMPATIBLE_N");
+ if (!value)
+ return -ENOENT;
+
+ sscanf(value, "%u", &count);
+ free(value);
+
+ info->compatible = calloc(count + 1, sizeof(*info->compatible));
+ if (!info->compatible)
+ return -ENOMEM;
+
+ for (i = 0; i < count; i++) {
+ value = sysfs_uevent_get(path, "OF_COMPATIBLE_%u", i);
+ if (!value) {
+ err = -ENOENT;
+ goto free;
+ }
+
+ info->compatible[i] = value;
+ }
+
+ return 0;
+
+free:
+ while (i--)
+ free(info->compatible[i]);
+
+ free(info->compatible);
+ return err;
+#else
+#warning "Missing implementation of drmParsePlatformDeviceInfo"
+ return -EINVAL;
+#endif
+}
+
+static int drmProcessPlatformDevice(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(drmPlatformBusInfo),
+ sizeof(drmPlatformDeviceInfo), &ptr);
+ if (!dev)
+ return -ENOMEM;
+
+ dev->bustype = DRM_BUS_PLATFORM;
+
+ dev->businfo.platform = (drmPlatformBusInfoPtr)ptr;
+
+ ret = drmParsePlatformBusInfo(maj, min, dev->businfo.platform);
+ if (ret < 0)
+ goto free_device;
+
+ if (fetch_deviceinfo) {
+ ptr += sizeof(drmPlatformBusInfo);
+ dev->deviceinfo.platform = (drmPlatformDeviceInfoPtr)ptr;
+
+ ret = drmParsePlatformDeviceInfo(maj, min, dev->deviceinfo.platform);
+ if (ret < 0)
+ goto free_device;
+ }
+
+ *device = dev;
+
+ return 0;
+
+free_device:
+ free(dev);
+ return ret;
+}
+
+static int drmParseHost1xBusInfo(int maj, int min, drmHost1xBusInfoPtr info)
+{
+#ifdef __linux__
+ char path[PATH_MAX + 1], *name;
+
+ snprintf(path, sizeof(path), "/sys/dev/char/%d:%d/device", maj, min);
+
+ name = sysfs_uevent_get(path, "OF_FULLNAME");
+ if (!name)
+ return -ENOENT;
+
+ strncpy(info->fullname, name, DRM_HOST1X_DEVICE_NAME_LEN);
+ info->fullname[DRM_HOST1X_DEVICE_NAME_LEN - 1] = '\0';
+ free(name);
+
+ return 0;
+#else
+#warning "Missing implementation of drmParseHost1xBusInfo"
+ return -EINVAL;
+#endif
+}
+
+static int drmParseHost1xDeviceInfo(int maj, int min,
+ drmHost1xDeviceInfoPtr info)
+{
+#ifdef __linux__
+ char path[PATH_MAX + 1], *value;
+ unsigned int count, i;
+ int err;
+
+ snprintf(path, sizeof(path), "/sys/dev/char/%d:%d/device", maj, min);
+
+ value = sysfs_uevent_get(path, "OF_COMPATIBLE_N");
+ if (!value)
+ return -ENOENT;
+
+ sscanf(value, "%u", &count);
+ free(value);
+
+ info->compatible = calloc(count + 1, sizeof(*info->compatible));
+ if (!info->compatible)
+ return -ENOMEM;
+
+ for (i = 0; i < count; i++) {
+ value = sysfs_uevent_get(path, "OF_COMPATIBLE_%u", i);
+ if (!value) {
+ err = -ENOENT;
+ goto free;
+ }
+
+ info->compatible[i] = value;
+ }
+
+ return 0;
+
+free:
+ while (i--)
+ free(info->compatible[i]);
+
+ free(info->compatible);
+ return err;
+#else
+#warning "Missing implementation of drmParseHost1xDeviceInfo"
+ return -EINVAL;
+#endif
+}
+
+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;
}
@@ -3187,25 +3699,40 @@ static void drmFoldDuplicatedDevices(drmDevicePtr local_devices[], int count)
}
}
+/* 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 drmGetDevice(int fd, drmDevicePtr *device)
+int drmGetDevice2(int fd, uint32_t flags, drmDevicePtr *device)
{
#ifdef __OpenBSD__
- drmDevicePtr d;
- struct stat sbuf;
- char node[PATH_MAX + 1];
- char d_name[PATH_MAX + 1];
- int maj, min, n;
- int ret;
- int max_count = 1;
+ /*
+ * 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;
@@ -3219,18 +3746,39 @@ int drmGetDevice(int fd, drmDevicePtr *device)
if (maj != DRM_MAJOR || !S_ISCHR(sbuf.st_mode))
return -EINVAL;
- n = snprintf(d_name, PATH_MAX, "drm%d", min);
- if (n == -1 || n >= PATH_MAX)
- return -errno;
+ node_type = drmGetMinorType(min);
+ if (node_type == -1)
+ return -ENODEV;
- n = snprintf(node, PATH_MAX, DRM_DEV_NAME, DRM_DIR_NAME, min);
+ 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;
- ret = drmProcessPciDevice(&d, d_name, node, DRM_NODE_PRIMARY,
- maj, min, true);
+ 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;
@@ -3250,6 +3798,9 @@ int drmGetDevice(int fd, drmDevicePtr *device)
int max_count = 16;
dev_t find_rdev;
+ if (drm_device_validate_flags(flags))
+ return -EINVAL;
+
if (fd == -1 || device == NULL)
return -EINVAL;
@@ -3296,12 +3847,33 @@ int drmGetDevice(int fd, drmDevicePtr *device)
switch (subsystem_type) {
case DRM_BUS_PCI:
- ret = drmProcessPciDevice(&d, dent->d_name, node, node_type,
- maj, min, true);
+ ret = drmProcessPciDevice(&d, node, node_type, maj, min, true, flags);
if (ret)
- goto free_devices;
+ 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;
}
@@ -3335,7 +3907,7 @@ int drmGetDevice(int fd, drmDevicePtr *device)
closedir(sysdir);
free(local_devices);
if (*device == NULL)
- return -ENODEV;
+ return -ENODEV;
return 0;
free_devices:
@@ -3349,8 +3921,23 @@ free_locals:
}
/**
+ * 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
@@ -3359,8 +3946,11 @@ free_locals:
* 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 drmGetDevices(drmDevicePtr devices[], int max_devices)
+int drmGetDevices2(uint32_t flags, drmDevicePtr devices[], int max_devices)
{
drmDevicePtr *local_devices;
drmDevicePtr device;
@@ -3373,6 +3963,9 @@ int drmGetDevices(drmDevicePtr devices[], int max_devices)
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;
@@ -3406,12 +3999,37 @@ int drmGetDevices(drmDevicePtr devices[], int max_devices)
switch (subsystem_type) {
case DRM_BUS_PCI:
- ret = drmProcessPciDevice(&device, dent->d_name, node, node_type,
- maj, min, devices != NULL);
+ 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)
goto free_devices;
break;
+
+ case DRM_BUS_PLATFORM:
+ ret = drmProcessPlatformDevice(&device, node, node_type, maj, min,
+ devices != NULL, flags);
+ if (ret)
+ goto free_devices;
+
+ break;
+
+ case DRM_BUS_HOST1X:
+ ret = drmProcessHost1xDevice(&device, node, node_type, maj, min,
+ devices != NULL, flags);
+ if (ret)
+ goto free_devices;
+
+ break;
+
default:
continue;
}
@@ -3458,3 +4076,92 @@ 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
+}