bb0c392cf7c1175db80bd395ea2d62a09cfa8b8b
[mesa.git] / src / loader / loader.c
1 /*
2 * Copyright (C) 2013 Rob Clark <robclark@freedesktop.org>
3 *
4 * This code is derived from the following files.
5 *
6 * * src/glx/dri3_common.c
7 * Copyright © 2013 Keith Packard
8 *
9 * * src/egl/drivers/dri2/common.c
10 * * src/gbm/backends/dri/driver_name.c
11 * Copyright © 2011 Intel Corporation
12 *
13 * Authors:
14 * Kristian Høgsberg <krh@bitplanet.net>
15 * Benjamin Franzke <benjaminfranzke@googlemail.com>
16 *
17 * * src/gallium/targets/egl-static/egl.c
18 * Copyright (C) 2010-2011 LunarG Inc.
19 *
20 * Authors:
21 * Chia-I Wu <olv@lunarg.com>
22 *
23 * * src/gallium/state_trackers/egl/drm/native_drm.c
24 * Copyright (C) 2010 Chia-I Wu <olv@0xlab.org>
25 *
26 * * src/egl/drivers/dri2/platform_android.c
27 *
28 * Copyright (C) 2010-2011 Chia-I Wu <olvaffe@gmail.com>
29 * Copyright (C) 2010-2011 LunarG Inc.
30 *
31 * Based on platform_x11, which has
32 *
33 * Copyright © 2011 Intel Corporation
34 *
35 * * src/gallium/auxiliary/pipe-loader/pipe_loader_drm.c
36 * Copyright 2011 Intel Corporation
37 * Copyright 2012 Francisco Jerez
38 * All Rights Reserved.
39 *
40 * Authors:
41 * Kristian Høgsberg <krh@bitplanet.net>
42 * Benjamin Franzke <benjaminfranzke@googlemail.com>
43 *
44 * Permission is hereby granted, free of charge, to any person obtaining a
45 * copy of this software and associated documentation files (the "Software"),
46 * to deal in the Software without restriction, including without limitation
47 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
48 * and/or sell copies of the Software, and to permit persons to whom the
49 * Software is furnished to do so, subject to the following conditions:
50 *
51 * The above copyright notice and this permission notice (including the next
52 * paragraph) shall be included in all copies or substantial portions of the
53 * Software.
54 *
55 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
56 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
57 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
58 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
59 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
60 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
61 * SOFTWARE.
62 *
63 * Authors:
64 * Rob Clark <robclark@freedesktop.org>
65 */
66
67 #include <errno.h>
68 #include <fcntl.h>
69 #include <sys/stat.h>
70 #include <stdarg.h>
71 #include <stdio.h>
72 #include <stdbool.h>
73 #include <string.h>
74 #ifdef MAJOR_IN_MKDEV
75 #include <sys/mkdev.h>
76 #endif
77 #ifdef MAJOR_IN_SYSMACROS
78 #include <sys/sysmacros.h>
79 #endif
80 #include "loader.h"
81
82 #ifdef HAVE_LIBDRM
83 #include <stdlib.h>
84 #include <unistd.h>
85 #include <xf86drm.h>
86 #ifdef USE_DRICONF
87 #include "xmlconfig.h"
88 #include "xmlpool.h"
89 #endif
90 #endif
91
92 #define __IS_LOADER
93 #include "pci_id_driver_map.h"
94
95 static void default_logger(int level, const char *fmt, ...)
96 {
97 if (level <= _LOADER_WARNING) {
98 va_list args;
99 va_start(args, fmt);
100 vfprintf(stderr, fmt, args);
101 va_end(args);
102 }
103 }
104
105 static void (*log_)(int level, const char *fmt, ...) = default_logger;
106
107 int
108 loader_open_device(const char *device_name)
109 {
110 int fd;
111 #ifdef O_CLOEXEC
112 fd = open(device_name, O_RDWR | O_CLOEXEC);
113 if (fd == -1 && errno == EINVAL)
114 #endif
115 {
116 fd = open(device_name, O_RDWR);
117 if (fd != -1)
118 fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
119 }
120 return fd;
121 }
122
123 #if defined(HAVE_LIBDRM)
124 #ifdef USE_DRICONF
125 static const char __driConfigOptionsLoader[] =
126 DRI_CONF_BEGIN
127 DRI_CONF_SECTION_INITIALIZATION
128 DRI_CONF_DEVICE_ID_PATH_TAG()
129 DRI_CONF_SECTION_END
130 DRI_CONF_END;
131
132 static char *loader_get_dri_config_device_id(void)
133 {
134 driOptionCache defaultInitOptions;
135 driOptionCache userInitOptions;
136 char *prime = NULL;
137
138 driParseOptionInfo(&defaultInitOptions, __driConfigOptionsLoader);
139 driParseConfigFiles(&userInitOptions, &defaultInitOptions, 0, "loader");
140 if (driCheckOption(&userInitOptions, "device_id", DRI_STRING))
141 prime = strdup(driQueryOptionstr(&userInitOptions, "device_id"));
142 driDestroyOptionCache(&userInitOptions);
143 driDestroyOptionInfo(&defaultInitOptions);
144
145 return prime;
146 }
147 #endif
148
149 static char *drm_construct_id_path_tag(drmDevicePtr device)
150 {
151 /* Length of "pci-xxxx_xx_xx_x\0" */
152 #define PCI_ID_PATH_TAG_LENGTH 17
153 char *tag = NULL;
154
155 if (device->bustype == DRM_BUS_PCI) {
156 tag = calloc(PCI_ID_PATH_TAG_LENGTH, sizeof(char));
157 if (tag == NULL)
158 return NULL;
159
160 snprintf(tag, PCI_ID_PATH_TAG_LENGTH, "pci-%04x_%02x_%02x_%1u",
161 device->businfo.pci->domain, device->businfo.pci->bus,
162 device->businfo.pci->dev, device->businfo.pci->func);
163 }
164 return tag;
165 }
166
167 static bool drm_device_matches_tag(drmDevicePtr device, const char *prime_tag)
168 {
169 char *tag = drm_construct_id_path_tag(device);
170 int ret;
171
172 if (tag == NULL)
173 return false;
174
175 ret = strcmp(tag, prime_tag);
176
177 free(tag);
178 return ret == 0;
179 }
180
181 static char *drm_get_id_path_tag_for_fd(int fd)
182 {
183 drmDevicePtr device;
184 char *tag;
185
186 if (drmGetDevice(fd, &device) != 0)
187 return NULL;
188
189 tag = drm_construct_id_path_tag(device);
190 drmFreeDevice(&device);
191 return tag;
192 }
193
194 int loader_get_user_preferred_fd(int default_fd, int *different_device)
195 {
196 /* Arbitrary "maximum" value of drm devices. */
197 #define MAX_DRM_DEVICES 32
198 const char *dri_prime = getenv("DRI_PRIME");
199 char *default_tag, *prime = NULL;
200 drmDevicePtr devices[MAX_DRM_DEVICES];
201 int i, num_devices, fd;
202 bool found = false;
203
204 if (dri_prime)
205 prime = strdup(dri_prime);
206 #ifdef USE_DRICONF
207 else
208 prime = loader_get_dri_config_device_id();
209 #endif
210
211 if (prime == NULL) {
212 *different_device = 0;
213 return default_fd;
214 }
215
216 default_tag = drm_get_id_path_tag_for_fd(default_fd);
217 if (default_tag == NULL)
218 goto err;
219
220 num_devices = drmGetDevices(devices, MAX_DRM_DEVICES);
221 if (num_devices < 0)
222 goto err;
223
224 /* two format are supported:
225 * "1": choose any other card than the card used by default.
226 * id_path_tag: (for example "pci-0000_02_00_0") choose the card
227 * with this id_path_tag.
228 */
229 if (!strcmp(prime,"1")) {
230 /* Hmm... detection for 2-7 seems to be broken. Oh well ...
231 * Pick the first render device that is not our own.
232 */
233 for (i = 0; i < num_devices; i++) {
234 if (devices[i]->available_nodes & 1 << DRM_NODE_RENDER &&
235 !drm_device_matches_tag(devices[i], default_tag)) {
236
237 found = true;
238 break;
239 }
240 }
241 } else {
242 for (i = 0; i < num_devices; i++) {
243 if (devices[i]->available_nodes & 1 << DRM_NODE_RENDER &&
244 drm_device_matches_tag(devices[i], prime)) {
245
246 found = true;
247 break;
248 }
249 }
250 }
251
252 if (!found) {
253 drmFreeDevices(devices, num_devices);
254 goto err;
255 }
256
257 fd = loader_open_device(devices[i]->nodes[DRM_NODE_RENDER]);
258 drmFreeDevices(devices, num_devices);
259 if (fd < 0)
260 goto err;
261
262 close(default_fd);
263
264 *different_device = !!strcmp(default_tag, prime);
265
266 free(default_tag);
267 free(prime);
268 return fd;
269
270 err:
271 *different_device = 0;
272
273 free(default_tag);
274 free(prime);
275 return default_fd;
276 }
277 #else
278 int loader_get_user_preferred_fd(int default_fd, int *different_device)
279 {
280 *different_device = 0;
281 return default_fd;
282 }
283 #endif
284
285 #if defined(HAVE_LIBDRM)
286 static int
287 dev_node_from_fd(int fd, unsigned int *maj, unsigned int *min)
288 {
289 struct stat buf;
290
291 if (fstat(fd, &buf) < 0) {
292 log_(_LOADER_WARNING, "MESA-LOADER: failed to stat fd %d\n", fd);
293 return -1;
294 }
295
296 if (!S_ISCHR(buf.st_mode)) {
297 log_(_LOADER_WARNING, "MESA-LOADER: fd %d not a character device\n", fd);
298 return -1;
299 }
300
301 *maj = major(buf.st_rdev);
302 *min = minor(buf.st_rdev);
303
304 return 0;
305 }
306 #endif
307
308 #if defined(HAVE_LIBDRM)
309
310 static int
311 drm_get_pci_id_for_fd(int fd, int *vendor_id, int *chip_id)
312 {
313 drmDevicePtr device;
314 int ret;
315
316 if (drmGetDevice(fd, &device) == 0) {
317 if (device->bustype == DRM_BUS_PCI) {
318 *vendor_id = device->deviceinfo.pci->vendor_id;
319 *chip_id = device->deviceinfo.pci->device_id;
320 ret = 1;
321 }
322 else {
323 log_(_LOADER_WARNING, "MESA-LOADER: device is not located on the PCI bus\n");
324 ret = 0;
325 }
326 drmFreeDevice(&device);
327 }
328 else {
329 log_(_LOADER_WARNING, "MESA-LOADER: failed to retrieve device information\n");
330 ret = 0;
331 }
332
333 return ret;
334 }
335 #endif
336
337
338 int
339 loader_get_pci_id_for_fd(int fd, int *vendor_id, int *chip_id)
340 {
341 #if HAVE_LIBDRM
342 if (drm_get_pci_id_for_fd(fd, vendor_id, chip_id))
343 return 1;
344 #endif
345 return 0;
346 }
347
348
349 #if defined(HAVE_LIBDRM)
350 static char *
351 drm_get_device_name_for_fd(int fd)
352 {
353 unsigned int maj, min;
354 char buf[0x40];
355 int n;
356
357 if (dev_node_from_fd(fd, &maj, &min) < 0)
358 return NULL;
359
360 n = snprintf(buf, sizeof(buf), DRM_DEV_NAME, DRM_DIR_NAME, min);
361 if (n == -1 || n >= sizeof(buf))
362 return NULL;
363
364 return strdup(buf);
365 }
366 #endif
367
368 char *
369 loader_get_device_name_for_fd(int fd)
370 {
371 char *result = NULL;
372
373 #if HAVE_LIBDRM
374 if ((result = drm_get_device_name_for_fd(fd)))
375 return result;
376 #endif
377 return result;
378 }
379
380 char *
381 loader_get_driver_for_fd(int fd, unsigned driver_types)
382 {
383 int vendor_id, chip_id, i, j;
384 char *driver = NULL;
385
386 if (!driver_types)
387 driver_types = _LOADER_GALLIUM | _LOADER_DRI;
388
389 if (!loader_get_pci_id_for_fd(fd, &vendor_id, &chip_id)) {
390
391 #if HAVE_LIBDRM
392 /* fallback to drmGetVersion(): */
393 drmVersionPtr version = drmGetVersion(fd);
394
395 if (!version) {
396 log_(_LOADER_WARNING, "failed to get driver name for fd %d\n", fd);
397 return NULL;
398 }
399
400 driver = strndup(version->name, version->name_len);
401 log_(_LOADER_INFO, "using driver %s for %d\n", driver, fd);
402
403 drmFreeVersion(version);
404 #endif
405
406 return driver;
407 }
408
409 for (i = 0; driver_map[i].driver; i++) {
410 if (vendor_id != driver_map[i].vendor_id)
411 continue;
412
413 if (!(driver_types & driver_map[i].driver_types))
414 continue;
415
416 if (driver_map[i].predicate && !driver_map[i].predicate(fd))
417 continue;
418
419 if (driver_map[i].num_chips_ids == -1) {
420 driver = strdup(driver_map[i].driver);
421 goto out;
422 }
423
424 for (j = 0; j < driver_map[i].num_chips_ids; j++)
425 if (driver_map[i].chip_ids[j] == chip_id) {
426 driver = strdup(driver_map[i].driver);
427 goto out;
428 }
429 }
430
431 out:
432 log_(driver ? _LOADER_DEBUG : _LOADER_WARNING,
433 "pci id for fd %d: %04x:%04x, driver %s\n",
434 fd, vendor_id, chip_id, driver);
435 return driver;
436 }
437
438 void
439 loader_set_logger(void (*logger)(int level, const char *fmt, ...))
440 {
441 log_ = logger;
442 }