egldevice.c revision 01e04c3f
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 112 if ((device->available_nodes & (1 << DRM_NODE_PRIMARY | 113 1 << DRM_NODE_RENDER)) == 0) 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 205/* Ideally we'll have an extension which passes the render node, 206 * instead of the card one + magic. 207 * 208 * Then we can move this in _eglQueryDeviceStringEXT below. Until then 209 * keep it separate. 210 */ 211const char * 212_eglGetDRMDeviceRenderNode(_EGLDevice *dev) 213{ 214 return dev->device->nodes[DRM_NODE_RENDER]; 215} 216 217EGLBoolean 218_eglQueryDeviceAttribEXT(_EGLDevice *dev, EGLint attribute, 219 EGLAttrib *value) 220{ 221 switch (attribute) { 222 default: 223 _eglError(EGL_BAD_ATTRIBUTE, "eglQueryDeviceStringEXT"); 224 return EGL_FALSE; 225 } 226} 227 228const char * 229_eglQueryDeviceStringEXT(_EGLDevice *dev, EGLint name) 230{ 231 switch (name) { 232 case EGL_EXTENSIONS: 233 return dev->extensions; 234#ifdef HAVE_LIBDRM 235 case EGL_DRM_DEVICE_FILE_EXT: 236 if (_eglDeviceSupports(dev, _EGL_DEVICE_DRM)) 237 return dev->device->nodes[DRM_NODE_PRIMARY]; 238 /* fall through */ 239#endif 240 default: 241 _eglError(EGL_BAD_PARAMETER, "eglQueryDeviceStringEXT"); 242 return NULL; 243 }; 244} 245 246/* Do a fresh lookup for devices. 247 * 248 * Walks through the DeviceList, discarding no longer available ones 249 * and adding new ones as applicable. 250 * 251 * Must be called with the global lock held. 252 */ 253static int 254_eglRefreshDeviceList(void) 255{ 256 MAYBE_UNUSED _EGLDevice *dev; 257 int count = 0; 258 259 dev = _eglGlobal.DeviceList; 260 261 /* The first device is always software */ 262 assert(dev); 263 assert(_eglDeviceSupports(dev, _EGL_DEVICE_SOFTWARE)); 264 count++; 265 266#ifdef HAVE_LIBDRM 267 drmDevicePtr devices[64]; 268 int num_devs, ret; 269 270 num_devs = drmGetDevices2(0, devices, ARRAY_SIZE(devices)); 271 for (int i = 0; i < num_devs; i++) { 272 ret = _eglAddDRMDevice(devices[i], NULL); 273 274 /* Device is not added - error or already present */ 275 if (ret != 0) 276 drmFreeDevice(&devices[i]); 277 278 if (ret >= 0) 279 count++; 280 } 281#endif 282 283 return count; 284} 285 286EGLBoolean 287_eglQueryDevicesEXT(EGLint max_devices, 288 _EGLDevice **devices, 289 EGLint *num_devices) 290{ 291 _EGLDevice *dev, *devs; 292 int i = 0, num_devs; 293 294 if ((devices && max_devices <= 0) || !num_devices) 295 return _eglError(EGL_BAD_PARAMETER, "eglQueryDevicesEXT"); 296 297 mtx_lock(_eglGlobal.Mutex); 298 299 num_devs = _eglRefreshDeviceList(); 300 devs = _eglGlobal.DeviceList; 301 302 /* bail early if we only care about the count */ 303 if (!devices) { 304 *num_devices = num_devs; 305 goto out; 306 } 307 308 *num_devices = MIN2(num_devs, max_devices); 309 310 for (i = 0, dev = devs; i < *num_devices; i++) { 311 devices[i] = dev; 312 dev = dev->Next; 313 } 314 315out: 316 mtx_unlock(_eglGlobal.Mutex); 317 318 return EGL_TRUE; 319} 320