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