egldriver.c revision 848b8605
1/**************************************************************************
2 *
3 * Copyright 2008 VMware, Inc.
4 * Copyright 2009-2010 Chia-I Wu <olvaffe@gmail.com>
5 * Copyright 2010-2011 LunarG, Inc.
6 * All Rights Reserved.
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a
9 * copy of this software and associated documentation files (the
10 * "Software"), to deal in the Software without restriction, including
11 * without limitation the rights to use, copy, modify, merge, publish,
12 * distribute, sub license, and/or sell copies of the Software, and to
13 * permit persons to whom the Software is furnished to do so, subject to
14 * the following conditions:
15 *
16 * The above copyright notice and this permission notice (including the
17 * next paragraph) shall be included in all copies or substantial portions
18 * of the Software.
19 *
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
23 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26 * DEALINGS IN THE SOFTWARE.
27 *
28 **************************************************************************/
29
30
31/**
32 * Functions for choosing and opening/loading device drivers.
33 */
34
35
36#include <assert.h>
37#include <string.h>
38#include <stdio.h>
39#include <stdlib.h>
40
41#include "eglstring.h"
42#include "egldefines.h"
43#include "egldisplay.h"
44#include "egldriver.h"
45#include "egllog.h"
46#include "eglmutex.h"
47
48#if defined(_EGL_OS_UNIX)
49#include <dlfcn.h>
50#include <sys/types.h>
51#include <dirent.h>
52#include <unistd.h>
53#endif
54
55
56typedef struct _egl_module {
57   char *Path;
58   _EGLMain_t BuiltIn;
59   void *Handle;
60   _EGLDriver *Driver;
61} _EGLModule;
62
63static _EGLMutex _eglModuleMutex = _EGL_MUTEX_INITIALIZER;
64static _EGLArray *_eglModules;
65
66const struct {
67   const char *name;
68   _EGLMain_t main;
69} _eglBuiltInDrivers[] = {
70#ifdef _EGL_BUILT_IN_DRIVER_GALLIUM
71   { "egl_gallium", _eglBuiltInDriverGALLIUM },
72#endif
73#ifdef _EGL_BUILT_IN_DRIVER_DRI2
74   { "egl_dri2", _eglBuiltInDriverDRI2 },
75#endif
76   { NULL, NULL }
77};
78
79/**
80 * Wrappers for dlopen/dlclose()
81 */
82#if defined(_EGL_OS_WINDOWS)
83
84
85typedef HMODULE lib_handle;
86
87static HMODULE
88open_library(const char *filename)
89{
90   return LoadLibrary(filename);
91}
92
93static void
94close_library(HMODULE lib)
95{
96   FreeLibrary(lib);
97}
98
99
100static const char *
101library_suffix(void)
102{
103   return ".dll";
104}
105
106
107#elif defined(_EGL_OS_UNIX)
108
109
110typedef void * lib_handle;
111
112static void *
113open_library(const char *filename)
114{
115   return dlopen(filename, RTLD_LAZY);
116}
117
118static void
119close_library(void *lib)
120{
121   dlclose(lib);
122}
123
124
125static const char *
126library_suffix(void)
127{
128   return ".so";
129}
130
131
132#endif
133
134
135/**
136 * Open the named driver and find its bootstrap function: _eglMain().
137 */
138static _EGLMain_t
139_eglOpenLibrary(const char *driverPath, lib_handle *handle)
140{
141   lib_handle lib;
142   _EGLMain_t mainFunc = NULL;
143   const char *error = "unknown error";
144
145   assert(driverPath);
146
147   _eglLog(_EGL_DEBUG, "dlopen(%s)", driverPath);
148   lib = open_library(driverPath);
149
150#if defined(_EGL_OS_WINDOWS)
151   /* XXX untested */
152   if (lib)
153      mainFunc = (_EGLMain_t) GetProcAddress(lib, "_eglMain");
154#elif defined(_EGL_OS_UNIX)
155   if (lib) {
156      union {
157         _EGLMain_t func;
158         void *ptr;
159      } tmp = { NULL };
160      /* direct cast gives a warning when compiled with -pedantic */
161      tmp.ptr = dlsym(lib, "_eglMain");
162      mainFunc = tmp.func;
163      if (!mainFunc)
164         error = dlerror();
165   }
166   else {
167      error = dlerror();
168   }
169#endif
170
171   if (!lib) {
172      _eglLog(_EGL_WARNING, "Could not open driver %s (%s)",
173              driverPath, error);
174      return NULL;
175   }
176
177   if (!mainFunc) {
178      _eglLog(_EGL_WARNING, "_eglMain not found in %s (%s)",
179              driverPath, error);
180      if (lib)
181         close_library(lib);
182      return NULL;
183   }
184
185   *handle = lib;
186   return mainFunc;
187}
188
189
190/**
191 * Load a module and create the driver object.
192 */
193static EGLBoolean
194_eglLoadModule(_EGLModule *mod)
195{
196   _EGLMain_t mainFunc;
197   lib_handle lib;
198   _EGLDriver *drv;
199
200   if (mod->Driver)
201      return EGL_TRUE;
202
203   if (mod->BuiltIn) {
204      lib = (lib_handle) NULL;
205      mainFunc = mod->BuiltIn;
206   }
207   else {
208      mainFunc = _eglOpenLibrary(mod->Path, &lib);
209      if (!mainFunc)
210         return EGL_FALSE;
211   }
212
213   drv = mainFunc(NULL);
214   if (!drv) {
215      if (lib)
216         close_library(lib);
217      return EGL_FALSE;
218   }
219
220   if (!drv->Name) {
221      _eglLog(_EGL_WARNING, "Driver loaded from %s has no name", mod->Path);
222      drv->Name = "UNNAMED";
223   }
224
225   mod->Handle = (void *) lib;
226   mod->Driver = drv;
227
228   return EGL_TRUE;
229}
230
231
232/**
233 * Unload a module.
234 */
235static void
236_eglUnloadModule(_EGLModule *mod)
237{
238#if defined(_EGL_OS_UNIX)
239   /* destroy the driver */
240   if (mod->Driver && mod->Driver->Unload)
241      mod->Driver->Unload(mod->Driver);
242
243   /*
244    * XXX At this point (atexit), the module might be the last reference to
245    * libEGL.  Closing the module might unmap libEGL and give problems.
246    */
247#if 0
248   if (mod->Handle)
249      close_library(mod->Handle);
250#endif
251#elif defined(_EGL_OS_WINDOWS)
252   /* XXX Windows unloads DLLs before atexit */
253#endif
254
255   mod->Driver = NULL;
256   mod->Handle = NULL;
257}
258
259
260/**
261 * Add a module to the module array.
262 */
263static _EGLModule *
264_eglAddModule(const char *path)
265{
266   _EGLModule *mod;
267   EGLint i;
268
269   if (!_eglModules) {
270      _eglModules = _eglCreateArray("Module", 8);
271      if (!_eglModules)
272         return NULL;
273   }
274
275   /* find duplicates */
276   for (i = 0; i < _eglModules->Size; i++) {
277      mod = _eglModules->Elements[i];
278      if (strcmp(mod->Path, path) == 0)
279         return mod;
280   }
281
282   /* allocate a new one */
283   mod = calloc(1, sizeof(*mod));
284   if (mod) {
285      mod->Path = _eglstrdup(path);
286      if (!mod->Path) {
287         free(mod);
288         mod = NULL;
289      }
290   }
291   if (mod) {
292      _eglAppendArray(_eglModules, (void *) mod);
293      _eglLog(_EGL_DEBUG, "added %s to module array", mod->Path);
294   }
295
296   return mod;
297}
298
299
300/**
301 * Free a module.
302 */
303static void
304_eglFreeModule(void *module)
305{
306   _EGLModule *mod = (_EGLModule *) module;
307
308   _eglUnloadModule(mod);
309   free(mod->Path);
310   free(mod);
311}
312
313
314/**
315 * A loader function for use with _eglPreloadForEach.  The loader data is the
316 * filename of the driver.   This function stops on the first valid driver.
317 */
318static EGLBoolean
319_eglLoaderFile(const char *dir, size_t len, void *loader_data)
320{
321   char path[1024];
322   const char *filename = (const char *) loader_data;
323   size_t flen = strlen(filename);
324
325   /* make a full path */
326   if (len + flen + 2 > sizeof(path))
327      return EGL_TRUE;
328   if (len) {
329      memcpy(path, dir, len);
330      path[len++] = '/';
331   }
332   memcpy(path + len, filename, flen);
333   len += flen;
334   path[len] = '\0';
335
336   if (library_suffix()) {
337      const char *suffix = library_suffix();
338      size_t slen = strlen(suffix);
339      const char *p;
340      EGLBoolean need_suffix;
341
342      p = filename + flen - slen;
343      need_suffix = (p < filename || strcmp(p, suffix) != 0);
344      if (need_suffix) {
345         /* overflow */
346         if (len + slen + 1 > sizeof(path))
347            return EGL_TRUE;
348         strcpy(path + len, suffix);
349      }
350   }
351
352#if defined(_EGL_OS_UNIX)
353   /* check if the file exists */
354   if (access(path, F_OK))
355      return EGL_TRUE;
356#endif
357
358   _eglAddModule(path);
359
360   return EGL_TRUE;
361}
362
363
364/**
365 * Run the callback function on each driver directory.
366 *
367 * The process may end prematurely if the callback function returns false.
368 */
369static void
370_eglPreloadForEach(const char *search_path,
371                   EGLBoolean (*loader)(const char *, size_t, void *),
372                   void *loader_data)
373{
374   const char *cur, *next;
375   size_t len;
376
377   cur = search_path;
378   while (cur) {
379      next = strchr(cur, ':');
380      len = (next) ? next - cur : strlen(cur);
381
382      if (!loader(cur, len, loader_data))
383         break;
384
385      cur = (next) ? next + 1 : NULL;
386   }
387}
388
389
390/**
391 * Return a list of colon-separated driver directories.
392 */
393static const char *
394_eglGetSearchPath(void)
395{
396   static char search_path[1024];
397
398#if defined(_EGL_OS_UNIX) || defined(_EGL_OS_WINDOWS)
399   if (search_path[0] == '\0') {
400      char *buf = search_path;
401      size_t len = sizeof(search_path);
402      EGLBoolean use_env;
403      char dir_sep;
404      int ret;
405
406#if defined(_EGL_OS_UNIX)
407      use_env = (geteuid() == getuid() && getegid() == getgid());
408      dir_sep = '/';
409#else
410      use_env = EGL_TRUE;
411      dir_sep = '\\';
412#endif
413
414      if (use_env) {
415         char *p;
416
417         /* extract the dirname from EGL_DRIVER */
418         p = getenv("EGL_DRIVER");
419         if (p && strchr(p, dir_sep)) {
420            ret = _eglsnprintf(buf, len, "%s", p);
421            if (ret > 0 && ret < len) {
422               p = strrchr(buf, dir_sep);
423               *p++ = ':';
424
425               len -= p - buf;
426               buf = p;
427            }
428         }
429
430         /* append EGL_DRIVERS_PATH */
431         p = getenv("EGL_DRIVERS_PATH");
432         if (p) {
433            ret = _eglsnprintf(buf, len, "%s:", p);
434            if (ret > 0 && ret < len) {
435               buf += ret;
436               len -= ret;
437            }
438         }
439      }
440      else {
441         _eglLog(_EGL_DEBUG,
442               "ignore EGL_DRIVERS_PATH for setuid/setgid binaries");
443      }
444
445      ret = _eglsnprintf(buf, len, "%s", _EGL_DRIVER_SEARCH_DIR);
446      if (ret < 0 || ret >= len)
447         search_path[0] = '\0';
448
449      _eglLog(_EGL_DEBUG, "EGL search path is %s", search_path);
450   }
451#endif /* defined(_EGL_OS_UNIX) || defined(_EGL_OS_WINDOWS) */
452
453   return search_path;
454}
455
456
457/**
458 * Add the user driver to the module array.
459 *
460 * The user driver is specified by EGL_DRIVER.
461 */
462static EGLBoolean
463_eglAddUserDriver(void)
464{
465   const char *search_path = _eglGetSearchPath();
466   char *env;
467   size_t name_len = 0;
468
469   env = getenv("EGL_DRIVER");
470#if defined(_EGL_OS_UNIX)
471   if (env && strchr(env, '/')) {
472      search_path = "";
473      if ((geteuid() != getuid() || getegid() != getgid())) {
474         _eglLog(_EGL_DEBUG,
475               "ignore EGL_DRIVER for setuid/setgid binaries");
476         env = NULL;
477      }
478   }
479   else if (env) {
480      char *suffix = strchr(env, '.');
481      name_len = (suffix) ? suffix - env : strlen(env);
482   }
483#else
484   if (env)
485      name_len = strlen(env);
486#endif /* _EGL_OS_UNIX */
487
488   /*
489    * Try built-in drivers first if we know the driver name.  This makes sure
490    * we do not load the outdated external driver that is still on the
491    * filesystem.
492    */
493   if (name_len) {
494      _EGLModule *mod;
495      EGLint i;
496
497      for (i = 0; _eglBuiltInDrivers[i].name; i++) {
498         if (strlen(_eglBuiltInDrivers[i].name) == name_len &&
499             !strncmp(_eglBuiltInDrivers[i].name, env, name_len)) {
500            mod = _eglAddModule(env);
501            if (mod)
502               mod->BuiltIn = _eglBuiltInDrivers[i].main;
503
504            return EGL_TRUE;
505         }
506      }
507   }
508
509   /* otherwise, treat env as a path */
510   if (env) {
511      _eglPreloadForEach(search_path, _eglLoaderFile, (void *) env);
512
513      return EGL_TRUE;
514   }
515
516   return EGL_FALSE;
517}
518
519
520/**
521 * Add egl_gallium to the module array.
522 */
523static void
524_eglAddGalliumDriver(void)
525{
526#ifndef _EGL_BUILT_IN_DRIVER_GALLIUM
527   void *external = (void *) "egl_gallium";
528   _eglPreloadForEach(_eglGetSearchPath(), _eglLoaderFile, external);
529#endif
530}
531
532
533/**
534 * Add built-in drivers to the module array.
535 */
536static void
537_eglAddBuiltInDrivers(void)
538{
539   _EGLModule *mod;
540   EGLint i;
541
542   for (i = 0; _eglBuiltInDrivers[i].name; i++) {
543      mod = _eglAddModule(_eglBuiltInDrivers[i].name);
544      if (mod)
545         mod->BuiltIn = _eglBuiltInDrivers[i].main;
546   }
547}
548
549
550/**
551 * Add drivers to the module array.  Drivers will be loaded as they are matched
552 * to displays.
553 */
554static EGLBoolean
555_eglAddDrivers(void)
556{
557   if (_eglModules)
558      return EGL_TRUE;
559
560   if (!_eglAddUserDriver()) {
561      /*
562       * Add other drivers only when EGL_DRIVER is not set.  The order here
563       * decides the priorities.
564       */
565      _eglAddGalliumDriver();
566      _eglAddBuiltInDrivers();
567   }
568
569   return (_eglModules != NULL);
570}
571
572
573/**
574 * A helper function for _eglMatchDriver.  It finds the first driver that can
575 * initialize the display and return.
576 */
577static _EGLDriver *
578_eglMatchAndInitialize(_EGLDisplay *dpy)
579{
580   _EGLDriver *drv = NULL;
581   EGLint i = 0;
582
583   if (!_eglAddDrivers()) {
584      _eglLog(_EGL_WARNING, "failed to find any driver");
585      return NULL;
586   }
587
588   if (dpy->Driver) {
589      drv = dpy->Driver;
590      /* no re-matching? */
591      if (!drv->API.Initialize(drv, dpy))
592         drv = NULL;
593      return drv;
594   }
595
596   while (i < _eglModules->Size) {
597      _EGLModule *mod = (_EGLModule *) _eglModules->Elements[i];
598
599      if (!_eglLoadModule(mod)) {
600         /* remove invalid modules */
601         _eglEraseArray(_eglModules, i, _eglFreeModule);
602         continue;
603      }
604
605      if (mod->Driver->API.Initialize(mod->Driver, dpy)) {
606         drv = mod->Driver;
607         break;
608      }
609      else {
610         i++;
611      }
612   }
613
614   return drv;
615}
616
617
618/**
619 * Match a display to a driver.  The display is initialized unless test_only is
620 * true.  The matching is done by finding the first driver that can initialize
621 * the display.
622 */
623_EGLDriver *
624_eglMatchDriver(_EGLDisplay *dpy, EGLBoolean test_only)
625{
626   _EGLDriver *best_drv;
627
628   assert(!dpy->Initialized);
629
630   _eglLockMutex(&_eglModuleMutex);
631
632   /* set options */
633   dpy->Options.TestOnly = test_only;
634   dpy->Options.UseFallback = EGL_FALSE;
635
636   best_drv = _eglMatchAndInitialize(dpy);
637   if (!best_drv) {
638      dpy->Options.UseFallback = EGL_TRUE;
639      best_drv = _eglMatchAndInitialize(dpy);
640   }
641
642   _eglUnlockMutex(&_eglModuleMutex);
643
644   if (best_drv) {
645      _eglLog(_EGL_DEBUG, "the best driver is %s%s",
646            best_drv->Name, (test_only) ? " (test only) " : "");
647      if (!test_only) {
648         dpy->Driver = best_drv;
649         dpy->Initialized = EGL_TRUE;
650      }
651   }
652
653   return best_drv;
654}
655
656
657__eglMustCastToProperFunctionPointerType
658_eglGetDriverProc(const char *procname)
659{
660   EGLint i;
661   _EGLProc proc = NULL;
662
663   if (!_eglModules) {
664      /* load the driver for the default display */
665      EGLDisplay egldpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
666      _EGLDisplay *dpy = _eglLookupDisplay(egldpy);
667      if (!dpy || !_eglMatchDriver(dpy, EGL_TRUE))
668         return NULL;
669   }
670
671   for (i = 0; i < _eglModules->Size; i++) {
672      _EGLModule *mod = (_EGLModule *) _eglModules->Elements[i];
673
674      if (!mod->Driver)
675         break;
676      proc = mod->Driver->API.GetProcAddress(mod->Driver, procname);
677      if (proc)
678         break;
679   }
680
681   return proc;
682}
683
684
685/**
686 * Unload all drivers.
687 */
688void
689_eglUnloadDrivers(void)
690{
691   /* this is called at atexit time */
692   if (_eglModules) {
693      _eglDestroyArray(_eglModules, _eglFreeModule);
694      _eglModules = NULL;
695   }
696}
697
698
699/**
700 * Invoke a callback function on each EGL search path.
701 *
702 * The first argument of the callback function is the name of the search path.
703 * The second argument is the length of the name.
704 */
705void
706_eglSearchPathForEach(EGLBoolean (*callback)(const char *, size_t, void *),
707                      void *callback_data)
708{
709   const char *search_path = _eglGetSearchPath();
710   _eglPreloadForEach(search_path, callback, callback_data);
711}
712