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
40 struct _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
53 void
_eglFiniDevice(void)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
82 EGLBoolean
_eglCheckDeviceHandle(EGLDeviceEXT device)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 */
107 static int
_eglAddDRMDevice(drmDevicePtr device,_EGLDevice ** out_dev)108 _eglAddDRMDevice(drmDevicePtr device, _EGLDevice **out_dev)
109 {
110 _EGLDevice *dev;
111 const int wanted_nodes = 1 << DRM_NODE_RENDER | 1 << DRM_NODE_PRIMARY;
112
113 if ((device->available_nodes & wanted_nodes) != wanted_nodes)
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 *
_eglAddDevice(int fd,bool software)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
186 out:
187 mtx_unlock(_eglGlobal.Mutex);
188 return dev;
189 }
190
191 EGLBoolean
_eglDeviceSupports(_EGLDevice * dev,_EGLDeviceExtension ext)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 */
211 const char *
_eglGetDRMDeviceRenderNode(_EGLDevice * dev)212 _eglGetDRMDeviceRenderNode(_EGLDevice *dev)
213 {
214 #ifdef HAVE_LIBDRM
215 return dev->device->nodes[DRM_NODE_RENDER];
216 #else
217 return NULL;
218 #endif
219 }
220
221 EGLBoolean
_eglQueryDeviceAttribEXT(_EGLDevice * dev,EGLint attribute,EGLAttrib * value)222 _eglQueryDeviceAttribEXT(_EGLDevice *dev, EGLint attribute,
223 EGLAttrib *value)
224 {
225 switch (attribute) {
226 default:
227 _eglError(EGL_BAD_ATTRIBUTE, "eglQueryDeviceStringEXT");
228 return EGL_FALSE;
229 }
230 }
231
232 const char *
_eglQueryDeviceStringEXT(_EGLDevice * dev,EGLint name)233 _eglQueryDeviceStringEXT(_EGLDevice *dev, EGLint name)
234 {
235 switch (name) {
236 case EGL_EXTENSIONS:
237 return dev->extensions;
238 #ifdef HAVE_LIBDRM
239 case EGL_DRM_DEVICE_FILE_EXT:
240 if (_eglDeviceSupports(dev, _EGL_DEVICE_DRM))
241 return dev->device->nodes[DRM_NODE_PRIMARY];
242 #endif
243 /* fall through */
244 default:
245 _eglError(EGL_BAD_PARAMETER, "eglQueryDeviceStringEXT");
246 return NULL;
247 };
248 }
249
250 /* Do a fresh lookup for devices.
251 *
252 * Walks through the DeviceList, discarding no longer available ones
253 * and adding new ones as applicable.
254 *
255 * Must be called with the global lock held.
256 */
257 static int
_eglRefreshDeviceList(void)258 _eglRefreshDeviceList(void)
259 {
260 ASSERTED _EGLDevice *dev;
261 int count = 0;
262
263 dev = _eglGlobal.DeviceList;
264
265 /* The first device is always software */
266 assert(dev);
267 assert(_eglDeviceSupports(dev, _EGL_DEVICE_SOFTWARE));
268 count++;
269
270 #ifdef HAVE_LIBDRM
271 drmDevicePtr devices[64];
272 int num_devs, ret;
273
274 num_devs = drmGetDevices2(0, devices, ARRAY_SIZE(devices));
275 for (int i = 0; i < num_devs; i++) {
276 ret = _eglAddDRMDevice(devices[i], NULL);
277
278 /* Device is not added - error or already present */
279 if (ret != 0)
280 drmFreeDevice(&devices[i]);
281
282 if (ret >= 0)
283 count++;
284 }
285 #endif
286
287 return count;
288 }
289
290 EGLBoolean
_eglQueryDevicesEXT(EGLint max_devices,_EGLDevice ** devices,EGLint * num_devices)291 _eglQueryDevicesEXT(EGLint max_devices,
292 _EGLDevice **devices,
293 EGLint *num_devices)
294 {
295 _EGLDevice *dev, *devs;
296 int i = 0, num_devs;
297
298 if ((devices && max_devices <= 0) || !num_devices)
299 return _eglError(EGL_BAD_PARAMETER, "eglQueryDevicesEXT");
300
301 mtx_lock(_eglGlobal.Mutex);
302
303 num_devs = _eglRefreshDeviceList();
304 devs = _eglGlobal.DeviceList;
305
306 /* bail early if we only care about the count */
307 if (!devices) {
308 *num_devices = num_devs;
309 goto out;
310 }
311
312 /* Push the first device (the software one) to the end of the list.
313 * Sending it to the user only if they've requested the full list.
314 *
315 * By default, the user is likely to pick the first device so having the
316 * software (aka least performant) one is not a good idea.
317 */
318 *num_devices = MIN2(num_devs, max_devices);
319
320 for (i = 0, dev = devs->Next; dev && i < max_devices; i++) {
321 devices[i] = dev;
322 dev = dev->Next;
323 }
324
325 /* User requested the full device list, add the sofware device. */
326 if (max_devices >= num_devs) {
327 assert(_eglDeviceSupports(devs, _EGL_DEVICE_SOFTWARE));
328 devices[num_devs - 1] = devs;
329 }
330
331 out:
332 mtx_unlock(_eglGlobal.Mutex);
333
334 return EGL_TRUE;
335 }
336