xref: /third_party/mesa3d/src/egl/main/egldevice.c (revision bf215546)
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