1b8e80941Smrg/**************************************************************************
2b8e80941Smrg *
3b8e80941Smrg * Copyright 2015, 2018 Collabora
4b8e80941Smrg * All Rights Reserved.
5b8e80941Smrg *
6b8e80941Smrg * Permission is hereby granted, free of charge, to any person obtaining a
7b8e80941Smrg * copy of this software and associated documentation files (the
8b8e80941Smrg * "Software"), to deal in the Software without restriction, including
9b8e80941Smrg * without limitation the rights to use, copy, modify, merge, publish,
10b8e80941Smrg * distribute, sub license, and/or sell copies of the Software, and to
11b8e80941Smrg * permit persons to whom the Software is furnished to do so, subject to
12b8e80941Smrg * the following conditions:
13b8e80941Smrg *
14b8e80941Smrg * The above copyright notice and this permission notice (including the
15b8e80941Smrg * next paragraph) shall be included in all copies or substantial portions
16b8e80941Smrg * of the Software.
17b8e80941Smrg *
18b8e80941Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19b8e80941Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20b8e80941Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
21b8e80941Smrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22b8e80941Smrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23b8e80941Smrg * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24b8e80941Smrg * DEALINGS IN THE SOFTWARE.
25b8e80941Smrg *
26b8e80941Smrg **************************************************************************/
27b8e80941Smrg
28b8e80941Smrg#ifdef HAVE_LIBDRM
29b8e80941Smrg#include <xf86drm.h>
30b8e80941Smrg#endif
31b8e80941Smrg#include "util/macros.h"
32b8e80941Smrg
33b8e80941Smrg#include "eglcurrent.h"
34b8e80941Smrg#include "egldevice.h"
35b8e80941Smrg#include "egllog.h"
36b8e80941Smrg#include "eglglobals.h"
37b8e80941Smrg#include "egltypedefs.h"
38b8e80941Smrg
39b8e80941Smrg
40b8e80941Smrgstruct _egl_device {
41b8e80941Smrg   _EGLDevice *Next;
42b8e80941Smrg
43b8e80941Smrg   const char *extensions;
44b8e80941Smrg
45b8e80941Smrg   EGLBoolean MESA_device_software;
46b8e80941Smrg   EGLBoolean EXT_device_drm;
47b8e80941Smrg
48b8e80941Smrg#ifdef HAVE_LIBDRM
49b8e80941Smrg   drmDevicePtr device;
50b8e80941Smrg#endif
51b8e80941Smrg};
52b8e80941Smrg
53b8e80941Smrgvoid
54b8e80941Smrg_eglFiniDevice(void)
55b8e80941Smrg{
56b8e80941Smrg   _EGLDevice *dev_list, *dev;
57b8e80941Smrg
58b8e80941Smrg   /* atexit function is called with global mutex locked */
59b8e80941Smrg
60b8e80941Smrg   dev_list = _eglGlobal.DeviceList;
61b8e80941Smrg
62b8e80941Smrg   /* The first device is static allocated SW device */
63b8e80941Smrg   assert(dev_list);
64b8e80941Smrg   assert(_eglDeviceSupports(dev_list, _EGL_DEVICE_SOFTWARE));
65b8e80941Smrg   dev_list = dev_list->Next;
66b8e80941Smrg
67b8e80941Smrg   while (dev_list) {
68b8e80941Smrg      /* pop list head */
69b8e80941Smrg      dev = dev_list;
70b8e80941Smrg      dev_list = dev_list->Next;
71b8e80941Smrg
72b8e80941Smrg#ifdef HAVE_LIBDRM
73b8e80941Smrg      assert(_eglDeviceSupports(dev, _EGL_DEVICE_DRM));
74b8e80941Smrg      drmFreeDevice(&dev->device);
75b8e80941Smrg#endif
76b8e80941Smrg      free(dev);
77b8e80941Smrg   }
78b8e80941Smrg
79b8e80941Smrg   _eglGlobal.DeviceList = NULL;
80b8e80941Smrg}
81b8e80941Smrg
82b8e80941SmrgEGLBoolean
83b8e80941Smrg_eglCheckDeviceHandle(EGLDeviceEXT device)
84b8e80941Smrg{
85b8e80941Smrg   _EGLDevice *cur;
86b8e80941Smrg
87b8e80941Smrg   mtx_lock(_eglGlobal.Mutex);
88b8e80941Smrg   cur = _eglGlobal.DeviceList;
89b8e80941Smrg   while (cur) {
90b8e80941Smrg      if (cur == (_EGLDevice *) device)
91b8e80941Smrg         break;
92b8e80941Smrg      cur = cur->Next;
93b8e80941Smrg   }
94b8e80941Smrg   mtx_unlock(_eglGlobal.Mutex);
95b8e80941Smrg   return (cur != NULL);
96b8e80941Smrg}
97b8e80941Smrg
98b8e80941Smrg_EGLDevice _eglSoftwareDevice = {
99b8e80941Smrg   .extensions = "EGL_MESA_device_software",
100b8e80941Smrg   .MESA_device_software = EGL_TRUE,
101b8e80941Smrg};
102b8e80941Smrg
103b8e80941Smrg#ifdef HAVE_LIBDRM
104b8e80941Smrg/*
105b8e80941Smrg * Negative value on error, zero if newly added, one if already in list.
106b8e80941Smrg */
107b8e80941Smrgstatic int
108b8e80941Smrg_eglAddDRMDevice(drmDevicePtr device, _EGLDevice **out_dev)
109b8e80941Smrg{
110b8e80941Smrg   _EGLDevice *dev;
111b8e80941Smrg   const int wanted_nodes = 1 << DRM_NODE_RENDER | 1 << DRM_NODE_PRIMARY;
112b8e80941Smrg
113b8e80941Smrg   if ((device->available_nodes & wanted_nodes) != wanted_nodes)
114b8e80941Smrg      return -1;
115b8e80941Smrg
116b8e80941Smrg   dev = _eglGlobal.DeviceList;
117b8e80941Smrg
118b8e80941Smrg   /* The first device is always software */
119b8e80941Smrg   assert(dev);
120b8e80941Smrg   assert(_eglDeviceSupports(dev, _EGL_DEVICE_SOFTWARE));
121b8e80941Smrg
122b8e80941Smrg   while (dev->Next) {
123b8e80941Smrg      dev = dev->Next;
124b8e80941Smrg
125b8e80941Smrg      assert(_eglDeviceSupports(dev, _EGL_DEVICE_DRM));
126b8e80941Smrg      if (drmDevicesEqual(device, dev->device) != 0) {
127b8e80941Smrg         if (out_dev)
128b8e80941Smrg            *out_dev = dev;
129b8e80941Smrg         return 1;
130b8e80941Smrg      }
131b8e80941Smrg   }
132b8e80941Smrg
133b8e80941Smrg   dev->Next = calloc(1, sizeof(_EGLDevice));
134b8e80941Smrg   if (!dev->Next) {
135b8e80941Smrg      if (out_dev)
136b8e80941Smrg         *out_dev = NULL;
137b8e80941Smrg      return -1;
138b8e80941Smrg   }
139b8e80941Smrg
140b8e80941Smrg   dev = dev->Next;
141b8e80941Smrg   dev->extensions = "EGL_EXT_device_drm";
142b8e80941Smrg   dev->EXT_device_drm = EGL_TRUE;
143b8e80941Smrg   dev->device = device;
144b8e80941Smrg
145b8e80941Smrg   if (out_dev)
146b8e80941Smrg      *out_dev = dev;
147b8e80941Smrg
148b8e80941Smrg   return 0;
149b8e80941Smrg}
150b8e80941Smrg#endif
151b8e80941Smrg
152b8e80941Smrg/* Adds a device in DeviceList, if needed for the given fd.
153b8e80941Smrg *
154b8e80941Smrg * If a software device, the fd is ignored.
155b8e80941Smrg */
156b8e80941Smrg_EGLDevice *
157b8e80941Smrg_eglAddDevice(int fd, bool software)
158b8e80941Smrg{
159b8e80941Smrg   _EGLDevice *dev;
160b8e80941Smrg
161b8e80941Smrg   mtx_lock(_eglGlobal.Mutex);
162b8e80941Smrg   dev = _eglGlobal.DeviceList;
163b8e80941Smrg
164b8e80941Smrg   /* The first device is always software */
165b8e80941Smrg   assert(dev);
166b8e80941Smrg   assert(_eglDeviceSupports(dev, _EGL_DEVICE_SOFTWARE));
167b8e80941Smrg   if (software)
168b8e80941Smrg      goto out;
169b8e80941Smrg
170b8e80941Smrg#ifdef HAVE_LIBDRM
171b8e80941Smrg   drmDevicePtr device;
172b8e80941Smrg
173b8e80941Smrg   if (drmGetDevice2(fd, 0, &device) != 0) {
174b8e80941Smrg      dev = NULL;
175b8e80941Smrg      goto out;
176b8e80941Smrg   }
177b8e80941Smrg
178b8e80941Smrg   /* Device is not added - error or already present */
179b8e80941Smrg   if (_eglAddDRMDevice(device, &dev) != 0)
180b8e80941Smrg      drmFreeDevice(&device);
181b8e80941Smrg#else
182b8e80941Smrg   _eglLog(_EGL_FATAL, "Driver bug: Built without libdrm, yet looking for HW device");
183b8e80941Smrg   dev = NULL;
184b8e80941Smrg#endif
185b8e80941Smrg
186b8e80941Smrgout:
187b8e80941Smrg   mtx_unlock(_eglGlobal.Mutex);
188b8e80941Smrg   return dev;
189b8e80941Smrg}
190b8e80941Smrg
191b8e80941SmrgEGLBoolean
192b8e80941Smrg_eglDeviceSupports(_EGLDevice *dev, _EGLDeviceExtension ext)
193b8e80941Smrg{
194b8e80941Smrg   switch (ext) {
195b8e80941Smrg   case _EGL_DEVICE_SOFTWARE:
196b8e80941Smrg      return dev->MESA_device_software;
197b8e80941Smrg   case _EGL_DEVICE_DRM:
198b8e80941Smrg      return dev->EXT_device_drm;
199b8e80941Smrg   default:
200b8e80941Smrg      assert(0);
201b8e80941Smrg      return EGL_FALSE;
202b8e80941Smrg   };
203b8e80941Smrg}
204b8e80941Smrg
205b8e80941SmrgEGLBoolean
206b8e80941Smrg_eglQueryDeviceAttribEXT(_EGLDevice *dev, EGLint attribute,
207b8e80941Smrg                         EGLAttrib *value)
208b8e80941Smrg{
209b8e80941Smrg   switch (attribute) {
210b8e80941Smrg   default:
211b8e80941Smrg      _eglError(EGL_BAD_ATTRIBUTE, "eglQueryDeviceStringEXT");
212b8e80941Smrg      return EGL_FALSE;
213b8e80941Smrg   }
214b8e80941Smrg}
215b8e80941Smrg
216b8e80941Smrgconst char *
217b8e80941Smrg_eglQueryDeviceStringEXT(_EGLDevice *dev, EGLint name)
218b8e80941Smrg{
219b8e80941Smrg   switch (name) {
220b8e80941Smrg   case EGL_EXTENSIONS:
221b8e80941Smrg      return dev->extensions;
222b8e80941Smrg#ifdef HAVE_LIBDRM
223b8e80941Smrg   case EGL_DRM_DEVICE_FILE_EXT:
224b8e80941Smrg      if (_eglDeviceSupports(dev, _EGL_DEVICE_DRM))
225b8e80941Smrg         return dev->device->nodes[DRM_NODE_PRIMARY];
226b8e80941Smrg      /* fall through */
227b8e80941Smrg#endif
228b8e80941Smrg   default:
229b8e80941Smrg      _eglError(EGL_BAD_PARAMETER, "eglQueryDeviceStringEXT");
230b8e80941Smrg      return NULL;
231b8e80941Smrg   };
232b8e80941Smrg}
233b8e80941Smrg
234b8e80941Smrg/* Do a fresh lookup for devices.
235b8e80941Smrg *
236b8e80941Smrg * Walks through the DeviceList, discarding no longer available ones
237b8e80941Smrg * and adding new ones as applicable.
238b8e80941Smrg *
239b8e80941Smrg * Must be called with the global lock held.
240b8e80941Smrg */
241b8e80941Smrgstatic int
242b8e80941Smrg_eglRefreshDeviceList(void)
243b8e80941Smrg{
244b8e80941Smrg   MAYBE_UNUSED _EGLDevice *dev;
245b8e80941Smrg   int count = 0;
246b8e80941Smrg
247b8e80941Smrg   dev = _eglGlobal.DeviceList;
248b8e80941Smrg
249b8e80941Smrg   /* The first device is always software */
250b8e80941Smrg   assert(dev);
251b8e80941Smrg   assert(_eglDeviceSupports(dev, _EGL_DEVICE_SOFTWARE));
252b8e80941Smrg   count++;
253b8e80941Smrg
254b8e80941Smrg#ifdef HAVE_LIBDRM
255b8e80941Smrg   drmDevicePtr devices[64];
256b8e80941Smrg   int num_devs, ret;
257b8e80941Smrg
258b8e80941Smrg   num_devs = drmGetDevices2(0, devices, ARRAY_SIZE(devices));
259b8e80941Smrg   for (int i = 0; i < num_devs; i++) {
260b8e80941Smrg      ret = _eglAddDRMDevice(devices[i], NULL);
261b8e80941Smrg
262b8e80941Smrg      /* Device is not added - error or already present */
263b8e80941Smrg      if (ret != 0)
264b8e80941Smrg         drmFreeDevice(&devices[i]);
265b8e80941Smrg
266b8e80941Smrg      if (ret >= 0)
267b8e80941Smrg         count++;
268b8e80941Smrg   }
269b8e80941Smrg#endif
270b8e80941Smrg
271b8e80941Smrg   return count;
272b8e80941Smrg}
273b8e80941Smrg
274b8e80941SmrgEGLBoolean
275b8e80941Smrg_eglQueryDevicesEXT(EGLint max_devices,
276b8e80941Smrg                    _EGLDevice **devices,
277b8e80941Smrg                    EGLint *num_devices)
278b8e80941Smrg{
279b8e80941Smrg   _EGLDevice *dev, *devs;
280b8e80941Smrg   int i = 0, num_devs;
281b8e80941Smrg
282b8e80941Smrg   if ((devices && max_devices <= 0) || !num_devices)
283b8e80941Smrg      return _eglError(EGL_BAD_PARAMETER, "eglQueryDevicesEXT");
284b8e80941Smrg
285b8e80941Smrg   mtx_lock(_eglGlobal.Mutex);
286b8e80941Smrg
287b8e80941Smrg   num_devs = _eglRefreshDeviceList();
288b8e80941Smrg   devs = _eglGlobal.DeviceList;
289b8e80941Smrg
290b8e80941Smrg   /* bail early if we only care about the count */
291b8e80941Smrg   if (!devices) {
292b8e80941Smrg      *num_devices = num_devs;
293b8e80941Smrg      goto out;
294b8e80941Smrg   }
295b8e80941Smrg
296b8e80941Smrg   *num_devices = MIN2(num_devs, max_devices);
297b8e80941Smrg
298b8e80941Smrg   for (i = 0, dev = devs; i < *num_devices; i++) {
299b8e80941Smrg      devices[i] = dev;
300b8e80941Smrg      dev = dev->Next;
301b8e80941Smrg   }
302b8e80941Smrg
303b8e80941Smrgout:
304b8e80941Smrg   mtx_unlock(_eglGlobal.Mutex);
305b8e80941Smrg
306b8e80941Smrg   return EGL_TRUE;
307b8e80941Smrg}
308