/* * Copyright © 2011 Intel Corporation * Copyright © 2021 NVIDIA Corporation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * * Authors: * Benjamin Franzke * James Jones */ #include #include #include #include #include #include #include #include #include "loader.h" #include "backend.h" #define ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0])) #define VER_MIN(a, b) ((a) < (b) ? (a) : (b)) extern const struct gbm_backend gbm_dri_backend; struct gbm_backend_desc { const char *name; const struct gbm_backend *backend; void *lib; }; static const struct gbm_backend_desc builtin_backends[] = { { "dri", &gbm_dri_backend }, }; #define BACKEND_LIB_SUFFIX "_gbm" static const char *backend_search_path_vars[] = { "GBM_BACKENDS_PATH", NULL }; static void free_backend_desc(const struct gbm_backend_desc *backend_desc) { assert(backend_desc->lib); dlclose(backend_desc->lib); free((void *)backend_desc->name); free((void *)backend_desc); } static struct gbm_backend_desc * create_backend_desc(const char *name, const struct gbm_backend *backend, void *lib) { struct gbm_backend_desc *new_desc = calloc(1, sizeof(*new_desc)); if (!new_desc) return NULL; new_desc->name = strdup(name); if (!new_desc->name) { free(new_desc); return NULL; } new_desc->backend = backend; new_desc->lib = lib; return new_desc; } static struct gbm_device * backend_create_device(const struct gbm_backend_desc *bd, int fd) { const uint32_t abi_ver = VER_MIN(GBM_BACKEND_ABI_VERSION, bd->backend->v0.backend_version); struct gbm_device *dev = bd->backend->v0.create_device(fd, abi_ver); if (dev) { if (abi_ver != dev->v0.backend_version) { _gbm_device_destroy(dev); return NULL; } dev->v0.backend_desc = bd; } return dev; } static struct gbm_device * load_backend(void *lib, int fd, const char *name) { struct gbm_device *dev = NULL; struct gbm_backend_desc *backend_desc; const struct gbm_backend *gbm_backend; GBM_GET_BACKEND_PROC_PTR get_backend; get_backend = dlsym(lib, GBM_GET_BACKEND_PROC_NAME); if (!get_backend) goto fail; gbm_backend = get_backend(&gbm_core); backend_desc = create_backend_desc(name, gbm_backend, lib); if (!backend_desc) goto fail; dev = backend_create_device(backend_desc, fd); if (!dev) free_backend_desc(backend_desc); return dev; fail: dlclose(lib); return NULL; } static struct gbm_device * find_backend(const char *name, int fd) { struct gbm_device *dev = NULL; const struct gbm_backend_desc *bd; void *lib; unsigned i; for (i = 0; i < ARRAY_SIZE(builtin_backends); ++i) { bd = &builtin_backends[i]; if (name && strcmp(bd->name, name)) continue; dev = backend_create_device(bd, fd); if (dev) break; } if (name && !dev) { lib = loader_open_driver_lib(name, BACKEND_LIB_SUFFIX, backend_search_path_vars, DEFAULT_BACKENDS_PATH, true); if (lib) dev = load_backend(lib, fd, name); } return dev; } static struct gbm_device * override_backend(int fd) { struct gbm_device *dev = NULL; const char *b; b = getenv("GBM_BACKEND"); if (b) dev = find_backend(b, fd); return dev; } static struct gbm_device * backend_from_driver_name(int fd) { struct gbm_device *dev = NULL; drmVersionPtr v = drmGetVersion(fd); void *lib; if (!v) return NULL; lib = loader_open_driver_lib(v->name, BACKEND_LIB_SUFFIX, backend_search_path_vars, DEFAULT_BACKENDS_PATH, false); if (lib) dev = load_backend(lib, fd, v->name); drmFreeVersion(v); return dev; } struct gbm_device * _gbm_create_device(int fd) { struct gbm_device *dev; dev = override_backend(fd); if (!dev) dev = backend_from_driver_name(fd); if (!dev) dev = find_backend(NULL, fd); return dev; } void _gbm_device_destroy(struct gbm_device *gbm) { const struct gbm_backend_desc *backend_desc = gbm->v0.backend_desc; gbm->v0.destroy(gbm); if (backend_desc && backend_desc->lib) free_backend_desc(backend_desc); }