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