13464ebd5Sriastradh/*
23464ebd5Sriastradh * Copyright © 2011 Intel Corporation
37ec681f3Smrg * Copyright © 2021 NVIDIA Corporation
43464ebd5Sriastradh *
53464ebd5Sriastradh * Permission is hereby granted, free of charge, to any person obtaining a
63464ebd5Sriastradh * copy of this software and associated documentation files (the "Software"),
73464ebd5Sriastradh * to deal in the Software without restriction, including without limitation
83464ebd5Sriastradh * the rights to use, copy, modify, merge, publish, distribute, sublicense,
93464ebd5Sriastradh * and/or sell copies of the Software, and to permit persons to whom the
103464ebd5Sriastradh * Software is furnished to do so, subject to the following conditions:
113464ebd5Sriastradh *
123464ebd5Sriastradh * The above copyright notice and this permission notice (including the next
133464ebd5Sriastradh * paragraph) shall be included in all copies or substantial portions of the
143464ebd5Sriastradh * Software.
153464ebd5Sriastradh *
163464ebd5Sriastradh * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
173464ebd5Sriastradh * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
183464ebd5Sriastradh * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
193464ebd5Sriastradh * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
203464ebd5Sriastradh * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
213464ebd5Sriastradh * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
223464ebd5Sriastradh * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
233464ebd5Sriastradh * DEALINGS IN THE SOFTWARE.
243464ebd5Sriastradh *
253464ebd5Sriastradh * Authors:
263464ebd5Sriastradh *    Benjamin Franzke <benjaminfranzke@googlemail.com>
277ec681f3Smrg *    James Jones <jajones@nvidia.com>
283464ebd5Sriastradh */
293464ebd5Sriastradh
303464ebd5Sriastradh#include <stdio.h>
313464ebd5Sriastradh#include <stddef.h>
323464ebd5Sriastradh#include <stdlib.h>
333464ebd5Sriastradh#include <string.h>
343464ebd5Sriastradh#include <limits.h>
357ec681f3Smrg#include <assert.h>
367ec681f3Smrg#include <dlfcn.h>
377ec681f3Smrg#include <xf86drm.h>
383464ebd5Sriastradh
397ec681f3Smrg#include "loader.h"
403464ebd5Sriastradh#include "backend.h"
413464ebd5Sriastradh
423464ebd5Sriastradh#define ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0]))
437ec681f3Smrg#define VER_MIN(a, b) ((a) < (b) ? (a) : (b))
443464ebd5Sriastradh
453464ebd5Sriastradhextern const struct gbm_backend gbm_dri_backend;
463464ebd5Sriastradh
477ec681f3Smrgstruct gbm_backend_desc {
483464ebd5Sriastradh   const char *name;
497ec681f3Smrg   const struct gbm_backend *backend;
507ec681f3Smrg   void *lib;
513464ebd5Sriastradh};
523464ebd5Sriastradh
537ec681f3Smrgstatic const struct gbm_backend_desc builtin_backends[] = {
547ec681f3Smrg   { "dri", &gbm_dri_backend },
553464ebd5Sriastradh};
563464ebd5Sriastradh
577ec681f3Smrg#define BACKEND_LIB_SUFFIX "_gbm"
587ec681f3Smrgstatic const char *backend_search_path_vars[] = {
597ec681f3Smrg   "GBM_BACKENDS_PATH",
607ec681f3Smrg   NULL
617ec681f3Smrg};
627ec681f3Smrg
637ec681f3Smrgstatic void
647ec681f3Smrgfree_backend_desc(const struct gbm_backend_desc *backend_desc)
653464ebd5Sriastradh{
667ec681f3Smrg   assert(backend_desc->lib);
673464ebd5Sriastradh
687ec681f3Smrg   dlclose(backend_desc->lib);
697ec681f3Smrg   free((void *)backend_desc->name);
707ec681f3Smrg   free((void *)backend_desc);
717ec681f3Smrg}
727ec681f3Smrg
737ec681f3Smrgstatic struct gbm_backend_desc *
747ec681f3Smrgcreate_backend_desc(const char *name,
757ec681f3Smrg                    const struct gbm_backend *backend,
767ec681f3Smrg                    void *lib)
777ec681f3Smrg{
787ec681f3Smrg   struct gbm_backend_desc *new_desc = calloc(1, sizeof(*new_desc));
797ec681f3Smrg
807ec681f3Smrg   if (!new_desc)
813464ebd5Sriastradh      return NULL;
823464ebd5Sriastradh
837ec681f3Smrg   new_desc->name = strdup(name);
847ec681f3Smrg
857ec681f3Smrg   if (!new_desc->name) {
867ec681f3Smrg      free(new_desc);
877ec681f3Smrg      return NULL;
883464ebd5Sriastradh   }
893464ebd5Sriastradh
907ec681f3Smrg   new_desc->backend = backend;
917ec681f3Smrg   new_desc->lib = lib;
927ec681f3Smrg
937ec681f3Smrg   return new_desc;
947ec681f3Smrg}
957ec681f3Smrg
967ec681f3Smrgstatic struct gbm_device *
977ec681f3Smrgbackend_create_device(const struct gbm_backend_desc *bd, int fd)
987ec681f3Smrg{
997ec681f3Smrg   const uint32_t abi_ver = VER_MIN(GBM_BACKEND_ABI_VERSION,
1007ec681f3Smrg                                    bd->backend->v0.backend_version);
1017ec681f3Smrg   struct gbm_device *dev = bd->backend->v0.create_device(fd, abi_ver);
1027ec681f3Smrg
1037ec681f3Smrg   if (dev) {
1047ec681f3Smrg      if (abi_ver != dev->v0.backend_version) {
1057ec681f3Smrg         _gbm_device_destroy(dev);
1067ec681f3Smrg         return NULL;
1077ec681f3Smrg      }
1087ec681f3Smrg      dev->v0.backend_desc = bd;
1097ec681f3Smrg   }
1107ec681f3Smrg
1117ec681f3Smrg   return dev;
1127ec681f3Smrg}
1137ec681f3Smrg
1147ec681f3Smrgstatic struct gbm_device *
1157ec681f3Smrgload_backend(void *lib, int fd, const char *name)
1167ec681f3Smrg{
1177ec681f3Smrg   struct gbm_device *dev = NULL;
1187ec681f3Smrg   struct gbm_backend_desc *backend_desc;
1197ec681f3Smrg   const struct gbm_backend *gbm_backend;
1207ec681f3Smrg   GBM_GET_BACKEND_PROC_PTR get_backend;
1217ec681f3Smrg
1227ec681f3Smrg   get_backend = dlsym(lib, GBM_GET_BACKEND_PROC_NAME);
1237ec681f3Smrg
1247ec681f3Smrg   if (!get_backend)
1257ec681f3Smrg      goto fail;
1267ec681f3Smrg
1277ec681f3Smrg   gbm_backend = get_backend(&gbm_core);
1287ec681f3Smrg   backend_desc = create_backend_desc(name, gbm_backend, lib);
1297ec681f3Smrg
1307ec681f3Smrg   if (!backend_desc)
1317ec681f3Smrg      goto fail;
1327ec681f3Smrg
1337ec681f3Smrg   dev = backend_create_device(backend_desc, fd);
1347ec681f3Smrg
1357ec681f3Smrg   if (!dev)
1367ec681f3Smrg      free_backend_desc(backend_desc);
1377ec681f3Smrg
1387ec681f3Smrg   return dev;
1397ec681f3Smrg
1407ec681f3Smrgfail:
1417ec681f3Smrg   dlclose(lib);
1427ec681f3Smrg   return NULL;
1433464ebd5Sriastradh}
1443464ebd5Sriastradh
1457ec681f3Smrgstatic struct gbm_device *
1467ec681f3Smrgfind_backend(const char *name, int fd)
1473464ebd5Sriastradh{
1487ec681f3Smrg   struct gbm_device *dev = NULL;
1497ec681f3Smrg   const struct gbm_backend_desc *bd;
1507ec681f3Smrg   void *lib;
15101e04c3fSmrg   unsigned i;
1523464ebd5Sriastradh
1537ec681f3Smrg   for (i = 0; i < ARRAY_SIZE(builtin_backends); ++i) {
1547ec681f3Smrg      bd = &builtin_backends[i];
1557ec681f3Smrg
1567ec681f3Smrg      if (name && strcmp(bd->name, name))
1577ec681f3Smrg         continue;
1587ec681f3Smrg
1597ec681f3Smrg      dev = backend_create_device(bd, fd);
1607ec681f3Smrg
1617ec681f3Smrg      if (dev)
1623464ebd5Sriastradh         break;
1633464ebd5Sriastradh   }
1643464ebd5Sriastradh
1657ec681f3Smrg   if (name && !dev) {
1667ec681f3Smrg      lib = loader_open_driver_lib(name, BACKEND_LIB_SUFFIX,
1677ec681f3Smrg                                   backend_search_path_vars,
1687ec681f3Smrg                                   DEFAULT_BACKENDS_PATH,
1697ec681f3Smrg                                   true);
1707ec681f3Smrg
1717ec681f3Smrg      if (lib)
1727ec681f3Smrg         dev = load_backend(lib, fd, name);
1737ec681f3Smrg   }
1747ec681f3Smrg
1757ec681f3Smrg   return dev;
1763464ebd5Sriastradh}
1773464ebd5Sriastradh
1787ec681f3Smrgstatic struct gbm_device *
1797ec681f3Smrgoverride_backend(int fd)
1803464ebd5Sriastradh{
1813464ebd5Sriastradh   struct gbm_device *dev = NULL;
1823464ebd5Sriastradh   const char *b;
1833464ebd5Sriastradh
1843464ebd5Sriastradh   b = getenv("GBM_BACKEND");
1853464ebd5Sriastradh   if (b)
1867ec681f3Smrg      dev = find_backend(b, fd);
1873464ebd5Sriastradh
1887ec681f3Smrg   return dev;
1897ec681f3Smrg}
1903464ebd5Sriastradh
1917ec681f3Smrgstatic struct gbm_device *
1927ec681f3Smrgbackend_from_driver_name(int fd)
1937ec681f3Smrg{
1947ec681f3Smrg   struct gbm_device *dev = NULL;
1957ec681f3Smrg   drmVersionPtr v = drmGetVersion(fd);
1967ec681f3Smrg   void *lib;
1977ec681f3Smrg
1987ec681f3Smrg   if (!v)
1997ec681f3Smrg      return NULL;
2007ec681f3Smrg
2017ec681f3Smrg   lib = loader_open_driver_lib(v->name, BACKEND_LIB_SUFFIX,
2027ec681f3Smrg                                backend_search_path_vars,
2037ec681f3Smrg                                DEFAULT_BACKENDS_PATH,
2047ec681f3Smrg                                false);
2057ec681f3Smrg
2067ec681f3Smrg   if (lib)
2077ec681f3Smrg      dev = load_backend(lib, fd, v->name);
2087ec681f3Smrg
2097ec681f3Smrg   drmFreeVersion(v);
2107ec681f3Smrg
2117ec681f3Smrg   return dev;
2127ec681f3Smrg}
2137ec681f3Smrg
2147ec681f3Smrgstruct gbm_device *
2157ec681f3Smrg_gbm_create_device(int fd)
2167ec681f3Smrg{
2177ec681f3Smrg   struct gbm_device *dev;
2187ec681f3Smrg
2197ec681f3Smrg   dev = override_backend(fd);
2207ec681f3Smrg
2217ec681f3Smrg   if (!dev)
2227ec681f3Smrg      dev = backend_from_driver_name(fd);
2237ec681f3Smrg
2247ec681f3Smrg   if (!dev)
2257ec681f3Smrg      dev = find_backend(NULL, fd);
2263464ebd5Sriastradh
2273464ebd5Sriastradh   return dev;
2283464ebd5Sriastradh}
2297ec681f3Smrg
2307ec681f3Smrgvoid
2317ec681f3Smrg_gbm_device_destroy(struct gbm_device *gbm)
2327ec681f3Smrg{
2337ec681f3Smrg   const struct gbm_backend_desc *backend_desc = gbm->v0.backend_desc;
2347ec681f3Smrg   gbm->v0.destroy(gbm);
2357ec681f3Smrg
2367ec681f3Smrg   if (backend_desc && backend_desc->lib)
2377ec681f3Smrg      free_backend_desc(backend_desc);
2387ec681f3Smrg}
239