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