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