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
41 struct _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
55 void
_eglFiniDevice(void)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
84 EGLBoolean
_eglCheckDeviceHandle(EGLDeviceEXT device)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 */
111 static int
_eglAddDRMDevice(drmDevicePtr device,_EGLDevice ** out_dev)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 *
_eglAddDevice(int fd,bool software)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
196 out:
197 mtx_unlock(_eglGlobal.Mutex);
198 return dev;
199 }
200
201 EGLBoolean
_eglDeviceSupports(_EGLDevice * dev,_EGLDeviceExtension ext)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 */
223 const char *
_eglGetDRMDeviceRenderNode(_EGLDevice * dev)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
233 EGLBoolean
_eglQueryDeviceAttribEXT(_EGLDevice * dev,EGLint attribute,EGLAttrib * value)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
244 const char *
_eglQueryDeviceStringEXT(_EGLDevice * dev,EGLint name)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 */
284 static int
_eglRefreshDeviceList(void)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
320 EGLBoolean
_eglQueryDevicesEXT(EGLint max_devices,_EGLDevice ** devices,EGLint * num_devices)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
361 out:
362 mtx_unlock(_eglGlobal.Mutex);
363
364 return EGL_TRUE;
365 }
366