loader.c revision af69d88d
1af69d88dSmrg/*
2af69d88dSmrg * Copyright (C) 2013 Rob Clark <robclark@freedesktop.org>
3af69d88dSmrg *
4af69d88dSmrg * This code is derived from the following files.
5af69d88dSmrg *
6af69d88dSmrg * * src/glx/dri3_common.c
7af69d88dSmrg * Copyright © 2013 Keith Packard
8af69d88dSmrg *
9af69d88dSmrg * * src/egl/drivers/dri2/common.c
10af69d88dSmrg * * src/gbm/backends/dri/driver_name.c
11af69d88dSmrg * Copyright © 2011 Intel Corporation
12af69d88dSmrg *
13af69d88dSmrg * Authors:
14af69d88dSmrg *    Kristian Høgsberg <krh@bitplanet.net>
15af69d88dSmrg *    Benjamin Franzke <benjaminfranzke@googlemail.com>
16af69d88dSmrg *
17af69d88dSmrg * * src/gallium/targets/egl-static/egl.c
18af69d88dSmrg * Copyright (C) 2010-2011 LunarG Inc.
19af69d88dSmrg *
20af69d88dSmrg * Authors:
21af69d88dSmrg *    Chia-I Wu <olv@lunarg.com>
22af69d88dSmrg *
23af69d88dSmrg * * src/gallium/state_trackers/egl/drm/native_drm.c
24af69d88dSmrg * Copyright (C) 2010 Chia-I Wu <olv@0xlab.org>
25af69d88dSmrg *
26af69d88dSmrg * * src/egl/drivers/dri2/platform_android.c
27af69d88dSmrg *
28af69d88dSmrg * Copyright (C) 2010-2011 Chia-I Wu <olvaffe@gmail.com>
29af69d88dSmrg * Copyright (C) 2010-2011 LunarG Inc.
30af69d88dSmrg *
31af69d88dSmrg * Based on platform_x11, which has
32af69d88dSmrg *
33af69d88dSmrg * Copyright © 2011 Intel Corporation
34af69d88dSmrg *
35af69d88dSmrg * * src/gallium/auxiliary/pipe-loader/pipe_loader_drm.c
36af69d88dSmrg * Copyright 2011 Intel Corporation
37af69d88dSmrg * Copyright 2012 Francisco Jerez
38af69d88dSmrg * All Rights Reserved.
39af69d88dSmrg *
40af69d88dSmrg * Authors:
41af69d88dSmrg *    Kristian Høgsberg <krh@bitplanet.net>
42af69d88dSmrg *    Benjamin Franzke <benjaminfranzke@googlemail.com>
43af69d88dSmrg *
44af69d88dSmrg * Permission is hereby granted, free of charge, to any person obtaining a
45af69d88dSmrg * copy of this software and associated documentation files (the "Software"),
46af69d88dSmrg * to deal in the Software without restriction, including without limitation
47af69d88dSmrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
48af69d88dSmrg * and/or sell copies of the Software, and to permit persons to whom the
49af69d88dSmrg * Software is furnished to do so, subject to the following conditions:
50af69d88dSmrg *
51af69d88dSmrg * The above copyright notice and this permission notice (including the next
52af69d88dSmrg * paragraph) shall be included in all copies or substantial portions of the
53af69d88dSmrg * Software.
54af69d88dSmrg *
55af69d88dSmrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
56af69d88dSmrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
57af69d88dSmrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
58af69d88dSmrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
59af69d88dSmrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
60af69d88dSmrg * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
61af69d88dSmrg * SOFTWARE.
62af69d88dSmrg *
63af69d88dSmrg * Authors:
64af69d88dSmrg *    Rob Clark <robclark@freedesktop.org>
65af69d88dSmrg */
66af69d88dSmrg
67af69d88dSmrg#include <stdarg.h>
68af69d88dSmrg#include <stdio.h>
69af69d88dSmrg#include <string.h>
70af69d88dSmrg#ifdef HAVE_LIBUDEV
71af69d88dSmrg#include <assert.h>
72af69d88dSmrg#include <dlfcn.h>
73af69d88dSmrg#include <fcntl.h>
74af69d88dSmrg#include <unistd.h>
75af69d88dSmrg#include <stdlib.h>
76af69d88dSmrg#include <errno.h>
77af69d88dSmrg#ifdef USE_DRICONF
78af69d88dSmrg#include "xmlconfig.h"
79af69d88dSmrg#include "xmlpool.h"
80af69d88dSmrg#endif
81af69d88dSmrg#endif
82af69d88dSmrg#ifdef HAVE_SYSFS
83af69d88dSmrg#include <sys/stat.h>
84af69d88dSmrg#include <sys/types.h>
85af69d88dSmrg#endif
86af69d88dSmrg#include "loader.h"
87af69d88dSmrg
88af69d88dSmrg#ifndef __NOT_HAVE_DRM_H
89af69d88dSmrg#include <xf86drm.h>
90af69d88dSmrg#endif
91af69d88dSmrg
92af69d88dSmrg#define __IS_LOADER
93af69d88dSmrg#include "pci_id_driver_map.h"
94af69d88dSmrg
95af69d88dSmrgstatic void default_logger(int level, const char *fmt, ...)
96af69d88dSmrg{
97af69d88dSmrg   if (level <= _LOADER_WARNING) {
98af69d88dSmrg      va_list args;
99af69d88dSmrg      va_start(args, fmt);
100af69d88dSmrg      vfprintf(stderr, fmt, args);
101af69d88dSmrg      va_end(args);
102af69d88dSmrg   }
103af69d88dSmrg}
104af69d88dSmrg
105af69d88dSmrgstatic void (*log_)(int level, const char *fmt, ...) = default_logger;
106af69d88dSmrg
107af69d88dSmrg#ifdef HAVE_LIBUDEV
108af69d88dSmrg#include <libudev.h>
109af69d88dSmrg
110af69d88dSmrgstatic void *udev_handle = NULL;
111af69d88dSmrg
112af69d88dSmrgstatic void *
113af69d88dSmrgudev_dlopen_handle(void)
114af69d88dSmrg{
115af69d88dSmrg   if (!udev_handle) {
116af69d88dSmrg      udev_handle = dlopen("libudev.so.1", RTLD_LOCAL | RTLD_LAZY);
117af69d88dSmrg
118af69d88dSmrg      if (!udev_handle) {
119af69d88dSmrg         /* libudev.so.1 changed the return types of the two unref functions
120af69d88dSmrg          * from voids to pointers.  We don't use those return values, and the
121af69d88dSmrg          * only ABI I've heard that cares about this kind of change (calling
122af69d88dSmrg          * a function with a void * return that actually only returns void)
123af69d88dSmrg          * might be ia64.
124af69d88dSmrg          */
125af69d88dSmrg         udev_handle = dlopen("libudev.so.0", RTLD_LOCAL | RTLD_LAZY);
126af69d88dSmrg
127af69d88dSmrg         if (!udev_handle) {
128af69d88dSmrg            log_(_LOADER_WARNING, "Couldn't dlopen libudev.so.1 or "
129af69d88dSmrg                 "libudev.so.0, driver detection may be broken.\n");
130af69d88dSmrg         }
131af69d88dSmrg      }
132af69d88dSmrg   }
133af69d88dSmrg
134af69d88dSmrg   return udev_handle;
135af69d88dSmrg}
136af69d88dSmrg
137af69d88dSmrgstatic int dlsym_failed = 0;
138af69d88dSmrg
139af69d88dSmrgstatic void *
140af69d88dSmrgchecked_dlsym(void *dlopen_handle, const char *name)
141af69d88dSmrg{
142af69d88dSmrg   void *result = dlsym(dlopen_handle, name);
143af69d88dSmrg   if (!result)
144af69d88dSmrg      dlsym_failed = 1;
145af69d88dSmrg   return result;
146af69d88dSmrg}
147af69d88dSmrg
148af69d88dSmrg#define UDEV_SYMBOL(ret, name, args) \
149af69d88dSmrg   ret (*name) args = checked_dlsym(udev_dlopen_handle(), #name);
150af69d88dSmrg
151af69d88dSmrg
152af69d88dSmrgstatic inline struct udev_device *
153af69d88dSmrgudev_device_new_from_fd(struct udev *udev, int fd)
154af69d88dSmrg{
155af69d88dSmrg   struct udev_device *device;
156af69d88dSmrg   struct stat buf;
157af69d88dSmrg   UDEV_SYMBOL(struct udev_device *, udev_device_new_from_devnum,
158af69d88dSmrg               (struct udev *udev, char type, dev_t devnum));
159af69d88dSmrg
160af69d88dSmrg   if (dlsym_failed)
161af69d88dSmrg      return NULL;
162af69d88dSmrg
163af69d88dSmrg   if (fstat(fd, &buf) < 0) {
164af69d88dSmrg      log_(_LOADER_WARNING, "MESA-LOADER: failed to stat fd %d\n", fd);
165af69d88dSmrg      return NULL;
166af69d88dSmrg   }
167af69d88dSmrg
168af69d88dSmrg   device = udev_device_new_from_devnum(udev, 'c', buf.st_rdev);
169af69d88dSmrg   if (device == NULL) {
170af69d88dSmrg      log_(_LOADER_WARNING,
171af69d88dSmrg              "MESA-LOADER: could not create udev device for fd %d\n", fd);
172af69d88dSmrg      return NULL;
173af69d88dSmrg   }
174af69d88dSmrg
175af69d88dSmrg   return device;
176af69d88dSmrg}
177af69d88dSmrg
178af69d88dSmrgstatic int
179af69d88dSmrglibudev_get_pci_id_for_fd(int fd, int *vendor_id, int *chip_id)
180af69d88dSmrg{
181af69d88dSmrg   struct udev *udev = NULL;
182af69d88dSmrg   struct udev_device *device = NULL, *parent;
183af69d88dSmrg   const char *pci_id;
184af69d88dSmrg   UDEV_SYMBOL(struct udev *, udev_new, (void));
185af69d88dSmrg   UDEV_SYMBOL(struct udev_device *, udev_device_get_parent,
186af69d88dSmrg               (struct udev_device *));
187af69d88dSmrg   UDEV_SYMBOL(const char *, udev_device_get_property_value,
188af69d88dSmrg               (struct udev_device *, const char *));
189af69d88dSmrg   UDEV_SYMBOL(struct udev_device *, udev_device_unref,
190af69d88dSmrg               (struct udev_device *));
191af69d88dSmrg   UDEV_SYMBOL(struct udev *, udev_unref, (struct udev *));
192af69d88dSmrg
193af69d88dSmrg   *chip_id = -1;
194af69d88dSmrg
195af69d88dSmrg   if (dlsym_failed)
196af69d88dSmrg      return 0;
197af69d88dSmrg
198af69d88dSmrg   udev = udev_new();
199af69d88dSmrg   device = udev_device_new_from_fd(udev, fd);
200af69d88dSmrg   if (!device)
201af69d88dSmrg      goto out;
202af69d88dSmrg
203af69d88dSmrg   parent = udev_device_get_parent(device);
204af69d88dSmrg   if (parent == NULL) {
205af69d88dSmrg      log_(_LOADER_WARNING, "MESA-LOADER: could not get parent device\n");
206af69d88dSmrg      goto out;
207af69d88dSmrg   }
208af69d88dSmrg
209af69d88dSmrg   pci_id = udev_device_get_property_value(parent, "PCI_ID");
210af69d88dSmrg   if (pci_id == NULL ||
211af69d88dSmrg       sscanf(pci_id, "%x:%x", vendor_id, chip_id) != 2) {
212af69d88dSmrg      log_(_LOADER_WARNING, "MESA-LOADER: malformed or no PCI ID\n");
213af69d88dSmrg      *chip_id = -1;
214af69d88dSmrg      goto out;
215af69d88dSmrg   }
216af69d88dSmrg
217af69d88dSmrgout:
218af69d88dSmrg   if (device)
219af69d88dSmrg      udev_device_unref(device);
220af69d88dSmrg   if (udev)
221af69d88dSmrg      udev_unref(udev);
222af69d88dSmrg
223af69d88dSmrg   return (*chip_id >= 0);
224af69d88dSmrg}
225af69d88dSmrg
226af69d88dSmrgstatic char *
227af69d88dSmrgget_render_node_from_id_path_tag(struct udev *udev,
228af69d88dSmrg                                 char *id_path_tag,
229af69d88dSmrg                                 char another_tag)
230af69d88dSmrg{
231af69d88dSmrg   struct udev_device *device;
232af69d88dSmrg   struct udev_enumerate *e;
233af69d88dSmrg   struct udev_list_entry *entry;
234af69d88dSmrg   const char *path, *id_path_tag_tmp;
235af69d88dSmrg   char *path_res;
236af69d88dSmrg   char found = 0;
237af69d88dSmrg   UDEV_SYMBOL(struct udev_enumerate *, udev_enumerate_new,
238af69d88dSmrg               (struct udev *));
239af69d88dSmrg   UDEV_SYMBOL(int, udev_enumerate_add_match_subsystem,
240af69d88dSmrg               (struct udev_enumerate *, const char *));
241af69d88dSmrg   UDEV_SYMBOL(int, udev_enumerate_add_match_sysname,
242af69d88dSmrg               (struct udev_enumerate *, const char *));
243af69d88dSmrg   UDEV_SYMBOL(int, udev_enumerate_scan_devices,
244af69d88dSmrg               (struct udev_enumerate *));
245af69d88dSmrg   UDEV_SYMBOL(struct udev_list_entry *, udev_enumerate_get_list_entry,
246af69d88dSmrg               (struct udev_enumerate *));
247af69d88dSmrg   UDEV_SYMBOL(struct udev_list_entry *, udev_list_entry_get_next,
248af69d88dSmrg               (struct udev_list_entry *));
249af69d88dSmrg   UDEV_SYMBOL(const char *, udev_list_entry_get_name,
250af69d88dSmrg               (struct udev_list_entry *));
251af69d88dSmrg   UDEV_SYMBOL(struct udev_device *, udev_device_new_from_syspath,
252af69d88dSmrg               (struct udev *, const char *));
253af69d88dSmrg   UDEV_SYMBOL(const char *, udev_device_get_property_value,
254af69d88dSmrg               (struct udev_device *, const char *));
255af69d88dSmrg   UDEV_SYMBOL(const char *, udev_device_get_devnode,
256af69d88dSmrg               (struct udev_device *));
257af69d88dSmrg   UDEV_SYMBOL(struct udev_device *, udev_device_unref,
258af69d88dSmrg               (struct udev_device *));
259af69d88dSmrg
260af69d88dSmrg   e = udev_enumerate_new(udev);
261af69d88dSmrg   udev_enumerate_add_match_subsystem(e, "drm");
262af69d88dSmrg   udev_enumerate_add_match_sysname(e, "render*");
263af69d88dSmrg
264af69d88dSmrg   udev_enumerate_scan_devices(e);
265af69d88dSmrg   udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(e)) {
266af69d88dSmrg      path = udev_list_entry_get_name(entry);
267af69d88dSmrg      device = udev_device_new_from_syspath(udev, path);
268af69d88dSmrg      if (!device)
269af69d88dSmrg         continue;
270af69d88dSmrg      id_path_tag_tmp = udev_device_get_property_value(device, "ID_PATH_TAG");
271af69d88dSmrg      if (id_path_tag_tmp) {
272af69d88dSmrg         if ((!another_tag && !strcmp(id_path_tag, id_path_tag_tmp)) ||
273af69d88dSmrg             (another_tag && strcmp(id_path_tag, id_path_tag_tmp))) {
274af69d88dSmrg            found = 1;
275af69d88dSmrg            break;
276af69d88dSmrg         }
277af69d88dSmrg      }
278af69d88dSmrg      udev_device_unref(device);
279af69d88dSmrg   }
280af69d88dSmrg
281af69d88dSmrg   if (found) {
282af69d88dSmrg      path_res = strdup(udev_device_get_devnode(device));
283af69d88dSmrg      udev_device_unref(device);
284af69d88dSmrg      return path_res;
285af69d88dSmrg   }
286af69d88dSmrg   return NULL;
287af69d88dSmrg}
288af69d88dSmrg
289af69d88dSmrgstatic char *
290af69d88dSmrgget_id_path_tag_from_fd(struct udev *udev, int fd)
291af69d88dSmrg{
292af69d88dSmrg   struct udev_device *device;
293af69d88dSmrg   const char *id_path_tag_tmp;
294af69d88dSmrg   char *id_path_tag;
295af69d88dSmrg   UDEV_SYMBOL(const char *, udev_device_get_property_value,
296af69d88dSmrg               (struct udev_device *, const char *));
297af69d88dSmrg   UDEV_SYMBOL(struct udev_device *, udev_device_unref,
298af69d88dSmrg               (struct udev_device *));
299af69d88dSmrg
300af69d88dSmrg   device = udev_device_new_from_fd(udev, fd);
301af69d88dSmrg   if (!device)
302af69d88dSmrg      return NULL;
303af69d88dSmrg
304af69d88dSmrg   id_path_tag_tmp = udev_device_get_property_value(device, "ID_PATH_TAG");
305af69d88dSmrg   if (!id_path_tag_tmp)
306af69d88dSmrg      return NULL;
307af69d88dSmrg
308af69d88dSmrg   id_path_tag = strdup(id_path_tag_tmp);
309af69d88dSmrg
310af69d88dSmrg   udev_device_unref(device);
311af69d88dSmrg   return id_path_tag;
312af69d88dSmrg}
313af69d88dSmrg
314af69d88dSmrgstatic int
315af69d88dSmrgdrm_open_device(const char *device_name)
316af69d88dSmrg{
317af69d88dSmrg   int fd;
318af69d88dSmrg#ifdef O_CLOEXEC
319af69d88dSmrg   fd = open(device_name, O_RDWR | O_CLOEXEC);
320af69d88dSmrg   if (fd == -1 && errno == EINVAL)
321af69d88dSmrg#endif
322af69d88dSmrg   {
323af69d88dSmrg      fd = open(device_name, O_RDWR);
324af69d88dSmrg      if (fd != -1)
325af69d88dSmrg         fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
326af69d88dSmrg   }
327af69d88dSmrg   return fd;
328af69d88dSmrg}
329af69d88dSmrg
330af69d88dSmrg#ifdef USE_DRICONF
331af69d88dSmrgconst char __driConfigOptionsLoader[] =
332af69d88dSmrgDRI_CONF_BEGIN
333af69d88dSmrg    DRI_CONF_SECTION_INITIALIZATION
334af69d88dSmrg        DRI_CONF_DEVICE_ID_PATH_TAG()
335af69d88dSmrg    DRI_CONF_SECTION_END
336af69d88dSmrgDRI_CONF_END;
337af69d88dSmrg#endif
338af69d88dSmrg
339af69d88dSmrgint loader_get_user_preferred_fd(int default_fd, int *different_device)
340af69d88dSmrg{
341af69d88dSmrg   struct udev *udev;
342af69d88dSmrg#ifdef USE_DRICONF
343af69d88dSmrg   driOptionCache defaultInitOptions;
344af69d88dSmrg   driOptionCache userInitOptions;
345af69d88dSmrg#endif
346af69d88dSmrg   const char *dri_prime = getenv("DRI_PRIME");
347af69d88dSmrg   char *prime = NULL;
348af69d88dSmrg   int is_different_device = 0, fd = default_fd;
349af69d88dSmrg   char *default_device_id_path_tag;
350af69d88dSmrg   char *device_name = NULL;
351af69d88dSmrg   char another_tag = 0;
352af69d88dSmrg   UDEV_SYMBOL(struct udev *, udev_new, (void));
353af69d88dSmrg   UDEV_SYMBOL(struct udev *, udev_unref, (struct udev *));
354af69d88dSmrg
355af69d88dSmrg   if (dri_prime)
356af69d88dSmrg      prime = strdup(dri_prime);
357af69d88dSmrg#ifdef USE_DRICONF
358af69d88dSmrg   else {
359af69d88dSmrg      driParseOptionInfo(&defaultInitOptions, __driConfigOptionsLoader);
360af69d88dSmrg      driParseConfigFiles(&userInitOptions, &defaultInitOptions, 0, "loader");
361af69d88dSmrg      if (driCheckOption(&userInitOptions, "device_id", DRI_STRING))
362af69d88dSmrg         prime = strdup(driQueryOptionstr(&userInitOptions, "device_id"));
363af69d88dSmrg      driDestroyOptionCache(&userInitOptions);
364af69d88dSmrg      driDestroyOptionInfo(&defaultInitOptions);
365af69d88dSmrg   }
366af69d88dSmrg#endif
367af69d88dSmrg
368af69d88dSmrg   if (prime == NULL) {
369af69d88dSmrg      *different_device = 0;
370af69d88dSmrg      return default_fd;
371af69d88dSmrg   }
372af69d88dSmrg
373af69d88dSmrg   udev = udev_new();
374af69d88dSmrg   if (!udev)
375af69d88dSmrg      goto prime_clean;
376af69d88dSmrg
377af69d88dSmrg   default_device_id_path_tag = get_id_path_tag_from_fd(udev, default_fd);
378af69d88dSmrg   if (!default_device_id_path_tag)
379af69d88dSmrg      goto udev_clean;
380af69d88dSmrg
381af69d88dSmrg   is_different_device = 1;
382af69d88dSmrg   /* two format are supported:
383af69d88dSmrg    * "1": choose any other card than the card used by default.
384af69d88dSmrg    * id_path_tag: (for example "pci-0000_02_00_0") choose the card
385af69d88dSmrg    * with this id_path_tag.
386af69d88dSmrg    */
387af69d88dSmrg   if (!strcmp(prime,"1")) {
388af69d88dSmrg      free(prime);
389af69d88dSmrg      prime = strdup(default_device_id_path_tag);
390af69d88dSmrg      /* request a card with a different card than the default card */
391af69d88dSmrg      another_tag = 1;
392af69d88dSmrg   } else if (!strcmp(default_device_id_path_tag, prime))
393af69d88dSmrg      /* we are to get a new fd (render-node) of the same device */
394af69d88dSmrg      is_different_device = 0;
395af69d88dSmrg
396af69d88dSmrg   device_name = get_render_node_from_id_path_tag(udev,
397af69d88dSmrg                                                  prime,
398af69d88dSmrg                                                  another_tag);
399af69d88dSmrg   if (device_name == NULL) {
400af69d88dSmrg      is_different_device = 0;
401af69d88dSmrg      goto default_device_clean;
402af69d88dSmrg   }
403af69d88dSmrg
404af69d88dSmrg   fd = drm_open_device(device_name);
405af69d88dSmrg   if (fd > 0) {
406af69d88dSmrg      close(default_fd);
407af69d88dSmrg   } else {
408af69d88dSmrg      fd = default_fd;
409af69d88dSmrg      is_different_device = 0;
410af69d88dSmrg   }
411af69d88dSmrg   free(device_name);
412af69d88dSmrg
413af69d88dSmrg default_device_clean:
414af69d88dSmrg   free(default_device_id_path_tag);
415af69d88dSmrg udev_clean:
416af69d88dSmrg   udev_unref(udev);
417af69d88dSmrg prime_clean:
418af69d88dSmrg   free(prime);
419af69d88dSmrg
420af69d88dSmrg   *different_device = is_different_device;
421af69d88dSmrg   return fd;
422af69d88dSmrg}
423af69d88dSmrg#else
424af69d88dSmrgint loader_get_user_preferred_fd(int default_fd, int *different_device)
425af69d88dSmrg{
426af69d88dSmrg   *different_device = 0;
427af69d88dSmrg   return default_fd;
428af69d88dSmrg}
429af69d88dSmrg#endif
430af69d88dSmrg
431af69d88dSmrg#if defined(HAVE_SYSFS)
432af69d88dSmrgstatic int
433af69d88dSmrgdev_node_from_fd(int fd, unsigned int *maj, unsigned int *min)
434af69d88dSmrg{
435af69d88dSmrg   struct stat buf;
436af69d88dSmrg
437af69d88dSmrg   if (fstat(fd, &buf) < 0) {
438af69d88dSmrg      log_(_LOADER_WARNING, "MESA-LOADER: failed to stat fd %d\n", fd);
439af69d88dSmrg      return -1;
440af69d88dSmrg   }
441af69d88dSmrg
442af69d88dSmrg   if (!S_ISCHR(buf.st_mode)) {
443af69d88dSmrg      log_(_LOADER_WARNING, "MESA-LOADER: fd %d not a character device\n", fd);
444af69d88dSmrg      return -1;
445af69d88dSmrg   }
446af69d88dSmrg
447af69d88dSmrg   *maj = major(buf.st_rdev);
448af69d88dSmrg   *min = minor(buf.st_rdev);
449af69d88dSmrg
450af69d88dSmrg   return 0;
451af69d88dSmrg}
452af69d88dSmrg
453af69d88dSmrgstatic int
454af69d88dSmrgsysfs_get_pci_id_for_fd(int fd, int *vendor_id, int *chip_id)
455af69d88dSmrg{
456af69d88dSmrg   unsigned int maj, min;
457af69d88dSmrg   FILE *f;
458af69d88dSmrg   char buf[0x40];
459af69d88dSmrg
460af69d88dSmrg   if (dev_node_from_fd(fd, &maj, &min) < 0) {
461af69d88dSmrg      *chip_id = -1;
462af69d88dSmrg      return 0;
463af69d88dSmrg   }
464af69d88dSmrg
465af69d88dSmrg   snprintf(buf, sizeof(buf), "/sys/dev/char/%d:%d/device/vendor", maj, min);
466af69d88dSmrg   if (!(f = fopen(buf, "r"))) {
467af69d88dSmrg      *chip_id = -1;
468af69d88dSmrg      return 0;
469af69d88dSmrg   }
470af69d88dSmrg   if (fscanf(f, "%x", vendor_id) != 1) {
471af69d88dSmrg      *chip_id = -1;
472af69d88dSmrg      fclose(f);
473af69d88dSmrg      return 0;
474af69d88dSmrg   }
475af69d88dSmrg   fclose(f);
476af69d88dSmrg   snprintf(buf, sizeof(buf), "/sys/dev/char/%d:%d/device/device", maj, min);
477af69d88dSmrg   if (!(f = fopen(buf, "r"))) {
478af69d88dSmrg      *chip_id = -1;
479af69d88dSmrg      return 0;
480af69d88dSmrg   }
481af69d88dSmrg   if (fscanf(f, "%x", chip_id) != 1) {
482af69d88dSmrg      *chip_id = -1;
483af69d88dSmrg      fclose(f);
484af69d88dSmrg      return 0;
485af69d88dSmrg   }
486af69d88dSmrg   fclose(f);
487af69d88dSmrg   return 1;
488af69d88dSmrg}
489af69d88dSmrg#endif
490af69d88dSmrg
491af69d88dSmrg#if !defined(__NOT_HAVE_DRM_H)
492af69d88dSmrg/* for i915 */
493af69d88dSmrg#include <i915_drm.h>
494af69d88dSmrg/* for radeon */
495af69d88dSmrg#include <radeon_drm.h>
496af69d88dSmrg
497af69d88dSmrgstatic int
498af69d88dSmrgdrm_get_pci_id_for_fd(int fd, int *vendor_id, int *chip_id)
499af69d88dSmrg{
500af69d88dSmrg   drmVersionPtr version;
501af69d88dSmrg
502af69d88dSmrg   *chip_id = -1;
503af69d88dSmrg
504af69d88dSmrg   version = drmGetVersion(fd);
505af69d88dSmrg   if (!version) {
506af69d88dSmrg      log_(_LOADER_WARNING, "MESA-LOADER: invalid drm fd\n");
507af69d88dSmrg      return 0;
508af69d88dSmrg   }
509af69d88dSmrg   if (!version->name) {
510af69d88dSmrg      log_(_LOADER_WARNING, "MESA-LOADER: unable to determine the driver name\n");
511af69d88dSmrg      drmFreeVersion(version);
512af69d88dSmrg      return 0;
513af69d88dSmrg   }
514af69d88dSmrg
515af69d88dSmrg   if (strcmp(version->name, "i915") == 0) {
516af69d88dSmrg      struct drm_i915_getparam gp;
517af69d88dSmrg      int ret;
518af69d88dSmrg
519af69d88dSmrg      *vendor_id = 0x8086;
520af69d88dSmrg
521af69d88dSmrg      memset(&gp, 0, sizeof(gp));
522af69d88dSmrg      gp.param = I915_PARAM_CHIPSET_ID;
523af69d88dSmrg      gp.value = chip_id;
524af69d88dSmrg      ret = drmCommandWriteRead(fd, DRM_I915_GETPARAM, &gp, sizeof(gp));
525af69d88dSmrg      if (ret) {
526af69d88dSmrg         log_(_LOADER_WARNING, "MESA-LOADER: failed to get param for i915\n");
527af69d88dSmrg	 *chip_id = -1;
528af69d88dSmrg      }
529af69d88dSmrg   }
530af69d88dSmrg   else if (strcmp(version->name, "radeon") == 0) {
531af69d88dSmrg      struct drm_radeon_info info;
532af69d88dSmrg      int ret;
533af69d88dSmrg
534af69d88dSmrg      *vendor_id = 0x1002;
535af69d88dSmrg
536af69d88dSmrg      memset(&info, 0, sizeof(info));
537af69d88dSmrg      info.request = RADEON_INFO_DEVICE_ID;
538af69d88dSmrg      info.value = (unsigned long) chip_id;
539af69d88dSmrg      ret = drmCommandWriteRead(fd, DRM_RADEON_INFO, &info, sizeof(info));
540af69d88dSmrg      if (ret) {
541af69d88dSmrg         log_(_LOADER_WARNING, "MESA-LOADER: failed to get info for radeon\n");
542af69d88dSmrg	 *chip_id = -1;
543af69d88dSmrg      }
544af69d88dSmrg   }
545af69d88dSmrg   else if (strcmp(version->name, "nouveau") == 0) {
546af69d88dSmrg      *vendor_id = 0x10de;
547af69d88dSmrg      /* not used */
548af69d88dSmrg      *chip_id = 0;
549af69d88dSmrg   }
550af69d88dSmrg   else if (strcmp(version->name, "vmwgfx") == 0) {
551af69d88dSmrg      *vendor_id = 0x15ad;
552af69d88dSmrg      /* assume SVGA II */
553af69d88dSmrg      *chip_id = 0x0405;
554af69d88dSmrg   }
555af69d88dSmrg
556af69d88dSmrg   drmFreeVersion(version);
557af69d88dSmrg
558af69d88dSmrg   return (*chip_id >= 0);
559af69d88dSmrg}
560af69d88dSmrg#endif
561af69d88dSmrg
562af69d88dSmrg
563af69d88dSmrgint
564af69d88dSmrgloader_get_pci_id_for_fd(int fd, int *vendor_id, int *chip_id)
565af69d88dSmrg{
566af69d88dSmrg#if HAVE_LIBUDEV
567af69d88dSmrg   if (libudev_get_pci_id_for_fd(fd, vendor_id, chip_id))
568af69d88dSmrg      return 1;
569af69d88dSmrg#endif
570af69d88dSmrg#if HAVE_SYSFS
571af69d88dSmrg   if (sysfs_get_pci_id_for_fd(fd, vendor_id, chip_id))
572af69d88dSmrg      return 1;
573af69d88dSmrg#endif
574af69d88dSmrg#if !defined(__NOT_HAVE_DRM_H)
575af69d88dSmrg   if (drm_get_pci_id_for_fd(fd, vendor_id, chip_id))
576af69d88dSmrg      return 1;
577af69d88dSmrg#endif
578af69d88dSmrg   return 0;
579af69d88dSmrg}
580af69d88dSmrg
581af69d88dSmrg
582af69d88dSmrg#ifdef HAVE_LIBUDEV
583af69d88dSmrgstatic char *
584af69d88dSmrglibudev_get_device_name_for_fd(int fd)
585af69d88dSmrg{
586af69d88dSmrg   char *device_name = NULL;
587af69d88dSmrg   struct udev *udev;
588af69d88dSmrg   struct udev_device *device;
589af69d88dSmrg   const char *const_device_name;
590af69d88dSmrg   UDEV_SYMBOL(struct udev *, udev_new, (void));
591af69d88dSmrg   UDEV_SYMBOL(const char *, udev_device_get_devnode,
592af69d88dSmrg               (struct udev_device *));
593af69d88dSmrg   UDEV_SYMBOL(struct udev_device *, udev_device_unref,
594af69d88dSmrg               (struct udev_device *));
595af69d88dSmrg   UDEV_SYMBOL(struct udev *, udev_unref, (struct udev *));
596af69d88dSmrg
597af69d88dSmrg   udev = udev_new();
598af69d88dSmrg   device = udev_device_new_from_fd(udev, fd);
599af69d88dSmrg   if (device == NULL)
600af69d88dSmrg      return NULL;
601af69d88dSmrg
602af69d88dSmrg   const_device_name = udev_device_get_devnode(device);
603af69d88dSmrg   if (!const_device_name)
604af69d88dSmrg      goto out;
605af69d88dSmrg   device_name = strdup(const_device_name);
606af69d88dSmrg
607af69d88dSmrgout:
608af69d88dSmrg   udev_device_unref(device);
609af69d88dSmrg   udev_unref(udev);
610af69d88dSmrg   return device_name;
611af69d88dSmrg}
612af69d88dSmrg#endif
613af69d88dSmrg
614af69d88dSmrg
615af69d88dSmrg#if HAVE_SYSFS
616af69d88dSmrgstatic char *
617af69d88dSmrgsysfs_get_device_name_for_fd(int fd)
618af69d88dSmrg{
619af69d88dSmrg   char *device_name = NULL;
620af69d88dSmrg   unsigned int maj, min;
621af69d88dSmrg   FILE *f;
622af69d88dSmrg   char buf[0x40];
623af69d88dSmrg   static const char match[9] = "\0DEVNAME=";
624af69d88dSmrg   int expected = 1;
625af69d88dSmrg
626af69d88dSmrg   if (dev_node_from_fd(fd, &maj, &min) < 0)
627af69d88dSmrg      return NULL;
628af69d88dSmrg
629af69d88dSmrg   snprintf(buf, sizeof(buf), "/sys/dev/char/%d:%d/uevent", maj, min);
630af69d88dSmrg   if (!(f = fopen(buf, "r")))
631af69d88dSmrg       return NULL;
632af69d88dSmrg
633af69d88dSmrg   while (expected < sizeof(match)) {
634af69d88dSmrg      int c = getc(f);
635af69d88dSmrg
636af69d88dSmrg      if (c == EOF) {
637af69d88dSmrg         fclose(f);
638af69d88dSmrg         return NULL;
639af69d88dSmrg      } else if (c == match[expected] )
640af69d88dSmrg         expected++;
641af69d88dSmrg      else
642af69d88dSmrg         expected = 0;
643af69d88dSmrg   }
644af69d88dSmrg
645af69d88dSmrg   strcpy(buf, "/dev/");
646af69d88dSmrg   if (fgets(buf + 5, sizeof(buf) - 5, f))
647af69d88dSmrg      device_name = strdup(buf);
648af69d88dSmrg
649af69d88dSmrg   fclose(f);
650af69d88dSmrg   return device_name;
651af69d88dSmrg}
652af69d88dSmrg#endif
653af69d88dSmrg
654af69d88dSmrg
655af69d88dSmrgchar *
656af69d88dSmrgloader_get_device_name_for_fd(int fd)
657af69d88dSmrg{
658af69d88dSmrg   char *result = NULL;
659af69d88dSmrg
660af69d88dSmrg#if HAVE_LIBUDEV
661af69d88dSmrg   if ((result = libudev_get_device_name_for_fd(fd)))
662af69d88dSmrg      return result;
663af69d88dSmrg#endif
664af69d88dSmrg#if HAVE_SYSFS
665af69d88dSmrg   if ((result = sysfs_get_device_name_for_fd(fd)))
666af69d88dSmrg      return result;
667af69d88dSmrg#endif
668af69d88dSmrg   return result;
669af69d88dSmrg}
670af69d88dSmrg
671af69d88dSmrgchar *
672af69d88dSmrgloader_get_driver_for_fd(int fd, unsigned driver_types)
673af69d88dSmrg{
674af69d88dSmrg   int vendor_id, chip_id, i, j;
675af69d88dSmrg   char *driver = NULL;
676af69d88dSmrg
677af69d88dSmrg   if (!driver_types)
678af69d88dSmrg      driver_types = _LOADER_GALLIUM | _LOADER_DRI;
679af69d88dSmrg
680af69d88dSmrg   if (!loader_get_pci_id_for_fd(fd, &vendor_id, &chip_id)) {
681af69d88dSmrg
682af69d88dSmrg#ifndef __NOT_HAVE_DRM_H
683af69d88dSmrg      /* fallback to drmGetVersion(): */
684af69d88dSmrg      drmVersionPtr version = drmGetVersion(fd);
685af69d88dSmrg
686af69d88dSmrg      if (!version) {
687af69d88dSmrg         log_(_LOADER_WARNING, "failed to get driver name for fd %d\n", fd);
688af69d88dSmrg         return NULL;
689af69d88dSmrg      }
690af69d88dSmrg
691af69d88dSmrg      driver = strndup(version->name, version->name_len);
692af69d88dSmrg      log_(_LOADER_INFO, "using driver %s for %d\n", driver, fd);
693af69d88dSmrg
694af69d88dSmrg      drmFreeVersion(version);
695af69d88dSmrg#endif
696af69d88dSmrg
697af69d88dSmrg      return driver;
698af69d88dSmrg   }
699af69d88dSmrg
700af69d88dSmrg   for (i = 0; driver_map[i].driver; i++) {
701af69d88dSmrg      if (vendor_id != driver_map[i].vendor_id)
702af69d88dSmrg         continue;
703af69d88dSmrg
704af69d88dSmrg      if (!(driver_types & driver_map[i].driver_types))
705af69d88dSmrg         continue;
706af69d88dSmrg
707af69d88dSmrg      if (driver_map[i].predicate && !driver_map[i].predicate(fd))
708af69d88dSmrg         continue;
709af69d88dSmrg
710af69d88dSmrg      if (driver_map[i].num_chips_ids == -1) {
711af69d88dSmrg         driver = strdup(driver_map[i].driver);
712af69d88dSmrg         goto out;
713af69d88dSmrg      }
714af69d88dSmrg
715af69d88dSmrg      for (j = 0; j < driver_map[i].num_chips_ids; j++)
716af69d88dSmrg         if (driver_map[i].chip_ids[j] == chip_id) {
717af69d88dSmrg            driver = strdup(driver_map[i].driver);
718af69d88dSmrg            goto out;
719af69d88dSmrg         }
720af69d88dSmrg   }
721af69d88dSmrg
722af69d88dSmrgout:
723af69d88dSmrg   log_(driver ? _LOADER_DEBUG : _LOADER_WARNING,
724af69d88dSmrg         "pci id for fd %d: %04x:%04x, driver %s\n",
725af69d88dSmrg         fd, vendor_id, chip_id, driver);
726af69d88dSmrg   return driver;
727af69d88dSmrg}
728af69d88dSmrg
729af69d88dSmrgvoid
730af69d88dSmrgloader_set_logger(void (*logger)(int level, const char *fmt, ...))
731af69d88dSmrg{
732af69d88dSmrg   log_ = logger;
733af69d88dSmrg}
734