diff options
author | Jonathan Gray <jsg@cvs.openbsd.org> | 2015-12-27 09:16:54 +0000 |
---|---|---|
committer | Jonathan Gray <jsg@cvs.openbsd.org> | 2015-12-27 09:16:54 +0000 |
commit | 46bb4450c74e5481f39141bea53e02c2936456cb (patch) | |
tree | 5a06c8b5e50b5ebdfde3615296340a1cf55a370f /lib/libdrm/xf86drm.c | |
parent | 5db7cad38be896315e2b897f68d1751d340a2483 (diff) |
Merge libdrm 2.4.65
Diffstat (limited to 'lib/libdrm/xf86drm.c')
-rw-r--r-- | lib/libdrm/xf86drm.c | 368 |
1 files changed, 361 insertions, 7 deletions
diff --git a/lib/libdrm/xf86drm.c b/lib/libdrm/xf86drm.c index 47b2af3a6..c99df1f0a 100644 --- a/lib/libdrm/xf86drm.c +++ b/lib/libdrm/xf86drm.c @@ -55,6 +55,7 @@ #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 @@ -137,7 +138,8 @@ drmMsg(const char *format, ...) { va_list ap; const char *env; - if (((env = getenv("LIBGL_DEBUG")) && strstr(env, "verbose")) || drm_server_info) + if (((env = getenv("LIBGL_DEBUG")) && strstr(env, "verbose")) || + (drm_server_info && drm_server_info->debug_print)) { va_start(ap, format); if (drm_server_info) { @@ -354,7 +356,7 @@ static int drmOpenDevice(dev_t dev, int minor, int type) sprintf(buf, dev_name, DRM_DIR_NAME, minor); drmMsg("drmOpenDevice: node name is %s\n", buf); - if (drm_server_info) { + if (drm_server_info && drm_server_info->get_perms) { drm_server_info->get_perms(&serv_group, &serv_mode); devmode = serv_mode ? serv_mode : DRM_DEV_MODE; devmode &= ~(S_IXUSR|S_IXGRP|S_IXOTH); @@ -378,7 +380,7 @@ static int drmOpenDevice(dev_t dev, int minor, int type) mknod(buf, S_IFCHR | devmode, dev); } - if (drm_server_info) { + if (drm_server_info && drm_server_info->get_perms) { group = ((int)serv_group >= 0) ? serv_group : DRM_DEV_GID; chown_check_return(buf, user, group); chmod(buf, devmode); @@ -428,7 +430,7 @@ wait_for_udev: return DRM_ERR_NOT_ROOT; remove(buf); mknod(buf, S_IFCHR | devmode, dev); - if (drm_server_info) { + if (drm_server_info && drm_server_info->get_perms) { chown_check_return(buf, user, group); chmod(buf, devmode); } @@ -761,7 +763,8 @@ int drmOpen(const char *name, const char *busid) */ int drmOpenWithType(const char *name, const char *busid, int type) { - if (!drmAvailable() && name != NULL && drm_server_info) { + if (!drmAvailable() && name != NULL && drm_server_info && + drm_server_info->load_module) { /* try to load the kernel module */ if (!drm_server_info->load_module(name)) { drmMsg("[drm] failed to load kernel module \"%s\"\n", name); @@ -2836,11 +2839,12 @@ static char *drmGetMinorNameForFD(int fd, int type) 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); } } @@ -2862,3 +2866,353 @@ char *drmGetRenderDeviceNameFromFd(int fd) { 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; + } +} + +/** + * 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; + } + + 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++; + } + } + + 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 |