egl: Make _eglUnloadDrivers no-op on Windows.
[mesa.git] / src / egl / main / egldriver.c
1 /**
2 * Functions for choosing and opening/loading device drivers.
3 */
4
5
6 #include <assert.h>
7 #include <string.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10
11 #include "eglstring.h"
12 #include "eglconfig.h"
13 #include "eglcontext.h"
14 #include "egldefines.h"
15 #include "egldisplay.h"
16 #include "egldriver.h"
17 #include "eglglobals.h"
18 #include "egllog.h"
19 #include "eglmisc.h"
20 #include "eglmode.h"
21 #include "eglscreen.h"
22 #include "eglstring.h"
23 #include "eglsurface.h"
24 #include "eglimage.h"
25
26 #if defined(_EGL_OS_UNIX)
27 #include <dlfcn.h>
28 #include <sys/types.h>
29 #include <dirent.h>
30 #include <unistd.h>
31 #endif
32
33
34 /**
35 * Wrappers for dlopen/dlclose()
36 */
37 #if defined(_EGL_OS_WINDOWS)
38
39
40 /* XXX Need to decide how to do dynamic name lookup on Windows */
41 static const char *DefaultDriverNames[] = {
42 "egl_gallium"
43 };
44
45 typedef HMODULE lib_handle;
46
47 static HMODULE
48 open_library(const char *filename)
49 {
50 return LoadLibrary(filename);
51 }
52
53 static void
54 close_library(HMODULE lib)
55 {
56 FreeLibrary(lib);
57 }
58
59
60 static const char *
61 library_suffix(void)
62 {
63 return ".dll";
64 }
65
66
67 #elif defined(_EGL_OS_UNIX)
68
69
70 static const char *DefaultDriverNames[] = {
71 "egl_gallium",
72 "egl_dri2",
73 "egl_glx"
74 };
75
76 typedef void * lib_handle;
77
78 static void *
79 open_library(const char *filename)
80 {
81 return dlopen(filename, RTLD_LAZY);
82 }
83
84 static void
85 close_library(void *lib)
86 {
87 dlclose(lib);
88 }
89
90
91 static const char *
92 library_suffix(void)
93 {
94 return ".so";
95 }
96
97
98 #endif
99
100
101 /**
102 * Open the named driver and find its bootstrap function: _eglMain().
103 */
104 static _EGLMain_t
105 _eglOpenLibrary(const char *driverPath, lib_handle *handle)
106 {
107 lib_handle lib;
108 _EGLMain_t mainFunc = NULL;
109 const char *error = "unknown error";
110
111 assert(driverPath);
112
113 _eglLog(_EGL_DEBUG, "dlopen(%s)", driverPath);
114 lib = open_library(driverPath);
115
116 #if defined(_EGL_OS_WINDOWS)
117 /* XXX untested */
118 if (lib)
119 mainFunc = (_EGLMain_t) GetProcAddress(lib, "_eglMain");
120 #elif defined(_EGL_OS_UNIX)
121 if (lib) {
122 union {
123 _EGLMain_t func;
124 void *ptr;
125 } tmp = { NULL };
126 /* direct cast gives a warning when compiled with -pedantic */
127 tmp.ptr = dlsym(lib, "_eglMain");
128 mainFunc = tmp.func;
129 if (!mainFunc)
130 error = dlerror();
131 }
132 else {
133 error = dlerror();
134 }
135 #endif
136
137 if (!lib) {
138 _eglLog(_EGL_WARNING, "Could not open driver %s (%s)",
139 driverPath, error);
140 if (!getenv("EGL_DRIVER"))
141 _eglLog(_EGL_WARNING,
142 "The driver can be overridden by setting EGL_DRIVER");
143 return NULL;
144 }
145
146 if (!mainFunc) {
147 _eglLog(_EGL_WARNING, "_eglMain not found in %s (%s)",
148 driverPath, error);
149 if (lib)
150 close_library(lib);
151 return NULL;
152 }
153
154 *handle = lib;
155 return mainFunc;
156 }
157
158
159 /**
160 * Load the named driver.
161 */
162 static _EGLDriver *
163 _eglLoadDriver(const char *path, const char *args)
164 {
165 _EGLMain_t mainFunc;
166 lib_handle lib;
167 _EGLDriver *drv = NULL;
168
169 mainFunc = _eglOpenLibrary(path, &lib);
170 if (!mainFunc)
171 return NULL;
172
173 drv = mainFunc(args);
174 if (!drv) {
175 if (lib)
176 close_library(lib);
177 return NULL;
178 }
179
180 if (!drv->Name) {
181 _eglLog(_EGL_WARNING, "Driver loaded from %s has no name", path);
182 drv->Name = "UNNAMED";
183 }
184
185 drv->Path = _eglstrdup(path);
186 drv->Args = (args) ? _eglstrdup(args) : NULL;
187 if (!drv->Path || (args && !drv->Args)) {
188 if (drv->Path)
189 free((char *) drv->Path);
190 if (drv->Args)
191 free((char *) drv->Args);
192 drv->Unload(drv);
193 if (lib)
194 close_library(lib);
195 return NULL;
196 }
197
198 drv->LibHandle = lib;
199
200 return drv;
201 }
202
203
204 /**
205 * Match a display to a preloaded driver.
206 *
207 * The matching is done by finding the driver with the highest score.
208 */
209 _EGLDriver *
210 _eglMatchDriver(_EGLDisplay *dpy)
211 {
212 _EGLDriver *best_drv = NULL;
213 EGLint best_score = -1, i;
214
215 /*
216 * this function is called after preloading and the drivers never change
217 * after preloading.
218 */
219 for (i = 0; i < _eglGlobal.NumDrivers; i++) {
220 _EGLDriver *drv = _eglGlobal.Drivers[i];
221 EGLint score;
222
223 score = (drv->Probe) ? drv->Probe(drv, dpy) : 0;
224 if (score > best_score) {
225 if (best_drv) {
226 _eglLog(_EGL_DEBUG, "driver %s has higher score than %s",
227 drv->Name, best_drv->Name);
228 }
229
230 best_drv = drv;
231 best_score = score;
232 /* perfect match */
233 if (score >= 100)
234 break;
235 }
236 }
237
238 return best_drv;
239 }
240
241
242 /**
243 * A loader function for use with _eglPreloadForEach. The loader data is the
244 * filename of the driver. This function stops on the first valid driver.
245 */
246 static EGLBoolean
247 _eglLoaderFile(const char *dir, size_t len, void *loader_data)
248 {
249 _EGLDriver *drv;
250 char path[1024];
251 const char *filename = (const char *) loader_data;
252 size_t flen = strlen(filename);
253
254 /* make a full path */
255 if (len + flen + 2 > sizeof(path))
256 return EGL_TRUE;
257 if (len) {
258 memcpy(path, dir, len);
259 path[len++] = '/';
260 }
261 memcpy(path + len, filename, flen);
262 len += flen;
263 path[len] = '\0';
264
265 if (library_suffix() == NULL || strstr(path, library_suffix()))
266 drv = _eglLoadDriver(path, NULL);
267 else {
268 const char *suffix = library_suffix();
269 size_t slen = strlen(suffix);
270 const char *p;
271 EGLBoolean need_suffix;
272
273 p = filename + flen - slen;
274 need_suffix = (p < filename || strcmp(p, suffix) != 0);
275 if (need_suffix && len + slen + 1 <= sizeof(path)) {
276 strcpy(path + len, suffix);
277 drv = _eglLoadDriver(path, NULL);
278 } else {
279 drv = NULL;
280 }
281 }
282 if (!drv)
283 return EGL_TRUE;
284
285 /* remember the driver and stop */
286 _eglGlobal.Drivers[_eglGlobal.NumDrivers++] = drv;
287 return EGL_FALSE;
288 }
289
290
291 /**
292 * Run the preload function on each driver directory and return the number of
293 * drivers loaded.
294 *
295 * The process may end prematurely if the callback function returns false.
296 */
297 static EGLint
298 _eglPreloadForEach(const char *search_path,
299 EGLBoolean (*loader)(const char *, size_t, void *),
300 void *loader_data)
301 {
302 const char *cur, *next;
303 size_t len;
304 EGLint num_drivers = _eglGlobal.NumDrivers;
305
306 cur = search_path;
307 while (cur) {
308 next = strchr(cur, ':');
309 len = (next) ? next - cur : strlen(cur);
310
311 if (!loader(cur, len, loader_data))
312 break;
313
314 cur = (next) ? next + 1 : NULL;
315 }
316
317 return (_eglGlobal.NumDrivers - num_drivers);
318 }
319
320
321 /**
322 * Return a list of colon-separated driver directories.
323 */
324 static const char *
325 _eglGetSearchPath(void)
326 {
327 static const char *search_path;
328
329 #if defined(_EGL_OS_UNIX) || defined(_EGL_OS_WINDOWS)
330 if (!search_path) {
331 static char buffer[1024];
332 const char *p;
333 int ret;
334
335 p = getenv("EGL_DRIVERS_PATH");
336 #if defined(_EGL_OS_UNIX)
337 if (p && (geteuid() != getuid() || getegid() != getgid())) {
338 _eglLog(_EGL_DEBUG,
339 "ignore EGL_DRIVERS_PATH for setuid/setgid binaries");
340 p = NULL;
341 }
342 #endif /* _EGL_OS_UNIX */
343
344 if (p) {
345 ret = _eglsnprintf(buffer, sizeof(buffer),
346 "%s:%s", p, _EGL_DRIVER_SEARCH_DIR);
347 if (ret > 0 && ret < sizeof(buffer))
348 search_path = buffer;
349 }
350 }
351 if (!search_path)
352 search_path = _EGL_DRIVER_SEARCH_DIR;
353 #else
354 search_path = "";
355 #endif
356
357 return search_path;
358 }
359
360
361 /**
362 * Preload a user driver.
363 *
364 * A user driver can be specified by EGL_DRIVER.
365 */
366 static EGLBoolean
367 _eglPreloadUserDriver(void)
368 {
369 const char *search_path = _eglGetSearchPath();
370 char *env;
371
372 env = getenv("EGL_DRIVER");
373 #if defined(_EGL_OS_UNIX)
374 if (env && strchr(env, '/')) {
375 search_path = "";
376 if ((geteuid() != getuid() || getegid() != getgid())) {
377 _eglLog(_EGL_DEBUG,
378 "ignore EGL_DRIVER for setuid/setgid binaries");
379 env = NULL;
380 }
381 }
382 #endif /* _EGL_OS_UNIX */
383 if (!env)
384 return EGL_FALSE;
385
386 if (!_eglPreloadForEach(search_path, _eglLoaderFile, (void *) env)) {
387 _eglLog(_EGL_WARNING, "EGL_DRIVER is set to an invalid driver");
388 return EGL_FALSE;
389 }
390
391 return EGL_TRUE;
392 }
393
394
395 /**
396 * Preload drivers.
397 *
398 * This function loads the driver modules and creates the corresponding
399 * _EGLDriver objects.
400 */
401 EGLBoolean
402 _eglPreloadDrivers(void)
403 {
404 EGLBoolean loaded;
405
406 /* protect the preloading process */
407 _eglLockMutex(_eglGlobal.Mutex);
408
409 /* already preloaded */
410 if (_eglGlobal.NumDrivers) {
411 _eglUnlockMutex(_eglGlobal.Mutex);
412 return EGL_TRUE;
413 }
414
415 loaded = _eglPreloadUserDriver();
416
417 _eglUnlockMutex(_eglGlobal.Mutex);
418
419 return loaded;
420 }
421
422 /**
423 * Unload preloaded drivers.
424 */
425 void
426 _eglUnloadDrivers(void)
427 {
428 #if defined(_EGL_OS_UNIX)
429 EGLint i;
430
431 /* this is called at atexit time */
432 for (i = 0; i < _eglGlobal.NumDrivers; i++) {
433 _EGLDriver *drv = _eglGlobal.Drivers[i];
434 lib_handle handle = drv->LibHandle;
435
436 if (drv->Path)
437 free((char *) drv->Path);
438 if (drv->Args)
439 free((char *) drv->Args);
440
441 /* destroy driver */
442 if (drv->Unload)
443 drv->Unload(drv);
444
445 if (handle)
446 close_library(handle);
447 _eglGlobal.Drivers[i] = NULL;
448 }
449
450 _eglGlobal.NumDrivers = 0;
451 #elif defined(_EGL_OS_WINDOWS)
452 /* XXX Windows unloads DLLs before atexit */
453 #endif
454 }
455
456 _EGLDriver *
457 _eglLoadDefaultDriver(EGLDisplay dpy, EGLint *major, EGLint *minor)
458 {
459 _EGLDriver *drv = NULL;
460 EGLBoolean ok;
461 int i;
462
463 _eglLockMutex(_eglGlobal.Mutex);
464
465 for (i = 0; i < ARRAY_SIZE(DefaultDriverNames); i++) {
466 _eglPreloadForEach(_eglGetSearchPath(),
467 _eglLoaderFile, (void *) DefaultDriverNames[i]);
468 if (_eglGlobal.NumDrivers == 0)
469 continue;
470 drv = _eglGlobal.Drivers[0];
471
472 _eglUnlockMutex(_eglGlobal.Mutex);
473 ok = drv->API.Initialize(drv, dpy, major, minor);
474 _eglLockMutex(_eglGlobal.Mutex);
475 if (ok)
476 break;
477
478 _eglUnloadDrivers();
479 }
480
481 _eglUnlockMutex(_eglGlobal.Mutex);
482
483 return _eglGlobal.NumDrivers > 0 ? drv : NULL;
484 }
485
486
487 /**
488 * Plug all the available fallback routines into the given driver's
489 * dispatch table.
490 */
491 void
492 _eglInitDriverFallbacks(_EGLDriver *drv)
493 {
494 /* If a pointer is set to NULL, then the device driver _really_ has
495 * to implement it.
496 */
497 drv->API.Initialize = NULL;
498 drv->API.Terminate = NULL;
499
500 drv->API.GetConfigs = _eglGetConfigs;
501 drv->API.ChooseConfig = _eglChooseConfig;
502 drv->API.GetConfigAttrib = _eglGetConfigAttrib;
503
504 drv->API.CreateContext = _eglCreateContext;
505 drv->API.DestroyContext = _eglDestroyContext;
506 drv->API.MakeCurrent = _eglMakeCurrent;
507 drv->API.QueryContext = _eglQueryContext;
508
509 drv->API.CreateWindowSurface = _eglCreateWindowSurface;
510 drv->API.CreatePixmapSurface = _eglCreatePixmapSurface;
511 drv->API.CreatePbufferSurface = _eglCreatePbufferSurface;
512 drv->API.DestroySurface = _eglDestroySurface;
513 drv->API.QuerySurface = _eglQuerySurface;
514 drv->API.SurfaceAttrib = _eglSurfaceAttrib;
515 drv->API.BindTexImage = _eglBindTexImage;
516 drv->API.ReleaseTexImage = _eglReleaseTexImage;
517 drv->API.SwapInterval = _eglSwapInterval;
518 drv->API.SwapBuffers = _eglSwapBuffers;
519 drv->API.CopyBuffers = _eglCopyBuffers;
520
521 drv->API.QueryString = _eglQueryString;
522 drv->API.WaitClient = _eglWaitClient;
523 drv->API.WaitNative = _eglWaitNative;
524
525 #ifdef EGL_MESA_screen_surface
526 drv->API.ChooseModeMESA = _eglChooseModeMESA;
527 drv->API.GetModesMESA = _eglGetModesMESA;
528 drv->API.GetModeAttribMESA = _eglGetModeAttribMESA;
529 drv->API.GetScreensMESA = _eglGetScreensMESA;
530 drv->API.CreateScreenSurfaceMESA = _eglCreateScreenSurfaceMESA;
531 drv->API.ShowScreenSurfaceMESA = _eglShowScreenSurfaceMESA;
532 drv->API.ScreenPositionMESA = _eglScreenPositionMESA;
533 drv->API.QueryScreenMESA = _eglQueryScreenMESA;
534 drv->API.QueryScreenSurfaceMESA = _eglQueryScreenSurfaceMESA;
535 drv->API.QueryScreenModeMESA = _eglQueryScreenModeMESA;
536 drv->API.QueryModeStringMESA = _eglQueryModeStringMESA;
537 #endif /* EGL_MESA_screen_surface */
538
539 #ifdef EGL_VERSION_1_2
540 drv->API.CreatePbufferFromClientBuffer = _eglCreatePbufferFromClientBuffer;
541 #endif /* EGL_VERSION_1_2 */
542
543 #ifdef EGL_KHR_image_base
544 drv->API.CreateImageKHR = _eglCreateImageKHR;
545 drv->API.DestroyImageKHR = _eglDestroyImageKHR;
546 #endif /* EGL_KHR_image_base */
547 }
548
549
550 /**
551 * Invoke a callback function on each EGL search path.
552 *
553 * The first argument of the callback function is the name of the search path.
554 * The second argument is the length of the name.
555 */
556 void
557 _eglSearchPathForEach(EGLBoolean (*callback)(const char *, size_t, void *),
558 void *callback_data)
559 {
560 const char *search_path = _eglGetSearchPath();
561 _eglPreloadForEach(search_path, callback, callback_data);
562 }