loader: remove libudev_get_device_name_for_fd and related code
[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_SYSFS) || 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 HAVE_SYSFS
350 static char *
351 sysfs_get_device_name_for_fd(int fd)
352 {
353 char *device_name = NULL;
354 unsigned int maj, min;
355 FILE *f;
356 char buf[0x40];
357 static const char match[9] = "\nDEVNAME=";
358 int expected = 1;
359
360 if (dev_node_from_fd(fd, &maj, &min) < 0)
361 return NULL;
362
363 snprintf(buf, sizeof(buf), "/sys/dev/char/%d:%d/uevent", maj, min);
364 if (!(f = fopen(buf, "r")))
365 return NULL;
366
367 while (expected < sizeof(match)) {
368 int c = getc(f);
369
370 if (c == EOF) {
371 fclose(f);
372 return NULL;
373 } else if (c == match[expected] )
374 expected++;
375 else
376 expected = 0;
377 }
378
379 strcpy(buf, "/dev/");
380 if (fgets(buf + 5, sizeof(buf) - 5, f)) {
381 buf[strcspn(buf, "\n")] = '\0';
382 device_name = strdup(buf);
383 }
384
385 fclose(f);
386 return device_name;
387 }
388 #endif
389
390 #if defined(HAVE_LIBDRM)
391 static char *
392 drm_get_device_name_for_fd(int fd)
393 {
394 unsigned int maj, min;
395 char buf[0x40];
396 int n;
397
398 if (dev_node_from_fd(fd, &maj, &min) < 0)
399 return NULL;
400
401 n = snprintf(buf, sizeof(buf), DRM_DEV_NAME, DRM_DIR_NAME, min);
402 if (n == -1 || n >= sizeof(buf))
403 return NULL;
404
405 return strdup(buf);
406 }
407 #endif
408
409 char *
410 loader_get_device_name_for_fd(int fd)
411 {
412 char *result = NULL;
413
414 #if HAVE_SYSFS
415 if ((result = sysfs_get_device_name_for_fd(fd)))
416 return result;
417 #endif
418 #if HAVE_LIBDRM
419 if ((result = drm_get_device_name_for_fd(fd)))
420 return result;
421 #endif
422 return result;
423 }
424
425 char *
426 loader_get_driver_for_fd(int fd, unsigned driver_types)
427 {
428 int vendor_id, chip_id, i, j;
429 char *driver = NULL;
430
431 if (!driver_types)
432 driver_types = _LOADER_GALLIUM | _LOADER_DRI;
433
434 if (!loader_get_pci_id_for_fd(fd, &vendor_id, &chip_id)) {
435
436 #if HAVE_LIBDRM
437 /* fallback to drmGetVersion(): */
438 drmVersionPtr version = drmGetVersion(fd);
439
440 if (!version) {
441 log_(_LOADER_WARNING, "failed to get driver name for fd %d\n", fd);
442 return NULL;
443 }
444
445 driver = strndup(version->name, version->name_len);
446 log_(_LOADER_INFO, "using driver %s for %d\n", driver, fd);
447
448 drmFreeVersion(version);
449 #endif
450
451 return driver;
452 }
453
454 for (i = 0; driver_map[i].driver; i++) {
455 if (vendor_id != driver_map[i].vendor_id)
456 continue;
457
458 if (!(driver_types & driver_map[i].driver_types))
459 continue;
460
461 if (driver_map[i].predicate && !driver_map[i].predicate(fd))
462 continue;
463
464 if (driver_map[i].num_chips_ids == -1) {
465 driver = strdup(driver_map[i].driver);
466 goto out;
467 }
468
469 for (j = 0; j < driver_map[i].num_chips_ids; j++)
470 if (driver_map[i].chip_ids[j] == chip_id) {
471 driver = strdup(driver_map[i].driver);
472 goto out;
473 }
474 }
475
476 out:
477 log_(driver ? _LOADER_DEBUG : _LOADER_WARNING,
478 "pci id for fd %d: %04x:%04x, driver %s\n",
479 fd, vendor_id, chip_id, driver);
480 return driver;
481 }
482
483 void
484 loader_set_logger(void (*logger)(int level, const char *fmt, ...))
485 {
486 log_ = logger;
487 }