xref: /third_party/mesa3d/src/gbm/main/backend.c (revision bf215546)
1/*
2 * Copyright © 2011 Intel Corporation
3 * Copyright © 2021 NVIDIA Corporation
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice (including the next
13 * paragraph) shall be included in all copies or substantial portions of the
14 * Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
20 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
21 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23 * DEALINGS IN THE SOFTWARE.
24 *
25 * Authors:
26 *    Benjamin Franzke <benjaminfranzke@googlemail.com>
27 *    James Jones <jajones@nvidia.com>
28 */
29
30#include <stdio.h>
31#include <stddef.h>
32#include <stdlib.h>
33#include <string.h>
34#include <limits.h>
35#include <assert.h>
36#include <dlfcn.h>
37#include <xf86drm.h>
38
39#include "loader.h"
40#include "backend.h"
41
42#define ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0]))
43#define VER_MIN(a, b) ((a) < (b) ? (a) : (b))
44
45extern const struct gbm_backend gbm_dri_backend;
46
47struct gbm_backend_desc {
48   const char *name;
49   const struct gbm_backend *backend;
50   void *lib;
51};
52
53static const struct gbm_backend_desc builtin_backends[] = {
54   { "dri", &gbm_dri_backend },
55};
56
57#define BACKEND_LIB_SUFFIX "_gbm"
58static const char *backend_search_path_vars[] = {
59   "GBM_BACKENDS_PATH",
60   NULL
61};
62
63static void
64free_backend_desc(const struct gbm_backend_desc *backend_desc)
65{
66   assert(backend_desc->lib);
67
68   dlclose(backend_desc->lib);
69   free((void *)backend_desc->name);
70   free((void *)backend_desc);
71}
72
73static struct gbm_backend_desc *
74create_backend_desc(const char *name,
75                    const struct gbm_backend *backend,
76                    void *lib)
77{
78   struct gbm_backend_desc *new_desc = calloc(1, sizeof(*new_desc));
79
80   if (!new_desc)
81      return NULL;
82
83   new_desc->name = strdup(name);
84
85   if (!new_desc->name) {
86      free(new_desc);
87      return NULL;
88   }
89
90   new_desc->backend = backend;
91   new_desc->lib = lib;
92
93   return new_desc;
94}
95
96static struct gbm_device *
97backend_create_device(const struct gbm_backend_desc *bd, int fd)
98{
99   const uint32_t abi_ver = VER_MIN(GBM_BACKEND_ABI_VERSION,
100                                    bd->backend->v0.backend_version);
101   struct gbm_device *dev = bd->backend->v0.create_device(fd, abi_ver);
102
103   if (dev) {
104      if (abi_ver != dev->v0.backend_version) {
105         _gbm_device_destroy(dev);
106         return NULL;
107      }
108      dev->v0.backend_desc = bd;
109   }
110
111   return dev;
112}
113
114static struct gbm_device *
115load_backend(void *lib, int fd, const char *name)
116{
117   struct gbm_device *dev = NULL;
118   struct gbm_backend_desc *backend_desc;
119   const struct gbm_backend *gbm_backend;
120   GBM_GET_BACKEND_PROC_PTR get_backend;
121
122   get_backend = dlsym(lib, GBM_GET_BACKEND_PROC_NAME);
123
124   if (!get_backend)
125      goto fail;
126
127   gbm_backend = get_backend(&gbm_core);
128   backend_desc = create_backend_desc(name, gbm_backend, lib);
129
130   if (!backend_desc)
131      goto fail;
132
133   dev = backend_create_device(backend_desc, fd);
134
135   if (!dev)
136      free_backend_desc(backend_desc);
137
138   return dev;
139
140fail:
141   dlclose(lib);
142   return NULL;
143}
144
145static struct gbm_device *
146find_backend(const char *name, int fd)
147{
148   struct gbm_device *dev = NULL;
149   const struct gbm_backend_desc *bd;
150   void *lib;
151   unsigned i;
152
153   for (i = 0; i < ARRAY_SIZE(builtin_backends); ++i) {
154      bd = &builtin_backends[i];
155
156      if (name && strcmp(bd->name, name))
157         continue;
158
159      dev = backend_create_device(bd, fd);
160
161      if (dev)
162         break;
163   }
164
165   if (name && !dev) {
166      lib = loader_open_driver_lib(name, BACKEND_LIB_SUFFIX,
167                                   backend_search_path_vars,
168                                   DEFAULT_BACKENDS_PATH,
169                                   true);
170
171      if (lib)
172         dev = load_backend(lib, fd, name);
173   }
174
175   return dev;
176}
177
178static struct gbm_device *
179override_backend(int fd)
180{
181   struct gbm_device *dev = NULL;
182   const char *b;
183
184   b = getenv("GBM_BACKEND");
185   if (b)
186      dev = find_backend(b, fd);
187
188   return dev;
189}
190
191static struct gbm_device *
192backend_from_driver_name(int fd)
193{
194   struct gbm_device *dev = NULL;
195   drmVersionPtr v = drmGetVersion(fd);
196   void *lib;
197
198   if (!v)
199      return NULL;
200
201   lib = loader_open_driver_lib(v->name, BACKEND_LIB_SUFFIX,
202                                backend_search_path_vars,
203                                DEFAULT_BACKENDS_PATH,
204                                false);
205
206   if (lib)
207      dev = load_backend(lib, fd, v->name);
208
209   drmFreeVersion(v);
210
211   return dev;
212}
213
214struct gbm_device *
215_gbm_create_device(int fd)
216{
217   struct gbm_device *dev;
218
219   dev = override_backend(fd);
220
221   if (!dev)
222      dev = backend_from_driver_name(fd);
223
224   if (!dev)
225      dev = find_backend(NULL, fd);
226
227   return dev;
228}
229
230void
231_gbm_device_destroy(struct gbm_device *gbm)
232{
233   const struct gbm_backend_desc *backend_desc = gbm->v0.backend_desc;
234   gbm->v0.destroy(gbm);
235
236   if (backend_desc && backend_desc->lib)
237      free_backend_desc(backend_desc);
238}
239