Merge branch 'master' of ssh://git.freedesktop.org/git/mesa/mesa into pipe-video
[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 "egldefines.h"
13 #include "egldisplay.h"
14 #include "egldriver.h"
15 #include "egllog.h"
16 #include "eglmutex.h"
17
18 #if defined(_EGL_OS_UNIX)
19 #include <dlfcn.h>
20 #include <sys/types.h>
21 #include <dirent.h>
22 #include <unistd.h>
23 #endif
24
25
26 typedef struct _egl_module {
27 char *Path;
28 void *Handle;
29 _EGLDriver *Driver;
30 } _EGLModule;
31
32 static _EGL_DECLARE_MUTEX(_eglModuleMutex);
33 static _EGLArray *_eglModules;
34
35
36 /**
37 * Wrappers for dlopen/dlclose()
38 */
39 #if defined(_EGL_OS_WINDOWS)
40
41
42 typedef HMODULE lib_handle;
43
44 static HMODULE
45 open_library(const char *filename)
46 {
47 return LoadLibrary(filename);
48 }
49
50 static void
51 close_library(HMODULE lib)
52 {
53 FreeLibrary(lib);
54 }
55
56
57 static const char *
58 library_suffix(void)
59 {
60 return ".dll";
61 }
62
63
64 #elif defined(_EGL_OS_UNIX)
65
66
67 typedef void * lib_handle;
68
69 static void *
70 open_library(const char *filename)
71 {
72 return dlopen(filename, RTLD_LAZY);
73 }
74
75 static void
76 close_library(void *lib)
77 {
78 dlclose(lib);
79 }
80
81
82 static const char *
83 library_suffix(void)
84 {
85 return ".so";
86 }
87
88
89 #endif
90
91
92 /**
93 * Open the named driver and find its bootstrap function: _eglMain().
94 */
95 static _EGLMain_t
96 _eglOpenLibrary(const char *driverPath, lib_handle *handle)
97 {
98 lib_handle lib;
99 _EGLMain_t mainFunc = NULL;
100 const char *error = "unknown error";
101
102 assert(driverPath);
103
104 _eglLog(_EGL_DEBUG, "dlopen(%s)", driverPath);
105 lib = open_library(driverPath);
106
107 #if defined(_EGL_OS_WINDOWS)
108 /* XXX untested */
109 if (lib)
110 mainFunc = (_EGLMain_t) GetProcAddress(lib, "_eglMain");
111 #elif defined(_EGL_OS_UNIX)
112 if (lib) {
113 union {
114 _EGLMain_t func;
115 void *ptr;
116 } tmp = { NULL };
117 /* direct cast gives a warning when compiled with -pedantic */
118 tmp.ptr = dlsym(lib, "_eglMain");
119 mainFunc = tmp.func;
120 if (!mainFunc)
121 error = dlerror();
122 }
123 else {
124 error = dlerror();
125 }
126 #endif
127
128 if (!lib) {
129 _eglLog(_EGL_WARNING, "Could not open driver %s (%s)",
130 driverPath, error);
131 if (!getenv("EGL_DRIVER"))
132 _eglLog(_EGL_WARNING,
133 "The driver can be overridden by setting EGL_DRIVER");
134 return NULL;
135 }
136
137 if (!mainFunc) {
138 _eglLog(_EGL_WARNING, "_eglMain not found in %s (%s)",
139 driverPath, error);
140 if (lib)
141 close_library(lib);
142 return NULL;
143 }
144
145 *handle = lib;
146 return mainFunc;
147 }
148
149
150 /**
151 * Load a module and create the driver object.
152 */
153 static EGLBoolean
154 _eglLoadModule(_EGLModule *mod)
155 {
156 _EGLMain_t mainFunc;
157 lib_handle lib;
158 _EGLDriver *drv;
159
160 mainFunc = _eglOpenLibrary(mod->Path, &lib);
161 if (!mainFunc)
162 return EGL_FALSE;
163
164 drv = mainFunc(NULL);
165 if (!drv) {
166 if (lib)
167 close_library(lib);
168 return EGL_FALSE;
169 }
170
171 if (!drv->Name) {
172 _eglLog(_EGL_WARNING, "Driver loaded from %s has no name", mod->Path);
173 drv->Name = "UNNAMED";
174 }
175
176 mod->Handle = (void *) lib;
177 mod->Driver = drv;
178
179 return EGL_TRUE;
180 }
181
182
183 /**
184 * Unload a module.
185 */
186 static void
187 _eglUnloadModule(_EGLModule *mod)
188 {
189 /* destroy the driver */
190 if (mod->Driver && mod->Driver->Unload)
191 mod->Driver->Unload(mod->Driver);
192 if (mod->Handle)
193 close_library(mod->Handle);
194
195 mod->Driver = NULL;
196 mod->Handle = NULL;
197 }
198
199
200 /**
201 * Add a module to the module array.
202 */
203 static _EGLModule *
204 _eglAddModule(const char *path)
205 {
206 _EGLModule *mod;
207 EGLint i;
208
209 if (!_eglModules) {
210 _eglModules = _eglCreateArray("Module", 8);
211 if (!_eglModules)
212 return NULL;
213 }
214
215 /* find duplicates */
216 for (i = 0; i < _eglModules->Size; i++) {
217 mod = _eglModules->Elements[i];
218 if (strcmp(mod->Path, path) == 0)
219 return mod;
220 }
221
222 /* allocate a new one */
223 mod = calloc(1, sizeof(*mod));
224 if (mod) {
225 mod->Path = _eglstrdup(path);
226 if (!mod->Path) {
227 free(mod);
228 mod = NULL;
229 }
230 }
231 if (mod) {
232 _eglAppendArray(_eglModules, (void *) mod);
233 _eglLog(_EGL_DEBUG, "added %s to module array", mod->Path);
234 }
235
236 return mod;
237 }
238
239
240 /**
241 * Free a module.
242 */
243 static void
244 _eglFreeModule(void *module)
245 {
246 _EGLModule *mod = (_EGLModule *) module;
247
248 _eglUnloadModule(mod);
249 free(mod->Path);
250 free(mod);
251 }
252
253
254 /**
255 * A loader function for use with _eglPreloadForEach. The loader data is the
256 * filename of the driver. This function stops on the first valid driver.
257 */
258 static EGLBoolean
259 _eglLoaderFile(const char *dir, size_t len, void *loader_data)
260 {
261 char path[1024];
262 const char *filename = (const char *) loader_data;
263 size_t flen = strlen(filename);
264
265 /* make a full path */
266 if (len + flen + 2 > sizeof(path))
267 return EGL_TRUE;
268 if (len) {
269 memcpy(path, dir, len);
270 path[len++] = '/';
271 }
272 memcpy(path + len, filename, flen);
273 len += flen;
274 path[len] = '\0';
275
276 if (library_suffix()) {
277 const char *suffix = library_suffix();
278 size_t slen = strlen(suffix);
279 const char *p;
280 EGLBoolean need_suffix;
281
282 p = filename + flen - slen;
283 need_suffix = (p < filename || strcmp(p, suffix) != 0);
284 if (need_suffix) {
285 /* overflow */
286 if (len + slen + 1 > sizeof(path))
287 return EGL_TRUE;
288 strcpy(path + len, suffix);
289 }
290 }
291
292 #if defined(_EGL_OS_UNIX)
293 /* check if the file exists */
294 if (access(path, F_OK))
295 return EGL_TRUE;
296 #endif
297
298 _eglAddModule(path);
299
300 return EGL_TRUE;
301 }
302
303
304 /**
305 * A loader function for use with _eglPreloadForEach. The loader data is the
306 * pattern (prefix) of the files to look for.
307 */
308 static EGLBoolean
309 _eglLoaderPattern(const char *dir, size_t len, void *loader_data)
310 {
311 #if defined(_EGL_OS_UNIX)
312 const char *prefix, *suffix;
313 size_t prefix_len, suffix_len;
314 DIR *dirp;
315 struct dirent *dirent;
316 char path[1024];
317
318 if (len + 2 > sizeof(path))
319 return EGL_TRUE;
320 if (len) {
321 memcpy(path, dir, len);
322 path[len++] = '/';
323 }
324 path[len] = '\0';
325
326 dirp = opendir(path);
327 if (!dirp)
328 return EGL_TRUE;
329
330 prefix = (const char *) loader_data;
331 prefix_len = strlen(prefix);
332 suffix = library_suffix();
333 suffix_len = (suffix) ? strlen(suffix) : 0;
334
335 while ((dirent = readdir(dirp))) {
336 size_t dirent_len = strlen(dirent->d_name);
337 const char *p;
338
339 /* match the prefix */
340 if (strncmp(dirent->d_name, prefix, prefix_len) != 0)
341 continue;
342 /* match the suffix */
343 if (suffix) {
344 p = dirent->d_name + dirent_len - suffix_len;
345 if (p < dirent->d_name || strcmp(p, suffix) != 0)
346 continue;
347 }
348
349 /* make a full path and add it to the module array */
350 if (len + dirent_len + 1 <= sizeof(path)) {
351 strcpy(path + len, dirent->d_name);
352 _eglAddModule(path);
353 }
354 }
355
356 closedir(dirp);
357
358 return EGL_TRUE;
359 #else /* _EGL_OS_UNIX */
360 /* stop immediately */
361 return EGL_FALSE;
362 #endif
363 }
364
365
366 /**
367 * Run the callback function on each driver directory.
368 *
369 * The process may end prematurely if the callback function returns false.
370 */
371 static void
372 _eglPreloadForEach(const char *search_path,
373 EGLBoolean (*loader)(const char *, size_t, void *),
374 void *loader_data)
375 {
376 const char *cur, *next;
377 size_t len;
378
379 cur = search_path;
380 while (cur) {
381 next = strchr(cur, ':');
382 len = (next) ? next - cur : strlen(cur);
383
384 if (!loader(cur, len, loader_data))
385 break;
386
387 cur = (next) ? next + 1 : NULL;
388 }
389 }
390
391
392 /**
393 * Return a list of colon-separated driver directories.
394 */
395 static const char *
396 _eglGetSearchPath(void)
397 {
398 static const char *search_path;
399
400 #if defined(_EGL_OS_UNIX) || defined(_EGL_OS_WINDOWS)
401 if (!search_path) {
402 static char buffer[1024];
403 const char *p;
404 int ret;
405
406 p = getenv("EGL_DRIVERS_PATH");
407 #if defined(_EGL_OS_UNIX)
408 if (p && (geteuid() != getuid() || getegid() != getgid())) {
409 _eglLog(_EGL_DEBUG,
410 "ignore EGL_DRIVERS_PATH for setuid/setgid binaries");
411 p = NULL;
412 }
413 #endif /* _EGL_OS_UNIX */
414
415 if (p) {
416 ret = _eglsnprintf(buffer, sizeof(buffer),
417 "%s:%s", p, _EGL_DRIVER_SEARCH_DIR);
418 if (ret > 0 && ret < sizeof(buffer))
419 search_path = buffer;
420 }
421 }
422 if (!search_path)
423 search_path = _EGL_DRIVER_SEARCH_DIR;
424 #else
425 search_path = "";
426 #endif
427
428 return search_path;
429 }
430
431
432 /**
433 * Add the user driver to the module array.
434 *
435 * The user driver is specified by EGL_DRIVER.
436 */
437 static void
438 _eglAddUserDriver(void)
439 {
440 const char *search_path = _eglGetSearchPath();
441 char *env;
442
443 env = getenv("EGL_DRIVER");
444 #if defined(_EGL_OS_UNIX)
445 if (env && strchr(env, '/')) {
446 search_path = "";
447 if ((geteuid() != getuid() || getegid() != getgid())) {
448 _eglLog(_EGL_DEBUG,
449 "ignore EGL_DRIVER for setuid/setgid binaries");
450 env = NULL;
451 }
452 }
453 #endif /* _EGL_OS_UNIX */
454 if (env)
455 _eglPreloadForEach(search_path, _eglLoaderFile, (void *) env);
456 }
457
458
459 /**
460 * Add default drivers to the module array.
461 */
462 static void
463 _eglAddDefaultDrivers(void)
464 {
465 const char *search_path = _eglGetSearchPath();
466 EGLint i;
467 #if defined(_EGL_OS_WINDOWS)
468 const char *DefaultDriverNames[] = {
469 "egl_gallium"
470 };
471 #elif defined(_EGL_OS_UNIX)
472 const char *DefaultDriverNames[] = {
473 "egl_gallium",
474 "egl_dri2",
475 "egl_glx"
476 };
477 #endif
478
479 for (i = 0; i < ARRAY_SIZE(DefaultDriverNames); i++) {
480 void *name = (void *) DefaultDriverNames[i];
481 _eglPreloadForEach(search_path, _eglLoaderFile, name);
482 }
483 }
484
485
486 /**
487 * Add drivers to the module array. Drivers will be loaded as they are matched
488 * to displays.
489 */
490 static EGLBoolean
491 _eglAddDrivers(void)
492 {
493 if (_eglModules)
494 return EGL_TRUE;
495
496 /* the order here decides the priorities of the drivers */
497 _eglAddUserDriver();
498 _eglAddDefaultDrivers();
499 _eglPreloadForEach(_eglGetSearchPath(), _eglLoaderPattern, (void *) "egl_");
500
501 return (_eglModules != NULL);
502 }
503
504
505 /**
506 * Match a display to a driver. The display is initialized unless use_probe is
507 * true.
508 *
509 * The matching is done by finding the first driver that can initialize the
510 * display, or when use_probe is true, the driver with highest score.
511 */
512 _EGLDriver *
513 _eglMatchDriver(_EGLDisplay *dpy, EGLBoolean use_probe)
514 {
515 _EGLModule *mod;
516 _EGLDriver *best_drv = NULL;
517 EGLint best_score = 0;
518 EGLint major, minor, i;
519
520 _eglLockMutex(&_eglModuleMutex);
521
522 if (!_eglAddDrivers()) {
523 _eglUnlockMutex(&_eglModuleMutex);
524 return EGL_FALSE;
525 }
526
527 /* match the loaded modules */
528 for (i = 0; i < _eglModules->Size; i++) {
529 mod = (_EGLModule *) _eglModules->Elements[i];
530 if (!mod->Driver)
531 break;
532
533 if (use_probe) {
534 EGLint score = (mod->Driver->Probe) ?
535 mod->Driver->Probe(mod->Driver, dpy) : 1;
536 if (score > best_score) {
537 best_drv = mod->Driver;
538 best_score = score;
539 }
540 }
541 else {
542 if (mod->Driver->API.Initialize(mod->Driver, dpy, &major, &minor)) {
543 best_drv = mod->Driver;
544 best_score = 100;
545 }
546 }
547 /* perfect match */
548 if (best_score >= 100)
549 break;
550 }
551
552 /* load more modules */
553 if (!best_drv) {
554 EGLint first_unloaded = i;
555
556 while (i < _eglModules->Size) {
557 mod = (_EGLModule *) _eglModules->Elements[i];
558 assert(!mod->Driver);
559
560 if (!_eglLoadModule(mod)) {
561 /* remove invalid modules */
562 _eglEraseArray(_eglModules, i, _eglFreeModule);
563 continue;
564 }
565
566 if (use_probe) {
567 best_score = (mod->Driver->Probe) ?
568 mod->Driver->Probe(mod->Driver, dpy) : 1;
569 }
570 else {
571 if (mod->Driver->API.Initialize(mod->Driver, dpy, &major, &minor))
572 best_score = 100;
573 }
574
575 if (best_score > 0) {
576 best_drv = mod->Driver;
577 /* loaded modules come before unloaded ones */
578 if (first_unloaded != i) {
579 void *tmp = _eglModules->Elements[i];
580 _eglModules->Elements[i] =
581 _eglModules->Elements[first_unloaded];
582 _eglModules->Elements[first_unloaded] = tmp;
583 }
584 break;
585 }
586 else {
587 _eglUnloadModule(mod);
588 i++;
589 }
590 }
591 }
592
593 _eglUnlockMutex(&_eglModuleMutex);
594
595 if (best_drv) {
596 _eglLog(_EGL_DEBUG, "the best driver is %s (score %d)",
597 best_drv->Name, best_score);
598 if (!use_probe) {
599 dpy->Driver = best_drv;
600 dpy->Initialized = EGL_TRUE;
601 dpy->APImajor = major;
602 dpy->APIminor = minor;
603 }
604 }
605
606 return best_drv;
607 }
608
609
610 __eglMustCastToProperFunctionPointerType
611 _eglGetDriverProc(const char *procname)
612 {
613 EGLint i;
614 _EGLProc proc = NULL;
615
616 if (!_eglModules) {
617 /* load the driver for the default display */
618 EGLDisplay egldpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
619 _EGLDisplay *dpy = _eglLookupDisplay(egldpy);
620 if (!dpy || !_eglMatchDriver(dpy, EGL_TRUE))
621 return NULL;
622 }
623
624 for (i = 0; i < _eglModules->Size; i++) {
625 _EGLModule *mod = (_EGLModule *) _eglModules->Elements[i];
626
627 if (!mod->Driver)
628 break;
629 proc = mod->Driver->API.GetProcAddress(mod->Driver, procname);
630 if (proc)
631 break;
632 }
633
634 return proc;
635 }
636
637
638 /**
639 * Unload all drivers.
640 */
641 void
642 _eglUnloadDrivers(void)
643 {
644 /* this is called at atexit time */
645 if (_eglModules) {
646 #if defined(_EGL_OS_UNIX)
647 _eglDestroyArray(_eglModules, _eglFreeModule);
648 #elif defined(_EGL_OS_WINDOWS)
649 /* XXX Windows unloads DLLs before atexit */
650 _eglDestroyArray(_eglModules, NULL);
651 #endif
652 _eglModules = NULL;
653 }
654 }
655
656
657 /**
658 * Invoke a callback function on each EGL search path.
659 *
660 * The first argument of the callback function is the name of the search path.
661 * The second argument is the length of the name.
662 */
663 void
664 _eglSearchPathForEach(EGLBoolean (*callback)(const char *, size_t, void *),
665 void *callback_data)
666 {
667 const char *search_path = _eglGetSearchPath();
668 _eglPreloadForEach(search_path, callback, callback_data);
669 }