egl: Revisit global data locking.
[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 #include "eglconfig.h"
11 #include "eglcontext.h"
12 #include "egldefines.h"
13 #include "egldisplay.h"
14 #include "egldriver.h"
15 #include "eglglobals.h"
16 #include "egllog.h"
17 #include "eglmisc.h"
18 #include "eglmode.h"
19 #include "eglscreen.h"
20 #include "eglstring.h"
21 #include "eglsurface.h"
22 #include "eglimage.h"
23
24 #if defined(_EGL_PLATFORM_POSIX)
25 #include <dlfcn.h>
26 #include <sys/types.h>
27 #include <dirent.h>
28 #include <unistd.h>
29 #endif
30
31
32 /**
33 * Wrappers for dlopen/dlclose()
34 */
35 #if defined(_EGL_PLATFORM_WINDOWS)
36
37
38 /* XXX Need to decide how to do dynamic name lookup on Windows */
39 static const char DefaultDriverName[] = "TBD";
40
41 typedef HMODULE lib_handle;
42
43 static HMODULE
44 open_library(const char *filename)
45 {
46 return LoadLibrary(filename);
47 }
48
49 static void
50 close_library(HMODULE lib)
51 {
52 FreeLibrary(lib);
53 }
54
55
56 static const char *
57 library_suffix(void)
58 {
59 return ".dll";
60 }
61
62
63 #elif defined(_EGL_PLATFORM_POSIX)
64
65
66 static const char DefaultDriverName[] = "egl_glx";
67
68 typedef void * lib_handle;
69
70 static void *
71 open_library(const char *filename)
72 {
73 return dlopen(filename, RTLD_LAZY);
74 }
75
76 static void
77 close_library(void *lib)
78 {
79 dlclose(lib);
80 }
81
82
83 static const char *
84 library_suffix(void)
85 {
86 return ".so";
87 }
88
89
90 #else /* _EGL_PLATFORM_NO_OS */
91
92
93 static const char DefaultDriverName[] = "builtin";
94
95 typedef void *lib_handle;
96
97 static INLINE void *
98 open_library(const char *filename)
99 {
100 return (void *) filename;
101 }
102
103 static INLINE void
104 close_library(void *lib)
105 {
106 }
107
108
109 static const char *
110 library_suffix(void)
111 {
112 return NULL;
113 }
114
115
116 #endif
117
118
119 #define NUM_PROBE_CACHE_SLOTS 8
120 static struct {
121 EGLint keys[NUM_PROBE_CACHE_SLOTS];
122 const void *values[NUM_PROBE_CACHE_SLOTS];
123 } _eglProbeCache;
124
125
126 /**
127 * Open the named driver and find its bootstrap function: _eglMain().
128 */
129 static _EGLMain_t
130 _eglOpenLibrary(const char *driverPath, lib_handle *handle)
131 {
132 lib_handle lib;
133 _EGLMain_t mainFunc = NULL;
134 const char *error = "unknown error";
135
136 assert(driverPath);
137
138 _eglLog(_EGL_DEBUG, "dlopen(%s)", driverPath);
139 lib = open_library(driverPath);
140
141 #if defined(_EGL_PLATFORM_WINDOWS)
142 /* XXX untested */
143 if (lib)
144 mainFunc = (_EGLMain_t) GetProcAddress(lib, "_eglMain");
145 #elif defined(_EGL_PLATFORM_POSIX)
146 if (lib) {
147 mainFunc = (_EGLMain_t) dlsym(lib, "_eglMain");
148 if (!mainFunc)
149 error = dlerror();
150 }
151 else {
152 error = dlerror();
153 }
154 #else /* _EGL_PLATFORM_NO_OS */
155 /* must be the default driver name */
156 if (strcmp(driverPath, DefaultDriverName) == 0)
157 mainFunc = (_EGLMain_t) _eglMain;
158 else
159 error = "not builtin driver";
160 #endif
161
162 if (!lib) {
163 _eglLog(_EGL_WARNING, "Could not open driver %s (%s)",
164 driverPath, error);
165 if (!getenv("EGL_DRIVER"))
166 _eglLog(_EGL_WARNING,
167 "The driver can be overridden by setting EGL_DRIVER");
168 return NULL;
169 }
170
171 if (!mainFunc) {
172 _eglLog(_EGL_WARNING, "_eglMain not found in %s (%s)",
173 driverPath, error);
174 if (lib)
175 close_library(lib);
176 return NULL;
177 }
178
179 *handle = lib;
180 return mainFunc;
181 }
182
183
184 /**
185 * Load the named driver.
186 */
187 static _EGLDriver *
188 _eglLoadDriver(const char *path, const char *args)
189 {
190 _EGLMain_t mainFunc;
191 lib_handle lib;
192 _EGLDriver *drv = NULL;
193
194 mainFunc = _eglOpenLibrary(path, &lib);
195 if (!mainFunc)
196 return NULL;
197
198 drv = mainFunc(args);
199 if (!drv) {
200 if (lib)
201 close_library(lib);
202 return NULL;
203 }
204
205 if (!drv->Name) {
206 _eglLog(_EGL_WARNING, "Driver loaded from %s has no name", path);
207 drv->Name = "UNNAMED";
208 }
209
210 drv->Path = _eglstrdup(path);
211 drv->Args = (args) ? _eglstrdup(args) : NULL;
212 if (!drv->Path || (args && !drv->Args)) {
213 if (drv->Path)
214 free((char *) drv->Path);
215 if (drv->Args)
216 free((char *) drv->Args);
217 drv->Unload(drv);
218 if (lib)
219 close_library(lib);
220 return NULL;
221 }
222
223 drv->LibHandle = lib;
224
225 return drv;
226 }
227
228
229 /**
230 * Match a display to a preloaded driver.
231 *
232 * The matching is done by finding the driver with the highest score.
233 */
234 _EGLDriver *
235 _eglMatchDriver(_EGLDisplay *dpy)
236 {
237 _EGLDriver *best_drv = NULL;
238 EGLint best_score = -1, i;
239
240 /*
241 * this function is called after preloading and the drivers never change
242 * after preloading.
243 */
244 for (i = 0; i < _eglGlobal.NumDrivers; i++) {
245 _EGLDriver *drv = _eglGlobal.Drivers[i];
246 EGLint score;
247
248 score = (drv->Probe) ? drv->Probe(drv, dpy) : 0;
249 if (score > best_score) {
250 if (best_drv) {
251 _eglLog(_EGL_DEBUG, "driver %s has higher score than %s",
252 drv->Name, best_drv->Name);
253 }
254
255 best_drv = drv;
256 best_score = score;
257 /* perfect match */
258 if (score >= 100)
259 break;
260 }
261 }
262
263 return best_drv;
264 }
265
266
267 /**
268 * A loader function for use with _eglPreloadForEach. The loader data is the
269 * filename of the driver. This function stops on the first valid driver.
270 */
271 static EGLBoolean
272 _eglLoaderFile(const char *dir, size_t len, void *loader_data)
273 {
274 _EGLDriver *drv;
275 char path[1024];
276 const char *filename = (const char *) loader_data;
277 size_t flen = strlen(filename);
278
279 /* make a full path */
280 if (len + flen + 2 > sizeof(path))
281 return EGL_TRUE;
282 if (len) {
283 memcpy(path, dir, len);
284 path[len++] = '/';
285 }
286 memcpy(path + len, filename, flen);
287 len += flen;
288 path[len] = '\0';
289
290 drv = _eglLoadDriver(path, NULL);
291 /* fix the path and load again */
292 if (!drv && library_suffix()) {
293 const char *suffix = library_suffix();
294 size_t slen = strlen(suffix);
295 const char *p;
296 EGLBoolean need_suffix;
297
298 p = filename + flen - slen;
299 need_suffix = (p < filename || strcmp(p, suffix) != 0);
300 if (need_suffix && len + slen + 1 <= sizeof(path)) {
301 strcpy(path + len, suffix);
302 drv = _eglLoadDriver(path, NULL);
303 }
304 }
305 if (!drv)
306 return EGL_TRUE;
307
308 /* remember the driver and stop */
309 _eglGlobal.Drivers[_eglGlobal.NumDrivers++] = drv;
310 return EGL_FALSE;
311 }
312
313
314 /**
315 * A loader function for use with _eglPreloadForEach. The loader data is the
316 * pattern (prefix) of the files to look for.
317 */
318 static EGLBoolean
319 _eglLoaderPattern(const char *dir, size_t len, void *loader_data)
320 {
321 #if defined(_EGL_PLATFORM_POSIX)
322 const char *prefix, *suffix;
323 size_t prefix_len, suffix_len;
324 DIR *dirp;
325 struct dirent *dirent;
326 char path[1024];
327
328 if (len + 2 > sizeof(path))
329 return EGL_TRUE;
330 if (len) {
331 memcpy(path, dir, len);
332 path[len++] = '/';
333 }
334 path[len] = '\0';
335
336 dirp = opendir(path);
337 if (!dirp)
338 return EGL_TRUE;
339
340 prefix = (const char *) loader_data;
341 prefix_len = strlen(prefix);
342 suffix = library_suffix();
343 suffix_len = (suffix) ? strlen(suffix) : 0;
344
345 while ((dirent = readdir(dirp))) {
346 _EGLDriver *drv;
347 size_t dirent_len = strlen(dirent->d_name);
348 const char *p;
349
350 /* match the prefix */
351 if (strncmp(dirent->d_name, prefix, prefix_len) != 0)
352 continue;
353 /* match the suffix */
354 if (suffix) {
355 p = dirent->d_name + dirent_len - suffix_len;
356 if (p < dirent->d_name || strcmp(p, suffix) != 0)
357 continue;
358 }
359
360 /* make a full path and load the driver */
361 if (len + dirent_len + 1 <= sizeof(path)) {
362 strcpy(path + len, dirent->d_name);
363 drv = _eglLoadDriver(path, NULL);
364 if (drv)
365 _eglGlobal.Drivers[_eglGlobal.NumDrivers++] = drv;
366 }
367 }
368
369 closedir(dirp);
370
371 return EGL_TRUE;
372 #else /* _EGL_PLATFORM_POSIX */
373 /* stop immediately */
374 return EGL_FALSE;
375 #endif
376 }
377
378
379 /**
380 * Run the preload function on each driver directory and return the number of
381 * drivers loaded.
382 *
383 * The process may end prematurely if the callback function returns false.
384 */
385 static EGLint
386 _eglPreloadForEach(const char *search_path,
387 EGLBoolean (*loader)(const char *, size_t, void *),
388 void *loader_data)
389 {
390 const char *cur, *next;
391 size_t len;
392 EGLint num_drivers = _eglGlobal.NumDrivers;
393
394 cur = search_path;
395 while (cur) {
396 next = strchr(cur, ':');
397 len = (next) ? next - cur : strlen(cur);
398
399 if (!loader(cur, len, loader_data))
400 break;
401
402 cur = (next) ? next + 1 : NULL;
403 }
404
405 return (_eglGlobal.NumDrivers - num_drivers);
406 }
407
408
409 /**
410 * Return a list of colon-separated driver directories.
411 */
412 static const char *
413 _eglGetSearchPath(void)
414 {
415 static const char *search_path;
416
417 #if defined(_EGL_PLATFORM_POSIX) || defined(_EGL_PLATFORM_WINDOWS)
418 if (!search_path) {
419 static char buffer[1024];
420 const char *p;
421 int ret;
422
423 p = getenv("EGL_DRIVERS_PATH");
424 #if defined(_EGL_PLATFORM_POSIX)
425 if (p && (geteuid() != getuid() || getegid() != getgid())) {
426 _eglLog(_EGL_DEBUG,
427 "ignore EGL_DRIVERS_PATH for setuid/setgid binaries");
428 p = NULL;
429 }
430 #endif /* _EGL_PLATFORM_POSIX */
431
432 if (p) {
433 ret = snprintf(buffer, sizeof(buffer),
434 "%s:%s", p, _EGL_DRIVER_SEARCH_DIR);
435 if (ret > 0 && ret < sizeof(buffer))
436 search_path = buffer;
437 }
438 }
439 if (!search_path)
440 search_path = _EGL_DRIVER_SEARCH_DIR;
441 #else
442 search_path = "";
443 #endif
444
445 return search_path;
446 }
447
448
449 /**
450 * Preload a user driver.
451 *
452 * A user driver can be specified by EGL_DRIVER.
453 */
454 static EGLBoolean
455 _eglPreloadUserDriver(void)
456 {
457 const char *search_path = _eglGetSearchPath();
458 char *env;
459
460 env = getenv("EGL_DRIVER");
461 #if defined(_EGL_PLATFORM_POSIX)
462 if (env && strchr(env, '/')) {
463 search_path = "";
464 if ((geteuid() != getuid() || getegid() != getgid())) {
465 _eglLog(_EGL_DEBUG,
466 "ignore EGL_DRIVER for setuid/setgid binaries");
467 env = NULL;
468 }
469 }
470 #endif /* _EGL_PLATFORM_POSIX */
471 if (!env)
472 return EGL_FALSE;
473
474 if (!_eglPreloadForEach(search_path, _eglLoaderFile, (void *) env)) {
475 _eglLog(_EGL_WARNING, "EGL_DRIVER is set to an invalid driver");
476 return EGL_FALSE;
477 }
478
479 return EGL_TRUE;
480 }
481
482
483 /**
484 * Preload display drivers.
485 *
486 * Display drivers are a set of drivers that support a certain display system.
487 * The display system may be specified by EGL_DISPLAY.
488 *
489 * FIXME This makes libEGL a memory hog if an user driver is not specified and
490 * there are many display drivers.
491 */
492 static EGLBoolean
493 _eglPreloadDisplayDrivers(void)
494 {
495 const char *dpy;
496 char prefix[32];
497 int ret;
498
499 dpy = getenv("EGL_DISPLAY");
500 if (!dpy || !dpy[0])
501 dpy = _EGL_DEFAULT_DISPLAY;
502 if (!dpy || !dpy[0])
503 return EGL_FALSE;
504
505 ret = snprintf(prefix, sizeof(prefix), "egl_%s_", dpy);
506 if (ret < 0 || ret >= sizeof(prefix))
507 return EGL_FALSE;
508
509 return (_eglPreloadForEach(_eglGetSearchPath(),
510 _eglLoaderPattern, (void *) prefix) > 0);
511 }
512
513
514 /**
515 * Preload the default driver.
516 */
517 static EGLBoolean
518 _eglPreloadDefaultDriver(void)
519 {
520 return (_eglPreloadForEach(_eglGetSearchPath(),
521 _eglLoaderFile, (void *) DefaultDriverName) > 0);
522 }
523
524
525 /**
526 * Preload drivers.
527 *
528 * This function loads the driver modules and creates the corresponding
529 * _EGLDriver objects.
530 */
531 EGLBoolean
532 _eglPreloadDrivers(void)
533 {
534 EGLBoolean loaded;
535
536 /* protect the preloading process */
537 _eglLockMutex(_eglGlobal.Mutex);
538
539 /* already preloaded */
540 if (_eglGlobal.NumDrivers) {
541 _eglUnlockMutex(_eglGlobal.Mutex);
542 return EGL_TRUE;
543 }
544
545 loaded = (_eglPreloadUserDriver() ||
546 _eglPreloadDisplayDrivers() ||
547 _eglPreloadDefaultDriver());
548
549 _eglUnlockMutex(_eglGlobal.Mutex);
550
551 return loaded;
552 }
553
554
555 /**
556 * Unload preloaded drivers.
557 */
558 void
559 _eglUnloadDrivers(void)
560 {
561 EGLint i;
562
563 /* this is called at atexit time */
564 for (i = 0; i < _eglGlobal.NumDrivers; i++) {
565 _EGLDriver *drv = _eglGlobal.Drivers[i];
566 lib_handle handle = drv->LibHandle;
567
568 if (drv->Path)
569 free((char *) drv->Path);
570 if (drv->Args)
571 free((char *) drv->Args);
572
573 /* destroy driver */
574 if (drv->Unload)
575 drv->Unload(drv);
576
577 if (handle)
578 close_library(handle);
579 _eglGlobal.Drivers[i] = NULL;
580 }
581
582 _eglGlobal.NumDrivers = 0;
583 }
584
585
586 /**
587 * Plug all the available fallback routines into the given driver's
588 * dispatch table.
589 */
590 void
591 _eglInitDriverFallbacks(_EGLDriver *drv)
592 {
593 /* If a pointer is set to NULL, then the device driver _really_ has
594 * to implement it.
595 */
596 drv->API.Initialize = NULL;
597 drv->API.Terminate = NULL;
598
599 drv->API.GetConfigs = _eglGetConfigs;
600 drv->API.ChooseConfig = _eglChooseConfig;
601 drv->API.GetConfigAttrib = _eglGetConfigAttrib;
602
603 drv->API.CreateContext = _eglCreateContext;
604 drv->API.DestroyContext = _eglDestroyContext;
605 drv->API.MakeCurrent = _eglMakeCurrent;
606 drv->API.QueryContext = _eglQueryContext;
607
608 drv->API.CreateWindowSurface = _eglCreateWindowSurface;
609 drv->API.CreatePixmapSurface = _eglCreatePixmapSurface;
610 drv->API.CreatePbufferSurface = _eglCreatePbufferSurface;
611 drv->API.DestroySurface = _eglDestroySurface;
612 drv->API.QuerySurface = _eglQuerySurface;
613 drv->API.SurfaceAttrib = _eglSurfaceAttrib;
614 drv->API.BindTexImage = _eglBindTexImage;
615 drv->API.ReleaseTexImage = _eglReleaseTexImage;
616 drv->API.SwapInterval = _eglSwapInterval;
617 drv->API.SwapBuffers = _eglSwapBuffers;
618 drv->API.CopyBuffers = _eglCopyBuffers;
619
620 drv->API.QueryString = _eglQueryString;
621 drv->API.WaitClient = _eglWaitClient;
622 drv->API.WaitNative = _eglWaitNative;
623
624 #ifdef EGL_MESA_screen_surface
625 drv->API.ChooseModeMESA = _eglChooseModeMESA;
626 drv->API.GetModesMESA = _eglGetModesMESA;
627 drv->API.GetModeAttribMESA = _eglGetModeAttribMESA;
628 drv->API.GetScreensMESA = _eglGetScreensMESA;
629 drv->API.CreateScreenSurfaceMESA = _eglCreateScreenSurfaceMESA;
630 drv->API.ShowScreenSurfaceMESA = _eglShowScreenSurfaceMESA;
631 drv->API.ScreenPositionMESA = _eglScreenPositionMESA;
632 drv->API.QueryScreenMESA = _eglQueryScreenMESA;
633 drv->API.QueryScreenSurfaceMESA = _eglQueryScreenSurfaceMESA;
634 drv->API.QueryScreenModeMESA = _eglQueryScreenModeMESA;
635 drv->API.QueryModeStringMESA = _eglQueryModeStringMESA;
636 #endif /* EGL_MESA_screen_surface */
637
638 #ifdef EGL_VERSION_1_2
639 drv->API.CreatePbufferFromClientBuffer = _eglCreatePbufferFromClientBuffer;
640 #endif /* EGL_VERSION_1_2 */
641
642 #ifdef EGL_KHR_image_base
643 drv->API.CreateImageKHR = _eglCreateImageKHR;
644 drv->API.DestroyImageKHR = _eglDestroyImageKHR;
645 #endif /* EGL_KHR_image_base */
646 }
647
648
649 /**
650 * Set the probe cache at the given key.
651 *
652 * A key, instead of a _EGLDriver, is used to allow the probe cache to be share
653 * by multiple drivers.
654 */
655 void
656 _eglSetProbeCache(EGLint key, const void *val)
657 {
658 EGLint idx;
659
660 for (idx = 0; idx < NUM_PROBE_CACHE_SLOTS; idx++) {
661 if (!_eglProbeCache.keys[idx] || _eglProbeCache.keys[idx] == key)
662 break;
663 }
664 assert(key > 0);
665 assert(idx < NUM_PROBE_CACHE_SLOTS);
666
667 _eglProbeCache.keys[idx] = key;
668 _eglProbeCache.values[idx] = val;
669 }
670
671
672 /**
673 * Return the probe cache at the given key.
674 */
675 const void *
676 _eglGetProbeCache(EGLint key)
677 {
678 EGLint idx;
679
680 for (idx = 0; idx < NUM_PROBE_CACHE_SLOTS; idx++) {
681 if (!_eglProbeCache.keys[idx] || _eglProbeCache.keys[idx] == key)
682 break;
683 }
684
685 return (idx < NUM_PROBE_CACHE_SLOTS && _eglProbeCache.keys[idx] == key) ?
686 _eglProbeCache.values[idx] : NULL;
687 }