/************************************************************************** * * Copyright 2015, 2018 Collabora * All Rights Reserved. * * 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, sub license, 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 * THE AUTHORS OR COPYRIGHT HOLDERS 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. * **************************************************************************/ #ifdef HAVE_LIBDRM #include #endif #include "util/compiler.h" #include "util/macros.h" #include "eglcurrent.h" #include "egldevice.h" #include "eglglobals.h" #include "egllog.h" #include "egltypedefs.h" struct _egl_device { _EGLDevice *Next; const char *extensions; EGLBoolean MESA_device_software; EGLBoolean EXT_device_drm; EGLBoolean EXT_device_drm_render_node; #ifdef HAVE_LIBDRM drmDevicePtr device; #endif }; void _eglFiniDevice(void) { _EGLDevice *dev_list, *dev; /* atexit function is called with global mutex locked */ dev_list = _eglGlobal.DeviceList; /* The first device is static allocated SW device */ assert(dev_list); assert(_eglDeviceSupports(dev_list, _EGL_DEVICE_SOFTWARE)); dev_list = dev_list->Next; while (dev_list) { /* pop list head */ dev = dev_list; dev_list = dev_list->Next; #ifdef HAVE_LIBDRM assert(_eglDeviceSupports(dev, _EGL_DEVICE_DRM)); drmFreeDevice(&dev->device); #endif free(dev); } _eglGlobal.DeviceList = NULL; } EGLBoolean _eglCheckDeviceHandle(EGLDeviceEXT device) { _EGLDevice *cur; simple_mtx_lock(_eglGlobal.Mutex); cur = _eglGlobal.DeviceList; while (cur) { if (cur == (_EGLDevice *)device) break; cur = cur->Next; } simple_mtx_unlock(_eglGlobal.Mutex); return (cur != NULL); } _EGLDevice _eglSoftwareDevice = { /* TODO: EGL_EXT_device_drm support for KMS + llvmpipe */ .extensions = "EGL_MESA_device_software EGL_EXT_device_drm_render_node", .MESA_device_software = EGL_TRUE, .EXT_device_drm_render_node = EGL_TRUE, }; #ifdef HAVE_LIBDRM /* * Negative value on error, zero if newly added, one if already in list. */ static int _eglAddDRMDevice(drmDevicePtr device) { _EGLDevice *dev; assert(device->available_nodes & ((1 << DRM_NODE_RENDER))); /* TODO: uncomment this assert, which is a sanity check. * * assert(device->available_nodes & ((1 << DRM_NODE_PRIMARY))); * * DRM shim does not expose a primary node, so the CI would fail if we had * this assert. DRM shim is being used to run shader-db. We need to * investigate what should be done (probably fixing DRM shim). */ dev = _eglGlobal.DeviceList; /* The first device is always software */ assert(dev); assert(_eglDeviceSupports(dev, _EGL_DEVICE_SOFTWARE)); while (dev->Next) { dev = dev->Next; assert(_eglDeviceSupports(dev, _EGL_DEVICE_DRM)); if (drmDevicesEqual(device, dev->device) != 0) return 1; } dev->Next = calloc(1, sizeof(_EGLDevice)); if (!dev->Next) return -1; dev = dev->Next; dev->extensions = "EGL_EXT_device_drm EGL_EXT_device_drm_render_node"; dev->EXT_device_drm = EGL_TRUE; dev->EXT_device_drm_render_node = EGL_TRUE; dev->device = device; return 0; } #endif /* Finds a device in DeviceList, for the given fd. * * The fd must be of a render-capable device, as there are only render-capable * devices in DeviceList. * * If a software device, the fd is ignored. */ _EGLDevice * _eglFindDevice(int fd, bool software) { _EGLDevice *dev; simple_mtx_lock(_eglGlobal.Mutex); dev = _eglGlobal.DeviceList; /* The first device is always software */ assert(dev); assert(_eglDeviceSupports(dev, _EGL_DEVICE_SOFTWARE)); if (software) goto out; #ifdef HAVE_LIBDRM drmDevicePtr device; if (drmGetDevice2(fd, 0, &device) != 0) { dev = NULL; goto out; } while (dev->Next) { dev = dev->Next; if (_eglDeviceSupports(dev, _EGL_DEVICE_DRM) && drmDevicesEqual(device, dev->device) != 0) { goto cleanup_drm; } } /* Couldn't find an EGLDevice for the device. */ dev = NULL; cleanup_drm: drmFreeDevice(&device); #else _eglLog(_EGL_FATAL, "Driver bug: Built without libdrm, yet looking for HW device"); dev = NULL; #endif out: simple_mtx_unlock(_eglGlobal.Mutex); return dev; } #ifdef HAVE_LIBDRM drmDevicePtr _eglDeviceDrm(_EGLDevice *dev) { if (!dev) return NULL; return dev->device; } #endif _EGLDevice * _eglDeviceNext(_EGLDevice *dev) { if (!dev) return NULL; return dev->Next; } EGLBoolean _eglDeviceSupports(_EGLDevice *dev, _EGLDeviceExtension ext) { switch (ext) { case _EGL_DEVICE_SOFTWARE: return dev->MESA_device_software; case _EGL_DEVICE_DRM: return dev->EXT_device_drm; case _EGL_DEVICE_DRM_RENDER_NODE: return dev->EXT_device_drm_render_node; default: assert(0); return EGL_FALSE; }; } EGLBoolean _eglQueryDeviceAttribEXT(_EGLDevice *dev, EGLint attribute, EGLAttrib *value) { switch (attribute) { default: _eglError(EGL_BAD_ATTRIBUTE, "eglQueryDeviceAttribEXT"); return EGL_FALSE; } } const char * _eglQueryDeviceStringEXT(_EGLDevice *dev, EGLint name) { switch (name) { case EGL_EXTENSIONS: return dev->extensions; case EGL_DRM_DEVICE_FILE_EXT: if (!_eglDeviceSupports(dev, _EGL_DEVICE_DRM)) break; #ifdef HAVE_LIBDRM return dev->device->nodes[DRM_NODE_PRIMARY]; #else /* This should never happen: we don't yet support EGL_DEVICE_DRM for the * software device, and physical devices are only exposed when libdrm is * available. */ assert(0); break; #endif case EGL_DRM_RENDER_NODE_FILE_EXT: if (!_eglDeviceSupports(dev, _EGL_DEVICE_DRM_RENDER_NODE)) break; #ifdef HAVE_LIBDRM /* EGLDevice represents a software device, so no render node * should be advertised. */ if (_eglDeviceSupports(dev, _EGL_DEVICE_SOFTWARE)) return NULL; /* We create EGLDevice's only for render capable devices. */ assert(dev->device->available_nodes & (1 << DRM_NODE_RENDER)); return dev->device->nodes[DRM_NODE_RENDER]; #else /* Physical devices are only exposed when libdrm is available. */ assert(_eglDeviceSupports(dev, _EGL_DEVICE_SOFTWARE)); return NULL; #endif } _eglError(EGL_BAD_PARAMETER, "eglQueryDeviceStringEXT"); return NULL; } /* Do a fresh lookup for devices. * * Walks through the DeviceList, discarding no longer available ones * and adding new ones as applicable. * * Must be called with the global lock held. */ int _eglDeviceRefreshList(void) { ASSERTED _EGLDevice *dev; int count = 0; dev = _eglGlobal.DeviceList; /* The first device is always software */ assert(dev); assert(_eglDeviceSupports(dev, _EGL_DEVICE_SOFTWARE)); count++; #ifdef HAVE_LIBDRM drmDevicePtr devices[64]; int num_devs, ret; num_devs = drmGetDevices2(0, devices, ARRAY_SIZE(devices)); for (int i = 0; i < num_devs; i++) { if (!(devices[i]->available_nodes & (1 << DRM_NODE_RENDER))) { drmFreeDevice(&devices[i]); continue; } ret = _eglAddDRMDevice(devices[i]); /* Device is not added - error or already present */ if (ret != 0) drmFreeDevice(&devices[i]); if (ret >= 0) count++; } #endif return count; } EGLBoolean _eglQueryDevicesEXT(EGLint max_devices, _EGLDevice **devices, EGLint *num_devices) { _EGLDevice *dev, *devs, *swrast; int i = 0, num_devs; if ((devices && max_devices <= 0) || !num_devices) return _eglError(EGL_BAD_PARAMETER, "eglQueryDevicesEXT"); simple_mtx_lock(_eglGlobal.Mutex); num_devs = _eglDeviceRefreshList(); devs = _eglGlobal.DeviceList; #ifdef HAVE_SWRAST swrast = devs; #else swrast = NULL; num_devs--; #endif /* The first device is swrast. Start with the non-swrast device. */ devs = devs->Next; /* bail early if we only care about the count */ if (!devices) { *num_devices = num_devs; goto out; } *num_devices = MIN2(num_devs, max_devices); /* Add non-swrast devices first and add swrast last. * * By default, the user is likely to pick the first device so having the * software (aka least performant) one is not a good idea. */ for (i = 0, dev = devs; dev && i < max_devices; i++) { devices[i] = dev; dev = dev->Next; } /* User requested the full device list, add the software device. */ if (max_devices >= num_devs && swrast) { assert(_eglDeviceSupports(swrast, _EGL_DEVICE_SOFTWARE)); devices[num_devs - 1] = swrast; } out: simple_mtx_unlock(_eglGlobal.Mutex); return EGL_TRUE; }