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 "eglglobals.h"
37 #include "egllog.h"
38 #include "egltypedefs.h"
39
40 struct _egl_device {
41 _EGLDevice *Next;
42
43 const char *extensions;
44
45 EGLBoolean MESA_device_software;
46 EGLBoolean EXT_device_drm;
47 EGLBoolean EXT_device_drm_render_node;
48
49 #ifdef HAVE_LIBDRM
50 drmDevicePtr device;
51 #endif
52 };
53
54 void
_eglFiniDevice(void)55 _eglFiniDevice(void)
56 {
57 _EGLDevice *dev_list, *dev;
58
59 /* atexit function is called with global mutex locked */
60
61 dev_list = _eglGlobal.DeviceList;
62
63 /* The first device is static allocated SW device */
64 assert(dev_list);
65 assert(_eglDeviceSupports(dev_list, _EGL_DEVICE_SOFTWARE));
66 dev_list = dev_list->Next;
67
68 while (dev_list) {
69 /* pop list head */
70 dev = dev_list;
71 dev_list = dev_list->Next;
72
73 #ifdef HAVE_LIBDRM
74 assert(_eglDeviceSupports(dev, _EGL_DEVICE_DRM));
75 drmFreeDevice(&dev->device);
76 #endif
77 free(dev);
78 }
79
80 _eglGlobal.DeviceList = NULL;
81 }
82
83 EGLBoolean
_eglCheckDeviceHandle(EGLDeviceEXT device)84 _eglCheckDeviceHandle(EGLDeviceEXT device)
85 {
86 _EGLDevice *cur;
87
88 simple_mtx_lock(_eglGlobal.Mutex);
89 cur = _eglGlobal.DeviceList;
90 while (cur) {
91 if (cur == (_EGLDevice *)device)
92 break;
93 cur = cur->Next;
94 }
95 simple_mtx_unlock(_eglGlobal.Mutex);
96 return (cur != NULL);
97 }
98
99 _EGLDevice _eglSoftwareDevice = {
100 /* TODO: EGL_EXT_device_drm support for KMS + llvmpipe */
101 .extensions = "EGL_MESA_device_software EGL_EXT_device_drm_render_node",
102 .MESA_device_software = EGL_TRUE,
103 .EXT_device_drm_render_node = EGL_TRUE,
104 };
105
106 #ifdef HAVE_LIBDRM
107 /*
108 * Negative value on error, zero if newly added, one if already in list.
109 */
110 static int
_eglAddDRMDevice(drmDevicePtr device)111 _eglAddDRMDevice(drmDevicePtr device)
112 {
113 _EGLDevice *dev;
114
115 assert(device->available_nodes & ((1 << DRM_NODE_RENDER)));
116
117 /* TODO: uncomment this assert, which is a sanity check.
118 *
119 * assert(device->available_nodes & ((1 << DRM_NODE_PRIMARY)));
120 *
121 * DRM shim does not expose a primary node, so the CI would fail if we had
122 * this assert. DRM shim is being used to run shader-db. We need to
123 * investigate what should be done (probably fixing DRM shim).
124 */
125
126 dev = _eglGlobal.DeviceList;
127
128 /* The first device is always software */
129 assert(dev);
130 assert(_eglDeviceSupports(dev, _EGL_DEVICE_SOFTWARE));
131
132 while (dev->Next) {
133 dev = dev->Next;
134
135 assert(_eglDeviceSupports(dev, _EGL_DEVICE_DRM));
136 if (drmDevicesEqual(device, dev->device) != 0)
137 return 1;
138 }
139
140 dev->Next = calloc(1, sizeof(_EGLDevice));
141 if (!dev->Next)
142 return -1;
143
144 dev = dev->Next;
145 dev->extensions = "EGL_EXT_device_drm EGL_EXT_device_drm_render_node";
146 dev->EXT_device_drm = EGL_TRUE;
147 dev->EXT_device_drm_render_node = EGL_TRUE;
148 dev->device = device;
149
150 return 0;
151 }
152 #endif
153
154 /* Finds a device in DeviceList, for the given fd.
155 *
156 * The fd must be of a render-capable device, as there are only render-capable
157 * devices in DeviceList.
158 *
159 * If a software device, the fd is ignored.
160 */
161 _EGLDevice *
_eglFindDevice(int fd,bool software)162 _eglFindDevice(int fd, bool software)
163 {
164 _EGLDevice *dev;
165
166 simple_mtx_lock(_eglGlobal.Mutex);
167 dev = _eglGlobal.DeviceList;
168
169 /* The first device is always software */
170 assert(dev);
171 assert(_eglDeviceSupports(dev, _EGL_DEVICE_SOFTWARE));
172 if (software)
173 goto out;
174
175 #ifdef HAVE_LIBDRM
176 drmDevicePtr device;
177
178 if (drmGetDevice2(fd, 0, &device) != 0) {
179 dev = NULL;
180 goto out;
181 }
182
183 while (dev->Next) {
184 dev = dev->Next;
185
186 if (_eglDeviceSupports(dev, _EGL_DEVICE_DRM) &&
187 drmDevicesEqual(device, dev->device) != 0) {
188 goto cleanup_drm;
189 }
190 }
191
192 /* Couldn't find an EGLDevice for the device. */
193 dev = NULL;
194
195 cleanup_drm:
196 drmFreeDevice(&device);
197
198 #else
199 _eglLog(_EGL_FATAL,
200 "Driver bug: Built without libdrm, yet looking for HW device");
201 dev = NULL;
202 #endif
203
204 out:
205 simple_mtx_unlock(_eglGlobal.Mutex);
206 return dev;
207 }
208
209 #ifdef HAVE_LIBDRM
210 drmDevicePtr
_eglDeviceDrm(_EGLDevice * dev)211 _eglDeviceDrm(_EGLDevice *dev)
212 {
213 if (!dev)
214 return NULL;
215
216 return dev->device;
217 }
218 #endif
219
220 _EGLDevice *
_eglDeviceNext(_EGLDevice * dev)221 _eglDeviceNext(_EGLDevice *dev)
222 {
223 if (!dev)
224 return NULL;
225
226 return dev->Next;
227 }
228
229 EGLBoolean
_eglDeviceSupports(_EGLDevice * dev,_EGLDeviceExtension ext)230 _eglDeviceSupports(_EGLDevice *dev, _EGLDeviceExtension ext)
231 {
232 switch (ext) {
233 case _EGL_DEVICE_SOFTWARE:
234 return dev->MESA_device_software;
235 case _EGL_DEVICE_DRM:
236 return dev->EXT_device_drm;
237 case _EGL_DEVICE_DRM_RENDER_NODE:
238 return dev->EXT_device_drm_render_node;
239 default:
240 assert(0);
241 return EGL_FALSE;
242 };
243 }
244
245 EGLBoolean
_eglQueryDeviceAttribEXT(_EGLDevice * dev,EGLint attribute,EGLAttrib * value)246 _eglQueryDeviceAttribEXT(_EGLDevice *dev, EGLint attribute, EGLAttrib *value)
247 {
248 switch (attribute) {
249 default:
250 _eglError(EGL_BAD_ATTRIBUTE, "eglQueryDeviceAttribEXT");
251 return EGL_FALSE;
252 }
253 }
254
255 const char *
_eglQueryDeviceStringEXT(_EGLDevice * dev,EGLint name)256 _eglQueryDeviceStringEXT(_EGLDevice *dev, EGLint name)
257 {
258 switch (name) {
259 case EGL_EXTENSIONS:
260 return dev->extensions;
261 case EGL_DRM_DEVICE_FILE_EXT:
262 if (!_eglDeviceSupports(dev, _EGL_DEVICE_DRM))
263 break;
264 #ifdef HAVE_LIBDRM
265 return dev->device->nodes[DRM_NODE_PRIMARY];
266 #else
267 /* This should never happen: we don't yet support EGL_DEVICE_DRM for the
268 * software device, and physical devices are only exposed when libdrm is
269 * available. */
270 assert(0);
271 break;
272 #endif
273 case EGL_DRM_RENDER_NODE_FILE_EXT:
274 if (!_eglDeviceSupports(dev, _EGL_DEVICE_DRM_RENDER_NODE))
275 break;
276 #ifdef HAVE_LIBDRM
277 /* EGLDevice represents a software device, so no render node
278 * should be advertised. */
279 if (_eglDeviceSupports(dev, _EGL_DEVICE_SOFTWARE))
280 return NULL;
281 /* We create EGLDevice's only for render capable devices. */
282 assert(dev->device->available_nodes & (1 << DRM_NODE_RENDER));
283 return dev->device->nodes[DRM_NODE_RENDER];
284 #else
285 /* Physical devices are only exposed when libdrm is available. */
286 assert(_eglDeviceSupports(dev, _EGL_DEVICE_SOFTWARE));
287 return NULL;
288 #endif
289 }
290 _eglError(EGL_BAD_PARAMETER, "eglQueryDeviceStringEXT");
291 return NULL;
292 }
293
294 /* Do a fresh lookup for devices.
295 *
296 * Walks through the DeviceList, discarding no longer available ones
297 * and adding new ones as applicable.
298 *
299 * Must be called with the global lock held.
300 */
301 int
_eglDeviceRefreshList(void)302 _eglDeviceRefreshList(void)
303 {
304 ASSERTED _EGLDevice *dev;
305 int count = 0;
306
307 dev = _eglGlobal.DeviceList;
308
309 /* The first device is always software */
310 assert(dev);
311 assert(_eglDeviceSupports(dev, _EGL_DEVICE_SOFTWARE));
312 count++;
313
314 #ifdef HAVE_LIBDRM
315 drmDevicePtr devices[64];
316 int num_devs, ret;
317
318 num_devs = drmGetDevices2(0, devices, ARRAY_SIZE(devices));
319 for (int i = 0; i < num_devs; i++) {
320 if (!(devices[i]->available_nodes & (1 << DRM_NODE_RENDER))) {
321 drmFreeDevice(&devices[i]);
322 continue;
323 }
324
325 ret = _eglAddDRMDevice(devices[i]);
326
327 /* Device is not added - error or already present */
328 if (ret != 0)
329 drmFreeDevice(&devices[i]);
330
331 if (ret >= 0)
332 count++;
333 }
334 #endif
335
336 return count;
337 }
338
339 EGLBoolean
_eglQueryDevicesEXT(EGLint max_devices,_EGLDevice ** devices,EGLint * num_devices)340 _eglQueryDevicesEXT(EGLint max_devices, _EGLDevice **devices,
341 EGLint *num_devices)
342 {
343 _EGLDevice *dev, *devs, *swrast;
344 int i = 0, num_devs;
345
346 if ((devices && max_devices <= 0) || !num_devices)
347 return _eglError(EGL_BAD_PARAMETER, "eglQueryDevicesEXT");
348
349 simple_mtx_lock(_eglGlobal.Mutex);
350
351 num_devs = _eglDeviceRefreshList();
352 devs = _eglGlobal.DeviceList;
353
354 #ifdef HAVE_SWRAST
355 swrast = devs;
356 #else
357 swrast = NULL;
358 num_devs--;
359 #endif
360
361 /* The first device is swrast. Start with the non-swrast device. */
362 devs = devs->Next;
363
364 /* bail early if we only care about the count */
365 if (!devices) {
366 *num_devices = num_devs;
367 goto out;
368 }
369
370 *num_devices = MIN2(num_devs, max_devices);
371
372 /* Add non-swrast devices first and add swrast last.
373 *
374 * By default, the user is likely to pick the first device so having the
375 * software (aka least performant) one is not a good idea.
376 */
377 for (i = 0, dev = devs; dev && i < max_devices; i++) {
378 devices[i] = dev;
379 dev = dev->Next;
380 }
381
382 /* User requested the full device list, add the software device. */
383 if (max_devices >= num_devs && swrast) {
384 assert(_eglDeviceSupports(swrast, _EGL_DEVICE_SOFTWARE));
385 devices[num_devs - 1] = swrast;
386 }
387
388 out:
389 simple_mtx_unlock(_eglGlobal.Mutex);
390
391 return EGL_TRUE;
392 }
393