loader.c revision 848b8605
1848b8605Smrg/*
2848b8605Smrg * Copyright (C) 2013 Rob Clark <robclark@freedesktop.org>
3848b8605Smrg *
4848b8605Smrg * This code is derived from the following files.
5848b8605Smrg *
6848b8605Smrg * * src/glx/dri3_common.c
7848b8605Smrg * Copyright © 2013 Keith Packard
8848b8605Smrg *
9848b8605Smrg * * src/egl/drivers/dri2/common.c
10848b8605Smrg * * src/gbm/backends/dri/driver_name.c
11848b8605Smrg * Copyright © 2011 Intel Corporation
12848b8605Smrg *
13848b8605Smrg * Authors:
14848b8605Smrg *    Kristian Høgsberg <krh@bitplanet.net>
15848b8605Smrg *    Benjamin Franzke <benjaminfranzke@googlemail.com>
16848b8605Smrg *
17848b8605Smrg * * src/gallium/targets/egl-static/egl.c
18848b8605Smrg * Copyright (C) 2010-2011 LunarG Inc.
19848b8605Smrg *
20848b8605Smrg * Authors:
21848b8605Smrg *    Chia-I Wu <olv@lunarg.com>
22848b8605Smrg *
23848b8605Smrg * * src/gallium/state_trackers/egl/drm/native_drm.c
24848b8605Smrg * Copyright (C) 2010 Chia-I Wu <olv@0xlab.org>
25848b8605Smrg *
26848b8605Smrg * * src/egl/drivers/dri2/platform_android.c
27848b8605Smrg *
28848b8605Smrg * Copyright (C) 2010-2011 Chia-I Wu <olvaffe@gmail.com>
29848b8605Smrg * Copyright (C) 2010-2011 LunarG Inc.
30848b8605Smrg *
31848b8605Smrg * Based on platform_x11, which has
32848b8605Smrg *
33848b8605Smrg * Copyright © 2011 Intel Corporation
34848b8605Smrg *
35848b8605Smrg * * src/gallium/auxiliary/pipe-loader/pipe_loader_drm.c
36848b8605Smrg * Copyright 2011 Intel Corporation
37848b8605Smrg * Copyright 2012 Francisco Jerez
38848b8605Smrg * All Rights Reserved.
39848b8605Smrg *
40848b8605Smrg * Authors:
41848b8605Smrg *    Kristian Høgsberg <krh@bitplanet.net>
42848b8605Smrg *    Benjamin Franzke <benjaminfranzke@googlemail.com>
43848b8605Smrg *
44848b8605Smrg * Permission is hereby granted, free of charge, to any person obtaining a
45848b8605Smrg * copy of this software and associated documentation files (the "Software"),
46848b8605Smrg * to deal in the Software without restriction, including without limitation
47848b8605Smrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
48848b8605Smrg * and/or sell copies of the Software, and to permit persons to whom the
49848b8605Smrg * Software is furnished to do so, subject to the following conditions:
50848b8605Smrg *
51848b8605Smrg * The above copyright notice and this permission notice (including the next
52848b8605Smrg * paragraph) shall be included in all copies or substantial portions of the
53848b8605Smrg * Software.
54848b8605Smrg *
55848b8605Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
56848b8605Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
57848b8605Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
58848b8605Smrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
59848b8605Smrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
60848b8605Smrg * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
61848b8605Smrg * SOFTWARE.
62848b8605Smrg *
63848b8605Smrg * Authors:
64848b8605Smrg *    Rob Clark <robclark@freedesktop.org>
65848b8605Smrg */
66848b8605Smrg
67848b8605Smrg#include <stdarg.h>
68848b8605Smrg#include <stdio.h>
69848b8605Smrg#include <string.h>
70848b8605Smrg#ifdef HAVE_LIBUDEV
71848b8605Smrg#include <assert.h>
72848b8605Smrg#include <dlfcn.h>
73848b8605Smrg#include <fcntl.h>
74848b8605Smrg#include <unistd.h>
75848b8605Smrg#include <stdlib.h>
76848b8605Smrg#include <errno.h>
77848b8605Smrg#ifdef USE_DRICONF
78848b8605Smrg#include "xmlconfig.h"
79848b8605Smrg#include "xmlpool.h"
80848b8605Smrg#endif
81848b8605Smrg#endif
82848b8605Smrg#ifdef HAVE_SYSFS
83848b8605Smrg#include <sys/stat.h>
84848b8605Smrg#include <sys/types.h>
85848b8605Smrg#endif
86848b8605Smrg#include "loader.h"
87848b8605Smrg
88848b8605Smrg#ifndef __NOT_HAVE_DRM_H
89848b8605Smrg#include <xf86drm.h>
90848b8605Smrg#endif
91848b8605Smrg
92848b8605Smrg#define __IS_LOADER
93848b8605Smrg#include "pci_id_driver_map.h"
94848b8605Smrg
95848b8605Smrgstatic void default_logger(int level, const char *fmt, ...)
96848b8605Smrg{
97848b8605Smrg   if (level <= _LOADER_WARNING) {
98848b8605Smrg      va_list args;
99848b8605Smrg      va_start(args, fmt);
100848b8605Smrg      vfprintf(stderr, fmt, args);
101848b8605Smrg      va_end(args);
102848b8605Smrg   }
103848b8605Smrg}
104848b8605Smrg
105848b8605Smrgstatic void (*log_)(int level, const char *fmt, ...) = default_logger;
106848b8605Smrg
107848b8605Smrg#ifdef HAVE_LIBUDEV
108848b8605Smrg#include <libudev.h>
109848b8605Smrg
110848b8605Smrgstatic void *udev_handle = NULL;
111848b8605Smrg
112848b8605Smrgstatic void *
113848b8605Smrgudev_dlopen_handle(void)
114848b8605Smrg{
115848b8605Smrg   if (!udev_handle) {
116848b8605Smrg      udev_handle = dlopen("libudev.so.1", RTLD_LOCAL | RTLD_LAZY);
117848b8605Smrg
118848b8605Smrg      if (!udev_handle) {
119848b8605Smrg         /* libudev.so.1 changed the return types of the two unref functions
120848b8605Smrg          * from voids to pointers.  We don't use those return values, and the
121848b8605Smrg          * only ABI I've heard that cares about this kind of change (calling
122848b8605Smrg          * a function with a void * return that actually only returns void)
123848b8605Smrg          * might be ia64.
124848b8605Smrg          */
125848b8605Smrg         udev_handle = dlopen("libudev.so.0", RTLD_LOCAL | RTLD_LAZY);
126848b8605Smrg
127848b8605Smrg         if (!udev_handle) {
128848b8605Smrg            log_(_LOADER_WARNING, "Couldn't dlopen libudev.so.1 or "
129848b8605Smrg                 "libudev.so.0, driver detection may be broken.\n");
130848b8605Smrg         }
131848b8605Smrg      }
132848b8605Smrg   }
133848b8605Smrg
134848b8605Smrg   return udev_handle;
135848b8605Smrg}
136848b8605Smrg
137848b8605Smrgstatic int dlsym_failed = 0;
138848b8605Smrg
139848b8605Smrgstatic void *
140848b8605Smrgchecked_dlsym(void *dlopen_handle, const char *name)
141848b8605Smrg{
142848b8605Smrg   void *result = dlsym(dlopen_handle, name);
143848b8605Smrg   if (!result)
144848b8605Smrg      dlsym_failed = 1;
145848b8605Smrg   return result;
146848b8605Smrg}
147848b8605Smrg
148848b8605Smrg#define UDEV_SYMBOL(ret, name, args) \
149848b8605Smrg   ret (*name) args = checked_dlsym(udev_dlopen_handle(), #name);
150848b8605Smrg
151848b8605Smrg
152848b8605Smrgstatic inline struct udev_device *
153848b8605Smrgudev_device_new_from_fd(struct udev *udev, int fd)
154848b8605Smrg{
155848b8605Smrg   struct udev_device *device;
156848b8605Smrg   struct stat buf;
157848b8605Smrg   UDEV_SYMBOL(struct udev_device *, udev_device_new_from_devnum,
158848b8605Smrg               (struct udev *udev, char type, dev_t devnum));
159848b8605Smrg
160848b8605Smrg   if (dlsym_failed)
161848b8605Smrg      return NULL;
162848b8605Smrg
163848b8605Smrg   if (fstat(fd, &buf) < 0) {
164848b8605Smrg      log_(_LOADER_WARNING, "MESA-LOADER: failed to stat fd %d\n", fd);
165848b8605Smrg      return NULL;
166848b8605Smrg   }
167848b8605Smrg
168848b8605Smrg   device = udev_device_new_from_devnum(udev, 'c', buf.st_rdev);
169848b8605Smrg   if (device == NULL) {
170848b8605Smrg      log_(_LOADER_WARNING,
171848b8605Smrg              "MESA-LOADER: could not create udev device for fd %d\n", fd);
172848b8605Smrg      return NULL;
173848b8605Smrg   }
174848b8605Smrg
175848b8605Smrg   return device;
176848b8605Smrg}
177848b8605Smrg
178848b8605Smrgstatic int
179848b8605Smrglibudev_get_pci_id_for_fd(int fd, int *vendor_id, int *chip_id)
180848b8605Smrg{
181848b8605Smrg   struct udev *udev = NULL;
182848b8605Smrg   struct udev_device *device = NULL, *parent;
183848b8605Smrg   const char *pci_id;
184848b8605Smrg   UDEV_SYMBOL(struct udev *, udev_new, (void));
185848b8605Smrg   UDEV_SYMBOL(struct udev_device *, udev_device_get_parent,
186848b8605Smrg               (struct udev_device *));
187848b8605Smrg   UDEV_SYMBOL(const char *, udev_device_get_property_value,
188848b8605Smrg               (struct udev_device *, const char *));
189848b8605Smrg   UDEV_SYMBOL(struct udev_device *, udev_device_unref,
190848b8605Smrg               (struct udev_device *));
191848b8605Smrg   UDEV_SYMBOL(struct udev *, udev_unref, (struct udev *));
192848b8605Smrg
193848b8605Smrg   *chip_id = -1;
194848b8605Smrg
195848b8605Smrg   if (dlsym_failed)
196848b8605Smrg      return 0;
197848b8605Smrg
198848b8605Smrg   udev = udev_new();
199848b8605Smrg   device = udev_device_new_from_fd(udev, fd);
200848b8605Smrg   if (!device)
201848b8605Smrg      goto out;
202848b8605Smrg
203848b8605Smrg   parent = udev_device_get_parent(device);
204848b8605Smrg   if (parent == NULL) {
205848b8605Smrg      log_(_LOADER_WARNING, "MESA-LOADER: could not get parent device\n");
206848b8605Smrg      goto out;
207848b8605Smrg   }
208848b8605Smrg
209848b8605Smrg   pci_id = udev_device_get_property_value(parent, "PCI_ID");
210848b8605Smrg   if (pci_id == NULL ||
211848b8605Smrg       sscanf(pci_id, "%x:%x", vendor_id, chip_id) != 2) {
212848b8605Smrg      log_(_LOADER_WARNING, "MESA-LOADER: malformed or no PCI ID\n");
213848b8605Smrg      *chip_id = -1;
214848b8605Smrg      goto out;
215848b8605Smrg   }
216848b8605Smrg
217848b8605Smrgout:
218848b8605Smrg   if (device)
219848b8605Smrg      udev_device_unref(device);
220848b8605Smrg   if (udev)
221848b8605Smrg      udev_unref(udev);
222848b8605Smrg
223848b8605Smrg   return (*chip_id >= 0);
224848b8605Smrg}
225848b8605Smrg
226848b8605Smrgstatic char *
227848b8605Smrgget_render_node_from_id_path_tag(struct udev *udev,
228848b8605Smrg                                 char *id_path_tag,
229848b8605Smrg                                 char another_tag)
230848b8605Smrg{
231848b8605Smrg   struct udev_device *device;
232848b8605Smrg   struct udev_enumerate *e;
233848b8605Smrg   struct udev_list_entry *entry;
234848b8605Smrg   const char *path, *id_path_tag_tmp;
235848b8605Smrg   char *path_res;
236848b8605Smrg   char found = 0;
237848b8605Smrg   UDEV_SYMBOL(struct udev_enumerate *, udev_enumerate_new,
238848b8605Smrg               (struct udev *));
239848b8605Smrg   UDEV_SYMBOL(int, udev_enumerate_add_match_subsystem,
240848b8605Smrg               (struct udev_enumerate *, const char *));
241848b8605Smrg   UDEV_SYMBOL(int, udev_enumerate_add_match_sysname,
242848b8605Smrg               (struct udev_enumerate *, const char *));
243848b8605Smrg   UDEV_SYMBOL(int, udev_enumerate_scan_devices,
244848b8605Smrg               (struct udev_enumerate *));
245848b8605Smrg   UDEV_SYMBOL(struct udev_list_entry *, udev_enumerate_get_list_entry,
246848b8605Smrg               (struct udev_enumerate *));
247848b8605Smrg   UDEV_SYMBOL(struct udev_list_entry *, udev_list_entry_get_next,
248848b8605Smrg               (struct udev_list_entry *));
249848b8605Smrg   UDEV_SYMBOL(const char *, udev_list_entry_get_name,
250848b8605Smrg               (struct udev_list_entry *));
251848b8605Smrg   UDEV_SYMBOL(struct udev_device *, udev_device_new_from_syspath,
252848b8605Smrg               (struct udev *, const char *));
253848b8605Smrg   UDEV_SYMBOL(const char *, udev_device_get_property_value,
254848b8605Smrg               (struct udev_device *, const char *));
255848b8605Smrg   UDEV_SYMBOL(const char *, udev_device_get_devnode,
256848b8605Smrg               (struct udev_device *));
257848b8605Smrg   UDEV_SYMBOL(struct udev_device *, udev_device_unref,
258848b8605Smrg               (struct udev_device *));
259848b8605Smrg
260848b8605Smrg   e = udev_enumerate_new(udev);
261848b8605Smrg   udev_enumerate_add_match_subsystem(e, "drm");
262848b8605Smrg   udev_enumerate_add_match_sysname(e, "render*");
263848b8605Smrg
264848b8605Smrg   udev_enumerate_scan_devices(e);
265848b8605Smrg   udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(e)) {
266848b8605Smrg      path = udev_list_entry_get_name(entry);
267848b8605Smrg      device = udev_device_new_from_syspath(udev, path);
268848b8605Smrg      if (!device)
269848b8605Smrg         continue;
270848b8605Smrg      id_path_tag_tmp = udev_device_get_property_value(device, "ID_PATH_TAG");
271848b8605Smrg      if (id_path_tag_tmp) {
272848b8605Smrg         if ((!another_tag && !strcmp(id_path_tag, id_path_tag_tmp)) ||
273848b8605Smrg             (another_tag && strcmp(id_path_tag, id_path_tag_tmp))) {
274848b8605Smrg            found = 1;
275848b8605Smrg            break;
276848b8605Smrg         }
277848b8605Smrg      }
278848b8605Smrg      udev_device_unref(device);
279848b8605Smrg   }
280848b8605Smrg
281848b8605Smrg   if (found) {
282848b8605Smrg      path_res = strdup(udev_device_get_devnode(device));
283848b8605Smrg      udev_device_unref(device);
284848b8605Smrg      return path_res;
285848b8605Smrg   }
286848b8605Smrg   return NULL;
287848b8605Smrg}
288848b8605Smrg
289848b8605Smrgstatic char *
290848b8605Smrgget_id_path_tag_from_fd(struct udev *udev, int fd)
291848b8605Smrg{
292848b8605Smrg   struct udev_device *device;
293848b8605Smrg   const char *id_path_tag_tmp;
294848b8605Smrg   char *id_path_tag;
295848b8605Smrg   UDEV_SYMBOL(const char *, udev_device_get_property_value,
296848b8605Smrg               (struct udev_device *, const char *));
297848b8605Smrg   UDEV_SYMBOL(struct udev_device *, udev_device_unref,
298848b8605Smrg               (struct udev_device *));
299848b8605Smrg
300848b8605Smrg   device = udev_device_new_from_fd(udev, fd);
301848b8605Smrg   if (!device)
302848b8605Smrg      return NULL;
303848b8605Smrg
304848b8605Smrg   id_path_tag_tmp = udev_device_get_property_value(device, "ID_PATH_TAG");
305848b8605Smrg   if (!id_path_tag_tmp)
306848b8605Smrg      return NULL;
307848b8605Smrg
308848b8605Smrg   id_path_tag = strdup(id_path_tag_tmp);
309848b8605Smrg
310848b8605Smrg   udev_device_unref(device);
311848b8605Smrg   return id_path_tag;
312848b8605Smrg}
313848b8605Smrg
314848b8605Smrgstatic int
315848b8605Smrgdrm_open_device(const char *device_name)
316848b8605Smrg{
317848b8605Smrg   int fd;
318848b8605Smrg#ifdef O_CLOEXEC
319848b8605Smrg   fd = open(device_name, O_RDWR | O_CLOEXEC);
320848b8605Smrg   if (fd == -1 && errno == EINVAL)
321848b8605Smrg#endif
322848b8605Smrg   {
323848b8605Smrg      fd = open(device_name, O_RDWR);
324848b8605Smrg      if (fd != -1)
325848b8605Smrg         fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
326848b8605Smrg   }
327848b8605Smrg   return fd;
328848b8605Smrg}
329848b8605Smrg
330848b8605Smrg#ifdef USE_DRICONF
331848b8605Smrgconst char __driConfigOptionsLoader[] =
332848b8605SmrgDRI_CONF_BEGIN
333848b8605Smrg    DRI_CONF_SECTION_INITIALIZATION
334848b8605Smrg        DRI_CONF_DEVICE_ID_PATH_TAG()
335848b8605Smrg    DRI_CONF_SECTION_END
336848b8605SmrgDRI_CONF_END;
337848b8605Smrg#endif
338848b8605Smrg
339848b8605Smrgint loader_get_user_preferred_fd(int default_fd, int *different_device)
340848b8605Smrg{
341848b8605Smrg   struct udev *udev;
342848b8605Smrg#ifdef USE_DRICONF
343848b8605Smrg   driOptionCache defaultInitOptions;
344848b8605Smrg   driOptionCache userInitOptions;
345848b8605Smrg#endif
346848b8605Smrg   const char *dri_prime = getenv("DRI_PRIME");
347848b8605Smrg   char *prime = NULL;
348848b8605Smrg   int is_different_device = 0, fd = default_fd;
349848b8605Smrg   char *default_device_id_path_tag;
350848b8605Smrg   char *device_name = NULL;
351848b8605Smrg   char another_tag = 0;
352848b8605Smrg   UDEV_SYMBOL(struct udev *, udev_new, (void));
353848b8605Smrg   UDEV_SYMBOL(struct udev *, udev_unref, (struct udev *));
354848b8605Smrg
355848b8605Smrg   if (dri_prime)
356848b8605Smrg      prime = strdup(dri_prime);
357848b8605Smrg#ifdef USE_DRICONF
358848b8605Smrg   else {
359848b8605Smrg      driParseOptionInfo(&defaultInitOptions, __driConfigOptionsLoader);
360848b8605Smrg      driParseConfigFiles(&userInitOptions, &defaultInitOptions, 0, "loader");
361848b8605Smrg      if (driCheckOption(&userInitOptions, "device_id", DRI_STRING))
362848b8605Smrg         prime = strdup(driQueryOptionstr(&userInitOptions, "device_id"));
363848b8605Smrg      driDestroyOptionCache(&userInitOptions);
364848b8605Smrg      driDestroyOptionInfo(&defaultInitOptions);
365848b8605Smrg   }
366848b8605Smrg#endif
367848b8605Smrg
368848b8605Smrg   if (prime == NULL) {
369848b8605Smrg      *different_device = 0;
370848b8605Smrg      return default_fd;
371848b8605Smrg   }
372848b8605Smrg
373848b8605Smrg   udev = udev_new();
374848b8605Smrg   if (!udev)
375848b8605Smrg      goto prime_clean;
376848b8605Smrg
377848b8605Smrg   default_device_id_path_tag = get_id_path_tag_from_fd(udev, default_fd);
378848b8605Smrg   if (!default_device_id_path_tag)
379848b8605Smrg      goto udev_clean;
380848b8605Smrg
381848b8605Smrg   is_different_device = 1;
382848b8605Smrg   /* two format are supported:
383848b8605Smrg    * "1": choose any other card than the card used by default.
384848b8605Smrg    * id_path_tag: (for example "pci-0000_02_00_0") choose the card
385848b8605Smrg    * with this id_path_tag.
386848b8605Smrg    */
387848b8605Smrg   if (!strcmp(prime,"1")) {
388848b8605Smrg      free(prime);
389848b8605Smrg      prime = strdup(default_device_id_path_tag);
390848b8605Smrg      /* request a card with a different card than the default card */
391848b8605Smrg      another_tag = 1;
392848b8605Smrg   } else if (!strcmp(default_device_id_path_tag, prime))
393848b8605Smrg      /* we are to get a new fd (render-node) of the same device */
394848b8605Smrg      is_different_device = 0;
395848b8605Smrg
396848b8605Smrg   device_name = get_render_node_from_id_path_tag(udev,
397848b8605Smrg                                                  prime,
398848b8605Smrg                                                  another_tag);
399848b8605Smrg   if (device_name == NULL) {
400848b8605Smrg      is_different_device = 0;
401848b8605Smrg      goto default_device_clean;
402848b8605Smrg   }
403848b8605Smrg
404848b8605Smrg   fd = drm_open_device(device_name);
405848b8605Smrg   if (fd > 0) {
406848b8605Smrg      close(default_fd);
407848b8605Smrg   } else {
408848b8605Smrg      fd = default_fd;
409848b8605Smrg      is_different_device = 0;
410848b8605Smrg   }
411848b8605Smrg   free(device_name);
412848b8605Smrg
413848b8605Smrg default_device_clean:
414848b8605Smrg   free(default_device_id_path_tag);
415848b8605Smrg udev_clean:
416848b8605Smrg   udev_unref(udev);
417848b8605Smrg prime_clean:
418848b8605Smrg   free(prime);
419848b8605Smrg
420848b8605Smrg   *different_device = is_different_device;
421848b8605Smrg   return fd;
422848b8605Smrg}
423848b8605Smrg#else
424848b8605Smrgint loader_get_user_preferred_fd(int default_fd, int *different_device)
425848b8605Smrg{
426848b8605Smrg   *different_device = 0;
427848b8605Smrg   return default_fd;
428848b8605Smrg}
429848b8605Smrg#endif
430848b8605Smrg
431848b8605Smrg#if defined(HAVE_SYSFS)
432848b8605Smrgstatic int
433848b8605Smrgdev_node_from_fd(int fd, unsigned int *maj, unsigned int *min)
434848b8605Smrg{
435848b8605Smrg   struct stat buf;
436848b8605Smrg
437848b8605Smrg   if (fstat(fd, &buf) < 0) {
438848b8605Smrg      log_(_LOADER_WARNING, "MESA-LOADER: failed to stat fd %d\n", fd);
439848b8605Smrg      return -1;
440848b8605Smrg   }
441848b8605Smrg
442848b8605Smrg   if (!S_ISCHR(buf.st_mode)) {
443848b8605Smrg      log_(_LOADER_WARNING, "MESA-LOADER: fd %d not a character device\n", fd);
444848b8605Smrg      return -1;
445848b8605Smrg   }
446848b8605Smrg
447848b8605Smrg   *maj = major(buf.st_rdev);
448848b8605Smrg   *min = minor(buf.st_rdev);
449848b8605Smrg
450848b8605Smrg   return 0;
451848b8605Smrg}
452848b8605Smrg
453848b8605Smrgstatic int
454848b8605Smrgsysfs_get_pci_id_for_fd(int fd, int *vendor_id, int *chip_id)
455848b8605Smrg{
456848b8605Smrg   unsigned int maj, min;
457848b8605Smrg   FILE *f;
458848b8605Smrg   char buf[0x40];
459848b8605Smrg
460848b8605Smrg   if (dev_node_from_fd(fd, &maj, &min) < 0) {
461848b8605Smrg      *chip_id = -1;
462848b8605Smrg      return 0;
463848b8605Smrg   }
464848b8605Smrg
465848b8605Smrg   snprintf(buf, sizeof(buf), "/sys/dev/char/%d:%d/device/vendor", maj, min);
466848b8605Smrg   if (!(f = fopen(buf, "r"))) {
467848b8605Smrg      *chip_id = -1;
468848b8605Smrg      return 0;
469848b8605Smrg   }
470848b8605Smrg   if (fscanf(f, "%x", vendor_id) != 1) {
471848b8605Smrg      *chip_id = -1;
472848b8605Smrg      fclose(f);
473848b8605Smrg      return 0;
474848b8605Smrg   }
475848b8605Smrg   fclose(f);
476848b8605Smrg   snprintf(buf, sizeof(buf), "/sys/dev/char/%d:%d/device/device", maj, min);
477848b8605Smrg   if (!(f = fopen(buf, "r"))) {
478848b8605Smrg      *chip_id = -1;
479848b8605Smrg      return 0;
480848b8605Smrg   }
481848b8605Smrg   if (fscanf(f, "%x", chip_id) != 1) {
482848b8605Smrg      *chip_id = -1;
483848b8605Smrg      fclose(f);
484848b8605Smrg      return 0;
485848b8605Smrg   }
486848b8605Smrg   fclose(f);
487848b8605Smrg   return 1;
488848b8605Smrg}
489848b8605Smrg#endif
490848b8605Smrg
491848b8605Smrg#if !defined(__NOT_HAVE_DRM_H)
492848b8605Smrg/* for i915 */
493848b8605Smrg#include <i915_drm.h>
494848b8605Smrg/* for radeon */
495848b8605Smrg#include <radeon_drm.h>
496848b8605Smrg
497848b8605Smrgstatic int
498848b8605Smrgdrm_get_pci_id_for_fd(int fd, int *vendor_id, int *chip_id)
499848b8605Smrg{
500848b8605Smrg   drmVersionPtr version;
501848b8605Smrg
502848b8605Smrg   *chip_id = -1;
503848b8605Smrg
504848b8605Smrg   version = drmGetVersion(fd);
505848b8605Smrg   if (!version) {
506848b8605Smrg      log_(_LOADER_WARNING, "MESA-LOADER: invalid drm fd\n");
507848b8605Smrg      return 0;
508848b8605Smrg   }
509848b8605Smrg   if (!version->name) {
510848b8605Smrg      log_(_LOADER_WARNING, "MESA-LOADER: unable to determine the driver name\n");
511848b8605Smrg      drmFreeVersion(version);
512848b8605Smrg      return 0;
513848b8605Smrg   }
514848b8605Smrg
515848b8605Smrg   if (strcmp(version->name, "i915") == 0) {
516848b8605Smrg      struct drm_i915_getparam gp;
517848b8605Smrg      int ret;
518848b8605Smrg
519848b8605Smrg      *vendor_id = 0x8086;
520848b8605Smrg
521848b8605Smrg      memset(&gp, 0, sizeof(gp));
522848b8605Smrg      gp.param = I915_PARAM_CHIPSET_ID;
523848b8605Smrg      gp.value = chip_id;
524848b8605Smrg      ret = drmCommandWriteRead(fd, DRM_I915_GETPARAM, &gp, sizeof(gp));
525848b8605Smrg      if (ret) {
526848b8605Smrg         log_(_LOADER_WARNING, "MESA-LOADER: failed to get param for i915\n");
527848b8605Smrg	 *chip_id = -1;
528848b8605Smrg      }
529848b8605Smrg   }
530848b8605Smrg   else if (strcmp(version->name, "radeon") == 0) {
531848b8605Smrg      struct drm_radeon_info info;
532848b8605Smrg      int ret;
533848b8605Smrg
534848b8605Smrg      *vendor_id = 0x1002;
535848b8605Smrg
536848b8605Smrg      memset(&info, 0, sizeof(info));
537848b8605Smrg      info.request = RADEON_INFO_DEVICE_ID;
538848b8605Smrg      info.value = (unsigned long) chip_id;
539848b8605Smrg      ret = drmCommandWriteRead(fd, DRM_RADEON_INFO, &info, sizeof(info));
540848b8605Smrg      if (ret) {
541848b8605Smrg         log_(_LOADER_WARNING, "MESA-LOADER: failed to get info for radeon\n");
542848b8605Smrg	 *chip_id = -1;
543848b8605Smrg      }
544848b8605Smrg   }
545848b8605Smrg   else if (strcmp(version->name, "nouveau") == 0) {
546848b8605Smrg      *vendor_id = 0x10de;
547848b8605Smrg      /* not used */
548848b8605Smrg      *chip_id = 0;
549848b8605Smrg   }
550848b8605Smrg   else if (strcmp(version->name, "vmwgfx") == 0) {
551848b8605Smrg      *vendor_id = 0x15ad;
552848b8605Smrg      /* assume SVGA II */
553848b8605Smrg      *chip_id = 0x0405;
554848b8605Smrg   }
555848b8605Smrg
556848b8605Smrg   drmFreeVersion(version);
557848b8605Smrg
558848b8605Smrg   return (*chip_id >= 0);
559848b8605Smrg}
560848b8605Smrg#endif
561848b8605Smrg
562848b8605Smrg
563848b8605Smrgint
564848b8605Smrgloader_get_pci_id_for_fd(int fd, int *vendor_id, int *chip_id)
565848b8605Smrg{
566848b8605Smrg#if HAVE_LIBUDEV
567848b8605Smrg   if (libudev_get_pci_id_for_fd(fd, vendor_id, chip_id))
568848b8605Smrg      return 1;
569848b8605Smrg#endif
570848b8605Smrg#if HAVE_SYSFS
571848b8605Smrg   if (sysfs_get_pci_id_for_fd(fd, vendor_id, chip_id))
572848b8605Smrg      return 1;
573848b8605Smrg#endif
574848b8605Smrg#if !defined(__NOT_HAVE_DRM_H)
575848b8605Smrg   if (drm_get_pci_id_for_fd(fd, vendor_id, chip_id))
576848b8605Smrg      return 1;
577848b8605Smrg#endif
578848b8605Smrg   return 0;
579848b8605Smrg}
580848b8605Smrg
581848b8605Smrg
582848b8605Smrg#ifdef HAVE_LIBUDEV
583848b8605Smrgstatic char *
584848b8605Smrglibudev_get_device_name_for_fd(int fd)
585848b8605Smrg{
586848b8605Smrg   char *device_name = NULL;
587848b8605Smrg   struct udev *udev;
588848b8605Smrg   struct udev_device *device;
589848b8605Smrg   const char *const_device_name;
590848b8605Smrg   UDEV_SYMBOL(struct udev *, udev_new, (void));
591848b8605Smrg   UDEV_SYMBOL(const char *, udev_device_get_devnode,
592848b8605Smrg               (struct udev_device *));
593848b8605Smrg   UDEV_SYMBOL(struct udev_device *, udev_device_unref,
594848b8605Smrg               (struct udev_device *));
595848b8605Smrg   UDEV_SYMBOL(struct udev *, udev_unref, (struct udev *));
596848b8605Smrg
597848b8605Smrg   udev = udev_new();
598848b8605Smrg   device = udev_device_new_from_fd(udev, fd);
599848b8605Smrg   if (device == NULL)
600848b8605Smrg      return NULL;
601848b8605Smrg
602848b8605Smrg   const_device_name = udev_device_get_devnode(device);
603848b8605Smrg   if (!const_device_name)
604848b8605Smrg      goto out;
605848b8605Smrg   device_name = strdup(const_device_name);
606848b8605Smrg
607848b8605Smrgout:
608848b8605Smrg   udev_device_unref(device);
609848b8605Smrg   udev_unref(udev);
610848b8605Smrg   return device_name;
611848b8605Smrg}
612848b8605Smrg#endif
613848b8605Smrg
614848b8605Smrg
615848b8605Smrg#if HAVE_SYSFS
616848b8605Smrgstatic char *
617848b8605Smrgsysfs_get_device_name_for_fd(int fd)
618848b8605Smrg{
619848b8605Smrg   char *device_name = NULL;
620848b8605Smrg   unsigned int maj, min;
621848b8605Smrg   FILE *f;
622848b8605Smrg   char buf[0x40];
623848b8605Smrg   static const char match[9] = "\0DEVNAME=";
624848b8605Smrg   int expected = 1;
625848b8605Smrg
626848b8605Smrg   if (dev_node_from_fd(fd, &maj, &min) < 0)
627848b8605Smrg      return NULL;
628848b8605Smrg
629848b8605Smrg   snprintf(buf, sizeof(buf), "/sys/dev/char/%d:%d/uevent", maj, min);
630848b8605Smrg   if (!(f = fopen(buf, "r")))
631848b8605Smrg       return NULL;
632848b8605Smrg
633848b8605Smrg   while (expected < sizeof(match)) {
634848b8605Smrg      int c = getc(f);
635848b8605Smrg
636848b8605Smrg      if (c == EOF) {
637848b8605Smrg         fclose(f);
638848b8605Smrg         return NULL;
639848b8605Smrg      } else if (c == match[expected] )
640848b8605Smrg         expected++;
641848b8605Smrg      else
642848b8605Smrg         expected = 0;
643848b8605Smrg   }
644848b8605Smrg
645848b8605Smrg   strcpy(buf, "/dev/");
646848b8605Smrg   if (fgets(buf + 5, sizeof(buf) - 5, f))
647848b8605Smrg      device_name = strdup(buf);
648848b8605Smrg
649848b8605Smrg   fclose(f);
650848b8605Smrg   return device_name;
651848b8605Smrg}
652848b8605Smrg#endif
653848b8605Smrg
654848b8605Smrg
655848b8605Smrgchar *
656848b8605Smrgloader_get_device_name_for_fd(int fd)
657848b8605Smrg{
658848b8605Smrg   char *result = NULL;
659848b8605Smrg
660848b8605Smrg#if HAVE_LIBUDEV
661848b8605Smrg   if ((result = libudev_get_device_name_for_fd(fd)))
662848b8605Smrg      return result;
663848b8605Smrg#endif
664848b8605Smrg#if HAVE_SYSFS
665848b8605Smrg   if ((result = sysfs_get_device_name_for_fd(fd)))
666848b8605Smrg      return result;
667848b8605Smrg#endif
668848b8605Smrg   return result;
669848b8605Smrg}
670848b8605Smrg
671848b8605Smrgchar *
672848b8605Smrgloader_get_driver_for_fd(int fd, unsigned driver_types)
673848b8605Smrg{
674848b8605Smrg   int vendor_id, chip_id, i, j;
675848b8605Smrg   char *driver = NULL;
676848b8605Smrg
677848b8605Smrg   if (!driver_types)
678848b8605Smrg      driver_types = _LOADER_GALLIUM | _LOADER_DRI;
679848b8605Smrg
680848b8605Smrg   if (!loader_get_pci_id_for_fd(fd, &vendor_id, &chip_id)) {
681848b8605Smrg
682848b8605Smrg#ifndef __NOT_HAVE_DRM_H
683848b8605Smrg      /* fallback to drmGetVersion(): */
684848b8605Smrg      drmVersionPtr version = drmGetVersion(fd);
685848b8605Smrg
686848b8605Smrg      if (!version) {
687848b8605Smrg         log_(_LOADER_WARNING, "failed to get driver name for fd %d\n", fd);
688848b8605Smrg         return NULL;
689848b8605Smrg      }
690848b8605Smrg
691848b8605Smrg      driver = strndup(version->name, version->name_len);
692848b8605Smrg      log_(_LOADER_INFO, "using driver %s for %d\n", driver, fd);
693848b8605Smrg
694848b8605Smrg      drmFreeVersion(version);
695848b8605Smrg#endif
696848b8605Smrg
697848b8605Smrg      return driver;
698848b8605Smrg   }
699848b8605Smrg
700848b8605Smrg   for (i = 0; driver_map[i].driver; i++) {
701848b8605Smrg      if (vendor_id != driver_map[i].vendor_id)
702848b8605Smrg         continue;
703848b8605Smrg
704848b8605Smrg      if (!(driver_types & driver_map[i].driver_types))
705848b8605Smrg         continue;
706848b8605Smrg
707848b8605Smrg      if (driver_map[i].predicate && !driver_map[i].predicate(fd))
708848b8605Smrg         continue;
709848b8605Smrg
710848b8605Smrg      if (driver_map[i].num_chips_ids == -1) {
711848b8605Smrg         driver = strdup(driver_map[i].driver);
712848b8605Smrg         goto out;
713848b8605Smrg      }
714848b8605Smrg
715848b8605Smrg      for (j = 0; j < driver_map[i].num_chips_ids; j++)
716848b8605Smrg         if (driver_map[i].chip_ids[j] == chip_id) {
717848b8605Smrg            driver = strdup(driver_map[i].driver);
718848b8605Smrg            goto out;
719848b8605Smrg         }
720848b8605Smrg   }
721848b8605Smrg
722848b8605Smrgout:
723848b8605Smrg   log_(driver ? _LOADER_DEBUG : _LOADER_WARNING,
724848b8605Smrg         "pci id for fd %d: %04x:%04x, driver %s\n",
725848b8605Smrg         fd, vendor_id, chip_id, driver);
726848b8605Smrg   return driver;
727848b8605Smrg}
728848b8605Smrg
729848b8605Smrgvoid
730848b8605Smrgloader_set_logger(void (*logger)(int level, const char *fmt, ...))
731848b8605Smrg{
732848b8605Smrg   log_ = logger;
733848b8605Smrg}
734