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/compiler.h" 32#include "util/macros.h" 33 34#include "eglcurrent.h" 35#include "egldevice.h" 36#include "egllog.h" 37#include "eglglobals.h" 38#include "egltypedefs.h" 39 40 41struct _egl_device { 42 _EGLDevice *Next; 43 44 const char *extensions; 45 46 EGLBoolean MESA_device_software; 47 EGLBoolean EXT_device_drm; 48 EGLBoolean EXT_device_drm_render_node; 49 50#ifdef HAVE_LIBDRM 51 drmDevicePtr device; 52#endif 53}; 54 55void 56_eglFiniDevice(void) 57{ 58 _EGLDevice *dev_list, *dev; 59 60 /* atexit function is called with global mutex locked */ 61 62 dev_list = _eglGlobal.DeviceList; 63 64 /* The first device is static allocated SW device */ 65 assert(dev_list); 66 assert(_eglDeviceSupports(dev_list, _EGL_DEVICE_SOFTWARE)); 67 dev_list = dev_list->Next; 68 69 while (dev_list) { 70 /* pop list head */ 71 dev = dev_list; 72 dev_list = dev_list->Next; 73 74#ifdef HAVE_LIBDRM 75 assert(_eglDeviceSupports(dev, _EGL_DEVICE_DRM)); 76 drmFreeDevice(&dev->device); 77#endif 78 free(dev); 79 } 80 81 _eglGlobal.DeviceList = NULL; 82} 83 84EGLBoolean 85_eglCheckDeviceHandle(EGLDeviceEXT device) 86{ 87 _EGLDevice *cur; 88 89 mtx_lock(_eglGlobal.Mutex); 90 cur = _eglGlobal.DeviceList; 91 while (cur) { 92 if (cur == (_EGLDevice *) device) 93 break; 94 cur = cur->Next; 95 } 96 mtx_unlock(_eglGlobal.Mutex); 97 return (cur != NULL); 98} 99 100_EGLDevice _eglSoftwareDevice = { 101 /* TODO: EGL_EXT_device_drm support for KMS + llvmpipe */ 102 .extensions = "EGL_MESA_device_software EGL_EXT_device_drm_render_node", 103 .MESA_device_software = EGL_TRUE, 104 .EXT_device_drm_render_node = EGL_TRUE, 105}; 106 107#ifdef HAVE_LIBDRM 108/* 109 * Negative value on error, zero if newly added, one if already in list. 110 */ 111static int 112_eglAddDRMDevice(drmDevicePtr device, _EGLDevice **out_dev) 113{ 114 _EGLDevice *dev; 115 116 if ((device->available_nodes & (1 << DRM_NODE_PRIMARY | 117 1 << DRM_NODE_RENDER)) == 0) 118 return -1; 119 120 dev = _eglGlobal.DeviceList; 121 122 /* The first device is always software */ 123 assert(dev); 124 assert(_eglDeviceSupports(dev, _EGL_DEVICE_SOFTWARE)); 125 126 while (dev->Next) { 127 dev = dev->Next; 128 129 assert(_eglDeviceSupports(dev, _EGL_DEVICE_DRM)); 130 if (drmDevicesEqual(device, dev->device) != 0) { 131 if (out_dev) 132 *out_dev = dev; 133 return 1; 134 } 135 } 136 137 dev->Next = calloc(1, sizeof(_EGLDevice)); 138 if (!dev->Next) { 139 if (out_dev) 140 *out_dev = NULL; 141 return -1; 142 } 143 144 dev = dev->Next; 145 dev->extensions = "EGL_EXT_device_drm"; 146 dev->EXT_device_drm = EGL_TRUE; 147 dev->device = device; 148 149 /* TODO: EGL_EXT_device_drm_render_node support for kmsro + renderonly */ 150 if (device->available_nodes & (1 << DRM_NODE_RENDER)) { 151 dev->extensions = "EGL_EXT_device_drm EGL_EXT_device_drm_render_node"; 152 dev->EXT_device_drm_render_node = EGL_TRUE; 153 } 154 155 if (out_dev) 156 *out_dev = dev; 157 158 return 0; 159} 160#endif 161 162/* Adds a device in DeviceList, if needed for the given fd. 163 * 164 * If a software device, the fd is ignored. 165 */ 166_EGLDevice * 167_eglAddDevice(int fd, bool software) 168{ 169 _EGLDevice *dev; 170 171 mtx_lock(_eglGlobal.Mutex); 172 dev = _eglGlobal.DeviceList; 173 174 /* The first device is always software */ 175 assert(dev); 176 assert(_eglDeviceSupports(dev, _EGL_DEVICE_SOFTWARE)); 177 if (software) 178 goto out; 179 180#ifdef HAVE_LIBDRM 181 drmDevicePtr device; 182 183 if (drmGetDevice2(fd, 0, &device) != 0) { 184 dev = NULL; 185 goto out; 186 } 187 188 /* Device is not added - error or already present */ 189 if (_eglAddDRMDevice(device, &dev) != 0) 190 drmFreeDevice(&device); 191#else 192 _eglLog(_EGL_FATAL, "Driver bug: Built without libdrm, yet looking for HW device"); 193 dev = NULL; 194#endif 195 196out: 197 mtx_unlock(_eglGlobal.Mutex); 198 return dev; 199} 200 201EGLBoolean 202_eglDeviceSupports(_EGLDevice *dev, _EGLDeviceExtension ext) 203{ 204 switch (ext) { 205 case _EGL_DEVICE_SOFTWARE: 206 return dev->MESA_device_software; 207 case _EGL_DEVICE_DRM: 208 return dev->EXT_device_drm; 209 case _EGL_DEVICE_DRM_RENDER_NODE: 210 return dev->EXT_device_drm_render_node; 211 default: 212 assert(0); 213 return EGL_FALSE; 214 }; 215} 216 217/* Ideally we'll have an extension which passes the render node, 218 * instead of the card one + magic. 219 * 220 * Then we can move this in _eglQueryDeviceStringEXT below. Until then 221 * keep it separate. 222 */ 223const char * 224_eglGetDRMDeviceRenderNode(_EGLDevice *dev) 225{ 226#ifdef HAVE_LIBDRM 227 return dev->device->nodes[DRM_NODE_RENDER]; 228#else 229 return NULL; 230#endif 231} 232 233EGLBoolean 234_eglQueryDeviceAttribEXT(_EGLDevice *dev, EGLint attribute, 235 EGLAttrib *value) 236{ 237 switch (attribute) { 238 default: 239 _eglError(EGL_BAD_ATTRIBUTE, "eglQueryDeviceAttribEXT"); 240 return EGL_FALSE; 241 } 242} 243 244const char * 245_eglQueryDeviceStringEXT(_EGLDevice *dev, EGLint name) 246{ 247 switch (name) { 248 case EGL_EXTENSIONS: 249 return dev->extensions; 250 case EGL_DRM_DEVICE_FILE_EXT: 251 if (!_eglDeviceSupports(dev, _EGL_DEVICE_DRM)) 252 break; 253#ifdef HAVE_LIBDRM 254 return dev->device->nodes[DRM_NODE_PRIMARY]; 255#else 256 /* This should never happen: we don't yet support EGL_DEVICE_DRM for the 257 * software device, and physical devices are only exposed when libdrm is 258 * available. */ 259 assert(0); 260 break; 261#endif 262 case EGL_DRM_RENDER_NODE_FILE_EXT: 263 if (!_eglDeviceSupports(dev, _EGL_DEVICE_DRM_RENDER_NODE)) 264 break; 265#ifdef HAVE_LIBDRM 266 return dev->device ? dev->device->nodes[DRM_NODE_RENDER] : NULL; 267#else 268 /* Physical devices are only exposed when libdrm is available. */ 269 assert(_eglDeviceSupports(dev, _EGL_DEVICE_SOFTWARE)); 270 return NULL; 271#endif 272 } 273 _eglError(EGL_BAD_PARAMETER, "eglQueryDeviceStringEXT"); 274 return NULL; 275} 276 277/* Do a fresh lookup for devices. 278 * 279 * Walks through the DeviceList, discarding no longer available ones 280 * and adding new ones as applicable. 281 * 282 * Must be called with the global lock held. 283 */ 284static int 285_eglRefreshDeviceList(void) 286{ 287 ASSERTED _EGLDevice *dev; 288 int count = 0; 289 290 dev = _eglGlobal.DeviceList; 291 292 /* The first device is always software */ 293 assert(dev); 294 assert(_eglDeviceSupports(dev, _EGL_DEVICE_SOFTWARE)); 295 count++; 296 297#ifdef HAVE_LIBDRM 298 drmDevicePtr devices[64]; 299 int num_devs, ret; 300 301 num_devs = drmGetDevices2(0, devices, ARRAY_SIZE(devices)); 302 for (int i = 0; i < num_devs; i++) { 303 if (!(devices[i]->available_nodes & (1 << DRM_NODE_RENDER))) 304 continue; 305 306 ret = _eglAddDRMDevice(devices[i], NULL); 307 308 /* Device is not added - error or already present */ 309 if (ret != 0) 310 drmFreeDevice(&devices[i]); 311 312 if (ret >= 0) 313 count++; 314 } 315#endif 316 317 return count; 318} 319 320EGLBoolean 321_eglQueryDevicesEXT(EGLint max_devices, 322 _EGLDevice **devices, 323 EGLint *num_devices) 324{ 325 _EGLDevice *dev, *devs; 326 int i = 0, num_devs; 327 328 if ((devices && max_devices <= 0) || !num_devices) 329 return _eglError(EGL_BAD_PARAMETER, "eglQueryDevicesEXT"); 330 331 mtx_lock(_eglGlobal.Mutex); 332 333 num_devs = _eglRefreshDeviceList(); 334 devs = _eglGlobal.DeviceList; 335 336 /* bail early if we only care about the count */ 337 if (!devices) { 338 *num_devices = num_devs; 339 goto out; 340 } 341 342 /* Push the first device (the software one) to the end of the list. 343 * Sending it to the user only if they've requested the full list. 344 * 345 * By default, the user is likely to pick the first device so having the 346 * software (aka least performant) one is not a good idea. 347 */ 348 *num_devices = MIN2(num_devs, max_devices); 349 350 for (i = 0, dev = devs->Next; dev && i < max_devices; i++) { 351 devices[i] = dev; 352 dev = dev->Next; 353 } 354 355 /* User requested the full device list, add the sofware device. */ 356 if (max_devices >= num_devs) { 357 assert(_eglDeviceSupports(devs, _EGL_DEVICE_SOFTWARE)); 358 devices[num_devs - 1] = devs; 359 } 360 361out: 362 mtx_unlock(_eglGlobal.Mutex); 363 364 return EGL_TRUE; 365} 366