egldevice.c revision 01e04c3f
101e04c3fSmrg/**************************************************************************
201e04c3fSmrg *
301e04c3fSmrg * Copyright 2015, 2018 Collabora
401e04c3fSmrg * All Rights Reserved.
501e04c3fSmrg *
601e04c3fSmrg * Permission is hereby granted, free of charge, to any person obtaining a
701e04c3fSmrg * copy of this software and associated documentation files (the
801e04c3fSmrg * "Software"), to deal in the Software without restriction, including
901e04c3fSmrg * without limitation the rights to use, copy, modify, merge, publish,
1001e04c3fSmrg * distribute, sub license, and/or sell copies of the Software, and to
1101e04c3fSmrg * permit persons to whom the Software is furnished to do so, subject to
1201e04c3fSmrg * the following conditions:
1301e04c3fSmrg *
1401e04c3fSmrg * The above copyright notice and this permission notice (including the
1501e04c3fSmrg * next paragraph) shall be included in all copies or substantial portions
1601e04c3fSmrg * of the Software.
1701e04c3fSmrg *
1801e04c3fSmrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1901e04c3fSmrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
2001e04c3fSmrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
2101e04c3fSmrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2201e04c3fSmrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
2301e04c3fSmrg * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
2401e04c3fSmrg * DEALINGS IN THE SOFTWARE.
2501e04c3fSmrg *
2601e04c3fSmrg **************************************************************************/
2701e04c3fSmrg
2801e04c3fSmrg#ifdef HAVE_LIBDRM
2901e04c3fSmrg#include <xf86drm.h>
3001e04c3fSmrg#endif
3101e04c3fSmrg#include "util/macros.h"
3201e04c3fSmrg
3301e04c3fSmrg#include "eglcurrent.h"
3401e04c3fSmrg#include "egldevice.h"
3501e04c3fSmrg#include "egllog.h"
3601e04c3fSmrg#include "eglglobals.h"
3701e04c3fSmrg#include "egltypedefs.h"
3801e04c3fSmrg
3901e04c3fSmrg
4001e04c3fSmrgstruct _egl_device {
4101e04c3fSmrg   _EGLDevice *Next;
4201e04c3fSmrg
4301e04c3fSmrg   const char *extensions;
4401e04c3fSmrg
4501e04c3fSmrg   EGLBoolean MESA_device_software;
4601e04c3fSmrg   EGLBoolean EXT_device_drm;
4701e04c3fSmrg
4801e04c3fSmrg#ifdef HAVE_LIBDRM
4901e04c3fSmrg   drmDevicePtr device;
5001e04c3fSmrg#endif
5101e04c3fSmrg};
5201e04c3fSmrg
5301e04c3fSmrgvoid
5401e04c3fSmrg_eglFiniDevice(void)
5501e04c3fSmrg{
5601e04c3fSmrg   _EGLDevice *dev_list, *dev;
5701e04c3fSmrg
5801e04c3fSmrg   /* atexit function is called with global mutex locked */
5901e04c3fSmrg
6001e04c3fSmrg   dev_list = _eglGlobal.DeviceList;
6101e04c3fSmrg
6201e04c3fSmrg   /* The first device is static allocated SW device */
6301e04c3fSmrg   assert(dev_list);
6401e04c3fSmrg   assert(_eglDeviceSupports(dev_list, _EGL_DEVICE_SOFTWARE));
6501e04c3fSmrg   dev_list = dev_list->Next;
6601e04c3fSmrg
6701e04c3fSmrg   while (dev_list) {
6801e04c3fSmrg      /* pop list head */
6901e04c3fSmrg      dev = dev_list;
7001e04c3fSmrg      dev_list = dev_list->Next;
7101e04c3fSmrg
7201e04c3fSmrg#ifdef HAVE_LIBDRM
7301e04c3fSmrg      assert(_eglDeviceSupports(dev, _EGL_DEVICE_DRM));
7401e04c3fSmrg      drmFreeDevice(&dev->device);
7501e04c3fSmrg#endif
7601e04c3fSmrg      free(dev);
7701e04c3fSmrg   }
7801e04c3fSmrg
7901e04c3fSmrg   _eglGlobal.DeviceList = NULL;
8001e04c3fSmrg}
8101e04c3fSmrg
8201e04c3fSmrgEGLBoolean
8301e04c3fSmrg_eglCheckDeviceHandle(EGLDeviceEXT device)
8401e04c3fSmrg{
8501e04c3fSmrg   _EGLDevice *cur;
8601e04c3fSmrg
8701e04c3fSmrg   mtx_lock(_eglGlobal.Mutex);
8801e04c3fSmrg   cur = _eglGlobal.DeviceList;
8901e04c3fSmrg   while (cur) {
9001e04c3fSmrg      if (cur == (_EGLDevice *) device)
9101e04c3fSmrg         break;
9201e04c3fSmrg      cur = cur->Next;
9301e04c3fSmrg   }
9401e04c3fSmrg   mtx_unlock(_eglGlobal.Mutex);
9501e04c3fSmrg   return (cur != NULL);
9601e04c3fSmrg}
9701e04c3fSmrg
9801e04c3fSmrg_EGLDevice _eglSoftwareDevice = {
9901e04c3fSmrg   .extensions = "EGL_MESA_device_software",
10001e04c3fSmrg   .MESA_device_software = EGL_TRUE,
10101e04c3fSmrg};
10201e04c3fSmrg
10301e04c3fSmrg#ifdef HAVE_LIBDRM
10401e04c3fSmrg/*
10501e04c3fSmrg * Negative value on error, zero if newly added, one if already in list.
10601e04c3fSmrg */
10701e04c3fSmrgstatic int
10801e04c3fSmrg_eglAddDRMDevice(drmDevicePtr device, _EGLDevice **out_dev)
10901e04c3fSmrg{
11001e04c3fSmrg   _EGLDevice *dev;
11101e04c3fSmrg
11201e04c3fSmrg   if ((device->available_nodes & (1 << DRM_NODE_PRIMARY |
11301e04c3fSmrg                                   1 << DRM_NODE_RENDER)) == 0)
11401e04c3fSmrg      return -1;
11501e04c3fSmrg
11601e04c3fSmrg   dev = _eglGlobal.DeviceList;
11701e04c3fSmrg
11801e04c3fSmrg   /* The first device is always software */
11901e04c3fSmrg   assert(dev);
12001e04c3fSmrg   assert(_eglDeviceSupports(dev, _EGL_DEVICE_SOFTWARE));
12101e04c3fSmrg
12201e04c3fSmrg   while (dev->Next) {
12301e04c3fSmrg      dev = dev->Next;
12401e04c3fSmrg
12501e04c3fSmrg      assert(_eglDeviceSupports(dev, _EGL_DEVICE_DRM));
12601e04c3fSmrg      if (drmDevicesEqual(device, dev->device) != 0) {
12701e04c3fSmrg         if (out_dev)
12801e04c3fSmrg            *out_dev = dev;
12901e04c3fSmrg         return 1;
13001e04c3fSmrg      }
13101e04c3fSmrg   }
13201e04c3fSmrg
13301e04c3fSmrg   dev->Next = calloc(1, sizeof(_EGLDevice));
13401e04c3fSmrg   if (!dev->Next) {
13501e04c3fSmrg      if (out_dev)
13601e04c3fSmrg         *out_dev = NULL;
13701e04c3fSmrg      return -1;
13801e04c3fSmrg   }
13901e04c3fSmrg
14001e04c3fSmrg   dev = dev->Next;
14101e04c3fSmrg   dev->extensions = "EGL_EXT_device_drm";
14201e04c3fSmrg   dev->EXT_device_drm = EGL_TRUE;
14301e04c3fSmrg   dev->device = device;
14401e04c3fSmrg
14501e04c3fSmrg   if (out_dev)
14601e04c3fSmrg      *out_dev = dev;
14701e04c3fSmrg
14801e04c3fSmrg   return 0;
14901e04c3fSmrg}
15001e04c3fSmrg#endif
15101e04c3fSmrg
15201e04c3fSmrg/* Adds a device in DeviceList, if needed for the given fd.
15301e04c3fSmrg *
15401e04c3fSmrg * If a software device, the fd is ignored.
15501e04c3fSmrg */
15601e04c3fSmrg_EGLDevice *
15701e04c3fSmrg_eglAddDevice(int fd, bool software)
15801e04c3fSmrg{
15901e04c3fSmrg   _EGLDevice *dev;
16001e04c3fSmrg
16101e04c3fSmrg   mtx_lock(_eglGlobal.Mutex);
16201e04c3fSmrg   dev = _eglGlobal.DeviceList;
16301e04c3fSmrg
16401e04c3fSmrg   /* The first device is always software */
16501e04c3fSmrg   assert(dev);
16601e04c3fSmrg   assert(_eglDeviceSupports(dev, _EGL_DEVICE_SOFTWARE));
16701e04c3fSmrg   if (software)
16801e04c3fSmrg      goto out;
16901e04c3fSmrg
17001e04c3fSmrg#ifdef HAVE_LIBDRM
17101e04c3fSmrg   drmDevicePtr device;
17201e04c3fSmrg
17301e04c3fSmrg   if (drmGetDevice2(fd, 0, &device) != 0) {
17401e04c3fSmrg      dev = NULL;
17501e04c3fSmrg      goto out;
17601e04c3fSmrg   }
17701e04c3fSmrg
17801e04c3fSmrg   /* Device is not added - error or already present */
17901e04c3fSmrg   if (_eglAddDRMDevice(device, &dev) != 0)
18001e04c3fSmrg      drmFreeDevice(&device);
18101e04c3fSmrg#else
18201e04c3fSmrg   _eglLog(_EGL_FATAL, "Driver bug: Built without libdrm, yet looking for HW device");
18301e04c3fSmrg   dev = NULL;
18401e04c3fSmrg#endif
18501e04c3fSmrg
18601e04c3fSmrgout:
18701e04c3fSmrg   mtx_unlock(_eglGlobal.Mutex);
18801e04c3fSmrg   return dev;
18901e04c3fSmrg}
19001e04c3fSmrg
19101e04c3fSmrgEGLBoolean
19201e04c3fSmrg_eglDeviceSupports(_EGLDevice *dev, _EGLDeviceExtension ext)
19301e04c3fSmrg{
19401e04c3fSmrg   switch (ext) {
19501e04c3fSmrg   case _EGL_DEVICE_SOFTWARE:
19601e04c3fSmrg      return dev->MESA_device_software;
19701e04c3fSmrg   case _EGL_DEVICE_DRM:
19801e04c3fSmrg      return dev->EXT_device_drm;
19901e04c3fSmrg   default:
20001e04c3fSmrg      assert(0);
20101e04c3fSmrg      return EGL_FALSE;
20201e04c3fSmrg   };
20301e04c3fSmrg}
20401e04c3fSmrg
20501e04c3fSmrg/* Ideally we'll have an extension which passes the render node,
20601e04c3fSmrg * instead of the card one + magic.
20701e04c3fSmrg *
20801e04c3fSmrg * Then we can move this in _eglQueryDeviceStringEXT below. Until then
20901e04c3fSmrg * keep it separate.
21001e04c3fSmrg */
21101e04c3fSmrgconst char *
21201e04c3fSmrg_eglGetDRMDeviceRenderNode(_EGLDevice *dev)
21301e04c3fSmrg{
21401e04c3fSmrg   return dev->device->nodes[DRM_NODE_RENDER];
21501e04c3fSmrg}
21601e04c3fSmrg
21701e04c3fSmrgEGLBoolean
21801e04c3fSmrg_eglQueryDeviceAttribEXT(_EGLDevice *dev, EGLint attribute,
21901e04c3fSmrg                         EGLAttrib *value)
22001e04c3fSmrg{
22101e04c3fSmrg   switch (attribute) {
22201e04c3fSmrg   default:
22301e04c3fSmrg      _eglError(EGL_BAD_ATTRIBUTE, "eglQueryDeviceStringEXT");
22401e04c3fSmrg      return EGL_FALSE;
22501e04c3fSmrg   }
22601e04c3fSmrg}
22701e04c3fSmrg
22801e04c3fSmrgconst char *
22901e04c3fSmrg_eglQueryDeviceStringEXT(_EGLDevice *dev, EGLint name)
23001e04c3fSmrg{
23101e04c3fSmrg   switch (name) {
23201e04c3fSmrg   case EGL_EXTENSIONS:
23301e04c3fSmrg      return dev->extensions;
23401e04c3fSmrg#ifdef HAVE_LIBDRM
23501e04c3fSmrg   case EGL_DRM_DEVICE_FILE_EXT:
23601e04c3fSmrg      if (_eglDeviceSupports(dev, _EGL_DEVICE_DRM))
23701e04c3fSmrg         return dev->device->nodes[DRM_NODE_PRIMARY];
23801e04c3fSmrg      /* fall through */
23901e04c3fSmrg#endif
24001e04c3fSmrg   default:
24101e04c3fSmrg      _eglError(EGL_BAD_PARAMETER, "eglQueryDeviceStringEXT");
24201e04c3fSmrg      return NULL;
24301e04c3fSmrg   };
24401e04c3fSmrg}
24501e04c3fSmrg
24601e04c3fSmrg/* Do a fresh lookup for devices.
24701e04c3fSmrg *
24801e04c3fSmrg * Walks through the DeviceList, discarding no longer available ones
24901e04c3fSmrg * and adding new ones as applicable.
25001e04c3fSmrg *
25101e04c3fSmrg * Must be called with the global lock held.
25201e04c3fSmrg */
25301e04c3fSmrgstatic int
25401e04c3fSmrg_eglRefreshDeviceList(void)
25501e04c3fSmrg{
25601e04c3fSmrg   MAYBE_UNUSED _EGLDevice *dev;
25701e04c3fSmrg   int count = 0;
25801e04c3fSmrg
25901e04c3fSmrg   dev = _eglGlobal.DeviceList;
26001e04c3fSmrg
26101e04c3fSmrg   /* The first device is always software */
26201e04c3fSmrg   assert(dev);
26301e04c3fSmrg   assert(_eglDeviceSupports(dev, _EGL_DEVICE_SOFTWARE));
26401e04c3fSmrg   count++;
26501e04c3fSmrg
26601e04c3fSmrg#ifdef HAVE_LIBDRM
26701e04c3fSmrg   drmDevicePtr devices[64];
26801e04c3fSmrg   int num_devs, ret;
26901e04c3fSmrg
27001e04c3fSmrg   num_devs = drmGetDevices2(0, devices, ARRAY_SIZE(devices));
27101e04c3fSmrg   for (int i = 0; i < num_devs; i++) {
27201e04c3fSmrg      ret = _eglAddDRMDevice(devices[i], NULL);
27301e04c3fSmrg
27401e04c3fSmrg      /* Device is not added - error or already present */
27501e04c3fSmrg      if (ret != 0)
27601e04c3fSmrg         drmFreeDevice(&devices[i]);
27701e04c3fSmrg
27801e04c3fSmrg      if (ret >= 0)
27901e04c3fSmrg         count++;
28001e04c3fSmrg   }
28101e04c3fSmrg#endif
28201e04c3fSmrg
28301e04c3fSmrg   return count;
28401e04c3fSmrg}
28501e04c3fSmrg
28601e04c3fSmrgEGLBoolean
28701e04c3fSmrg_eglQueryDevicesEXT(EGLint max_devices,
28801e04c3fSmrg                    _EGLDevice **devices,
28901e04c3fSmrg                    EGLint *num_devices)
29001e04c3fSmrg{
29101e04c3fSmrg   _EGLDevice *dev, *devs;
29201e04c3fSmrg   int i = 0, num_devs;
29301e04c3fSmrg
29401e04c3fSmrg   if ((devices && max_devices <= 0) || !num_devices)
29501e04c3fSmrg      return _eglError(EGL_BAD_PARAMETER, "eglQueryDevicesEXT");
29601e04c3fSmrg
29701e04c3fSmrg   mtx_lock(_eglGlobal.Mutex);
29801e04c3fSmrg
29901e04c3fSmrg   num_devs = _eglRefreshDeviceList();
30001e04c3fSmrg   devs = _eglGlobal.DeviceList;
30101e04c3fSmrg
30201e04c3fSmrg   /* bail early if we only care about the count */
30301e04c3fSmrg   if (!devices) {
30401e04c3fSmrg      *num_devices = num_devs;
30501e04c3fSmrg      goto out;
30601e04c3fSmrg   }
30701e04c3fSmrg
30801e04c3fSmrg   *num_devices = MIN2(num_devs, max_devices);
30901e04c3fSmrg
31001e04c3fSmrg   for (i = 0, dev = devs; i < *num_devices; i++) {
31101e04c3fSmrg      devices[i] = dev;
31201e04c3fSmrg      dev = dev->Next;
31301e04c3fSmrg   }
31401e04c3fSmrg
31501e04c3fSmrgout:
31601e04c3fSmrg   mtx_unlock(_eglGlobal.Mutex);
31701e04c3fSmrg
31801e04c3fSmrg   return EGL_TRUE;
31901e04c3fSmrg}
320