| /* |
| * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. |
| * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. |
| * Copyright(c) 2019, Intel Corporation |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining a |
| * copy of this software and associated documentation files(the "Software"), |
| *to deal in the Software without restriction, including without limitation |
| * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
| *and/or sell copies of the Software, and to permit persons to whom the |
| * Software is furnished to do so, subject to the following conditions : |
| * |
| * The above copyright notice and this permission notice(including the next |
| * paragraph) shall be included in all copies or substantial portions of the |
| * Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| *FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL |
| * PRECISION INSIGHT AND / OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR |
| * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, |
| *ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
| * DEALINGS IN THE SOFTWARE. |
| */ |
| |
| #ifndef DRM_DEVICE_H_ |
| #define DRM_DEVICE_H_ |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <stdbool.h> |
| #include <unistd.h> |
| #include <string.h> |
| #include <strings.h> |
| #include <ctype.h> |
| #include <dirent.h> |
| #include <stddef.h> |
| #include <fcntl.h> |
| #include <errno.h> |
| #include <limits.h> |
| #include <signal.h> |
| #include <time.h> |
| #include <sys/sysmacros.h> //<sys/types.h> |
| #include <sys/stat.h> |
| #define stat_t struct stat |
| #include <sys/ioctl.h> |
| #include <sys/time.h> |
| #include <stdarg.h> |
| #ifdef MAJOR_IN_MKDEV |
| #include <sys/mkdev.h> |
| #endif |
| #ifdef MAJOR_IN_SYSMACROS |
| #include <sys/sysmacros.h> |
| #endif |
| #include <math.h> |
| #include <string> |
| |
| #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) |
| |
| /* Not all systems have MAP_FAILED defined */ |
| #ifndef MAP_FAILED |
| #define MAP_FAILED ((void *)-1) |
| #endif |
| |
| #define DRM_DEV_UID 0 |
| #define DRM_DEV_GID 0 |
| /* Default /dev/dri directory permissions 0755 */ |
| #define DRM_DEV_DIRMODE (S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH) |
| #define DRM_DEV_MODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP) |
| #define DRM_DEVICE_GET_PCI_REVISION (1 << 0) |
| |
| #ifdef __OpenBSD__ |
| #define DRM_DIR_NAME "/dev" |
| #define DRM_DEV_NAME "%s/drm%d" |
| #define DRM_CONTROL_DEV_NAME "%s/drmC%d" |
| #define DRM_RENDER_DEV_NAME "%s/drmR%d" |
| #else |
| #define DRM_DIR_NAME "/dev/dri" |
| #define DRM_DEV_NAME "%s/card%d" |
| #define DRM_CONTROL_DEV_NAME "%s/controlD%d" |
| #define DRM_RENDER_DEV_NAME "%s/renderD%d" |
| #define DRM_PROC_NAME "/proc/dri/" /* For backward Linux compatibility */ |
| #endif |
| |
| #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 |
| |
| #define DRM_ERR_NO_DEVICE (-1001) |
| #define DRM_ERR_NO_ACCESS (-1002) |
| #define DRM_ERR_NOT_ROOT (-1003) |
| #define DRM_ERR_INVALID (-1004) |
| #define DRM_ERR_NO_FD (-1005) |
| |
| #define DRM_AGP_NO_HANDLE 0 |
| |
| #define DRM_BUS_PCI 0 |
| #define DRM_BUS_USB 1 |
| #define DRM_BUS_PLATFORM 2 |
| #define DRM_BUS_HOST1X 3 |
| #define DRM_BUS_VIRTIO 0x10 |
| |
| #define DRM_NODE_PRIMARY 0 |
| #define DRM_NODE_CONTROL 1 |
| #define DRM_NODE_RENDER 2 |
| #define DRM_NODE_MAX 3 |
| |
| typedef unsigned int drmSize, *drmSizePtr; /**< For mapped regions */ |
| typedef void *drmAddress, **drmAddressPtr; /**< For mapped regions */ |
| |
| #if (__GNUC__ >= 3) |
| #define DRM_PRINTFLIKE(f, a) __attribute__ ((format(__printf__, f, a))) |
| #else |
| #define DRM_PRINTFLIKE(f, a) |
| #endif |
| |
| #define MIN2( A, B ) ( (A)<(B) ? (A) : (B) ) |
| #define MAX2( A, B ) ( (A)>(B) ? (A) : (B) ) |
| #define MAX3( A, B, C ) ((A) > (B) ? MAX2(A, C) : MAX2(B, C)) |
| |
| #define __align_mask(value, mask) (((value) + (mask)) & ~(mask)) |
| #define ALIGN(value, alignment) __align_mask(value, (__typeof__(value))((alignment) - 1)) |
| #define DRM_PLATFORM_DEVICE_NAME_LEN 512 |
| |
| typedef struct _drmPciBusInfo { |
| uint16_t domain; |
| uint8_t bus; |
| uint8_t dev; |
| uint8_t func; |
| } drmPciBusInfo, *drmPciBusInfoPtr; |
| |
| typedef struct _drmPciDeviceInfo { |
| uint32_t vendor_id; |
| uint32_t device_id; |
| uint32_t subvendor_id; |
| uint32_t subdevice_id; |
| uint32_t revision_id; |
| char driverInfo[1024]; |
| uint64_t videoMem[4] = {}; |
| uint64_t systemMem[4] = {}; |
| uint64_t sharedMem[4] = {}; |
| uint64_t ioportMem[4] = {}; |
| uint64_t ioMem[4] = {}; |
| } drmPciDeviceInfo, *drmPciDeviceInfoPtr; |
| |
| |
| typedef struct _drmUsbBusInfo { |
| uint8_t bus; |
| uint8_t dev; |
| } drmUsbBusInfo, *drmUsbBusInfoPtr; |
| |
| typedef struct _drmUsbDeviceInfo { |
| uint16_t vendor; |
| uint16_t product; |
| } drmUsbDeviceInfo, *drmUsbDeviceInfoPtr; |
| |
| |
| |
| typedef struct _drmPlatformBusInfo { |
| char fullname[DRM_PLATFORM_DEVICE_NAME_LEN + 1]; |
| } drmPlatformBusInfo, *drmPlatformBusInfoPtr; |
| |
| typedef struct _drmPlatformDeviceInfo { |
| char **compatible; /* NULL terminated list of compatible strings */ |
| } drmPlatformDeviceInfo, *drmPlatformDeviceInfoPtr; |
| |
| #define DRM_HOST1X_DEVICE_NAME_LEN 512 |
| |
| typedef struct _drmHost1xBusInfo { |
| char fullname[DRM_HOST1X_DEVICE_NAME_LEN + 1]; |
| } drmHost1xBusInfo, *drmHost1xBusInfoPtr; |
| |
| typedef struct _drmHost1xDeviceInfo { |
| char **compatible; /* NULL terminated list of compatible strings */ |
| } drmHost1xDeviceInfo, *drmHost1xDeviceInfoPtr; |
| |
| typedef struct _drmDevice { |
| char **nodes; /* DRM_NODE_MAX sized array */ |
| int available_nodes; /* DRM_NODE_* bitmask */ |
| int bustype; |
| union { |
| drmPciBusInfoPtr pci; |
| drmUsbBusInfoPtr usb; |
| drmPlatformBusInfoPtr platform; |
| drmHost1xBusInfoPtr host1x; |
| } businfo; |
| union { |
| drmPciDeviceInfoPtr pci; |
| drmUsbDeviceInfoPtr usb; |
| drmPlatformDeviceInfoPtr platform; |
| drmHost1xDeviceInfoPtr host1x; |
| } deviceinfo; |
| |
| unsigned int MaxThread = 0; |
| unsigned int EuNumber = 0; |
| unsigned int TileNumber = 0; |
| unsigned int reserved[16]; |
| } drmDevice, *drmDevicePtr; |
| |
| /* |
| * The kernel drm core has a number of places that assume maximum of |
| * 3x64 devices nodes. That's 64 for each of primary, control and |
| * render nodes. Rounded it up to 256 for simplicity. |
| */ |
| #define MAX_DRM_NODES 256 |
| |
| inline int memcpy_s(void *dst, size_t numberOfElements, const void *src, size_t count) |
| { |
| if ((dst == nullptr) || (src == nullptr)) |
| { |
| return EINVAL; |
| } |
| if (numberOfElements < count) |
| { |
| return ERANGE; |
| } |
| std::memcpy(dst, src, count); |
| return 0; |
| } |
| |
| /* 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); |
| } |
| |
| 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 /* length of the node number */; |
| } |
| |
| static bool drmNodeIsDRM(int maj, int min) |
| { |
| #ifdef __linux__ |
| char path[64]; |
| struct stat sbuf; |
| |
| snprintf(path, sizeof(path), "/sys/dev/char/%d:%d/device/drm", |
| maj, min); |
| return stat(path, &sbuf) == 0; |
| #else |
| return maj == DRM_MAJOR; |
| #endif |
| } |
| |
| static int drmDevicesEqual(drmDevicePtr a, drmDevicePtr b) |
| { |
| if (a == NULL || b == NULL) |
| return 0; |
| |
| if (a->bustype != b->bustype) |
| return 0; |
| |
| switch (a->bustype) { |
| case DRM_BUS_PCI: |
| return memcmp(a->businfo.pci, b->businfo.pci, sizeof(drmPciBusInfo)) == 0; |
| |
| case DRM_BUS_USB: |
| return memcmp(a->businfo.usb, b->businfo.usb, sizeof(drmUsbBusInfo)) == 0; |
| |
| case DRM_BUS_PLATFORM: |
| return memcmp(a->businfo.platform, b->businfo.platform, sizeof(drmPlatformBusInfo)) == 0; |
| |
| case DRM_BUS_HOST1X: |
| return memcmp(a->businfo.host1x, b->businfo.host1x, sizeof(drmHost1xBusInfo)) == 0; |
| |
| default: |
| break; |
| } |
| return 0; |
| } |
| |
| static drmDevicePtr drmDeviceAlloc(unsigned int type, const char *node, |
| size_t bus_size, size_t device_size, |
| char **ptrp) |
| { |
| size_t max_node_length, extra, size; |
| drmDevicePtr device; |
| unsigned int i; |
| char *ptr; |
| |
| max_node_length = ALIGN(drmGetMaxNodeName(), sizeof(void *)); |
| |
| extra = DRM_NODE_MAX * (sizeof(void *) + max_node_length); |
| |
| size = sizeof(*device) + extra + bus_size + device_size; |
| |
| device = (drmDevicePtr)calloc(1, size); |
| if (!device) |
| return NULL; |
| |
| device->available_nodes = 1 << type; |
| |
| ptr = (char *)device + sizeof(*device); |
| device->nodes = (char **)ptr; |
| |
| ptr += DRM_NODE_MAX * sizeof(void *); |
| |
| for (i = 0; i < DRM_NODE_MAX; i++) { |
| device->nodes[i] = ptr; |
| ptr += max_node_length; |
| } |
| |
| memcpy(device->nodes[type], node, max_node_length); |
| *ptrp = ptr; |
| |
| return device; |
| } |
| |
| 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; |
| } |
| |
| static int drmParseHost1xDeviceInfo(int maj, |
| int min, |
| drmHost1xDeviceInfoPtr info) |
| { |
| char path[PATH_MAX + 1]; |
| int err = 0; |
| |
| snprintf(path, sizeof(path), "/sys/dev/char/%d:%d/device", maj, min); |
| |
| char *value = sysfs_uevent_get(path, "OF_COMPATIBLE_N"); |
| if (!value) |
| { |
| return -ENOENT; |
| } |
| unsigned int count = 0; |
| int scanned_value_count = sscanf(value, "%u", &count); |
| free(value); |
| if (scanned_value_count <= 0 || 0 == count) |
| { |
| return -ENOENT; |
| } |
| |
| info->compatible = (char**)calloc(count + 1, sizeof(*info->compatible)); |
| if (!info->compatible) |
| { |
| return -ENOMEM; |
| } |
| |
| unsigned int i = 0; |
| for (; 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; |
| } |
| |
| static int drmParseHost1xBusInfo(int maj, int min, drmHost1xBusInfoPtr info) |
| { |
| 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; |
| } |
| |
| 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; |
| } |
| |
| static int drmParsePlatformBusInfo(int maj, int min, drmPlatformBusInfoPtr info) |
| { |
| 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; |
| } |
| |
| static int drmParsePlatformDeviceInfo(int maj, int min, |
| drmPlatformDeviceInfoPtr info) |
| { |
| 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); |
| if (count <= MAX_DRM_NODES) |
| { |
| info->compatible = (char**)calloc(count + 1, sizeof(*info->compatible)); |
| }else |
| return -ENOENT; |
| |
| 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; |
| } |
| |
| static void |
| get_pci_path(int maj, int min, char *pci_path) |
| { |
| char path[PATH_MAX + 1], *term; |
| |
| snprintf(path, sizeof(path), "/sys/dev/char/%d:%d/device", maj, min); |
| if (!realpath(path, pci_path)) { |
| strcpy(pci_path, path); |
| return; |
| } |
| |
| term = strrchr(pci_path, '/'); |
| if (term && strncmp(term, "/virtio", 7) == 0) |
| *term = 0; |
| } |
| |
| static int parse_separate_sysfs_files(int maj, int min, |
| drmPciDeviceInfoPtr device, |
| bool ignore_revision) |
| { |
| 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 + 128], pci_path[PATH_MAX]; |
| char resourcename[PATH_MAX + 64], driverpath[PATH_MAX + 64], drivername[PATH_MAX + 64], irqpath[PATH_MAX + 64]; |
| |
| unsigned int data[ARRAY_SIZE(attrs)]; |
| FILE *fp; |
| int ret; |
| char *dev_path; |
| char *driver_name; |
| |
| get_pci_path(maj, min, pci_path); |
| dev_path = strrchr(pci_path, '/'); |
| dev_path++; |
| |
| snprintf(driverpath, sizeof(driverpath), "%s/driver", pci_path); |
| snprintf(irqpath, sizeof(irqpath), "%s/irq", pci_path); |
| int fd = open(irqpath, O_RDONLY); |
| |
| char buffer[512] ; |
| memset(buffer, 0, sizeof(buffer)); |
| if (fd >= 0) |
| { |
| int count = 0; |
| count = read(fd, buffer, sizeof(buffer)); |
| close(fd); |
| } |
| |
| snprintf(resourcename, sizeof(resourcename), "%s/resource", pci_path); |
| |
| if (readlink(driverpath, drivername, PATH_MAX) < 0) |
| printf(" readlink -errno %d\n", errno); |
| |
| driver_name = strrchr(drivername, '/'); |
| driver_name++; |
| snprintf(device->driverInfo, sizeof(device->driverInfo), "Driver Module %s Bus %s IRQ %s", driver_name, dev_path, buffer); |
| |
| FILE*resource = fopen(resourcename, "r"); |
| |
| if (resource) |
| { |
| while (!feof(resource)) |
| { |
| unsigned long long start, end, flags; |
| |
| start = end = flags = 0; |
| |
| if (fscanf(resource, "%llx %llx %llx", &start, &end, &flags) != 3) |
| break; |
| |
| if (flags & 0x101) |
| { |
| device->ioportMem[0] = end - start + 1; |
| } |
| else |
| if (flags & 0x100) |
| { |
| device->ioMem[0] = end - start + 1; |
| } |
| else |
| if (flags & 0x200) |
| { |
| int i; |
| for ( i = 0; i < 4; i++) |
| { |
| if (device->videoMem[i] == 0) |
| { |
| device->videoMem[i] = end - start + 1; |
| break; |
| } |
| } |
| |
| } |
| } |
| fclose(resource); |
| } |
| |
| |
| for (unsigned i = ignore_revision ? 1 : 0; i < ARRAY_SIZE(attrs); i++) { |
| snprintf(path, PATH_MAX + 128, "%s/%s", pci_path, 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 + 128], pci_path[PATH_MAX + 1]; |
| unsigned char config[64]; |
| int fd, ret; |
| |
| get_pci_path(maj, min, pci_path); |
| |
| snprintf(path, PATH_MAX + 128, "%s/config", pci_path); |
| |
| 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; |
| } |
| |
| 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__) || defined(__DragonFly__) |
| struct drm_pciinfo pinfo; |
| int fd, type; |
| |
| type = drmGetMinorType(min); |
| if (type == -1) |
| return -ENODEV; |
| |
| fd = drmOpenMinor(min, 0, type); |
| if (fd < 0) |
| return -errno; |
| |
| if (drmIoctl(fd, DRM_IOCTL_GET_PCIINFO, &pinfo)) { |
| close(fd); |
| return -errno; |
| } |
| close(fd); |
| |
| device->vendor_id = pinfo.vendor_id; |
| device->device_id = pinfo.device_id; |
| device->revision_id = pinfo.revision_id; |
| device->subvendor_id = pinfo.subvendor_id; |
| device->subdevice_id = pinfo.subdevice_id; |
| |
| return 0; |
| #else |
| #warning "Missing implementation of drmParsePciDeviceInfo" |
| return -EINVAL; |
| #endif |
| */ |
| } |
| |
| static int drmParsePciBusInfo(int maj, int min, drmPciBusInfoPtr info) |
| { |
| unsigned int domain, bus, dev, func; |
| char pci_path[PATH_MAX + 1], *value; |
| int num; |
| get_pci_path(maj, min, pci_path); |
| value = sysfs_uevent_get(pci_path, "PCI_SLOT_NAME"); |
| if (!value) |
| return -ENOENT; |
| |
| num = sscanf(value, "%04x:%02x:%02x.%1u", &domain, &bus, &dev, &func); |
| free(value); |
| if (num != 4) |
| return -EINVAL; |
| info->domain = domain; |
| info->bus = bus; |
| info->dev = dev; |
| info->func = func; |
| return 0; |
| } |
| |
| 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 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); |
| dev->deviceinfo.pci = (drmPciDeviceInfoPtr)addr; |
| ret = drmParsePciDeviceInfo(maj, min, dev->deviceinfo.pci, flags); |
| |
| if (ret) |
| goto free_device; |
| } |
| |
| *device = dev; |
| |
| return 0; |
| |
| free_device: |
| free(dev); |
| return ret; |
| } |
| |
| static int drmParseUsbBusInfo(int maj, int min, drmUsbBusInfoPtr info) |
| { |
| 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; |
| } |
| |
| static int drmParseUsbDeviceInfo(int maj, int min, drmUsbDeviceInfoPtr info) |
| { |
| 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; |
| } |
| |
| 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 drmParseSubsystemType(int maj, int min) |
| { |
| #ifdef __linux__ |
| char path[PATH_MAX + 1]; |
| char link[PATH_MAX + 1] = ""; |
| char *name; |
| struct { |
| const char *name; |
| int bus_type; |
| } bus_types[] = { |
| { "/pci", DRM_BUS_PCI }, |
| { "/usb", DRM_BUS_USB }, |
| { "/platform", DRM_BUS_PLATFORM }, |
| { "/spi", DRM_BUS_PLATFORM }, |
| { "/host1x", DRM_BUS_HOST1X }, |
| { "/virtio", DRM_BUS_VIRTIO }, |
| }; |
| |
| 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; |
| |
| for (unsigned i = 0; i < ARRAY_SIZE(bus_types); i++) { |
| if (strncmp(name, bus_types[i].name, strlen(bus_types[i].name)) == 0) |
| return bus_types[i].bus_type; |
| } |
| |
| return -EINVAL; |
| #elif defined(__OpenBSD__) || defined(__DragonFly__) |
| return DRM_BUS_PCI; |
| #else |
| #warning "Missing implementation of drmParseSubsystemType" |
| return -EINVAL; |
| #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); |
| } |
| } |
| } |
| |
| 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; |
| } |
| |
| 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; |
| } |
| |
| static int |
| process_device(drmDevicePtr *device, const char *d_name, |
| int req_subsystem_type, |
| bool fetch_deviceinfo, uint32_t flags) |
| { |
| struct stat sbuf; |
| char node[PATH_MAX + 1]; |
| int node_type, subsystem_type; |
| unsigned int maj, min; |
| |
| node_type = drmGetNodeType(d_name); |
| if (node_type < 0) |
| return -1; |
| |
| snprintf(node, PATH_MAX, "%s/%s", DRM_DIR_NAME, d_name); |
| |
| if (stat(node, &sbuf)) |
| return -1; |
| |
| maj = major(sbuf.st_rdev); |
| min = minor(sbuf.st_rdev); |
| |
| if (!drmNodeIsDRM(maj, min) || !S_ISCHR(sbuf.st_mode)) |
| { |
| return -1; |
| } |
| |
| subsystem_type = drmParseSubsystemType(maj, min); |
| |
| if (req_subsystem_type != -1 && req_subsystem_type != subsystem_type) |
| return -1; |
| |
| switch (subsystem_type) { |
| case DRM_BUS_PCI: |
| case DRM_BUS_VIRTIO: |
| return drmProcessPciDevice(device, node, node_type, maj, min, |
| fetch_deviceinfo, flags); |
| case DRM_BUS_USB: |
| return drmProcessUsbDevice(device, node, node_type, maj, min, |
| fetch_deviceinfo, flags); |
| case DRM_BUS_PLATFORM: |
| return drmProcessPlatformDevice(device, node, node_type, maj, min, |
| fetch_deviceinfo, flags); |
| case DRM_BUS_HOST1X: |
| return drmProcessHost1xDevice(device, node, node_type, maj, min, |
| fetch_deviceinfo, flags); |
| default: |
| return -1; |
| } |
| } |
| |
| /* 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]); |
| } |
| } |
| } |
| } |
| |
| /** |
| * 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[MAX_DRM_NODES]; |
| drmDevicePtr device; |
| DIR *sysdir; |
| struct dirent *dent; |
| int ret, i, node_count, device_count; |
| |
| if (drm_device_validate_flags(flags)) |
| return -EINVAL; |
| |
| sysdir = opendir(DRM_DIR_NAME); |
| if (!sysdir) |
| return -errno; |
| |
| i = 0; |
| while ((dent = readdir(sysdir))) { |
| ret = process_device(&device, dent->d_name, -1, devices != NULL, flags); |
| if (ret) |
| continue; |
| |
| if (i >= MAX_DRM_NODES) { |
| fprintf(stderr, "More than %d drm nodes detected. " |
| "Please report - that should not happen.\n" |
| "Skipping extra nodes\n", MAX_DRM_NODES); |
| break; |
| } |
| 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); |
| return device_count; |
| } |
| |
| /** |
| * 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); |
| } |
| |
| |
| static int32_t GetRendererFileDescriptor(char * drm_node) |
| { |
| int32_t driFileDescriptor = -1; |
| |
| driFileDescriptor = open(drm_node, O_RDWR); |
| return driFileDescriptor; |
| } |
| |
| #endif // #ifndef DRM_DEVICE_H_ |