egldevice.c revision b8e80941
1/************************************************************************** 2 * 3 * Copyright 2015, 2018 Collabora 4 * All Rights Reserved. 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a 7 * copy of this software and associated documentation files (the 8 * "Software"), to deal in the Software without restriction, including 9 * without limitation the rights to use, copy, modify, merge, publish, 10 * distribute, sub license, and/or sell copies of the Software, and to 11 * permit persons to whom the Software is furnished to do so, subject to 12 * the following conditions: 13 * 14 * The above copyright notice and this permission notice (including the 15 * next paragraph) shall be included in all copies or substantial portions 16 * of the Software. 17 * 18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 21 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 24 * DEALINGS IN THE SOFTWARE. 25 * 26 **************************************************************************/ 27 28#ifdef HAVE_LIBDRM 29#include <xf86drm.h> 30#endif 31#include "util/macros.h" 32 33#include "eglcurrent.h" 34#include "egldevice.h" 35#include "egllog.h" 36#include "eglglobals.h" 37#include "egltypedefs.h" 38 39 40struct _egl_device { 41 _EGLDevice *Next; 42 43 const char *extensions; 44 45 EGLBoolean MESA_device_software; 46 EGLBoolean EXT_device_drm; 47 48#ifdef HAVE_LIBDRM 49 drmDevicePtr device; 50#endif 51}; 52 53void 54_eglFiniDevice(void) 55{ 56 _EGLDevice *dev_list, *dev; 57 58 /* atexit function is called with global mutex locked */ 59 60 dev_list = _eglGlobal.DeviceList; 61 62 /* The first device is static allocated SW device */ 63 assert(dev_list); 64 assert(_eglDeviceSupports(dev_list, _EGL_DEVICE_SOFTWARE)); 65 dev_list = dev_list->Next; 66 67 while (dev_list) { 68 /* pop list head */ 69 dev = dev_list; 70 dev_list = dev_list->Next; 71 72#ifdef HAVE_LIBDRM 73 assert(_eglDeviceSupports(dev, _EGL_DEVICE_DRM)); 74 drmFreeDevice(&dev->device); 75#endif 76 free(dev); 77 } 78 79 _eglGlobal.DeviceList = NULL; 80} 81 82EGLBoolean 83_eglCheckDeviceHandle(EGLDeviceEXT device) 84{ 85 _EGLDevice *cur; 86 87 mtx_lock(_eglGlobal.Mutex); 88 cur = _eglGlobal.DeviceList; 89 while (cur) { 90 if (cur == (_EGLDevice *) device) 91 break; 92 cur = cur->Next; 93 } 94 mtx_unlock(_eglGlobal.Mutex); 95 return (cur != NULL); 96} 97 98_EGLDevice _eglSoftwareDevice = { 99 .extensions = "EGL_MESA_device_software", 100 .MESA_device_software = EGL_TRUE, 101}; 102 103#ifdef HAVE_LIBDRM 104/* 105 * Negative value on error, zero if newly added, one if already in list. 106 */ 107static int 108_eglAddDRMDevice(drmDevicePtr device, _EGLDevice **out_dev) 109{ 110 _EGLDevice *dev; 111 const int wanted_nodes = 1 << DRM_NODE_RENDER | 1 << DRM_NODE_PRIMARY; 112 113 if ((device->available_nodes & wanted_nodes) != wanted_nodes) 114 return -1; 115 116 dev = _eglGlobal.DeviceList; 117 118 /* The first device is always software */ 119 assert(dev); 120 assert(_eglDeviceSupports(dev, _EGL_DEVICE_SOFTWARE)); 121 122 while (dev->Next) { 123 dev = dev->Next; 124 125 assert(_eglDeviceSupports(dev, _EGL_DEVICE_DRM)); 126 if (drmDevicesEqual(device, dev->device) != 0) { 127 if (out_dev) 128 *out_dev = dev; 129 return 1; 130 } 131 } 132 133 dev->Next = calloc(1, sizeof(_EGLDevice)); 134 if (!dev->Next) { 135 if (out_dev) 136 *out_dev = NULL; 137 return -1; 138 } 139 140 dev = dev->Next; 141 dev->extensions = "EGL_EXT_device_drm"; 142 dev->EXT_device_drm = EGL_TRUE; 143 dev->device = device; 144 145 if (out_dev) 146 *out_dev = dev; 147 148 return 0; 149} 150#endif 151 152/* Adds a device in DeviceList, if needed for the given fd. 153 * 154 * If a software device, the fd is ignored. 155 */ 156_EGLDevice * 157_eglAddDevice(int fd, bool software) 158{ 159 _EGLDevice *dev; 160 161 mtx_lock(_eglGlobal.Mutex); 162 dev = _eglGlobal.DeviceList; 163 164 /* The first device is always software */ 165 assert(dev); 166 assert(_eglDeviceSupports(dev, _EGL_DEVICE_SOFTWARE)); 167 if (software) 168 goto out; 169 170#ifdef HAVE_LIBDRM 171 drmDevicePtr device; 172 173 if (drmGetDevice2(fd, 0, &device) != 0) { 174 dev = NULL; 175 goto out; 176 } 177 178 /* Device is not added - error or already present */ 179 if (_eglAddDRMDevice(device, &dev) != 0) 180 drmFreeDevice(&device); 181#else 182 _eglLog(_EGL_FATAL, "Driver bug: Built without libdrm, yet looking for HW device"); 183 dev = NULL; 184#endif 185 186out: 187 mtx_unlock(_eglGlobal.Mutex); 188 return dev; 189} 190 191EGLBoolean 192_eglDeviceSupports(_EGLDevice *dev, _EGLDeviceExtension ext) 193{ 194 switch (ext) { 195 case _EGL_DEVICE_SOFTWARE: 196 return dev->MESA_device_software; 197 case _EGL_DEVICE_DRM: 198 return dev->EXT_device_drm; 199 default: 200 assert(0); 201 return EGL_FALSE; 202 }; 203} 204 205EGLBoolean 206_eglQueryDeviceAttribEXT(_EGLDevice *dev, EGLint attribute, 207 EGLAttrib *value) 208{ 209 switch (attribute) { 210 default: 211 _eglError(EGL_BAD_ATTRIBUTE, "eglQueryDeviceStringEXT"); 212 return EGL_FALSE; 213 } 214} 215 216const char * 217_eglQueryDeviceStringEXT(_EGLDevice *dev, EGLint name) 218{ 219 switch (name) { 220 case EGL_EXTENSIONS: 221 return dev->extensions; 222#ifdef HAVE_LIBDRM 223 case EGL_DRM_DEVICE_FILE_EXT: 224 if (_eglDeviceSupports(dev, _EGL_DEVICE_DRM)) 225 return dev->device->nodes[DRM_NODE_PRIMARY]; 226 /* fall through */ 227#endif 228 default: 229 _eglError(EGL_BAD_PARAMETER, "eglQueryDeviceStringEXT"); 230 return NULL; 231 }; 232} 233 234/* Do a fresh lookup for devices. 235 * 236 * Walks through the DeviceList, discarding no longer available ones 237 * and adding new ones as applicable. 238 * 239 * Must be called with the global lock held. 240 */ 241static int 242_eglRefreshDeviceList(void) 243{ 244 MAYBE_UNUSED _EGLDevice *dev; 245 int count = 0; 246 247 dev = _eglGlobal.DeviceList; 248 249 /* The first device is always software */ 250 assert(dev); 251 assert(_eglDeviceSupports(dev, _EGL_DEVICE_SOFTWARE)); 252 count++; 253 254#ifdef HAVE_LIBDRM 255 drmDevicePtr devices[64]; 256 int num_devs, ret; 257 258 num_devs = drmGetDevices2(0, devices, ARRAY_SIZE(devices)); 259 for (int i = 0; i < num_devs; i++) { 260 ret = _eglAddDRMDevice(devices[i], NULL); 261 262 /* Device is not added - error or already present */ 263 if (ret != 0) 264 drmFreeDevice(&devices[i]); 265 266 if (ret >= 0) 267 count++; 268 } 269#endif 270 271 return count; 272} 273 274EGLBoolean 275_eglQueryDevicesEXT(EGLint max_devices, 276 _EGLDevice **devices, 277 EGLint *num_devices) 278{ 279 _EGLDevice *dev, *devs; 280 int i = 0, num_devs; 281 282 if ((devices && max_devices <= 0) || !num_devices) 283 return _eglError(EGL_BAD_PARAMETER, "eglQueryDevicesEXT"); 284 285 mtx_lock(_eglGlobal.Mutex); 286 287 num_devs = _eglRefreshDeviceList(); 288 devs = _eglGlobal.DeviceList; 289 290 /* bail early if we only care about the count */ 291 if (!devices) { 292 *num_devices = num_devs; 293 goto out; 294 } 295 296 *num_devices = MIN2(num_devs, max_devices); 297 298 for (i = 0, dev = devs; i < *num_devices; i++) { 299 devices[i] = dev; 300 dev = dev->Next; 301 } 302 303out: 304 mtx_unlock(_eglGlobal.Mutex); 305 306 return EGL_TRUE; 307} 308